3 """A basic HTTP proxy server that intercepts communication with
4 BitTorrent trackers and optionally spoofs the amount of data
5 uploaded. Free from artificial colours and preservatives. Web 2.0
19 class CheatHandler(BaseHTTPServer
.BaseHTTPRequestHandler
):
20 """Used by HTTPServer to handle HTTP requests"""
25 """Called by BaseHTTPRequestHandler when a GET request is
26 received from a client."""
28 cheatpath
= cheatbt
.cheat(self
.path
, CheatHandler
.mappings
)
30 logger
= logging
.getLogger("cheatproxy")
31 logger
.info(cheatpath
)
33 (scheme
, netloc
, path
, params
, query
, fragment
) = \
34 urlparse
.urlparse(cheatpath
, 'http')
36 # TODO: https support.
37 if scheme
!= 'http' or fragment
or not netloc
:
41 soc
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
44 if self
._connect
_to
(netloc
, soc
):
45 request
= urlparse
.urlunparse(('', '', path
, params
, query
, ''))
46 soc
.send("%s %s %s\r\n" % (self
.command
, request
,
47 self
.request_version
))
48 self
.headers
['Connection'] = 'close'
49 del self
.headers
['Proxy-Connection']
50 # This is naughty. But rfc822.Message, which self.headers is a
51 # subclass of, insists on converting headers to lowercase when
52 # accessed conventionally (i.e. as a dict).
53 for header
in self
.headers
.headers
:
54 soc
.send(header
.strip() + '\r\n')
55 logger
.debug(repr(header
))
60 self
.connection
.close()
62 def _connect_to(self
, netloc
, soc
):
63 """Attempt to establish a connection to the tracker."""
67 host_port
= (netloc
[:i
], int(netloc
[i
+1:]))
69 host_port
= (netloc
, 80)
72 soc
.connect(host_port
)
79 def _read_write(self
, soc
, max_idling
=20):
80 """Pass data between the remote server and the client. I
83 rlist
= [self
.connection
, soc
]
88 (ins
, _
, exs
) = select
.select(rlist
, wlist
, rlist
, 3)
103 if count
== max_idling
:
107 """Prints usage information and exits."""
110 usage: %s [-b host] [-p port] [-f file] [-v] [-d] [-h]
112 -b host IP or hostname to bind to. Default is localhost.
113 -p port Port to listen on. Default is 8000.
114 -f file Mappings file.
117 -h What you're reading.
125 rootlogger
= logging
.getLogger("")
126 ch
= logging
.StreamHandler()
128 logging
.Formatter("%(asctime)s:%(name)s:%(levelname)s %(message)s"))
129 rootlogger
.addHandler(ch
)
132 opts
, _
= getopt
.getopt(sys
.argv
[1:], "b:f:p:hvd")
133 except getopt
.GetoptError
:
136 for opt
, val
in opts
:
142 CheatHandler
.mappings
= cheatbt
.load_mappings(val
)
144 rootlogger
.setLevel(logging
.INFO
)
146 rootlogger
.setLevel(logging
.DEBUG
)
150 httpd
= BaseHTTPServer
.HTTPServer((host
, port
), CheatHandler
)
152 logger
= logging
.getLogger("cheatproxy")
153 logger
.info("listening on %s:%d" % (host
, port
))
155 httpd
.serve_forever()
157 if __name__
== "__main__":
160 except KeyboardInterrupt: