mercurial/hgweb/server.py
changeset 2355 eb08fb4d41e1
parent 2328 f789602ba840
child 2391 d351a3be3371
equal deleted inserted replaced
2352:61909dfb316d 2355:eb08fb4d41e1
       
     1 # hgweb.py - web interface to a mercurial repository
       
     2 #
       
     3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
       
     4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
       
     5 #
       
     6 # This software may be used and distributed according to the terms
       
     7 # of the GNU General Public License, incorporated herein by reference.
       
     8 
       
     9 from mercurial.demandload import demandload
       
    10 import os, sys, errno
       
    11 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
       
    12 demandload(globals(), "mercurial:ui,hg,util,templater")
       
    13 demandload(globals(), "mercurial.hgweb.request:hgrequest")
       
    14 from mercurial.i18n import gettext as _
       
    15 
       
    16 def _splitURI(uri):
       
    17     """ Return path and query splited from uri
       
    18 
       
    19     Just like CGI environment, the path is unquoted, the query is
       
    20     not.
       
    21     """
       
    22     if '?' in uri:
       
    23         path, query = uri.split('?', 1)
       
    24     else:
       
    25         path, query = uri, ''
       
    26     return urllib.unquote(path), query
       
    27 
       
    28 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
       
    29     def __init__(self, *args, **kargs):
       
    30         BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
       
    31 
       
    32     def log_error(self, format, *args):
       
    33         errorlog = self.server.errorlog
       
    34         errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
       
    35                                              self.log_date_time_string(),
       
    36                                              format % args))
       
    37 
       
    38     def log_message(self, format, *args):
       
    39         accesslog = self.server.accesslog
       
    40         accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
       
    41                                               self.log_date_time_string(),
       
    42                                               format % args))
       
    43 
       
    44     def do_POST(self):
       
    45         try:
       
    46             self.do_hgweb()
       
    47         except socket.error, inst:
       
    48             if inst[0] != errno.EPIPE:
       
    49                 raise
       
    50 
       
    51     def do_GET(self):
       
    52         self.do_POST()
       
    53 
       
    54     def do_hgweb(self):
       
    55         path_info, query = _splitURI(self.path)
       
    56 
       
    57         env = {}
       
    58         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
       
    59         env['REQUEST_METHOD'] = self.command
       
    60         env['SERVER_NAME'] = self.server.server_name
       
    61         env['SERVER_PORT'] = str(self.server.server_port)
       
    62         env['REQUEST_URI'] = "/"
       
    63         env['PATH_INFO'] = path_info
       
    64         if query:
       
    65             env['QUERY_STRING'] = query
       
    66         host = self.address_string()
       
    67         if host != self.client_address[0]:
       
    68             env['REMOTE_HOST'] = host
       
    69             env['REMOTE_ADDR'] = self.client_address[0]
       
    70 
       
    71         if self.headers.typeheader is None:
       
    72             env['CONTENT_TYPE'] = self.headers.type
       
    73         else:
       
    74             env['CONTENT_TYPE'] = self.headers.typeheader
       
    75         length = self.headers.getheader('content-length')
       
    76         if length:
       
    77             env['CONTENT_LENGTH'] = length
       
    78         accept = []
       
    79         for line in self.headers.getallmatchingheaders('accept'):
       
    80             if line[:1] in "\t\n\r ":
       
    81                 accept.append(line.strip())
       
    82             else:
       
    83                 accept = accept + line[7:].split(',')
       
    84         env['HTTP_ACCEPT'] = ','.join(accept)
       
    85 
       
    86         req = hgrequest(self.rfile, self.wfile, env)
       
    87         self.send_response(200, "Script output follows")
       
    88         self.server.make_and_run_handler(req)
       
    89 
       
    90 def create_server(ui, repo, webdirmaker, repoviewmaker):
       
    91     use_threads = True
       
    92 
       
    93     def openlog(opt, default):
       
    94         if opt and opt != '-':
       
    95             return open(opt, 'w')
       
    96         return default
       
    97 
       
    98     address = ui.config("web", "address", "")
       
    99     port = int(ui.config("web", "port", 8000))
       
   100     use_ipv6 = ui.configbool("web", "ipv6")
       
   101     webdir_conf = ui.config("web", "webdir_conf")
       
   102     accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
       
   103     errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
       
   104 
       
   105     if use_threads:
       
   106         try:
       
   107             from threading import activeCount
       
   108         except ImportError:
       
   109             use_threads = False
       
   110 
       
   111     if use_threads:
       
   112         _mixin = SocketServer.ThreadingMixIn
       
   113     else:
       
   114         if hasattr(os, "fork"):
       
   115             _mixin = SocketServer.ForkingMixIn
       
   116         else:
       
   117             class _mixin: pass
       
   118 
       
   119     class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
       
   120         def __init__(self, *args, **kargs):
       
   121             BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
       
   122             self.accesslog = accesslog
       
   123             self.errorlog = errorlog
       
   124             self.repo = repo
       
   125             self.webdir_conf = webdir_conf
       
   126             self.webdirmaker = webdirmaker
       
   127             self.repoviewmaker = repoviewmaker
       
   128 
       
   129         def make_and_run_handler(self, req):
       
   130             if self.webdir_conf:
       
   131                 hgwebobj = self.webdirmaker(self.server.webdir_conf)
       
   132             elif self.repo is not None:
       
   133                 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
       
   134                                                              repo.origroot))
       
   135             else:
       
   136                 raise hg.RepoError(_('no repo found'))
       
   137             hgwebobj.run(req)
       
   138 
       
   139     class IPv6HTTPServer(MercurialHTTPServer):
       
   140         address_family = getattr(socket, 'AF_INET6', None)
       
   141 
       
   142         def __init__(self, *args, **kwargs):
       
   143             if self.address_family is None:
       
   144                 raise hg.RepoError(_('IPv6 not available on this system'))
       
   145             super(IPv6HTTPServer, self).__init__(*args, **kargs)
       
   146 
       
   147     if use_ipv6:
       
   148         return IPv6HTTPServer((address, port), _hgwebhandler)
       
   149     else:
       
   150         return MercurialHTTPServer((address, port), _hgwebhandler)