##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r18380:a4d7fd7a default
r19776:a9e92b11 merge default
Show More
server.py
330 lines | 11.5 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/server.py - The standalone hg web server.
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Thomas Arendsen Hein
sync with -stable
r4016 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
Nicolas Dumazet
pylint, pyflakes: remove unused or duplicate imports
r10905 from mercurial import util, error
Augie Fackler
hgweb: add support for 100-continue as recommended by PEP 333.
r13570 from mercurial.hgweb import common
Martin Geisler
i18n: import _ instead of gettext
r7225 from mercurial.i18n import _
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
def _splitURI(uri):
Mads Kiilerich
improve some comments and docstrings, fixing issues found when spell checking
r17427 """Return path and query that has been split from uri
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Just like CGI environment, the path is unquoted, the query is
not.
"""
if '?' in uri:
path, query = uri.split('?', 1)
else:
path, query = uri, ''
return urllib.unquote(path), query
Eric Hopper
This patch make several WSGI related alterations....
r2506 class _error_logger(object):
def __init__(self, handler):
self.handler = handler
def flush(self):
pass
Benoit Boissinot
hgweb: fix errors spotted by pychecker
r3130 def write(self, str):
Eric Hopper
This patch make several WSGI related alterations....
r2506 self.writelines(str.split('\n'))
Benoit Boissinot
hgweb: fix errors spotted by pychecker
r3130 def writelines(self, seq):
Eric Hopper
This patch make several WSGI related alterations....
r2506 for msg in seq:
self.handler.log_error("HG error: %s", msg)
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 class _httprequesthandler(BaseHTTPServer.BaseHTTPRequestHandler):
Wesley J. Landaker
Make hg serve set the wsgi.url_scheme property correctly....
r4870
url_scheme = 'http'
Thomas Arendsen Hein
removed trailing whitespace
r4957
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 @staticmethod
def preparehttpserver(httpserver, ssl_cert):
"""Prepare .socket of new HTTPServer instance"""
pass
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 def __init__(self, *args, **kargs):
Vadim Gelfer
http server: support persistent connections....
r2434 self.protocol_version = 'HTTP/1.1'
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
Patrick Mezard
hgweb.server: flush log files after every access
r5549 def _log_any(self, fp, format, *args):
fp.write("%s - - [%s] %s\n" % (self.client_address[0],
self.log_date_time_string(),
format % args))
fp.flush()
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 def log_error(self, format, *args):
Patrick Mezard
hgweb.server: flush log files after every access
r5549 self._log_any(self.server.errorlog, format, *args)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
def log_message(self, format, *args):
Patrick Mezard
hgweb.server: flush log files after every access
r5549 self._log_any(self.server.accesslog, format, *args)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Steven Brown
httprepo: long arguments support (issue2126)...
r14093 def log_request(self, code='-', size='-'):
xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
self.log_message('"%s" %s %s%s',
self.requestline, str(code), str(size),
''.join([' %s:%s' % h for h in sorted(xheaders)]))
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 def do_write(self):
try:
self.do_hgweb()
except socket.error, inst:
if inst[0] != errno.EPIPE:
raise
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 def do_POST(self):
try:
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 self.do_write()
Mads Kiilerich
serve: catch and log all Exceptions, not only StandardException...
r13443 except Exception:
Thomas Arendsen Hein
Handle exceptions in do_hgweb: Send "Internal Server Error", log traceback
r4015 self._start_response("500 Internal Server Error", [])
self._write("Internal Server Error")
tb = "".join(traceback.format_exception(*sys.exc_info()))
Martin Geisler
wrap string literals in error messages
r8663 self.log_error("Exception happened during processing "
"request '%s':\n%s", self.path, tb)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
def do_GET(self):
self.do_POST()
def do_hgweb(self):
Michele Cella
adding "prefix" option to "hg serve" (command line and [web] section)...
r5835 path, query = _splitURI(self.path)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
env = {}
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['REQUEST_METHOD'] = self.command
env['SERVER_NAME'] = self.server.server_name
env['SERVER_PORT'] = str(self.server.server_port)
Brendan Cully
hgweb: support for generating and parsing NWI URLs
r3263 env['REQUEST_URI'] = self.path
Michele Cella
adding "prefix" option to "hg serve" (command line and [web] section)...
r5835 env['SCRIPT_NAME'] = self.server.prefix
env['PATH_INFO'] = path[len(self.server.prefix):]
Matt Mackall
hg serve: don't do DNS lookups
r4359 env['REMOTE_HOST'] = self.client_address[0]
env['REMOTE_ADDR'] = self.client_address[0]
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 if query:
env['QUERY_STRING'] = query
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633 for header in [h for h in self.headers.keys()
Eric Hopper
Fix server to set up a more WSGI compliant environment.
r2505 if h not in ('content-type', 'content-length')]:
hkey = 'HTTP_' + header.replace('-', '_').upper()
hval = self.headers.getheader(header)
hval = hval.replace('\n', '').strip()
if hval:
env[hkey] = hval
env['SERVER_PROTOCOL'] = self.request_version
Eric Hopper
This patch make several WSGI related alterations....
r2506 env['wsgi.version'] = (1, 0)
Brendan Cully
cosmetics
r4871 env['wsgi.url_scheme'] = self.url_scheme
Augie Fackler
hgweb: add support for 100-continue as recommended by PEP 333.
r13570 if env.get('HTTP_EXPECT', '').lower() == '100-continue':
self.rfile = common.continuereader(self.rfile, self.wfile.write)
Eric Hopper
This patch make several WSGI related alterations....
r2506 env['wsgi.input'] = self.rfile
env['wsgi.errors'] = _error_logger(self)
env['wsgi.multithread'] = isinstance(self.server,
SocketServer.ThreadingMixIn)
env['wsgi.multiprocess'] = isinstance(self.server,
SocketServer.ForkingMixIn)
env['wsgi.run_once'] = 0
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Eric Hopper
This patch make several WSGI related alterations....
r2506 self.saved_status = None
self.saved_headers = []
self.sent_headers = False
Eric Hopper
Put support for persistent connections back in.
r2508 self.length = None
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 self._chunked = None
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 for chunk in self.server.application(env, self._start_response):
self._write(chunk)
Mads Kiilerich
serve: send response headers even if response has no body...
r18349 if not self.sent_headers:
self.send_headers()
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 self._done()
Eric Hopper
This patch make several WSGI related alterations....
r2506
def send_headers(self):
if not self.saved_status:
Martin Geisler
wrap string literals in error messages
r8663 raise AssertionError("Sending headers before "
"start_response() called")
Eric Hopper
This patch make several WSGI related alterations....
r2506 saved_status = self.saved_status.split(None, 1)
saved_status[0] = int(saved_status[0])
self.send_response(*saved_status)
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 self.length = None
self._chunked = False
Eric Hopper
This patch make several WSGI related alterations....
r2506 for h in self.saved_headers:
self.send_header(*h)
Eric Hopper
Put support for persistent connections back in.
r2508 if h[0].lower() == 'content-length':
self.length = int(h[1])
Mads Kiilerich
serve: don't send any content headers with 304 responses...
r18380 if (self.length is None and
saved_status[0] != common.HTTP_NOT_MODIFIED):
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 self._chunked = (not self.close_connection and
self.request_version == "HTTP/1.1")
if self._chunked:
self.send_header('Transfer-Encoding', 'chunked')
else:
self.send_header('Connection', 'close')
Eric Hopper
This patch make several WSGI related alterations....
r2506 self.end_headers()
self.sent_headers = True
def _start_response(self, http_status, headers, exc_info=None):
code, msg = http_status.split(None, 1)
code = int(code)
self.saved_status = http_status
Eric Hopper
Put support for persistent connections back in.
r2508 bad_headers = ('connection', 'transfer-encoding')
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633 self.saved_headers = [h for h in headers
if h[0].lower() not in bad_headers]
Eric Hopper
This patch make several WSGI related alterations....
r2506 return self._write
def _write(self, data):
if not self.saved_status:
raise AssertionError("data written before start_response() called")
elif not self.sent_headers:
self.send_headers()
Eric Hopper
Put support for persistent connections back in.
r2508 if self.length is not None:
if len(data) > self.length:
Martin Geisler
wrap string literals in error messages
r8663 raise AssertionError("Content-length header sent, but more "
"bytes than specified are being written.")
Eric Hopper
Put support for persistent connections back in.
r2508 self.length = self.length - len(data)
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 elif self._chunked and data:
data = '%x\r\n%s\r\n' % (len(data), data)
Eric Hopper
This patch make several WSGI related alterations....
r2506 self.wfile.write(data)
self.wfile.flush()
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Mads Kiilerich
serve: use chunked encoding in hgweb responses...
r18354 def _done(self):
if self._chunked:
self.wfile.write('0\r\n\r\n')
self.wfile.flush()
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 class _httprequesthandleropenssl(_httprequesthandler):
"""HTTPS handler based on pyOpenSSL"""
Wesley J. Landaker
Make hg serve set the wsgi.url_scheme property correctly....
r4870
url_scheme = 'https'
Thomas Arendsen Hein
removed trailing whitespace
r4957
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 @staticmethod
def preparehttpserver(httpserver, ssl_cert):
try:
import OpenSSL
OpenSSL.SSL.Context
except ImportError:
raise util.Abort(_("SSL support is unavailable"))
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
ctx.use_privatekey_file(ssl_cert)
ctx.use_certificate_file(ssl_cert)
sock = socket.socket(httpserver.address_family, httpserver.socket_type)
httpserver.socket = OpenSSL.SSL.Connection(ctx, sock)
httpserver.server_bind()
httpserver.server_activate()
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 def setup(self):
self.connection = self.request
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
def do_write(self):
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 import OpenSSL
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 try:
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 _httprequesthandler.do_write(self)
except OpenSSL.SSL.SysCallError, inst:
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 if inst.args[0] != errno.EPIPE:
raise
def handle_one_request(self):
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 import OpenSSL
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 try:
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 _httprequesthandler.handle_one_request(self)
except (OpenSSL.SSL.SysCallError, OpenSSL.SSL.ZeroReturnError):
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 self.close_connection = True
pass
Mads Kiilerich
hgweb: use Pythons ssl module for HTTPS serve when using Python 2.6 or later...
r12784 class _httprequesthandlerssl(_httprequesthandler):
"""HTTPS handler based on Pythons ssl module (introduced in 2.6)"""
url_scheme = 'https'
@staticmethod
def preparehttpserver(httpserver, ssl_cert):
try:
import ssl
ssl.wrap_socket
except ImportError:
raise util.Abort(_("SSL support is unavailable"))
httpserver.socket = ssl.wrap_socket(httpserver.socket, server_side=True,
Mads Kiilerich
hgweb: let HTTPS serve use more compatible and less secure encryption...
r12797 certfile=ssl_cert, ssl_version=ssl.PROTOCOL_SSLv23)
Mads Kiilerich
hgweb: use Pythons ssl module for HTTPS serve when using Python 2.6 or later...
r12784
def setup(self):
self.connection = self.request
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
Dirkjan Ochtman
server: externalize and streamline mixin setup
r10639 try:
from threading import activeCount
Matt Mackall
pyflakes: clean up some import noise
r15223 activeCount() # silence pyflakes
Dirkjan Ochtman
server: externalize and streamline mixin setup
r10639 _mixin = SocketServer.ThreadingMixIn
except ImportError:
Augie Fackler
hgweb: move remaining hasattr calls to safehasattr
r14957 if util.safehasattr(os, "fork"):
Dirkjan Ochtman
server: externalize and streamline mixin setup
r10639 _mixin = SocketServer.ForkingMixIn
else:
Thomas Arendsen Hein
classes: fix class style problems found by b071cd58af50...
r14764 class _mixin(object):
Dirkjan Ochtman
server: externalize and streamline mixin setup
r10639 pass
Dirkjan Ochtman
server: unnest server classes into module namespace
r10643 def openlog(opt, default):
if opt and opt != '-':
return open(opt, 'a')
return default
class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
# SO_REUSEADDR has broken semantics on windows
if os.name == 'nt':
allow_reuse_address = 0
def __init__(self, ui, app, addr, handler, **kwargs):
BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
self.daemon_threads = True
self.application = app
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 handler.preparehttpserver(self, ui.config('web', 'certificate'))
Dirkjan Ochtman
server: unnest server classes into module namespace
r10643
prefix = ui.config('web', 'prefix', '')
if prefix:
prefix = '/' + prefix.strip('/')
self.prefix = prefix
alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
self.accesslog = alog
self.errorlog = elog
self.addr, self.port = self.socket.getsockname()[0:2]
self.fqaddr = socket.getfqdn(addr[0])
class IPv6HTTPServer(MercurialHTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
def __init__(self, *args, **kwargs):
if self.address_family is None:
raise error.RepoError(_('IPv6 is not available on this system'))
super(IPv6HTTPServer, self).__init__(*args, **kwargs)
Dirkjan Ochtman
server: initialize wsgi app in command, then wrap server around it
r10644 def create_server(ui, app):
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Dirkjan Ochtman
server: initialize wsgi app in command, then wrap server around it
r10644 if ui.config('web', 'certificate'):
Mads Kiilerich
hgweb: use Pythons ssl module for HTTPS serve when using Python 2.6 or later...
r12784 if sys.version_info >= (2, 6):
handler = _httprequesthandlerssl
else:
handler = _httprequesthandleropenssl
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 else:
Mads Kiilerich
hgweb: refactor all pyOpenSSL references into one class
r12783 handler = _httprequesthandler
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860
Dirkjan Ochtman
server: initialize wsgi app in command, then wrap server around it
r10644 if ui.configbool('web', 'ipv6'):
Dirkjan Ochtman
server: abstract setup of ipv6 vs. normal server
r10641 cls = IPv6HTTPServer
else:
cls = MercurialHTTPServer
Dirkjan Ochtman
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes)...
r8224 # ugly hack due to python issue5853 (for threaded use)
import mimetypes; mimetypes.init()
Dirkjan Ochtman
server: initialize wsgi app in command, then wrap server around it
r10644 address = ui.config('web', 'address', '')
Brodie Rao
mail/hgweb: support service names for ports (issue2350)...
r12076 port = util.getport(ui.config('web', 'port', 8000))
Matt Mackall
hgweb: internalize some socket details
r3628 try:
Dirkjan Ochtman
server: initialize wsgi app in command, then wrap server around it
r10644 return cls(ui, app, (address, port), handler)
Matt Mackall
hgweb: internalize some socket details
r3628 except socket.error, inst:
Stephen Deasey
hgweb: clarify which address and port can/cannot be bound at startup (bug 769)...
r6262 raise util.Abort(_("cannot start server at '%s:%d': %s")
% (address, port, inst.args[1]))