##// END OF EJS Templates
wsgicgi: change stdin to binary mode
wsgicgi: change stdin to binary mode

File last commit:

r4379:80c7fa62 merge default
r4484:7605da1c default
Show More
server.py
243 lines | 9.0 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>
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
from mercurial.demandload import demandload
import os, sys, errno
Thomas Arendsen Hein
Handle exceptions in do_hgweb: Send "Internal Server Error", log traceback
r4015 demandload(globals(), "urllib BaseHTTPServer socket SocketServer traceback")
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 demandload(globals(), "mercurial:ui,hg,util,templater")
Eric Hopper
This patch make several WSGI related alterations....
r2506 demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 from mercurial.i18n import gettext as _
def _splitURI(uri):
""" Return path and query splited from uri
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)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
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)
def log_error(self, format, *args):
errorlog = self.server.errorlog
Matt Mackall
hg serve: don't do DNS lookups
r4359 errorlog.write("%s - - [%s] %s\n" % (self.client_address[0],
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 self.log_date_time_string(),
format % args))
def log_message(self, format, *args):
accesslog = self.server.accesslog
Matt Mackall
hg serve: don't do DNS lookups
r4359 accesslog.write("%s - - [%s] %s\n" % (self.client_address[0],
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 self.log_date_time_string(),
format % args))
def do_POST(self):
try:
Thomas Arendsen Hein
Handle exceptions in do_hgweb: Send "Internal Server Error", log traceback
r4015 try:
self.do_hgweb()
except socket.error, inst:
if inst[0] != errno.EPIPE:
raise
except StandardError, inst:
self._start_response("500 Internal Server Error", [])
self._write("Internal Server Error")
tb = "".join(traceback.format_exception(*sys.exc_info()))
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):
path_info, query = _splitURI(self.path)
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
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 env['PATH_INFO'] = path_info
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
Eric Hopper
Fix server to set up a more WSGI compliant environment.
r2505 for header in [h for h in self.headers.keys() \
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)
env['wsgi.url_scheme'] = 'http'
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.close_connection = True
self.saved_status = None
self.saved_headers = []
self.sent_headers = False
Eric Hopper
Put support for persistent connections back in.
r2508 self.length = None
Eric Hopper
This patch make several WSGI related alterations....
r2506 req = self.server.reqmaker(env, self._start_response)
for data in req:
if data:
self._write(data)
def send_headers(self):
if not self.saved_status:
raise AssertionError("Sending headers before start_response() called")
saved_status = self.saved_status.split(None, 1)
saved_status[0] = int(saved_status[0])
self.send_response(*saved_status)
Eric Hopper
Put support for persistent connections back in.
r2508 should_close = True
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':
should_close = False
self.length = int(h[1])
Alexis S. L. Carvalho
Respect "Connection: close" headers sent by HTTP clients....
r2582 # The value of the Connection header is a list of case-insensitive
# tokens separated by commas and optional whitespace.
Vadim Gelfer
clean up trailing white space.
r2600 if 'close' in [token.strip().lower() for token in
Alexis S. L. Carvalho
Respect "Connection: close" headers sent by HTTP clients....
r2582 self.headers.get('connection', '').split(',')]:
should_close = True
Eric Hopper
Put support for persistent connections back in.
r2508 if should_close:
self.send_header('Connection', 'close')
self.close_connection = should_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')
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:
raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
self.length = self.length - len(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
Eric Hopper
Adjusting hgweb splitup to be a little cleaner.
r2392 def create_server(ui, repo):
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 use_threads = True
def openlog(opt, default):
if opt and opt != '-':
return open(opt, 'w')
return default
address = ui.config("web", "address", "")
port = int(ui.config("web", "port", 8000))
use_ipv6 = ui.configbool("web", "ipv6")
webdir_conf = ui.config("web", "webdir_conf")
accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
if use_threads:
try:
from threading import activeCount
except ImportError:
use_threads = False
if use_threads:
_mixin = SocketServer.ThreadingMixIn
else:
if hasattr(os, "fork"):
_mixin = SocketServer.ForkingMixIn
else:
Thomas Arendsen Hein
white space and line break cleanups
r3673 class _mixin:
pass
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
Patrick Mezard
web/server: disable address reuse option for BaseHTTPServer on windows...
r4130
# SO_REUSEADDR has broken semantics on windows
if os.name == 'nt':
allow_reuse_address = 0
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 def __init__(self, *args, **kargs):
BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
self.accesslog = accesslog
self.errorlog = errorlog
Brendan Cully
Make hgweb threads into daemon threads....
r2650 self.daemon_threads = True
Alexis S. L. Carvalho
avoid wsgiapplication <-> MercurialHTTPServer circular reference
r4245 def make_handler():
if webdir_conf:
hgwebobj = hgwebdir(webdir_conf, ui)
elif repo is not None:
hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
else:
raise hg.RepoError(_("There is no Mercurial repository here"
" (.hg not found)"))
return hgwebobj
self.reqmaker = wsgiapplication(make_handler)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Brendan Cully
Fix hg serve -6 getsockname handling
r3836 addr, port = self.socket.getsockname()[:2]
if addr in ('0.0.0.0', '::'):
Matt Mackall
hgweb: internalize some socket details
r3628 addr = socket.gethostname()
else:
try:
addr = socket.gethostbyaddr(addr)[0]
except socket.error:
pass
self.addr, self.port = addr, port
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 class IPv6HTTPServer(MercurialHTTPServer):
address_family = getattr(socket, 'AF_INET6', None)
def __init__(self, *args, **kwargs):
if self.address_family is None:
raise hg.RepoError(_('IPv6 not available on this system'))
Eric Hopper
Fix two small bugs that would've prevented the web interface and IPv6...
r2507 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355
Matt Mackall
hgweb: internalize some socket details
r3628 try:
if use_ipv6:
return IPv6HTTPServer((address, port), _hgwebhandler)
else:
return MercurialHTTPServer((address, port), _hgwebhandler)
except socket.error, inst:
raise util.Abort(_('cannot start server: %s') % inst.args[1])