tinyproxy.py
157 lines
| 5.2 KiB
| text/x-python
|
PythonLexer
/ tests / tinyproxy.py
|
r2337 | #!/usr/bin/env python | ||
|
r28646 | from __future__ import absolute_import, print_function | ||
|
r27302 | |||
|
r2337 | __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" | ||||
|
r27302 | import BaseHTTPServer | ||
|
r28773 | import SocketServer | ||
|
r27302 | import os | ||
import select | ||||
import socket | ||||
import urlparse | ||||
|
r2337 | |||
class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler): | ||||
__base = BaseHTTPServer.BaseHTTPRequestHandler | ||||
__base_handle = __base.handle | ||||
server_version = "TinyHTTPProxy/" + __version__ | ||||
rbufsize = 0 # self.rfile Be unbuffered | ||||
def handle(self): | ||||
|
r27637 | (ip, port) = self.client_address | ||
|
r14971 | allowed = getattr(self, 'allowed_clients', None) | ||
if allowed is not None and ip not in allowed: | ||||
|
r2337 | self.raw_requestline = self.rfile.readline() | ||
|
r10282 | if self.parse_request(): | ||
self.send_error(403) | ||||
|
r2337 | else: | ||
self.__base_handle() | ||||
|
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)])) | ||||
|
r2337 | def _connect_to(self, netloc, soc): | ||
i = netloc.find(':') | ||||
if i >= 0: | ||||
|
r10282 | host_port = netloc[:i], int(netloc[i + 1:]) | ||
|
r2337 | else: | ||
host_port = netloc, 80 | ||||
|
r28646 | print("\t" "connect to %s:%d" % host_port) | ||
|
r2337 | try: soc.connect(host_port) | ||
|
r25660 | except socket.error as arg: | ||
|
r2337 | try: msg = arg[1] | ||
|
r16703 | except (IndexError, TypeError): msg = arg | ||
|
r2337 | 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: | ||||
|
r28646 | print("\t" "bye") | ||
|
r2337 | 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: | ||||
|
r28646 | print("\t" "bye") | ||
|
r2337 | soc.close() | ||
self.connection.close() | ||||
def _read_write(self, soc, max_idling=20): | ||||
iw = [self.connection, soc] | ||||
ow = [] | ||||
count = 0 | ||||
|
r14494 | while True: | ||
|
r2337 | count += 1 | ||
(ins, _, exs) = select.select(iw, ow, iw, 3) | ||||
|
r10282 | if exs: | ||
break | ||||
|
r2337 | if ins: | ||
for i in ins: | ||||
if i is soc: | ||||
out = self.connection | ||||
else: | ||||
out = soc | ||||
|
r18519 | try: | ||
data = i.recv(8192) | ||||
except socket.error: | ||||
break | ||||
|
r2337 | if data: | ||
out.send(data) | ||||
count = 0 | ||||
else: | ||||
|
r28646 | print("\t" "idle", count) | ||
|
r10282 | if count == max_idling: | ||
break | ||||
|
r2337 | |||
do_HEAD = do_GET | ||||
do_POST = do_GET | ||||
do_PUT = do_GET | ||||
|
r10282 | do_DELETE = do_GET | ||
|
r2337 | |||
class ThreadingHTTPServer (SocketServer.ThreadingMixIn, | ||||
|
r16300 | BaseHTTPServer.HTTPServer): | ||
def __init__(self, *args, **kwargs): | ||||
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs) | ||||
a = open("proxy.pid", "w") | ||||
a.write(str(os.getpid()) + "\n") | ||||
a.close() | ||||
|
r2337 | |||
if __name__ == '__main__': | ||||
from sys import argv | ||||
if argv[1:] and argv[1] in ('-h', '--help'): | ||||
|
r28646 | print(argv[0], "[port [allowed_client_name ...]]") | ||
|
r2337 | else: | ||
if argv[2:]: | ||||
allowed = [] | ||||
for name in argv[2:]: | ||||
client = socket.gethostbyname(name) | ||||
allowed.append(client) | ||||
|
r28646 | print("Accept: %s (%s)" % (client, name)) | ||
|
r2337 | ProxyHandler.allowed_clients = allowed | ||
del argv[2:] | ||||
else: | ||||
|
r28646 | print("Any clients will be served...") | ||
|
r2337 | BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer) | ||