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