comparison tests/tinyproxy.py @ 2337:3f24bc5dee81

http: fix many problems with url parsing and auth. added proxy test. problems fixed: - https scheme handled properly for real and proxy urls. - url of form "http://user:password@host:port/path" now ok. - no-proxy check uses proper host names.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Mon, 22 May 2006 15:42:49 -0700
parents
children
comparison
equal deleted inserted replaced
2336:f77edcffb837 2337:3f24bc5dee81
1 #!/usr/bin/env python
2
3 __doc__ = """Tiny HTTP Proxy.
4
5 This module implements GET, HEAD, POST, PUT and DELETE methods
6 on BaseHTTPServer, and behaves as an HTTP proxy. The CONNECT
7 method is also implemented experimentally, but has not been
8 tested yet.
9
10 Any help will be greatly appreciated. SUZUKI Hisao
11 """
12
13 __version__ = "0.2.1"
14
15 import BaseHTTPServer, select, socket, SocketServer, urlparse
16
17 class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
18 __base = BaseHTTPServer.BaseHTTPRequestHandler
19 __base_handle = __base.handle
20
21 server_version = "TinyHTTPProxy/" + __version__
22 rbufsize = 0 # self.rfile Be unbuffered
23
24 def handle(self):
25 (ip, port) = self.client_address
26 if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
27 self.raw_requestline = self.rfile.readline()
28 if self.parse_request(): self.send_error(403)
29 else:
30 self.__base_handle()
31
32 def _connect_to(self, netloc, soc):
33 i = netloc.find(':')
34 if i >= 0:
35 host_port = netloc[:i], int(netloc[i+1:])
36 else:
37 host_port = netloc, 80
38 print "\t" "connect to %s:%d" % host_port
39 try: soc.connect(host_port)
40 except socket.error, arg:
41 try: msg = arg[1]
42 except: msg = arg
43 self.send_error(404, msg)
44 return 0
45 return 1
46
47 def do_CONNECT(self):
48 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
49 try:
50 if self._connect_to(self.path, soc):
51 self.log_request(200)
52 self.wfile.write(self.protocol_version +
53 " 200 Connection established\r\n")
54 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
55 self.wfile.write("\r\n")
56 self._read_write(soc, 300)
57 finally:
58 print "\t" "bye"
59 soc.close()
60 self.connection.close()
61
62 def do_GET(self):
63 (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
64 self.path, 'http')
65 if scm != 'http' or fragment or not netloc:
66 self.send_error(400, "bad url %s" % self.path)
67 return
68 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
69 try:
70 if self._connect_to(netloc, soc):
71 self.log_request()
72 soc.send("%s %s %s\r\n" % (
73 self.command,
74 urlparse.urlunparse(('', '', path, params, query, '')),
75 self.request_version))
76 self.headers['Connection'] = 'close'
77 del self.headers['Proxy-Connection']
78 for key_val in self.headers.items():
79 soc.send("%s: %s\r\n" % key_val)
80 soc.send("\r\n")
81 self._read_write(soc)
82 finally:
83 print "\t" "bye"
84 soc.close()
85 self.connection.close()
86
87 def _read_write(self, soc, max_idling=20):
88 iw = [self.connection, soc]
89 ow = []
90 count = 0
91 while 1:
92 count += 1
93 (ins, _, exs) = select.select(iw, ow, iw, 3)
94 if exs: break
95 if ins:
96 for i in ins:
97 if i is soc:
98 out = self.connection
99 else:
100 out = soc
101 data = i.recv(8192)
102 if data:
103 out.send(data)
104 count = 0
105 else:
106 print "\t" "idle", count
107 if count == max_idling: break
108
109 do_HEAD = do_GET
110 do_POST = do_GET
111 do_PUT = do_GET
112 do_DELETE=do_GET
113
114 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
115 BaseHTTPServer.HTTPServer): pass
116
117 if __name__ == '__main__':
118 from sys import argv
119 if argv[1:] and argv[1] in ('-h', '--help'):
120 print argv[0], "[port [allowed_client_name ...]]"
121 else:
122 if argv[2:]:
123 allowed = []
124 for name in argv[2:]:
125 client = socket.gethostbyname(name)
126 allowed.append(client)
127 print "Accept: %s (%s)" % (client, name)
128 ProxyHandler.allowed_clients = allowed
129 del argv[2:]
130 else:
131 print "Any clients will be served..."
132 BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer)