tinyproxy.py
142 lines
| 4.8 KiB
| text/x-python
|
PythonLexer
/ tests / tinyproxy.py
Vadim Gelfer
|
r2337 | #!/usr/bin/env python | ||
__doc__ = """Tiny HTTP Proxy. | ||||
This module implements GET, HEAD, POST, PUT and DELETE methods | ||||
on BaseHTTPServer, and behaves as an HTTP proxy. The CONNECT | ||||
method is also implemented experimentally, but has not been | ||||
tested yet. | ||||
Any help will be greatly appreciated. SUZUKI Hisao | ||||
""" | ||||
__version__ = "0.2.1" | ||||
import BaseHTTPServer, select, socket, SocketServer, urlparse | ||||
class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler): | ||||
__base = BaseHTTPServer.BaseHTTPRequestHandler | ||||
__base_handle = __base.handle | ||||
server_version = "TinyHTTPProxy/" + __version__ | ||||
rbufsize = 0 # self.rfile Be unbuffered | ||||
def handle(self): | ||||
(ip, port) = self.client_address | ||||
Augie Fackler
|
r14971 | allowed = getattr(self, 'allowed_clients', None) | ||
if allowed is not None and ip not in allowed: | ||||
Vadim Gelfer
|
r2337 | self.raw_requestline = self.rfile.readline() | ||
Matt Mackall
|
r10282 | if self.parse_request(): | ||
self.send_error(403) | ||||
Vadim Gelfer
|
r2337 | else: | ||
self.__base_handle() | ||||
Steven Brown
|
r14093 | def log_request(self, code='-', size='-'): | ||
xheaders = [h for h in self.headers.items() if h[0].startswith('x-')] | ||||
self.log_message('"%s" %s %s%s', | ||||
self.requestline, str(code), str(size), | ||||
''.join([' %s:%s' % h for h in sorted(xheaders)])) | ||||
Vadim Gelfer
|
r2337 | def _connect_to(self, netloc, soc): | ||
i = netloc.find(':') | ||||
if i >= 0: | ||||
Matt Mackall
|
r10282 | host_port = netloc[:i], int(netloc[i + 1:]) | ||
Vadim Gelfer
|
r2337 | else: | ||
host_port = netloc, 80 | ||||
print "\t" "connect to %s:%d" % host_port | ||||
try: soc.connect(host_port) | ||||
except socket.error, arg: | ||||
try: msg = arg[1] | ||||
except: msg = arg | ||||
self.send_error(404, msg) | ||||
return 0 | ||||
return 1 | ||||
def do_CONNECT(self): | ||||
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
try: | ||||
if self._connect_to(self.path, soc): | ||||
self.log_request(200) | ||||
self.wfile.write(self.protocol_version + | ||||
" 200 Connection established\r\n") | ||||
self.wfile.write("Proxy-agent: %s\r\n" % self.version_string()) | ||||
self.wfile.write("\r\n") | ||||
self._read_write(soc, 300) | ||||
finally: | ||||
print "\t" "bye" | ||||
soc.close() | ||||
self.connection.close() | ||||
def do_GET(self): | ||||
(scm, netloc, path, params, query, fragment) = urlparse.urlparse( | ||||
self.path, 'http') | ||||
if scm != 'http' or fragment or not netloc: | ||||
self.send_error(400, "bad url %s" % self.path) | ||||
return | ||||
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
try: | ||||
if self._connect_to(netloc, soc): | ||||
self.log_request() | ||||
soc.send("%s %s %s\r\n" % ( | ||||
self.command, | ||||
urlparse.urlunparse(('', '', path, params, query, '')), | ||||
self.request_version)) | ||||
self.headers['Connection'] = 'close' | ||||
del self.headers['Proxy-Connection'] | ||||
for key_val in self.headers.items(): | ||||
soc.send("%s: %s\r\n" % key_val) | ||||
soc.send("\r\n") | ||||
self._read_write(soc) | ||||
finally: | ||||
print "\t" "bye" | ||||
soc.close() | ||||
self.connection.close() | ||||
def _read_write(self, soc, max_idling=20): | ||||
iw = [self.connection, soc] | ||||
ow = [] | ||||
count = 0 | ||||
Martin Geisler
|
r14494 | while True: | ||
Vadim Gelfer
|
r2337 | count += 1 | ||
(ins, _, exs) = select.select(iw, ow, iw, 3) | ||||
Matt Mackall
|
r10282 | if exs: | ||
break | ||||
Vadim Gelfer
|
r2337 | if ins: | ||
for i in ins: | ||||
if i is soc: | ||||
out = self.connection | ||||
else: | ||||
out = soc | ||||
data = i.recv(8192) | ||||
if data: | ||||
out.send(data) | ||||
count = 0 | ||||
else: | ||||
print "\t" "idle", count | ||||
Matt Mackall
|
r10282 | if count == max_idling: | ||
break | ||||
Vadim Gelfer
|
r2337 | |||
do_HEAD = do_GET | ||||
do_POST = do_GET | ||||
do_PUT = do_GET | ||||
Matt Mackall
|
r10282 | do_DELETE = do_GET | ||
Vadim Gelfer
|
r2337 | |||
class ThreadingHTTPServer (SocketServer.ThreadingMixIn, | ||||
BaseHTTPServer.HTTPServer): pass | ||||
if __name__ == '__main__': | ||||
from sys import argv | ||||
if argv[1:] and argv[1] in ('-h', '--help'): | ||||
print argv[0], "[port [allowed_client_name ...]]" | ||||
else: | ||||
if argv[2:]: | ||||
allowed = [] | ||||
for name in argv[2:]: | ||||
client = socket.gethostbyname(name) | ||||
allowed.append(client) | ||||
print "Accept: %s (%s)" % (client, name) | ||||
ProxyHandler.allowed_clients = allowed | ||||
del argv[2:] | ||||
else: | ||||
print "Any clients will be served..." | ||||
BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer) | ||||