##// END OF EJS Templates
symlinks: use is_link wherever is_exec is used
symlinks: use is_link wherever is_exec is used

File last commit:

r3877:abaee83c default
r4002:d7b9ec58 default
Show More
server.py
239 lines | 8.7 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.
Matt Mackall
Replace demandload with new demandimport
r3877 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer
from mercurial import ui, hg, util, templater
from hgweb_mod import hgweb
from hgwebdir_mod import hgwebdir
from request import 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
errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
self.log_date_time_string(),
format % args))
def log_message(self, format, *args):
accesslog = self.server.accesslog
accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
self.log_date_time_string(),
format % args))
def do_POST(self):
try:
self.do_hgweb()
except socket.error, inst:
if inst[0] != errno.EPIPE:
raise
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
if query:
env['QUERY_STRING'] = query
host = self.address_string()
if host != self.client_address[0]:
env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0]
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):
def __init__(self, *args, **kargs):
BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
self.accesslog = accesslog
self.errorlog = errorlog
self.repo = repo
self.webdir_conf = webdir_conf
Eric Hopper
Adjusting hgweb splitup to be a little cleaner.
r2392 self.webdirmaker = hgwebdir
self.repoviewmaker = hgweb
Eric Hopper
This patch make several WSGI related alterations....
r2506 self.reqmaker = wsgiapplication(self.make_handler)
Brendan Cully
Make hgweb threads into daemon threads....
r2650 self.daemon_threads = True
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
This patch make several WSGI related alterations....
r2506 def make_handler(self):
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 if self.webdir_conf:
Thomas Arendsen Hein
Fixed 'hg serve --webdir-conf foo' which broke due to split of hgweb.
r2404 hgwebobj = self.webdirmaker(self.webdir_conf)
Eric Hopper
Splitting up hgweb so it's easier to change.
r2355 elif self.repo is not None:
hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
repo.origroot))
else:
Thomas Arendsen Hein
If local repository is missing, make error message clearer....
r3079 raise hg.RepoError(_("There is no Mercurial repository here"
" (.hg not found)"))
Eric Hopper
This patch make several WSGI related alterations....
r2506 return hgwebobj
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])