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