##// END OF EJS Templates
use untrusted settings in hgweb...
use untrusted settings in hgweb The only exceptions are web.static and web.templates, since they can be used to get any file that is readable by the user running the CGI script. Other options can be (ab)used to increase the use of the cpu (allow_bz2) or of the bandwidth (server.uncompressed), but they're trusted anyway.

File last commit:

r3263:3207e30b default
r3555:88106400 default
Show More
server.py
225 lines | 8.3 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
demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
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
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:
class _mixin: pass
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
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
if use_ipv6:
return IPv6HTTPServer((address, port), _hgwebhandler)
else:
return MercurialHTTPServer((address, port), _hgwebhandler)