##// END OF EJS Templates
compat: remove unnecessary diamond-shaped multiple inheritance
Alejandro Santos -
r9037:a232b90f default
parent child Browse files
Show More
@@ -1,298 +1,298
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, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 from mercurial import hg, util, error
10 from mercurial import hg, util, error
11 from hgweb_mod import hgweb
11 from hgweb_mod import hgweb
12 from hgwebdir_mod import hgwebdir
12 from hgwebdir_mod import hgwebdir
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14
14
15 def _splitURI(uri):
15 def _splitURI(uri):
16 """ Return path and query splited from uri
16 """ Return path and query splited from uri
17
17
18 Just like CGI environment, the path is unquoted, the query is
18 Just like CGI environment, the path is unquoted, the query is
19 not.
19 not.
20 """
20 """
21 if '?' in uri:
21 if '?' in uri:
22 path, query = uri.split('?', 1)
22 path, query = uri.split('?', 1)
23 else:
23 else:
24 path, query = uri, ''
24 path, query = uri, ''
25 return urllib.unquote(path), query
25 return urllib.unquote(path), query
26
26
27 class _error_logger(object):
27 class _error_logger(object):
28 def __init__(self, handler):
28 def __init__(self, handler):
29 self.handler = handler
29 self.handler = handler
30 def flush(self):
30 def flush(self):
31 pass
31 pass
32 def write(self, str):
32 def write(self, str):
33 self.writelines(str.split('\n'))
33 self.writelines(str.split('\n'))
34 def writelines(self, seq):
34 def writelines(self, seq):
35 for msg in seq:
35 for msg in seq:
36 self.handler.log_error("HG error: %s", msg)
36 self.handler.log_error("HG error: %s", msg)
37
37
38 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
38 class _hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
39
39
40 url_scheme = 'http'
40 url_scheme = 'http'
41
41
42 def __init__(self, *args, **kargs):
42 def __init__(self, *args, **kargs):
43 self.protocol_version = 'HTTP/1.1'
43 self.protocol_version = 'HTTP/1.1'
44 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
44 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
45
45
46 def _log_any(self, fp, format, *args):
46 def _log_any(self, fp, format, *args):
47 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
47 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
48 self.log_date_time_string(),
48 self.log_date_time_string(),
49 format % args))
49 format % args))
50 fp.flush()
50 fp.flush()
51
51
52 def log_error(self, format, *args):
52 def log_error(self, format, *args):
53 self._log_any(self.server.errorlog, format, *args)
53 self._log_any(self.server.errorlog, format, *args)
54
54
55 def log_message(self, format, *args):
55 def log_message(self, format, *args):
56 self._log_any(self.server.accesslog, format, *args)
56 self._log_any(self.server.accesslog, format, *args)
57
57
58 def do_write(self):
58 def do_write(self):
59 try:
59 try:
60 self.do_hgweb()
60 self.do_hgweb()
61 except socket.error, inst:
61 except socket.error, inst:
62 if inst[0] != errno.EPIPE:
62 if inst[0] != errno.EPIPE:
63 raise
63 raise
64
64
65 def do_POST(self):
65 def do_POST(self):
66 try:
66 try:
67 self.do_write()
67 self.do_write()
68 except StandardError:
68 except StandardError:
69 self._start_response("500 Internal Server Error", [])
69 self._start_response("500 Internal Server Error", [])
70 self._write("Internal Server Error")
70 self._write("Internal Server Error")
71 tb = "".join(traceback.format_exception(*sys.exc_info()))
71 tb = "".join(traceback.format_exception(*sys.exc_info()))
72 self.log_error("Exception happened during processing "
72 self.log_error("Exception happened during processing "
73 "request '%s':\n%s", self.path, tb)
73 "request '%s':\n%s", self.path, tb)
74
74
75 def do_GET(self):
75 def do_GET(self):
76 self.do_POST()
76 self.do_POST()
77
77
78 def do_hgweb(self):
78 def do_hgweb(self):
79 path, query = _splitURI(self.path)
79 path, query = _splitURI(self.path)
80
80
81 env = {}
81 env = {}
82 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
82 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
83 env['REQUEST_METHOD'] = self.command
83 env['REQUEST_METHOD'] = self.command
84 env['SERVER_NAME'] = self.server.server_name
84 env['SERVER_NAME'] = self.server.server_name
85 env['SERVER_PORT'] = str(self.server.server_port)
85 env['SERVER_PORT'] = str(self.server.server_port)
86 env['REQUEST_URI'] = self.path
86 env['REQUEST_URI'] = self.path
87 env['SCRIPT_NAME'] = self.server.prefix
87 env['SCRIPT_NAME'] = self.server.prefix
88 env['PATH_INFO'] = path[len(self.server.prefix):]
88 env['PATH_INFO'] = path[len(self.server.prefix):]
89 env['REMOTE_HOST'] = self.client_address[0]
89 env['REMOTE_HOST'] = self.client_address[0]
90 env['REMOTE_ADDR'] = self.client_address[0]
90 env['REMOTE_ADDR'] = self.client_address[0]
91 if query:
91 if query:
92 env['QUERY_STRING'] = query
92 env['QUERY_STRING'] = query
93
93
94 if self.headers.typeheader is None:
94 if self.headers.typeheader is None:
95 env['CONTENT_TYPE'] = self.headers.type
95 env['CONTENT_TYPE'] = self.headers.type
96 else:
96 else:
97 env['CONTENT_TYPE'] = self.headers.typeheader
97 env['CONTENT_TYPE'] = self.headers.typeheader
98 length = self.headers.getheader('content-length')
98 length = self.headers.getheader('content-length')
99 if length:
99 if length:
100 env['CONTENT_LENGTH'] = length
100 env['CONTENT_LENGTH'] = length
101 for header in [h for h in self.headers.keys()
101 for header in [h for h in self.headers.keys()
102 if h not in ('content-type', 'content-length')]:
102 if h not in ('content-type', 'content-length')]:
103 hkey = 'HTTP_' + header.replace('-', '_').upper()
103 hkey = 'HTTP_' + header.replace('-', '_').upper()
104 hval = self.headers.getheader(header)
104 hval = self.headers.getheader(header)
105 hval = hval.replace('\n', '').strip()
105 hval = hval.replace('\n', '').strip()
106 if hval:
106 if hval:
107 env[hkey] = hval
107 env[hkey] = hval
108 env['SERVER_PROTOCOL'] = self.request_version
108 env['SERVER_PROTOCOL'] = self.request_version
109 env['wsgi.version'] = (1, 0)
109 env['wsgi.version'] = (1, 0)
110 env['wsgi.url_scheme'] = self.url_scheme
110 env['wsgi.url_scheme'] = self.url_scheme
111 env['wsgi.input'] = self.rfile
111 env['wsgi.input'] = self.rfile
112 env['wsgi.errors'] = _error_logger(self)
112 env['wsgi.errors'] = _error_logger(self)
113 env['wsgi.multithread'] = isinstance(self.server,
113 env['wsgi.multithread'] = isinstance(self.server,
114 SocketServer.ThreadingMixIn)
114 SocketServer.ThreadingMixIn)
115 env['wsgi.multiprocess'] = isinstance(self.server,
115 env['wsgi.multiprocess'] = isinstance(self.server,
116 SocketServer.ForkingMixIn)
116 SocketServer.ForkingMixIn)
117 env['wsgi.run_once'] = 0
117 env['wsgi.run_once'] = 0
118
118
119 self.close_connection = True
119 self.close_connection = True
120 self.saved_status = None
120 self.saved_status = None
121 self.saved_headers = []
121 self.saved_headers = []
122 self.sent_headers = False
122 self.sent_headers = False
123 self.length = None
123 self.length = None
124 for chunk in self.server.application(env, self._start_response):
124 for chunk in self.server.application(env, self._start_response):
125 self._write(chunk)
125 self._write(chunk)
126
126
127 def send_headers(self):
127 def send_headers(self):
128 if not self.saved_status:
128 if not self.saved_status:
129 raise AssertionError("Sending headers before "
129 raise AssertionError("Sending headers before "
130 "start_response() called")
130 "start_response() called")
131 saved_status = self.saved_status.split(None, 1)
131 saved_status = self.saved_status.split(None, 1)
132 saved_status[0] = int(saved_status[0])
132 saved_status[0] = int(saved_status[0])
133 self.send_response(*saved_status)
133 self.send_response(*saved_status)
134 should_close = True
134 should_close = True
135 for h in self.saved_headers:
135 for h in self.saved_headers:
136 self.send_header(*h)
136 self.send_header(*h)
137 if h[0].lower() == 'content-length':
137 if h[0].lower() == 'content-length':
138 should_close = False
138 should_close = False
139 self.length = int(h[1])
139 self.length = int(h[1])
140 # The value of the Connection header is a list of case-insensitive
140 # The value of the Connection header is a list of case-insensitive
141 # tokens separated by commas and optional whitespace.
141 # tokens separated by commas and optional whitespace.
142 if 'close' in [token.strip().lower() for token in
142 if 'close' in [token.strip().lower() for token in
143 self.headers.get('connection', '').split(',')]:
143 self.headers.get('connection', '').split(',')]:
144 should_close = True
144 should_close = True
145 if should_close:
145 if should_close:
146 self.send_header('Connection', 'close')
146 self.send_header('Connection', 'close')
147 self.close_connection = should_close
147 self.close_connection = should_close
148 self.end_headers()
148 self.end_headers()
149 self.sent_headers = True
149 self.sent_headers = True
150
150
151 def _start_response(self, http_status, headers, exc_info=None):
151 def _start_response(self, http_status, headers, exc_info=None):
152 code, msg = http_status.split(None, 1)
152 code, msg = http_status.split(None, 1)
153 code = int(code)
153 code = int(code)
154 self.saved_status = http_status
154 self.saved_status = http_status
155 bad_headers = ('connection', 'transfer-encoding')
155 bad_headers = ('connection', 'transfer-encoding')
156 self.saved_headers = [h for h in headers
156 self.saved_headers = [h for h in headers
157 if h[0].lower() not in bad_headers]
157 if h[0].lower() not in bad_headers]
158 return self._write
158 return self._write
159
159
160 def _write(self, data):
160 def _write(self, data):
161 if not self.saved_status:
161 if not self.saved_status:
162 raise AssertionError("data written before start_response() called")
162 raise AssertionError("data written before start_response() called")
163 elif not self.sent_headers:
163 elif not self.sent_headers:
164 self.send_headers()
164 self.send_headers()
165 if self.length is not None:
165 if self.length is not None:
166 if len(data) > self.length:
166 if len(data) > self.length:
167 raise AssertionError("Content-length header sent, but more "
167 raise AssertionError("Content-length header sent, but more "
168 "bytes than specified are being written.")
168 "bytes than specified are being written.")
169 self.length = self.length - len(data)
169 self.length = self.length - len(data)
170 self.wfile.write(data)
170 self.wfile.write(data)
171 self.wfile.flush()
171 self.wfile.flush()
172
172
173 class _shgwebhandler(_hgwebhandler):
173 class _shgwebhandler(_hgwebhandler):
174
174
175 url_scheme = 'https'
175 url_scheme = 'https'
176
176
177 def setup(self):
177 def setup(self):
178 self.connection = self.request
178 self.connection = self.request
179 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
179 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
180 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
180 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
181
181
182 def do_write(self):
182 def do_write(self):
183 from OpenSSL.SSL import SysCallError
183 from OpenSSL.SSL import SysCallError
184 try:
184 try:
185 super(_shgwebhandler, self).do_write()
185 super(_shgwebhandler, self).do_write()
186 except SysCallError, inst:
186 except SysCallError, inst:
187 if inst.args[0] != errno.EPIPE:
187 if inst.args[0] != errno.EPIPE:
188 raise
188 raise
189
189
190 def handle_one_request(self):
190 def handle_one_request(self):
191 from OpenSSL.SSL import SysCallError, ZeroReturnError
191 from OpenSSL.SSL import SysCallError, ZeroReturnError
192 try:
192 try:
193 super(_shgwebhandler, self).handle_one_request()
193 super(_shgwebhandler, self).handle_one_request()
194 except (SysCallError, ZeroReturnError):
194 except (SysCallError, ZeroReturnError):
195 self.close_connection = True
195 self.close_connection = True
196 pass
196 pass
197
197
198 def create_server(ui, repo):
198 def create_server(ui, repo):
199 use_threads = True
199 use_threads = True
200
200
201 def openlog(opt, default):
201 def openlog(opt, default):
202 if opt and opt != '-':
202 if opt and opt != '-':
203 return open(opt, 'a')
203 return open(opt, 'a')
204 return default
204 return default
205
205
206 if repo is None:
206 if repo is None:
207 myui = ui
207 myui = ui
208 else:
208 else:
209 myui = repo.ui
209 myui = repo.ui
210 address = myui.config("web", "address", "")
210 address = myui.config("web", "address", "")
211 port = int(myui.config("web", "port", 8000))
211 port = int(myui.config("web", "port", 8000))
212 prefix = myui.config("web", "prefix", "")
212 prefix = myui.config("web", "prefix", "")
213 if prefix:
213 if prefix:
214 prefix = "/" + prefix.strip("/")
214 prefix = "/" + prefix.strip("/")
215 use_ipv6 = myui.configbool("web", "ipv6")
215 use_ipv6 = myui.configbool("web", "ipv6")
216 webdir_conf = myui.config("web", "webdir_conf")
216 webdir_conf = myui.config("web", "webdir_conf")
217 ssl_cert = myui.config("web", "certificate")
217 ssl_cert = myui.config("web", "certificate")
218 accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
218 accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
219 errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
219 errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
220
220
221 if use_threads:
221 if use_threads:
222 try:
222 try:
223 from threading import activeCount
223 from threading import activeCount
224 except ImportError:
224 except ImportError:
225 use_threads = False
225 use_threads = False
226
226
227 if use_threads:
227 if use_threads:
228 _mixin = SocketServer.ThreadingMixIn
228 _mixin = SocketServer.ThreadingMixIn
229 else:
229 else:
230 if hasattr(os, "fork"):
230 if hasattr(os, "fork"):
231 _mixin = SocketServer.ForkingMixIn
231 _mixin = SocketServer.ForkingMixIn
232 else:
232 else:
233 class _mixin:
233 class _mixin:
234 pass
234 pass
235
235
236 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
236 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
237
237
238 # SO_REUSEADDR has broken semantics on windows
238 # SO_REUSEADDR has broken semantics on windows
239 if os.name == 'nt':
239 if os.name == 'nt':
240 allow_reuse_address = 0
240 allow_reuse_address = 0
241
241
242 def __init__(self, *args, **kargs):
242 def __init__(self, *args, **kargs):
243 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
243 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
244 self.accesslog = accesslog
244 self.accesslog = accesslog
245 self.errorlog = errorlog
245 self.errorlog = errorlog
246 self.daemon_threads = True
246 self.daemon_threads = True
247 def make_handler():
247 def make_handler():
248 if webdir_conf:
248 if webdir_conf:
249 hgwebobj = hgwebdir(webdir_conf, ui)
249 hgwebobj = hgwebdir(webdir_conf, ui)
250 elif repo is not None:
250 elif repo is not None:
251 hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
251 hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
252 else:
252 else:
253 raise error.RepoError(_("There is no Mercurial repository"
253 raise error.RepoError(_("There is no Mercurial repository"
254 " here (.hg not found)"))
254 " here (.hg not found)"))
255 return hgwebobj
255 return hgwebobj
256 self.application = make_handler()
256 self.application = make_handler()
257
257
258 if ssl_cert:
258 if ssl_cert:
259 try:
259 try:
260 from OpenSSL import SSL
260 from OpenSSL import SSL
261 ctx = SSL.Context(SSL.SSLv23_METHOD)
261 ctx = SSL.Context(SSL.SSLv23_METHOD)
262 except ImportError:
262 except ImportError:
263 raise util.Abort(_("SSL support is unavailable"))
263 raise util.Abort(_("SSL support is unavailable"))
264 ctx.use_privatekey_file(ssl_cert)
264 ctx.use_privatekey_file(ssl_cert)
265 ctx.use_certificate_file(ssl_cert)
265 ctx.use_certificate_file(ssl_cert)
266 sock = socket.socket(self.address_family, self.socket_type)
266 sock = socket.socket(self.address_family, self.socket_type)
267 self.socket = SSL.Connection(ctx, sock)
267 self.socket = SSL.Connection(ctx, sock)
268 self.server_bind()
268 self.server_bind()
269 self.server_activate()
269 self.server_activate()
270
270
271 self.addr, self.port = self.socket.getsockname()[0:2]
271 self.addr, self.port = self.socket.getsockname()[0:2]
272 self.prefix = prefix
272 self.prefix = prefix
273 self.fqaddr = socket.getfqdn(address)
273 self.fqaddr = socket.getfqdn(address)
274
274
275 class IPv6HTTPServer(MercurialHTTPServer):
275 class IPv6HTTPServer(MercurialHTTPServer):
276 address_family = getattr(socket, 'AF_INET6', None)
276 address_family = getattr(socket, 'AF_INET6', None)
277
277
278 def __init__(self, *args, **kwargs):
278 def __init__(self, *args, **kwargs):
279 if self.address_family is None:
279 if self.address_family is None:
280 raise error.RepoError(_('IPv6 is not available on this system'))
280 raise error.RepoError(_('IPv6 is not available on this system'))
281 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
281 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
282
282
283 if ssl_cert:
283 if ssl_cert:
284 handler = _shgwebhandler
284 handler = _shgwebhandler
285 else:
285 else:
286 handler = _hgwebhandler
286 handler = _hgwebhandler
287
287
288 # ugly hack due to python issue5853 (for threaded use)
288 # ugly hack due to python issue5853 (for threaded use)
289 import mimetypes; mimetypes.init()
289 import mimetypes; mimetypes.init()
290
290
291 try:
291 try:
292 if use_ipv6:
292 if use_ipv6:
293 return IPv6HTTPServer((address, port), handler)
293 return IPv6HTTPServer((address, port), handler)
294 else:
294 else:
295 return MercurialHTTPServer((address, port), handler)
295 return MercurialHTTPServer((address, port), handler)
296 except socket.error, inst:
296 except socket.error, inst:
297 raise util.Abort(_("cannot start server at '%s:%d': %s")
297 raise util.Abort(_("cannot start server at '%s:%d': %s")
298 % (address, port, inst.args[1]))
298 % (address, port, inst.args[1]))
General Comments 0
You need to be logged in to leave comments. Login now