##// END OF EJS Templates
py3: compare http server's command with a native string...
Denis Laxalde -
r43793:92e24a13 stable
parent child Browse files
Show More
@@ -1,442 +1,442 b''
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import importlib
12 import importlib
13 import os
13 import os
14 import socket
14 import socket
15 import sys
15 import sys
16 import traceback
16 import traceback
17 import wsgiref.validate
17 import wsgiref.validate
18
18
19 from ..i18n import _
19 from ..i18n import _
20 from ..pycompat import (
20 from ..pycompat import (
21 getattr,
21 getattr,
22 open,
22 open,
23 )
23 )
24
24
25 from .. import (
25 from .. import (
26 encoding,
26 encoding,
27 error,
27 error,
28 pycompat,
28 pycompat,
29 util,
29 util,
30 )
30 )
31
31
32 httpservermod = util.httpserver
32 httpservermod = util.httpserver
33 socketserver = util.socketserver
33 socketserver = util.socketserver
34 urlerr = util.urlerr
34 urlerr = util.urlerr
35 urlreq = util.urlreq
35 urlreq = util.urlreq
36
36
37 from . import common
37 from . import common
38
38
39
39
40 def _splitURI(uri):
40 def _splitURI(uri):
41 """Return path and query that has been split from uri
41 """Return path and query that has been split from uri
42
42
43 Just like CGI environment, the path is unquoted, the query is
43 Just like CGI environment, the path is unquoted, the query is
44 not.
44 not.
45 """
45 """
46 if r'?' in uri:
46 if r'?' in uri:
47 path, query = uri.split(r'?', 1)
47 path, query = uri.split(r'?', 1)
48 else:
48 else:
49 path, query = uri, r''
49 path, query = uri, r''
50 return urlreq.unquote(path), query
50 return urlreq.unquote(path), query
51
51
52
52
53 class _error_logger(object):
53 class _error_logger(object):
54 def __init__(self, handler):
54 def __init__(self, handler):
55 self.handler = handler
55 self.handler = handler
56
56
57 def flush(self):
57 def flush(self):
58 pass
58 pass
59
59
60 def write(self, str):
60 def write(self, str):
61 self.writelines(str.split(b'\n'))
61 self.writelines(str.split(b'\n'))
62
62
63 def writelines(self, seq):
63 def writelines(self, seq):
64 for msg in seq:
64 for msg in seq:
65 self.handler.log_error(r"HG error: %s", encoding.strfromlocal(msg))
65 self.handler.log_error(r"HG error: %s", encoding.strfromlocal(msg))
66
66
67
67
68 class _httprequesthandler(httpservermod.basehttprequesthandler):
68 class _httprequesthandler(httpservermod.basehttprequesthandler):
69
69
70 url_scheme = b'http'
70 url_scheme = b'http'
71
71
72 @staticmethod
72 @staticmethod
73 def preparehttpserver(httpserver, ui):
73 def preparehttpserver(httpserver, ui):
74 """Prepare .socket of new HTTPServer instance"""
74 """Prepare .socket of new HTTPServer instance"""
75
75
76 def __init__(self, *args, **kargs):
76 def __init__(self, *args, **kargs):
77 self.protocol_version = r'HTTP/1.1'
77 self.protocol_version = r'HTTP/1.1'
78 httpservermod.basehttprequesthandler.__init__(self, *args, **kargs)
78 httpservermod.basehttprequesthandler.__init__(self, *args, **kargs)
79
79
80 def _log_any(self, fp, format, *args):
80 def _log_any(self, fp, format, *args):
81 fp.write(
81 fp.write(
82 pycompat.sysbytes(
82 pycompat.sysbytes(
83 r"%s - - [%s] %s"
83 r"%s - - [%s] %s"
84 % (
84 % (
85 self.client_address[0],
85 self.client_address[0],
86 self.log_date_time_string(),
86 self.log_date_time_string(),
87 format % args,
87 format % args,
88 )
88 )
89 )
89 )
90 + b'\n'
90 + b'\n'
91 )
91 )
92 fp.flush()
92 fp.flush()
93
93
94 def log_error(self, format, *args):
94 def log_error(self, format, *args):
95 self._log_any(self.server.errorlog, format, *args)
95 self._log_any(self.server.errorlog, format, *args)
96
96
97 def log_message(self, format, *args):
97 def log_message(self, format, *args):
98 self._log_any(self.server.accesslog, format, *args)
98 self._log_any(self.server.accesslog, format, *args)
99
99
100 def log_request(self, code=r'-', size=r'-'):
100 def log_request(self, code=r'-', size=r'-'):
101 xheaders = []
101 xheaders = []
102 if util.safehasattr(self, b'headers'):
102 if util.safehasattr(self, b'headers'):
103 xheaders = [
103 xheaders = [
104 h for h in self.headers.items() if h[0].startswith(r'x-')
104 h for h in self.headers.items() if h[0].startswith(r'x-')
105 ]
105 ]
106 self.log_message(
106 self.log_message(
107 r'"%s" %s %s%s',
107 r'"%s" %s %s%s',
108 self.requestline,
108 self.requestline,
109 str(code),
109 str(code),
110 str(size),
110 str(size),
111 r''.join([r' %s:%s' % h for h in sorted(xheaders)]),
111 r''.join([r' %s:%s' % h for h in sorted(xheaders)]),
112 )
112 )
113
113
114 def do_write(self):
114 def do_write(self):
115 try:
115 try:
116 self.do_hgweb()
116 self.do_hgweb()
117 except socket.error as inst:
117 except socket.error as inst:
118 if inst.errno != errno.EPIPE:
118 if inst.errno != errno.EPIPE:
119 raise
119 raise
120
120
121 def do_POST(self):
121 def do_POST(self):
122 try:
122 try:
123 self.do_write()
123 self.do_write()
124 except Exception as e:
124 except Exception as e:
125 # I/O below could raise another exception. So log the original
125 # I/O below could raise another exception. So log the original
126 # exception first to ensure it is recorded.
126 # exception first to ensure it is recorded.
127 if not (
127 if not (
128 isinstance(e, (OSError, socket.error))
128 isinstance(e, (OSError, socket.error))
129 and e.errno == errno.ECONNRESET
129 and e.errno == errno.ECONNRESET
130 ):
130 ):
131 tb = r"".join(traceback.format_exception(*sys.exc_info()))
131 tb = r"".join(traceback.format_exception(*sys.exc_info()))
132 # We need a native-string newline to poke in the log
132 # We need a native-string newline to poke in the log
133 # message, because we won't get a newline when using an
133 # message, because we won't get a newline when using an
134 # r-string. This is the easy way out.
134 # r-string. This is the easy way out.
135 newline = chr(10)
135 newline = chr(10)
136 self.log_error(
136 self.log_error(
137 r"Exception happened during processing "
137 r"Exception happened during processing "
138 r"request '%s':%s%s",
138 r"request '%s':%s%s",
139 self.path,
139 self.path,
140 newline,
140 newline,
141 tb,
141 tb,
142 )
142 )
143
143
144 self._start_response(r"500 Internal Server Error", [])
144 self._start_response(r"500 Internal Server Error", [])
145 self._write(b"Internal Server Error")
145 self._write(b"Internal Server Error")
146 self._done()
146 self._done()
147
147
148 def do_PUT(self):
148 def do_PUT(self):
149 self.do_POST()
149 self.do_POST()
150
150
151 def do_GET(self):
151 def do_GET(self):
152 self.do_POST()
152 self.do_POST()
153
153
154 def do_hgweb(self):
154 def do_hgweb(self):
155 self.sent_headers = False
155 self.sent_headers = False
156 path, query = _splitURI(self.path)
156 path, query = _splitURI(self.path)
157
157
158 # Ensure the slicing of path below is valid
158 # Ensure the slicing of path below is valid
159 if path != self.server.prefix and not path.startswith(
159 if path != self.server.prefix and not path.startswith(
160 self.server.prefix + b'/'
160 self.server.prefix + b'/'
161 ):
161 ):
162 self._start_response(pycompat.strurl(common.statusmessage(404)), [])
162 self._start_response(pycompat.strurl(common.statusmessage(404)), [])
163 if self.command == b'POST':
163 if self.command == r'POST':
164 # Paranoia: tell the client we're going to close the
164 # Paranoia: tell the client we're going to close the
165 # socket so they don't try and reuse a socket that
165 # socket so they don't try and reuse a socket that
166 # might have a POST body waiting to confuse us. We do
166 # might have a POST body waiting to confuse us. We do
167 # this by directly munging self.saved_headers because
167 # this by directly munging self.saved_headers because
168 # self._start_response ignores Connection headers.
168 # self._start_response ignores Connection headers.
169 self.saved_headers = [(r'Connection', r'Close')]
169 self.saved_headers = [(r'Connection', r'Close')]
170 self._write(b"Not Found")
170 self._write(b"Not Found")
171 self._done()
171 self._done()
172 return
172 return
173
173
174 env = {}
174 env = {}
175 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
175 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
176 env[r'REQUEST_METHOD'] = self.command
176 env[r'REQUEST_METHOD'] = self.command
177 env[r'SERVER_NAME'] = self.server.server_name
177 env[r'SERVER_NAME'] = self.server.server_name
178 env[r'SERVER_PORT'] = str(self.server.server_port)
178 env[r'SERVER_PORT'] = str(self.server.server_port)
179 env[r'REQUEST_URI'] = self.path
179 env[r'REQUEST_URI'] = self.path
180 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
180 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
181 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :])
181 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :])
182 env[r'REMOTE_HOST'] = self.client_address[0]
182 env[r'REMOTE_HOST'] = self.client_address[0]
183 env[r'REMOTE_ADDR'] = self.client_address[0]
183 env[r'REMOTE_ADDR'] = self.client_address[0]
184 env[r'QUERY_STRING'] = query or r''
184 env[r'QUERY_STRING'] = query or r''
185
185
186 if pycompat.ispy3:
186 if pycompat.ispy3:
187 if self.headers.get_content_type() is None:
187 if self.headers.get_content_type() is None:
188 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
188 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
189 else:
189 else:
190 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
190 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
191 length = self.headers.get(r'content-length')
191 length = self.headers.get(r'content-length')
192 else:
192 else:
193 if self.headers.typeheader is None:
193 if self.headers.typeheader is None:
194 env[r'CONTENT_TYPE'] = self.headers.type
194 env[r'CONTENT_TYPE'] = self.headers.type
195 else:
195 else:
196 env[r'CONTENT_TYPE'] = self.headers.typeheader
196 env[r'CONTENT_TYPE'] = self.headers.typeheader
197 length = self.headers.getheader(r'content-length')
197 length = self.headers.getheader(r'content-length')
198 if length:
198 if length:
199 env[r'CONTENT_LENGTH'] = length
199 env[r'CONTENT_LENGTH'] = length
200 for header in [
200 for header in [
201 h
201 h
202 for h in self.headers.keys()
202 for h in self.headers.keys()
203 if h.lower() not in (r'content-type', r'content-length')
203 if h.lower() not in (r'content-type', r'content-length')
204 ]:
204 ]:
205 hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
205 hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
206 hval = self.headers.get(header)
206 hval = self.headers.get(header)
207 hval = hval.replace(r'\n', r'').strip()
207 hval = hval.replace(r'\n', r'').strip()
208 if hval:
208 if hval:
209 env[hkey] = hval
209 env[hkey] = hval
210 env[r'SERVER_PROTOCOL'] = self.request_version
210 env[r'SERVER_PROTOCOL'] = self.request_version
211 env[r'wsgi.version'] = (1, 0)
211 env[r'wsgi.version'] = (1, 0)
212 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
212 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
213 if env.get(r'HTTP_EXPECT', b'').lower() == b'100-continue':
213 if env.get(r'HTTP_EXPECT', b'').lower() == b'100-continue':
214 self.rfile = common.continuereader(self.rfile, self.wfile.write)
214 self.rfile = common.continuereader(self.rfile, self.wfile.write)
215
215
216 env[r'wsgi.input'] = self.rfile
216 env[r'wsgi.input'] = self.rfile
217 env[r'wsgi.errors'] = _error_logger(self)
217 env[r'wsgi.errors'] = _error_logger(self)
218 env[r'wsgi.multithread'] = isinstance(
218 env[r'wsgi.multithread'] = isinstance(
219 self.server, socketserver.ThreadingMixIn
219 self.server, socketserver.ThreadingMixIn
220 )
220 )
221 if util.safehasattr(socketserver, b'ForkingMixIn'):
221 if util.safehasattr(socketserver, b'ForkingMixIn'):
222 env[r'wsgi.multiprocess'] = isinstance(
222 env[r'wsgi.multiprocess'] = isinstance(
223 self.server, socketserver.ForkingMixIn
223 self.server, socketserver.ForkingMixIn
224 )
224 )
225 else:
225 else:
226 env[r'wsgi.multiprocess'] = False
226 env[r'wsgi.multiprocess'] = False
227
227
228 env[r'wsgi.run_once'] = 0
228 env[r'wsgi.run_once'] = 0
229
229
230 wsgiref.validate.check_environ(env)
230 wsgiref.validate.check_environ(env)
231
231
232 self.saved_status = None
232 self.saved_status = None
233 self.saved_headers = []
233 self.saved_headers = []
234 self.length = None
234 self.length = None
235 self._chunked = None
235 self._chunked = None
236 for chunk in self.server.application(env, self._start_response):
236 for chunk in self.server.application(env, self._start_response):
237 self._write(chunk)
237 self._write(chunk)
238 if not self.sent_headers:
238 if not self.sent_headers:
239 self.send_headers()
239 self.send_headers()
240 self._done()
240 self._done()
241
241
242 def send_headers(self):
242 def send_headers(self):
243 if not self.saved_status:
243 if not self.saved_status:
244 raise AssertionError(
244 raise AssertionError(
245 b"Sending headers before start_response() called"
245 b"Sending headers before start_response() called"
246 )
246 )
247 saved_status = self.saved_status.split(None, 1)
247 saved_status = self.saved_status.split(None, 1)
248 saved_status[0] = int(saved_status[0])
248 saved_status[0] = int(saved_status[0])
249 self.send_response(*saved_status)
249 self.send_response(*saved_status)
250 self.length = None
250 self.length = None
251 self._chunked = False
251 self._chunked = False
252 for h in self.saved_headers:
252 for h in self.saved_headers:
253 self.send_header(*h)
253 self.send_header(*h)
254 if h[0].lower() == r'content-length':
254 if h[0].lower() == r'content-length':
255 self.length = int(h[1])
255 self.length = int(h[1])
256 if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED:
256 if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED:
257 self._chunked = (
257 self._chunked = (
258 not self.close_connection
258 not self.close_connection
259 and self.request_version == r'HTTP/1.1'
259 and self.request_version == r'HTTP/1.1'
260 )
260 )
261 if self._chunked:
261 if self._chunked:
262 self.send_header(r'Transfer-Encoding', r'chunked')
262 self.send_header(r'Transfer-Encoding', r'chunked')
263 else:
263 else:
264 self.send_header(r'Connection', r'close')
264 self.send_header(r'Connection', r'close')
265 self.end_headers()
265 self.end_headers()
266 self.sent_headers = True
266 self.sent_headers = True
267
267
268 def _start_response(self, http_status, headers, exc_info=None):
268 def _start_response(self, http_status, headers, exc_info=None):
269 assert isinstance(http_status, str)
269 assert isinstance(http_status, str)
270 code, msg = http_status.split(None, 1)
270 code, msg = http_status.split(None, 1)
271 code = int(code)
271 code = int(code)
272 self.saved_status = http_status
272 self.saved_status = http_status
273 bad_headers = (r'connection', r'transfer-encoding')
273 bad_headers = (r'connection', r'transfer-encoding')
274 self.saved_headers = [
274 self.saved_headers = [
275 h for h in headers if h[0].lower() not in bad_headers
275 h for h in headers if h[0].lower() not in bad_headers
276 ]
276 ]
277 return self._write
277 return self._write
278
278
279 def _write(self, data):
279 def _write(self, data):
280 if not self.saved_status:
280 if not self.saved_status:
281 raise AssertionError(b"data written before start_response() called")
281 raise AssertionError(b"data written before start_response() called")
282 elif not self.sent_headers:
282 elif not self.sent_headers:
283 self.send_headers()
283 self.send_headers()
284 if self.length is not None:
284 if self.length is not None:
285 if len(data) > self.length:
285 if len(data) > self.length:
286 raise AssertionError(
286 raise AssertionError(
287 b"Content-length header sent, but more "
287 b"Content-length header sent, but more "
288 b"bytes than specified are being written."
288 b"bytes than specified are being written."
289 )
289 )
290 self.length = self.length - len(data)
290 self.length = self.length - len(data)
291 elif self._chunked and data:
291 elif self._chunked and data:
292 data = b'%x\r\n%s\r\n' % (len(data), data)
292 data = b'%x\r\n%s\r\n' % (len(data), data)
293 self.wfile.write(data)
293 self.wfile.write(data)
294 self.wfile.flush()
294 self.wfile.flush()
295
295
296 def _done(self):
296 def _done(self):
297 if self._chunked:
297 if self._chunked:
298 self.wfile.write(b'0\r\n\r\n')
298 self.wfile.write(b'0\r\n\r\n')
299 self.wfile.flush()
299 self.wfile.flush()
300
300
301 def version_string(self):
301 def version_string(self):
302 if self.server.serverheader:
302 if self.server.serverheader:
303 return encoding.strfromlocal(self.server.serverheader)
303 return encoding.strfromlocal(self.server.serverheader)
304 return httpservermod.basehttprequesthandler.version_string(self)
304 return httpservermod.basehttprequesthandler.version_string(self)
305
305
306
306
307 class _httprequesthandlerssl(_httprequesthandler):
307 class _httprequesthandlerssl(_httprequesthandler):
308 """HTTPS handler based on Python's ssl module"""
308 """HTTPS handler based on Python's ssl module"""
309
309
310 url_scheme = b'https'
310 url_scheme = b'https'
311
311
312 @staticmethod
312 @staticmethod
313 def preparehttpserver(httpserver, ui):
313 def preparehttpserver(httpserver, ui):
314 try:
314 try:
315 from .. import sslutil
315 from .. import sslutil
316
316
317 sslutil.modernssl
317 sslutil.modernssl
318 except ImportError:
318 except ImportError:
319 raise error.Abort(_(b"SSL support is unavailable"))
319 raise error.Abort(_(b"SSL support is unavailable"))
320
320
321 certfile = ui.config(b'web', b'certificate')
321 certfile = ui.config(b'web', b'certificate')
322
322
323 # These config options are currently only meant for testing. Use
323 # These config options are currently only meant for testing. Use
324 # at your own risk.
324 # at your own risk.
325 cafile = ui.config(b'devel', b'servercafile')
325 cafile = ui.config(b'devel', b'servercafile')
326 reqcert = ui.configbool(b'devel', b'serverrequirecert')
326 reqcert = ui.configbool(b'devel', b'serverrequirecert')
327
327
328 httpserver.socket = sslutil.wrapserversocket(
328 httpserver.socket = sslutil.wrapserversocket(
329 httpserver.socket,
329 httpserver.socket,
330 ui,
330 ui,
331 certfile=certfile,
331 certfile=certfile,
332 cafile=cafile,
332 cafile=cafile,
333 requireclientcert=reqcert,
333 requireclientcert=reqcert,
334 )
334 )
335
335
336 def setup(self):
336 def setup(self):
337 self.connection = self.request
337 self.connection = self.request
338 self.rfile = self.request.makefile(r"rb", self.rbufsize)
338 self.rfile = self.request.makefile(r"rb", self.rbufsize)
339 self.wfile = self.request.makefile(r"wb", self.wbufsize)
339 self.wfile = self.request.makefile(r"wb", self.wbufsize)
340
340
341
341
342 try:
342 try:
343 import threading
343 import threading
344
344
345 threading.activeCount() # silence pyflakes and bypass demandimport
345 threading.activeCount() # silence pyflakes and bypass demandimport
346 _mixin = socketserver.ThreadingMixIn
346 _mixin = socketserver.ThreadingMixIn
347 except ImportError:
347 except ImportError:
348 if util.safehasattr(os, b"fork"):
348 if util.safehasattr(os, b"fork"):
349 _mixin = socketserver.ForkingMixIn
349 _mixin = socketserver.ForkingMixIn
350 else:
350 else:
351
351
352 class _mixin(object):
352 class _mixin(object):
353 pass
353 pass
354
354
355
355
356 def openlog(opt, default):
356 def openlog(opt, default):
357 if opt and opt != b'-':
357 if opt and opt != b'-':
358 return open(opt, b'ab')
358 return open(opt, b'ab')
359 return default
359 return default
360
360
361
361
362 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
362 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
363
363
364 # SO_REUSEADDR has broken semantics on windows
364 # SO_REUSEADDR has broken semantics on windows
365 if pycompat.iswindows:
365 if pycompat.iswindows:
366 allow_reuse_address = 0
366 allow_reuse_address = 0
367
367
368 def __init__(self, ui, app, addr, handler, **kwargs):
368 def __init__(self, ui, app, addr, handler, **kwargs):
369 httpservermod.httpserver.__init__(self, addr, handler, **kwargs)
369 httpservermod.httpserver.__init__(self, addr, handler, **kwargs)
370 self.daemon_threads = True
370 self.daemon_threads = True
371 self.application = app
371 self.application = app
372
372
373 handler.preparehttpserver(self, ui)
373 handler.preparehttpserver(self, ui)
374
374
375 prefix = ui.config(b'web', b'prefix')
375 prefix = ui.config(b'web', b'prefix')
376 if prefix:
376 if prefix:
377 prefix = b'/' + prefix.strip(b'/')
377 prefix = b'/' + prefix.strip(b'/')
378 self.prefix = prefix
378 self.prefix = prefix
379
379
380 alog = openlog(ui.config(b'web', b'accesslog'), ui.fout)
380 alog = openlog(ui.config(b'web', b'accesslog'), ui.fout)
381 elog = openlog(ui.config(b'web', b'errorlog'), ui.ferr)
381 elog = openlog(ui.config(b'web', b'errorlog'), ui.ferr)
382 self.accesslog = alog
382 self.accesslog = alog
383 self.errorlog = elog
383 self.errorlog = elog
384
384
385 self.addr, self.port = self.socket.getsockname()[0:2]
385 self.addr, self.port = self.socket.getsockname()[0:2]
386 self.fqaddr = socket.getfqdn(addr[0])
386 self.fqaddr = socket.getfqdn(addr[0])
387
387
388 self.serverheader = ui.config(b'web', b'server-header')
388 self.serverheader = ui.config(b'web', b'server-header')
389
389
390
390
391 class IPv6HTTPServer(MercurialHTTPServer):
391 class IPv6HTTPServer(MercurialHTTPServer):
392 address_family = getattr(socket, 'AF_INET6', None)
392 address_family = getattr(socket, 'AF_INET6', None)
393
393
394 def __init__(self, *args, **kwargs):
394 def __init__(self, *args, **kwargs):
395 if self.address_family is None:
395 if self.address_family is None:
396 raise error.RepoError(_(b'IPv6 is not available on this system'))
396 raise error.RepoError(_(b'IPv6 is not available on this system'))
397 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
397 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
398
398
399
399
400 def create_server(ui, app):
400 def create_server(ui, app):
401
401
402 if ui.config(b'web', b'certificate'):
402 if ui.config(b'web', b'certificate'):
403 handler = _httprequesthandlerssl
403 handler = _httprequesthandlerssl
404 else:
404 else:
405 handler = _httprequesthandler
405 handler = _httprequesthandler
406
406
407 if ui.configbool(b'web', b'ipv6'):
407 if ui.configbool(b'web', b'ipv6'):
408 cls = IPv6HTTPServer
408 cls = IPv6HTTPServer
409 else:
409 else:
410 cls = MercurialHTTPServer
410 cls = MercurialHTTPServer
411
411
412 # ugly hack due to python issue5853 (for threaded use)
412 # ugly hack due to python issue5853 (for threaded use)
413 try:
413 try:
414 import mimetypes
414 import mimetypes
415
415
416 mimetypes.init()
416 mimetypes.init()
417 except UnicodeDecodeError:
417 except UnicodeDecodeError:
418 # Python 2.x's mimetypes module attempts to decode strings
418 # Python 2.x's mimetypes module attempts to decode strings
419 # from Windows' ANSI APIs as ascii (fail), then re-encode them
419 # from Windows' ANSI APIs as ascii (fail), then re-encode them
420 # as ascii (clown fail), because the default Python Unicode
420 # as ascii (clown fail), because the default Python Unicode
421 # codec is hardcoded as ascii.
421 # codec is hardcoded as ascii.
422
422
423 sys.argv # unwrap demand-loader so that reload() works
423 sys.argv # unwrap demand-loader so that reload() works
424 # resurrect sys.setdefaultencoding()
424 # resurrect sys.setdefaultencoding()
425 try:
425 try:
426 importlib.reload(sys)
426 importlib.reload(sys)
427 except AttributeError:
427 except AttributeError:
428 reload(sys)
428 reload(sys)
429 oldenc = sys.getdefaultencoding()
429 oldenc = sys.getdefaultencoding()
430 sys.setdefaultencoding(b"latin1") # or any full 8-bit encoding
430 sys.setdefaultencoding(b"latin1") # or any full 8-bit encoding
431 mimetypes.init()
431 mimetypes.init()
432 sys.setdefaultencoding(oldenc)
432 sys.setdefaultencoding(oldenc)
433
433
434 address = ui.config(b'web', b'address')
434 address = ui.config(b'web', b'address')
435 port = util.getport(ui.config(b'web', b'port'))
435 port = util.getport(ui.config(b'web', b'port'))
436 try:
436 try:
437 return cls(ui, app, (address, port), handler)
437 return cls(ui, app, (address, port), handler)
438 except socket.error as inst:
438 except socket.error as inst:
439 raise error.Abort(
439 raise error.Abort(
440 _(b"cannot start server at '%s:%d': %s")
440 _(b"cannot start server at '%s:%d': %s")
441 % (address, port, encoding.strtolocal(inst.args[1]))
441 % (address, port, encoding.strtolocal(inst.args[1]))
442 )
442 )
General Comments 0
You need to be logged in to leave comments. Login now