##// END OF EJS Templates
server: always close http socket if responding with an error (issue6033)...
Augie Fackler -
r41544:197f092b 4.8.2 stable
parent child Browse files
Show More
@@ -1,373 +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[0] != errno.EPIPE:
97 if inst[0] != 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':
131 # Paranoia: tell the client we're going to close the
132 # socket so they don't try and reuse a socket that
133 # might have a POST body waiting to confuse us. We do
134 # this by directly munging self.saved_headers because
135 # self._start_response ignores Connection headers.
136 self.saved_headers = [(r'Connection', r'Close')]
130 self._write(b"Not Found")
137 self._write(b"Not Found")
131 self._done()
138 self._done()
132 return
139 return
133
140
134 env = {}
141 env = {}
135 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
142 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
136 env[r'REQUEST_METHOD'] = self.command
143 env[r'REQUEST_METHOD'] = self.command
137 env[r'SERVER_NAME'] = self.server.server_name
144 env[r'SERVER_NAME'] = self.server.server_name
138 env[r'SERVER_PORT'] = str(self.server.server_port)
145 env[r'SERVER_PORT'] = str(self.server.server_port)
139 env[r'REQUEST_URI'] = self.path
146 env[r'REQUEST_URI'] = self.path
140 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
147 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
141 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix):])
148 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix):])
142 env[r'REMOTE_HOST'] = self.client_address[0]
149 env[r'REMOTE_HOST'] = self.client_address[0]
143 env[r'REMOTE_ADDR'] = self.client_address[0]
150 env[r'REMOTE_ADDR'] = self.client_address[0]
144 env[r'QUERY_STRING'] = query or r''
151 env[r'QUERY_STRING'] = query or r''
145
152
146 if pycompat.ispy3:
153 if pycompat.ispy3:
147 if self.headers.get_content_type() is None:
154 if self.headers.get_content_type() is None:
148 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
155 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
149 else:
156 else:
150 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
157 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
151 length = self.headers.get(r'content-length')
158 length = self.headers.get(r'content-length')
152 else:
159 else:
153 if self.headers.typeheader is None:
160 if self.headers.typeheader is None:
154 env[r'CONTENT_TYPE'] = self.headers.type
161 env[r'CONTENT_TYPE'] = self.headers.type
155 else:
162 else:
156 env[r'CONTENT_TYPE'] = self.headers.typeheader
163 env[r'CONTENT_TYPE'] = self.headers.typeheader
157 length = self.headers.getheader(r'content-length')
164 length = self.headers.getheader(r'content-length')
158 if length:
165 if length:
159 env[r'CONTENT_LENGTH'] = length
166 env[r'CONTENT_LENGTH'] = length
160 for header in [h for h in self.headers.keys()
167 for header in [h for h in self.headers.keys()
161 if h not in (r'content-type', r'content-length')]:
168 if h not in (r'content-type', r'content-length')]:
162 hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
169 hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
163 hval = self.headers.get(header)
170 hval = self.headers.get(header)
164 hval = hval.replace(r'\n', r'').strip()
171 hval = hval.replace(r'\n', r'').strip()
165 if hval:
172 if hval:
166 env[hkey] = hval
173 env[hkey] = hval
167 env[r'SERVER_PROTOCOL'] = self.request_version
174 env[r'SERVER_PROTOCOL'] = self.request_version
168 env[r'wsgi.version'] = (1, 0)
175 env[r'wsgi.version'] = (1, 0)
169 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
176 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
170 if env.get(r'HTTP_EXPECT', '').lower() == '100-continue':
177 if env.get(r'HTTP_EXPECT', '').lower() == '100-continue':
171 self.rfile = common.continuereader(self.rfile, self.wfile.write)
178 self.rfile = common.continuereader(self.rfile, self.wfile.write)
172
179
173 env[r'wsgi.input'] = self.rfile
180 env[r'wsgi.input'] = self.rfile
174 env[r'wsgi.errors'] = _error_logger(self)
181 env[r'wsgi.errors'] = _error_logger(self)
175 env[r'wsgi.multithread'] = isinstance(self.server,
182 env[r'wsgi.multithread'] = isinstance(self.server,
176 socketserver.ThreadingMixIn)
183 socketserver.ThreadingMixIn)
177 if util.safehasattr(socketserver, 'ForkingMixIn'):
184 if util.safehasattr(socketserver, 'ForkingMixIn'):
178 env[r'wsgi.multiprocess'] = isinstance(self.server,
185 env[r'wsgi.multiprocess'] = isinstance(self.server,
179 socketserver.ForkingMixIn)
186 socketserver.ForkingMixIn)
180 else:
187 else:
181 env[r'wsgi.multiprocess'] = False
188 env[r'wsgi.multiprocess'] = False
182
189
183 env[r'wsgi.run_once'] = 0
190 env[r'wsgi.run_once'] = 0
184
191
185 wsgiref.validate.check_environ(env)
192 wsgiref.validate.check_environ(env)
186
193
187 self.saved_status = None
194 self.saved_status = None
188 self.saved_headers = []
195 self.saved_headers = []
189 self.length = None
196 self.length = None
190 self._chunked = None
197 self._chunked = None
191 for chunk in self.server.application(env, self._start_response):
198 for chunk in self.server.application(env, self._start_response):
192 self._write(chunk)
199 self._write(chunk)
193 if not self.sent_headers:
200 if not self.sent_headers:
194 self.send_headers()
201 self.send_headers()
195 self._done()
202 self._done()
196
203
197 def send_headers(self):
204 def send_headers(self):
198 if not self.saved_status:
205 if not self.saved_status:
199 raise AssertionError("Sending headers before "
206 raise AssertionError("Sending headers before "
200 "start_response() called")
207 "start_response() called")
201 saved_status = self.saved_status.split(None, 1)
208 saved_status = self.saved_status.split(None, 1)
202 saved_status[0] = int(saved_status[0])
209 saved_status[0] = int(saved_status[0])
203 self.send_response(*saved_status)
210 self.send_response(*saved_status)
204 self.length = None
211 self.length = None
205 self._chunked = False
212 self._chunked = False
206 for h in self.saved_headers:
213 for h in self.saved_headers:
207 self.send_header(*h)
214 self.send_header(*h)
208 if h[0].lower() == r'content-length':
215 if h[0].lower() == r'content-length':
209 self.length = int(h[1])
216 self.length = int(h[1])
210 if (self.length is None and
217 if (self.length is None and
211 saved_status[0] != common.HTTP_NOT_MODIFIED):
218 saved_status[0] != common.HTTP_NOT_MODIFIED):
212 self._chunked = (not self.close_connection and
219 self._chunked = (not self.close_connection and
213 self.request_version == r'HTTP/1.1')
220 self.request_version == r'HTTP/1.1')
214 if self._chunked:
221 if self._chunked:
215 self.send_header(r'Transfer-Encoding', r'chunked')
222 self.send_header(r'Transfer-Encoding', r'chunked')
216 else:
223 else:
217 self.send_header(r'Connection', r'close')
224 self.send_header(r'Connection', r'close')
218 self.end_headers()
225 self.end_headers()
219 self.sent_headers = True
226 self.sent_headers = True
220
227
221 def _start_response(self, http_status, headers, exc_info=None):
228 def _start_response(self, http_status, headers, exc_info=None):
222 assert isinstance(http_status, str)
229 assert isinstance(http_status, str)
223 code, msg = http_status.split(None, 1)
230 code, msg = http_status.split(None, 1)
224 code = int(code)
231 code = int(code)
225 self.saved_status = http_status
232 self.saved_status = http_status
226 bad_headers = (r'connection', r'transfer-encoding')
233 bad_headers = (r'connection', r'transfer-encoding')
227 self.saved_headers = [h for h in headers
234 self.saved_headers = [h for h in headers
228 if h[0].lower() not in bad_headers]
235 if h[0].lower() not in bad_headers]
229 return self._write
236 return self._write
230
237
231 def _write(self, data):
238 def _write(self, data):
232 if not self.saved_status:
239 if not self.saved_status:
233 raise AssertionError("data written before start_response() called")
240 raise AssertionError("data written before start_response() called")
234 elif not self.sent_headers:
241 elif not self.sent_headers:
235 self.send_headers()
242 self.send_headers()
236 if self.length is not None:
243 if self.length is not None:
237 if len(data) > self.length:
244 if len(data) > self.length:
238 raise AssertionError("Content-length header sent, but more "
245 raise AssertionError("Content-length header sent, but more "
239 "bytes than specified are being written.")
246 "bytes than specified are being written.")
240 self.length = self.length - len(data)
247 self.length = self.length - len(data)
241 elif self._chunked and data:
248 elif self._chunked and data:
242 data = '%x\r\n%s\r\n' % (len(data), data)
249 data = '%x\r\n%s\r\n' % (len(data), data)
243 self.wfile.write(data)
250 self.wfile.write(data)
244 self.wfile.flush()
251 self.wfile.flush()
245
252
246 def _done(self):
253 def _done(self):
247 if self._chunked:
254 if self._chunked:
248 self.wfile.write('0\r\n\r\n')
255 self.wfile.write('0\r\n\r\n')
249 self.wfile.flush()
256 self.wfile.flush()
250
257
251 def version_string(self):
258 def version_string(self):
252 if self.server.serverheader:
259 if self.server.serverheader:
253 return encoding.strfromlocal(self.server.serverheader)
260 return encoding.strfromlocal(self.server.serverheader)
254 return httpservermod.basehttprequesthandler.version_string(self)
261 return httpservermod.basehttprequesthandler.version_string(self)
255
262
256 class _httprequesthandlerssl(_httprequesthandler):
263 class _httprequesthandlerssl(_httprequesthandler):
257 """HTTPS handler based on Python's ssl module"""
264 """HTTPS handler based on Python's ssl module"""
258
265
259 url_scheme = 'https'
266 url_scheme = 'https'
260
267
261 @staticmethod
268 @staticmethod
262 def preparehttpserver(httpserver, ui):
269 def preparehttpserver(httpserver, ui):
263 try:
270 try:
264 from .. import sslutil
271 from .. import sslutil
265 sslutil.modernssl
272 sslutil.modernssl
266 except ImportError:
273 except ImportError:
267 raise error.Abort(_("SSL support is unavailable"))
274 raise error.Abort(_("SSL support is unavailable"))
268
275
269 certfile = ui.config('web', 'certificate')
276 certfile = ui.config('web', 'certificate')
270
277
271 # These config options are currently only meant for testing. Use
278 # These config options are currently only meant for testing. Use
272 # at your own risk.
279 # at your own risk.
273 cafile = ui.config('devel', 'servercafile')
280 cafile = ui.config('devel', 'servercafile')
274 reqcert = ui.configbool('devel', 'serverrequirecert')
281 reqcert = ui.configbool('devel', 'serverrequirecert')
275
282
276 httpserver.socket = sslutil.wrapserversocket(httpserver.socket,
283 httpserver.socket = sslutil.wrapserversocket(httpserver.socket,
277 ui,
284 ui,
278 certfile=certfile,
285 certfile=certfile,
279 cafile=cafile,
286 cafile=cafile,
280 requireclientcert=reqcert)
287 requireclientcert=reqcert)
281
288
282 def setup(self):
289 def setup(self):
283 self.connection = self.request
290 self.connection = self.request
284 self.rfile = self.request.makefile(r"rb", self.rbufsize)
291 self.rfile = self.request.makefile(r"rb", self.rbufsize)
285 self.wfile = self.request.makefile(r"wb", self.wbufsize)
292 self.wfile = self.request.makefile(r"wb", self.wbufsize)
286
293
287 try:
294 try:
288 import threading
295 import threading
289 threading.activeCount() # silence pyflakes and bypass demandimport
296 threading.activeCount() # silence pyflakes and bypass demandimport
290 _mixin = socketserver.ThreadingMixIn
297 _mixin = socketserver.ThreadingMixIn
291 except ImportError:
298 except ImportError:
292 if util.safehasattr(os, "fork"):
299 if util.safehasattr(os, "fork"):
293 _mixin = socketserver.ForkingMixIn
300 _mixin = socketserver.ForkingMixIn
294 else:
301 else:
295 class _mixin(object):
302 class _mixin(object):
296 pass
303 pass
297
304
298 def openlog(opt, default):
305 def openlog(opt, default):
299 if opt and opt != '-':
306 if opt and opt != '-':
300 return open(opt, 'ab')
307 return open(opt, 'ab')
301 return default
308 return default
302
309
303 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
310 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
304
311
305 # SO_REUSEADDR has broken semantics on windows
312 # SO_REUSEADDR has broken semantics on windows
306 if pycompat.iswindows:
313 if pycompat.iswindows:
307 allow_reuse_address = 0
314 allow_reuse_address = 0
308
315
309 def __init__(self, ui, app, addr, handler, **kwargs):
316 def __init__(self, ui, app, addr, handler, **kwargs):
310 httpservermod.httpserver.__init__(self, addr, handler, **kwargs)
317 httpservermod.httpserver.__init__(self, addr, handler, **kwargs)
311 self.daemon_threads = True
318 self.daemon_threads = True
312 self.application = app
319 self.application = app
313
320
314 handler.preparehttpserver(self, ui)
321 handler.preparehttpserver(self, ui)
315
322
316 prefix = ui.config('web', 'prefix')
323 prefix = ui.config('web', 'prefix')
317 if prefix:
324 if prefix:
318 prefix = '/' + prefix.strip('/')
325 prefix = '/' + prefix.strip('/')
319 self.prefix = prefix
326 self.prefix = prefix
320
327
321 alog = openlog(ui.config('web', 'accesslog'), ui.fout)
328 alog = openlog(ui.config('web', 'accesslog'), ui.fout)
322 elog = openlog(ui.config('web', 'errorlog'), ui.ferr)
329 elog = openlog(ui.config('web', 'errorlog'), ui.ferr)
323 self.accesslog = alog
330 self.accesslog = alog
324 self.errorlog = elog
331 self.errorlog = elog
325
332
326 self.addr, self.port = self.socket.getsockname()[0:2]
333 self.addr, self.port = self.socket.getsockname()[0:2]
327 self.fqaddr = socket.getfqdn(addr[0])
334 self.fqaddr = socket.getfqdn(addr[0])
328
335
329 self.serverheader = ui.config('web', 'server-header')
336 self.serverheader = ui.config('web', 'server-header')
330
337
331 class IPv6HTTPServer(MercurialHTTPServer):
338 class IPv6HTTPServer(MercurialHTTPServer):
332 address_family = getattr(socket, 'AF_INET6', None)
339 address_family = getattr(socket, 'AF_INET6', None)
333 def __init__(self, *args, **kwargs):
340 def __init__(self, *args, **kwargs):
334 if self.address_family is None:
341 if self.address_family is None:
335 raise error.RepoError(_('IPv6 is not available on this system'))
342 raise error.RepoError(_('IPv6 is not available on this system'))
336 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
343 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
337
344
338 def create_server(ui, app):
345 def create_server(ui, app):
339
346
340 if ui.config('web', 'certificate'):
347 if ui.config('web', 'certificate'):
341 handler = _httprequesthandlerssl
348 handler = _httprequesthandlerssl
342 else:
349 else:
343 handler = _httprequesthandler
350 handler = _httprequesthandler
344
351
345 if ui.configbool('web', 'ipv6'):
352 if ui.configbool('web', 'ipv6'):
346 cls = IPv6HTTPServer
353 cls = IPv6HTTPServer
347 else:
354 else:
348 cls = MercurialHTTPServer
355 cls = MercurialHTTPServer
349
356
350 # ugly hack due to python issue5853 (for threaded use)
357 # ugly hack due to python issue5853 (for threaded use)
351 try:
358 try:
352 import mimetypes
359 import mimetypes
353 mimetypes.init()
360 mimetypes.init()
354 except UnicodeDecodeError:
361 except UnicodeDecodeError:
355 # Python 2.x's mimetypes module attempts to decode strings
362 # Python 2.x's mimetypes module attempts to decode strings
356 # from Windows' ANSI APIs as ascii (fail), then re-encode them
363 # from Windows' ANSI APIs as ascii (fail), then re-encode them
357 # as ascii (clown fail), because the default Python Unicode
364 # as ascii (clown fail), because the default Python Unicode
358 # codec is hardcoded as ascii.
365 # codec is hardcoded as ascii.
359
366
360 sys.argv # unwrap demand-loader so that reload() works
367 sys.argv # unwrap demand-loader so that reload() works
361 reload(sys) # resurrect sys.setdefaultencoding()
368 reload(sys) # resurrect sys.setdefaultencoding()
362 oldenc = sys.getdefaultencoding()
369 oldenc = sys.getdefaultencoding()
363 sys.setdefaultencoding("latin1") # or any full 8-bit encoding
370 sys.setdefaultencoding("latin1") # or any full 8-bit encoding
364 mimetypes.init()
371 mimetypes.init()
365 sys.setdefaultencoding(oldenc)
372 sys.setdefaultencoding(oldenc)
366
373
367 address = ui.config('web', 'address')
374 address = ui.config('web', 'address')
368 port = util.getport(ui.config('web', 'port'))
375 port = util.getport(ui.config('web', 'port'))
369 try:
376 try:
370 return cls(ui, app, (address, port), handler)
377 return cls(ui, app, (address, port), handler)
371 except socket.error as inst:
378 except socket.error as inst:
372 raise error.Abort(_("cannot start server at '%s:%d': %s")
379 raise error.Abort(_("cannot start server at '%s:%d': %s")
373 % (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