mercurial/hgweb/__init__.py
changeset 2355 eb08fb4d41e1
parent 2328 f789602ba840
child 2356 2db831b33e8f
equal deleted inserted replaced
2352:61909dfb316d 2355:eb08fb4d41e1
     8 
     8 
     9 import os, cgi, sys
     9 import os, cgi, sys
    10 import mimetypes
    10 import mimetypes
    11 from mercurial.demandload import demandload
    11 from mercurial.demandload import demandload
    12 demandload(globals(), "time re socket zlib errno ConfigParser tempfile")
    12 demandload(globals(), "time re socket zlib errno ConfigParser tempfile")
    13 demandload(globals(), "StringIO BaseHTTPServer SocketServer urllib")
       
    14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
    13 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
       
    14 demandload(globals(), "mercurial.hgweb.request:hgrequest")
       
    15 demandload(globals(), "mercurial.hgweb.server:create_server")
    15 from mercurial.node import *
    16 from mercurial.node import *
    16 from mercurial.i18n import gettext as _
    17 from mercurial.i18n import gettext as _
    17 
       
    18 def splitURI(uri):
       
    19     """ Return path and query splited from uri
       
    20 
       
    21     Just like CGI environment, the path is unquoted, the query is
       
    22     not.
       
    23     """
       
    24     if '?' in uri:
       
    25         path, query = uri.split('?', 1)
       
    26     else:
       
    27         path, query = uri, ''
       
    28     return urllib.unquote(path), query
       
    29 
    18 
    30 def up(p):
    19 def up(p):
    31     if p[0] != "/":
    20     if p[0] != "/":
    32         p = "/" + p
    21         p = "/" + p
    33     if p[-1] == "/":
    22     if p[-1] == "/":
    66         ct = mimetypes.guess_type(path)[0] or "text/plain"
    55         ct = mimetypes.guess_type(path)[0] or "text/plain"
    67         return "Content-type: %s\n\n%s" % (ct, file(path).read())
    56         return "Content-type: %s\n\n%s" % (ct, file(path).read())
    68     except (TypeError, OSError):
    57     except (TypeError, OSError):
    69         # illegal fname or unreadable file
    58         # illegal fname or unreadable file
    70         return ""
    59         return ""
    71 
       
    72 class hgrequest(object):
       
    73     def __init__(self, inp=None, out=None, env=None):
       
    74         self.inp = inp or sys.stdin
       
    75         self.out = out or sys.stdout
       
    76         self.env = env or os.environ
       
    77         self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
       
    78 
       
    79     def write(self, *things):
       
    80         for thing in things:
       
    81             if hasattr(thing, "__iter__"):
       
    82                 for part in thing:
       
    83                     self.write(part)
       
    84             else:
       
    85                 try:
       
    86                     self.out.write(str(thing))
       
    87                 except socket.error, inst:
       
    88                     if inst[0] != errno.ECONNRESET:
       
    89                         raise
       
    90 
       
    91     def header(self, headers=[('Content-type','text/html')]):
       
    92         for header in headers:
       
    93             self.out.write("%s: %s\r\n" % header)
       
    94         self.out.write("\r\n")
       
    95 
       
    96     def httphdr(self, type, file="", size=0):
       
    97 
       
    98         headers = [('Content-type', type)]
       
    99         if file:
       
   100             headers.append(('Content-disposition', 'attachment; filename=%s' % file))
       
   101         if size > 0:
       
   102             headers.append(('Content-length', str(size)))
       
   103         self.header(headers)
       
   104 
    60 
   105 class hgweb(object):
    61 class hgweb(object):
   106     def __init__(self, repo, name=None):
    62     def __init__(self, repo, name=None):
   107         if type(repo) == type(""):
    63         if type(repo) == type(""):
   108             self.repo = hg.repository(ui.ui(), repo)
    64             self.repo = hg.repository(ui.ui(), repo)
   890             req.write(staticfile(static, fname)
   846             req.write(staticfile(static, fname)
   891                       or self.t("error", error="%r not found" % fname))
   847                       or self.t("error", error="%r not found" % fname))
   892 
   848 
   893         else:
   849         else:
   894             req.write(self.t("error"))
   850             req.write(self.t("error"))
   895 
       
   896 def create_server(ui, repo):
       
   897     use_threads = True
       
   898 
       
   899     def openlog(opt, default):
       
   900         if opt and opt != '-':
       
   901             return open(opt, 'w')
       
   902         return default
       
   903 
       
   904     address = ui.config("web", "address", "")
       
   905     port = int(ui.config("web", "port", 8000))
       
   906     use_ipv6 = ui.configbool("web", "ipv6")
       
   907     webdir_conf = ui.config("web", "webdir_conf")
       
   908     accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
       
   909     errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
       
   910 
       
   911     if use_threads:
       
   912         try:
       
   913             from threading import activeCount
       
   914         except ImportError:
       
   915             use_threads = False
       
   916 
       
   917     if use_threads:
       
   918         _mixin = SocketServer.ThreadingMixIn
       
   919     else:
       
   920         if hasattr(os, "fork"):
       
   921             _mixin = SocketServer.ForkingMixIn
       
   922         else:
       
   923             class _mixin: pass
       
   924 
       
   925     class MercurialHTTPServer(_mixin, BaseHTTPServer.HTTPServer):
       
   926         pass
       
   927 
       
   928     class IPv6HTTPServer(MercurialHTTPServer):
       
   929         address_family = getattr(socket, 'AF_INET6', None)
       
   930 
       
   931         def __init__(self, *args, **kwargs):
       
   932             if self.address_family is None:
       
   933                 raise hg.RepoError(_('IPv6 not available on this system'))
       
   934             BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
       
   935 
       
   936     class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
       
   937 
       
   938         def log_error(self, format, *args):
       
   939             errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
       
   940                                                  self.log_date_time_string(),
       
   941                                                  format % args))
       
   942 
       
   943         def log_message(self, format, *args):
       
   944             accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
       
   945                                                   self.log_date_time_string(),
       
   946                                                   format % args))
       
   947 
       
   948         def do_POST(self):
       
   949             try:
       
   950                 self.do_hgweb()
       
   951             except socket.error, inst:
       
   952                 if inst[0] != errno.EPIPE:
       
   953                     raise
       
   954 
       
   955         def do_GET(self):
       
   956             self.do_POST()
       
   957 
       
   958         def do_hgweb(self):
       
   959             path_info, query = splitURI(self.path)
       
   960 
       
   961             env = {}
       
   962             env['GATEWAY_INTERFACE'] = 'CGI/1.1'
       
   963             env['REQUEST_METHOD'] = self.command
       
   964             env['SERVER_NAME'] = self.server.server_name
       
   965             env['SERVER_PORT'] = str(self.server.server_port)
       
   966             env['REQUEST_URI'] = "/"
       
   967             env['PATH_INFO'] = path_info
       
   968             if query:
       
   969                 env['QUERY_STRING'] = query
       
   970             host = self.address_string()
       
   971             if host != self.client_address[0]:
       
   972                 env['REMOTE_HOST'] = host
       
   973                 env['REMOTE_ADDR'] = self.client_address[0]
       
   974 
       
   975             if self.headers.typeheader is None:
       
   976                 env['CONTENT_TYPE'] = self.headers.type
       
   977             else:
       
   978                 env['CONTENT_TYPE'] = self.headers.typeheader
       
   979             length = self.headers.getheader('content-length')
       
   980             if length:
       
   981                 env['CONTENT_LENGTH'] = length
       
   982             accept = []
       
   983             for line in self.headers.getallmatchingheaders('accept'):
       
   984                 if line[:1] in "\t\n\r ":
       
   985                     accept.append(line.strip())
       
   986                 else:
       
   987                     accept = accept + line[7:].split(',')
       
   988             env['HTTP_ACCEPT'] = ','.join(accept)
       
   989 
       
   990             req = hgrequest(self.rfile, self.wfile, env)
       
   991             self.send_response(200, "Script output follows")
       
   992 
       
   993             if webdir_conf:
       
   994                 hgwebobj = hgwebdir(webdir_conf)
       
   995             elif repo is not None:
       
   996                 hgwebobj = hgweb(repo.__class__(repo.ui, repo.origroot))
       
   997             else:
       
   998                 raise hg.RepoError(_('no repo found'))
       
   999             hgwebobj.run(req)
       
  1000 
       
  1001 
       
  1002     if use_ipv6:
       
  1003         return IPv6HTTPServer((address, port), hgwebhandler)
       
  1004     else:
       
  1005         return MercurialHTTPServer((address, port), hgwebhandler)
       
  1006 
   851 
  1007 # This is a stopgap
   852 # This is a stopgap
  1008 class hgwebdir(object):
   853 class hgwebdir(object):
  1009     def __init__(self, config):
   854     def __init__(self, config):
  1010         def cleannames(items):
   855         def cleannames(items):