|
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) |