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