diff upmana/mercurial/hgweb/server.py @ 135:dcf4fbe09b70 beta

Traipse Beta 'OpenRPG' {091010-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Beta) Added Bookmarks Fix to Remote Admin Commands Minor fix to text based Server Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Added 'boot' command to remote admin Added confirmation window for sent nodes Minor changes to allow for portability to an OpenSUSE linux OS Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Zoom Mouse plugin added Images added to Plugin UI Switching to Element Tree Map efficiency, from FlexiRPG Added Status Bar to Update Manager default_manifest.xml renamed to default_upmana.xml Cleaner clode for saved repositories New TrueDebug Class in orpg_log (See documentation for usage) Mercurial's hgweb folder is ported to upmana **Pretty important update that can help remove thousands of dead children from your gametree. **Children, <forms />, <group_atts />, <horizontal />, <cols />, <rows />, <height />, etc... are all tags now. Check your gametree and look for dead children!! **New Gamtree Recusion method, mapping, and context sensitivity. !!Alpha - Watch out for infinite loops!!
author sirebral
date Tue, 10 Nov 2009 14:11:28 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upmana/mercurial/hgweb/server.py	Tue Nov 10 14:11:28 2009 -0600
@@ -0,0 +1,298 @@
+# hgweb/server.py - The standalone hg web server.
+#
+# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
+# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
+from upmana.mercurial import hg, util, error
+from hgweb_mod import hgweb
+from hgwebdir_mod import hgwebdir
+from upmana.mercurial.i18n import _
+
+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
+
+class _error_logger(object):
+    def __init__(self, handler):
+        self.handler = handler
+    def flush(self):
+        pass
+    def write(self, str):
+        self.writelines(str.split('\n'))
+    def writelines(self, seq):
+        for msg in seq:
+            self.handler.log_error("HG error:  %s", msg)
+
+class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
+
+    url_scheme = 'http'
+
+    def __init__(self, *args, **kargs):
+        self.protocol_version = 'HTTP/1.1'
+        BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
+
+    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()
+
+    def log_error(self, format, *args):
+        self._log_any(self.server.errorlog, format, *args)
+
+    def log_message(self, format, *args):
+        self._log_any(self.server.accesslog, format, *args)
+
+    def do_write(self):
+        try:
+            self.do_hgweb()
+        except socket.error, inst:
+            if inst[0] != errno.EPIPE:
+                raise
+
+    def do_POST(self):
+        try:
+            self.do_write()
+        except StandardError:
+            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)
+
+    def do_GET(self):
+        self.do_POST()
+
+    def do_hgweb(self):
+        path, 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)
+        env['REQUEST_URI'] = self.path
+        env['SCRIPT_NAME'] = self.server.prefix
+        env['PATH_INFO'] = path[len(self.server.prefix):]
+        env['REMOTE_HOST'] = self.client_address[0]
+        env['REMOTE_ADDR'] = self.client_address[0]
+        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
+        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
+        env['wsgi.version'] = (1, 0)
+        env['wsgi.url_scheme'] = self.url_scheme
+        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
+
+        self.close_connection = True
+        self.saved_status = None
+        self.saved_headers = []
+        self.sent_headers = False
+        self.length = None
+        for chunk in self.server.application(env, self._start_response):
+            self._write(chunk)
+
+    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)
+        should_close = True
+        for h in self.saved_headers:
+            self.send_header(*h)
+            if h[0].lower() == 'content-length':
+                should_close = False
+                self.length = int(h[1])
+        # The value of the Connection header is a list of case-insensitive
+        # tokens separated by commas and optional whitespace.
+        if 'close' in [token.strip().lower() for token in
+                       self.headers.get('connection', '').split(',')]:
+            should_close = True
+        if should_close:
+            self.send_header('Connection', 'close')
+        self.close_connection = should_close
+        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
+        bad_headers = ('connection', 'transfer-encoding')
+        self.saved_headers = [h for h in headers
+                              if h[0].lower() not in bad_headers]
+        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()
+        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)
+        self.wfile.write(data)
+        self.wfile.flush()
+
+class _shgwebhandler(_hgwebhandler):
+
+    url_scheme = 'https'
+
+    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):
+        from OpenSSL.SSL import SysCallError
+        try:
+            super(_shgwebhandler, self).do_write()
+        except SysCallError, inst:
+            if inst.args[0] != errno.EPIPE:
+                raise
+
+    def handle_one_request(self):
+        from OpenSSL.SSL import SysCallError, ZeroReturnError
+        try:
+            super(_shgwebhandler, self).handle_one_request()
+        except (SysCallError, ZeroReturnError):
+            self.close_connection = True
+            pass
+
+def create_server(ui, repo):
+    use_threads = True
+
+    def openlog(opt, default):
+        if opt and opt != '-':
+            return open(opt, 'a')
+        return default
+
+    if repo is None:
+        myui = ui
+    else:
+        myui = repo.ui
+    address = myui.config("web", "address", "")
+    port = int(myui.config("web", "port", 8000))
+    prefix = myui.config("web", "prefix", "")
+    if prefix:
+        prefix = "/" + prefix.strip("/")
+    use_ipv6 = myui.configbool("web", "ipv6")
+    webdir_conf = myui.config("web", "webdir_conf")
+    ssl_cert = myui.config("web", "certificate")
+    accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
+    errorlog = openlog(myui.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):
+
+        # SO_REUSEADDR has broken semantics on windows
+        if os.name == 'nt':
+            allow_reuse_address = 0
+
+        def __init__(self, *args, **kargs):
+            BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
+            self.accesslog = accesslog
+            self.errorlog = errorlog
+            self.daemon_threads = True
+            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 error.RepoError(_("There is no Mercurial repository"
+                                            " here (.hg not found)"))
+                return hgwebobj
+            self.application = make_handler()
+
+            if ssl_cert:
+                try:
+                    from OpenSSL import SSL
+                    ctx = SSL.Context(SSL.SSLv23_METHOD)
+                except ImportError:
+                    raise util.Abort(_("SSL support is unavailable"))
+                ctx.use_privatekey_file(ssl_cert)
+                ctx.use_certificate_file(ssl_cert)
+                sock = socket.socket(self.address_family, self.socket_type)
+                self.socket = SSL.Connection(ctx, sock)
+                self.server_bind()
+                self.server_activate()
+
+            self.addr, self.port = self.socket.getsockname()[0:2]
+            self.prefix = prefix
+            self.fqaddr = socket.getfqdn(address)
+
+    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)
+
+    if ssl_cert:
+        handler = _shgwebhandler
+    else:
+        handler = _hgwebhandler
+
+    # ugly hack due to python issue5853 (for threaded use)
+    import mimetypes; mimetypes.init()
+
+    try:
+        if use_ipv6:
+            return IPv6HTTPServer((address, port), handler)
+        else:
+            return MercurialHTTPServer((address, port), handler)
+    except socket.error, inst:
+        raise util.Abort(_("cannot start server at '%s:%d': %s")
+                         % (address, port, inst.args[1]))