##// END OF EJS Templates
py3: port tinyproxy.py to work with Python 3...
Gregory Szorc -
r41858:97e2442a default
parent child Browse files
Show More
@@ -1,192 +1,198 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 from __future__ import absolute_import, print_function
3 from __future__ import absolute_import, print_function
4
4
5 __doc__ = """Tiny HTTP Proxy.
5 __doc__ = """Tiny HTTP Proxy.
6
6
7 This module implements GET, HEAD, POST, PUT and DELETE methods
7 This module implements GET, HEAD, POST, PUT and DELETE methods
8 on BaseHTTPServer, and behaves as an HTTP proxy. The CONNECT
8 on BaseHTTPServer, and behaves as an HTTP proxy. The CONNECT
9 method is also implemented experimentally, but has not been
9 method is also implemented experimentally, but has not been
10 tested yet.
10 tested yet.
11
11
12 Any help will be greatly appreciated. SUZUKI Hisao
12 Any help will be greatly appreciated. SUZUKI Hisao
13 """
13 """
14
14
15 __version__ = "0.2.1"
15 __version__ = "0.2.1"
16
16
17 import optparse
17 import optparse
18 import os
18 import os
19 import select
19 import select
20 import socket
20 import socket
21 import sys
21 import sys
22
22
23 from mercurial import util
23 from mercurial import (
24 pycompat,
25 util,
26 )
24
27
25 httpserver = util.httpserver
28 httpserver = util.httpserver
26 socketserver = util.socketserver
29 socketserver = util.socketserver
27 urlreq = util.urlreq
30 urlreq = util.urlreq
28
31
29 if os.environ.get('HGIPV6', '0') == '1':
32 if os.environ.get('HGIPV6', '0') == '1':
30 family = socket.AF_INET6
33 family = socket.AF_INET6
31 else:
34 else:
32 family = socket.AF_INET
35 family = socket.AF_INET
33
36
34 class ProxyHandler (httpserver.basehttprequesthandler):
37 class ProxyHandler (httpserver.basehttprequesthandler):
35 __base = httpserver.basehttprequesthandler
38 __base = httpserver.basehttprequesthandler
36 __base_handle = __base.handle
39 __base_handle = __base.handle
37
40
38 server_version = "TinyHTTPProxy/" + __version__
41 server_version = "TinyHTTPProxy/" + __version__
39 rbufsize = 0 # self.rfile Be unbuffered
42 rbufsize = 0 # self.rfile Be unbuffered
40
43
41 def handle(self):
44 def handle(self):
42 (ip, port) = self.client_address
45 (ip, port) = self.client_address
43 allowed = getattr(self, 'allowed_clients', None)
46 allowed = getattr(self, 'allowed_clients', None)
44 if allowed is not None and ip not in allowed:
47 if allowed is not None and ip not in allowed:
45 self.raw_requestline = self.rfile.readline()
48 self.raw_requestline = self.rfile.readline()
46 if self.parse_request():
49 if self.parse_request():
47 self.send_error(403)
50 self.send_error(403)
48 else:
51 else:
49 self.__base_handle()
52 self.__base_handle()
50
53
51 def log_request(self, code='-', size='-'):
54 def log_request(self, code='-', size='-'):
52 xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
55 xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
53 self.log_message('"%s" %s %s%s',
56 self.log_message('"%s" %s %s%s',
54 self.requestline, str(code), str(size),
57 self.requestline, str(code), str(size),
55 ''.join([' %s:%s' % h for h in sorted(xheaders)]))
58 ''.join([' %s:%s' % h for h in sorted(xheaders)]))
56 # Flush for Windows, so output isn't lost on TerminateProcess()
59 # Flush for Windows, so output isn't lost on TerminateProcess()
57 sys.stdout.flush()
60 sys.stdout.flush()
58 sys.stderr.flush()
61 sys.stderr.flush()
59
62
60 def _connect_to(self, netloc, soc):
63 def _connect_to(self, netloc, soc):
61 i = netloc.find(':')
64 i = netloc.find(':')
62 if i >= 0:
65 if i >= 0:
63 host_port = netloc[:i], int(netloc[i + 1:])
66 host_port = netloc[:i], int(netloc[i + 1:])
64 else:
67 else:
65 host_port = netloc, 80
68 host_port = netloc, 80
66 print("\t" "connect to %s:%d" % host_port)
69 print("\t" "connect to %s:%d" % host_port)
67 try: soc.connect(host_port)
70 try: soc.connect(host_port)
68 except socket.error as arg:
71 except socket.error as arg:
69 try: msg = arg[1]
72 try: msg = arg[1]
70 except (IndexError, TypeError): msg = arg
73 except (IndexError, TypeError): msg = arg
71 self.send_error(404, msg)
74 self.send_error(404, msg)
72 return 0
75 return 0
73 return 1
76 return 1
74
77
75 def do_CONNECT(self):
78 def do_CONNECT(self):
76 soc = socket.socket(family, socket.SOCK_STREAM)
79 soc = socket.socket(family, socket.SOCK_STREAM)
77 try:
80 try:
78 if self._connect_to(self.path, soc):
81 if self._connect_to(self.path, soc):
79 self.log_request(200)
82 self.log_request(200)
80 self.wfile.write(self.protocol_version +
83 self.wfile.write(pycompat.bytestr(self.protocol_version) +
81 " 200 Connection established\r\n")
84 b" 200 Connection established\r\n")
82 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
85 self.wfile.write(b"Proxy-agent: %s\r\n" %
83 self.wfile.write("\r\n")
86 pycompat.bytestr(self.version_string()))
87 self.wfile.write(b"\r\n")
84 self._read_write(soc, 300)
88 self._read_write(soc, 300)
85 finally:
89 finally:
86 print("\t" "bye")
90 print("\t" "bye")
87 soc.close()
91 soc.close()
88 self.connection.close()
92 self.connection.close()
89
93
90 def do_GET(self):
94 def do_GET(self):
91 (scm, netloc, path, params, query, fragment) = urlreq.urlparse(
95 (scm, netloc, path, params, query, fragment) = urlreq.urlparse(
92 self.path, 'http')
96 self.path, 'http')
93 if scm != 'http' or fragment or not netloc:
97 if scm != 'http' or fragment or not netloc:
94 self.send_error(400, "bad url %s" % self.path)
98 self.send_error(400, "bad url %s" % self.path)
95 return
99 return
96 soc = socket.socket(family, socket.SOCK_STREAM)
100 soc = socket.socket(family, socket.SOCK_STREAM)
97 try:
101 try:
98 if self._connect_to(netloc, soc):
102 if self._connect_to(netloc, soc):
99 self.log_request()
103 self.log_request()
100 soc.send("%s %s %s\r\n" % (
104 url = urlreq.urlunparse(('', '', path, params, query, ''))
101 self.command,
105 soc.send(b"%s %s %s\r\n" % (
102 urlreq.urlunparse(('', '', path, params, query, '')),
106 pycompat.bytestr(self.command),
103 self.request_version))
107 pycompat.bytestr(url),
108 pycompat.bytestr(self.request_version)))
104 self.headers['Connection'] = 'close'
109 self.headers['Connection'] = 'close'
105 del self.headers['Proxy-Connection']
110 del self.headers['Proxy-Connection']
106 for key_val in self.headers.items():
111 for key, val in self.headers.items():
107 soc.send("%s: %s\r\n" % key_val)
112 soc.send(b"%s: %s\r\n" % (pycompat.bytestr(key),
108 soc.send("\r\n")
113 pycompat.bytestr(val)))
114 soc.send(b"\r\n")
109 self._read_write(soc)
115 self._read_write(soc)
110 finally:
116 finally:
111 print("\t" "bye")
117 print("\t" "bye")
112 soc.close()
118 soc.close()
113 self.connection.close()
119 self.connection.close()
114
120
115 def _read_write(self, soc, max_idling=20):
121 def _read_write(self, soc, max_idling=20):
116 iw = [self.connection, soc]
122 iw = [self.connection, soc]
117 ow = []
123 ow = []
118 count = 0
124 count = 0
119 while True:
125 while True:
120 count += 1
126 count += 1
121 (ins, _, exs) = select.select(iw, ow, iw, 3)
127 (ins, _, exs) = select.select(iw, ow, iw, 3)
122 if exs:
128 if exs:
123 break
129 break
124 if ins:
130 if ins:
125 for i in ins:
131 for i in ins:
126 if i is soc:
132 if i is soc:
127 out = self.connection
133 out = self.connection
128 else:
134 else:
129 out = soc
135 out = soc
130 try:
136 try:
131 data = i.recv(8192)
137 data = i.recv(8192)
132 except socket.error:
138 except socket.error:
133 break
139 break
134 if data:
140 if data:
135 out.send(data)
141 out.send(data)
136 count = 0
142 count = 0
137 else:
143 else:
138 print("\t" "idle", count)
144 print("\t" "idle", count)
139 if count == max_idling:
145 if count == max_idling:
140 break
146 break
141
147
142 do_HEAD = do_GET
148 do_HEAD = do_GET
143 do_POST = do_GET
149 do_POST = do_GET
144 do_PUT = do_GET
150 do_PUT = do_GET
145 do_DELETE = do_GET
151 do_DELETE = do_GET
146
152
147 class ThreadingHTTPServer (socketserver.ThreadingMixIn,
153 class ThreadingHTTPServer (socketserver.ThreadingMixIn,
148 httpserver.httpserver):
154 httpserver.httpserver):
149 def __init__(self, *args, **kwargs):
155 def __init__(self, *args, **kwargs):
150 httpserver.httpserver.__init__(self, *args, **kwargs)
156 httpserver.httpserver.__init__(self, *args, **kwargs)
151 a = open("proxy.pid", "w")
157 a = open("proxy.pid", "w")
152 a.write(str(os.getpid()) + "\n")
158 a.write(str(os.getpid()) + "\n")
153 a.close()
159 a.close()
154
160
155 def runserver(port=8000, bind=""):
161 def runserver(port=8000, bind=""):
156 server_address = (bind, port)
162 server_address = (bind, port)
157 ProxyHandler.protocol_version = "HTTP/1.0"
163 ProxyHandler.protocol_version = "HTTP/1.0"
158 httpd = ThreadingHTTPServer(server_address, ProxyHandler)
164 httpd = ThreadingHTTPServer(server_address, ProxyHandler)
159 sa = httpd.socket.getsockname()
165 sa = httpd.socket.getsockname()
160 print("Serving HTTP on", sa[0], "port", sa[1], "...")
166 print("Serving HTTP on", sa[0], "port", sa[1], "...")
161 try:
167 try:
162 httpd.serve_forever()
168 httpd.serve_forever()
163 except KeyboardInterrupt:
169 except KeyboardInterrupt:
164 print("\nKeyboard interrupt received, exiting.")
170 print("\nKeyboard interrupt received, exiting.")
165 httpd.server_close()
171 httpd.server_close()
166 sys.exit(0)
172 sys.exit(0)
167
173
168 if __name__ == '__main__':
174 if __name__ == '__main__':
169 argv = sys.argv
175 argv = sys.argv
170 if argv[1:] and argv[1] in ('-h', '--help'):
176 if argv[1:] and argv[1] in ('-h', '--help'):
171 print(argv[0], "[port [allowed_client_name ...]]")
177 print(argv[0], "[port [allowed_client_name ...]]")
172 else:
178 else:
173 if argv[2:]:
179 if argv[2:]:
174 allowed = []
180 allowed = []
175 for name in argv[2:]:
181 for name in argv[2:]:
176 client = socket.gethostbyname(name)
182 client = socket.gethostbyname(name)
177 allowed.append(client)
183 allowed.append(client)
178 print("Accept: %s (%s)" % (client, name))
184 print("Accept: %s (%s)" % (client, name))
179 ProxyHandler.allowed_clients = allowed
185 ProxyHandler.allowed_clients = allowed
180 del argv[2:]
186 del argv[2:]
181 else:
187 else:
182 print("Any clients will be served...")
188 print("Any clients will be served...")
183
189
184 parser = optparse.OptionParser()
190 parser = optparse.OptionParser()
185 parser.add_option('-b', '--bind', metavar='ADDRESS',
191 parser.add_option('-b', '--bind', metavar='ADDRESS',
186 help='Specify alternate bind address '
192 help='Specify alternate bind address '
187 '[default: all interfaces]', default='')
193 '[default: all interfaces]', default='')
188 (options, args) = parser.parse_args()
194 (options, args) = parser.parse_args()
189 port = 8000
195 port = 8000
190 if len(args) == 1:
196 if len(args) == 1:
191 port = int(args[0])
197 port = int(args[0])
192 runserver(port, options.bind)
198 runserver(port, options.bind)
General Comments 0
You need to be logged in to leave comments. Login now