##// END OF EJS Templates
hgweb: generate last change date for an empty atom-bookmarks feed (issue5022)...
hgweb: generate last change date for an empty atom-bookmarks feed (issue5022) RFC 4287 states that atom feeds must have an <updated> element, so let's add one even when repo doesn't have a single bookmark.

File last commit:

r27046:37fcfe52 default
r28712:80e92247 default
Show More
server.py
322 lines | 10.8 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
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
import BaseHTTPServer
import SocketServer
import errno
import os
import socket
import sys
import traceback
import urllib
from ..i18n import _
from .. import (
error,
util,
)
from . import (
common,
)
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='-'):
David Soria Parra
hgweb: log headers only if headers were successfully parsed...
r19877 xheaders = []
if util.safehasattr(self, 'headers'):
xheaders = [h for h in self.headers.items()
if h[0].startswith('x-')]
Steven Brown
httprepo: long arguments support (issue2126)...
r14093 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()
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except socket.error as inst:
Brendan Cully
Add SSL support to hg serve, activated via --certificate option
r4860 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")
Gregory Szorc
hgweb: send proper HTTP response after uncaught exception...
r23409 self._done()
Thomas Arendsen Hein
Handle exceptions in do_hgweb: Send "Internal Server Error", log traceback
r4015 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: use Pythons ssl module for HTTPS serve when using Python 2.6 or later...
r12784 class _httprequesthandlerssl(_httprequesthandler):
timeless@mozdev.org
hgweb.server: fix _httprequesthandlerssl help text
r26202 """HTTPS handler based on Python's ssl module"""
Mads Kiilerich
hgweb: use Pythons ssl module for HTTPS serve when using Python 2.6 or later...
r12784
url_scheme = 'https'
@staticmethod
def preparehttpserver(httpserver, ssl_cert):
try:
import ssl
ssl.wrap_socket
except ImportError:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("SSL support is unavailable"))
Augie Fackler
hgweb: disable SSLv3 serving (BC)...
r23070 httpserver.socket = ssl.wrap_socket(
httpserver.socket, server_side=True,
certfile=ssl_cert, ssl_version=ssl.PROTOCOL_TLSv1)
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:
Yuya Nishihara
hgweb: use absolute_import
r27046 import threading
threading.activeCount() # silence pyflakes and bypass demandimport
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'):
Siddharth Agarwal
hgweb.server: drop support for Python 2.4
r26848 handler = _httprequesthandlerssl
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)
Matt Mackall
hgweb: hack around mimetypes encoding thinko (issue4160)...
r20357 try:
import mimetypes
mimetypes.init()
except UnicodeDecodeError:
# Python 2.x's mimetypes module attempts to decode strings
# from Windows' ANSI APIs as ascii (fail), then re-encode them
# as ascii (clown fail), because the default Python Unicode
# codec is hardcoded as ascii.
Yuya Nishihara
hgweb: make sure sys module is loaded prior to reload hack...
r20529 sys.argv # unwrap demand-loader so that reload() works
Matt Mackall
hgweb: hack around mimetypes encoding thinko (issue4160)...
r20357 reload(sys) # resurrect sys.setdefaultencoding()
oldenc = sys.getdefaultencoding()
sys.setdefaultencoding("latin1") # or any full 8-bit encoding
mimetypes.init()
sys.setdefaultencoding(oldenc)
Dirkjan Ochtman
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes)...
r8224
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)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except socket.error as inst:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("cannot start server at '%s:%d': %s")
Stephen Deasey
hgweb: clarify which address and port can/cannot be bound at startup (bug 769)...
r6262 % (address, port, inst.args[1]))