##// END OF EJS Templates
server: append to logfiles
Mirko Friedenhagen -
r5690:1b365c57 default
parent child Browse files
Show More
@@ -1,290 +1,290
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 from mercurial import ui, hg, util, templater
10 from mercurial import ui, hg, util, templater
11 from hgweb_mod import hgweb
11 from hgweb_mod import hgweb
12 from hgwebdir_mod import hgwebdir
12 from hgwebdir_mod import hgwebdir
13 from mercurial.i18n import gettext as _
13 from mercurial.i18n import gettext as _
14
14
15 def _splitURI(uri):
15 def _splitURI(uri):
16 """ Return path and query splited from uri
16 """ Return path and query splited from uri
17
17
18 Just like CGI environment, the path is unquoted, the query is
18 Just like CGI environment, the path is unquoted, the query is
19 not.
19 not.
20 """
20 """
21 if '?' in uri:
21 if '?' in uri:
22 path, query = uri.split('?', 1)
22 path, query = uri.split('?', 1)
23 else:
23 else:
24 path, query = uri, ''
24 path, query = uri, ''
25 return urllib.unquote(path), query
25 return urllib.unquote(path), query
26
26
27 class _error_logger(object):
27 class _error_logger(object):
28 def __init__(self, handler):
28 def __init__(self, handler):
29 self.handler = handler
29 self.handler = handler
30 def flush(self):
30 def flush(self):
31 pass
31 pass
32 def write(self, str):
32 def write(self, str):
33 self.writelines(str.split('\n'))
33 self.writelines(str.split('\n'))
34 def writelines(self, seq):
34 def writelines(self, seq):
35 for msg in seq:
35 for msg in seq:
36 self.handler.log_error("HG error: %s", msg)
36 self.handler.log_error("HG error: %s", msg)
37
37
38 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
38 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
39
39
40 url_scheme = 'http'
40 url_scheme = 'http'
41
41
42 def __init__(self, *args, **kargs):
42 def __init__(self, *args, **kargs):
43 self.protocol_version = 'HTTP/1.1'
43 self.protocol_version = 'HTTP/1.1'
44 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
44 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
45
45
46 def _log_any(self, fp, format, *args):
46 def _log_any(self, fp, format, *args):
47 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
47 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
48 self.log_date_time_string(),
48 self.log_date_time_string(),
49 format % args))
49 format % args))
50 fp.flush()
50 fp.flush()
51
51
52 def log_error(self, format, *args):
52 def log_error(self, format, *args):
53 self._log_any(self.server.errorlog, format, *args)
53 self._log_any(self.server.errorlog, format, *args)
54
54
55 def log_message(self, format, *args):
55 def log_message(self, format, *args):
56 self._log_any(self.server.accesslog, format, *args)
56 self._log_any(self.server.accesslog, format, *args)
57
57
58 def do_write(self):
58 def do_write(self):
59 try:
59 try:
60 self.do_hgweb()
60 self.do_hgweb()
61 except socket.error, inst:
61 except socket.error, inst:
62 if inst[0] != errno.EPIPE:
62 if inst[0] != errno.EPIPE:
63 raise
63 raise
64
64
65 def do_POST(self):
65 def do_POST(self):
66 try:
66 try:
67 self.do_write()
67 self.do_write()
68 except StandardError, inst:
68 except StandardError, inst:
69 self._start_response("500 Internal Server Error", [])
69 self._start_response("500 Internal Server Error", [])
70 self._write("Internal Server Error")
70 self._write("Internal Server Error")
71 tb = "".join(traceback.format_exception(*sys.exc_info()))
71 tb = "".join(traceback.format_exception(*sys.exc_info()))
72 self.log_error("Exception happened during processing request '%s':\n%s",
72 self.log_error("Exception happened during processing request '%s':\n%s",
73 self.path, tb)
73 self.path, tb)
74
74
75 def do_GET(self):
75 def do_GET(self):
76 self.do_POST()
76 self.do_POST()
77
77
78 def do_hgweb(self):
78 def do_hgweb(self):
79 path_info, query = _splitURI(self.path)
79 path_info, query = _splitURI(self.path)
80
80
81 env = {}
81 env = {}
82 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
82 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
83 env['REQUEST_METHOD'] = self.command
83 env['REQUEST_METHOD'] = self.command
84 env['SERVER_NAME'] = self.server.server_name
84 env['SERVER_NAME'] = self.server.server_name
85 env['SERVER_PORT'] = str(self.server.server_port)
85 env['SERVER_PORT'] = str(self.server.server_port)
86 env['REQUEST_URI'] = self.path
86 env['REQUEST_URI'] = self.path
87 env['SCRIPT_NAME'] = ''
87 env['SCRIPT_NAME'] = ''
88 env['PATH_INFO'] = path_info
88 env['PATH_INFO'] = path_info
89 env['REMOTE_HOST'] = self.client_address[0]
89 env['REMOTE_HOST'] = self.client_address[0]
90 env['REMOTE_ADDR'] = self.client_address[0]
90 env['REMOTE_ADDR'] = self.client_address[0]
91 if query:
91 if query:
92 env['QUERY_STRING'] = query
92 env['QUERY_STRING'] = query
93
93
94 if self.headers.typeheader is None:
94 if self.headers.typeheader is None:
95 env['CONTENT_TYPE'] = self.headers.type
95 env['CONTENT_TYPE'] = self.headers.type
96 else:
96 else:
97 env['CONTENT_TYPE'] = self.headers.typeheader
97 env['CONTENT_TYPE'] = self.headers.typeheader
98 length = self.headers.getheader('content-length')
98 length = self.headers.getheader('content-length')
99 if length:
99 if length:
100 env['CONTENT_LENGTH'] = length
100 env['CONTENT_LENGTH'] = length
101 for header in [h for h in self.headers.keys()
101 for header in [h for h in self.headers.keys()
102 if h not in ('content-type', 'content-length')]:
102 if h not in ('content-type', 'content-length')]:
103 hkey = 'HTTP_' + header.replace('-', '_').upper()
103 hkey = 'HTTP_' + header.replace('-', '_').upper()
104 hval = self.headers.getheader(header)
104 hval = self.headers.getheader(header)
105 hval = hval.replace('\n', '').strip()
105 hval = hval.replace('\n', '').strip()
106 if hval:
106 if hval:
107 env[hkey] = hval
107 env[hkey] = hval
108 env['SERVER_PROTOCOL'] = self.request_version
108 env['SERVER_PROTOCOL'] = self.request_version
109 env['wsgi.version'] = (1, 0)
109 env['wsgi.version'] = (1, 0)
110 env['wsgi.url_scheme'] = self.url_scheme
110 env['wsgi.url_scheme'] = self.url_scheme
111 env['wsgi.input'] = self.rfile
111 env['wsgi.input'] = self.rfile
112 env['wsgi.errors'] = _error_logger(self)
112 env['wsgi.errors'] = _error_logger(self)
113 env['wsgi.multithread'] = isinstance(self.server,
113 env['wsgi.multithread'] = isinstance(self.server,
114 SocketServer.ThreadingMixIn)
114 SocketServer.ThreadingMixIn)
115 env['wsgi.multiprocess'] = isinstance(self.server,
115 env['wsgi.multiprocess'] = isinstance(self.server,
116 SocketServer.ForkingMixIn)
116 SocketServer.ForkingMixIn)
117 env['wsgi.run_once'] = 0
117 env['wsgi.run_once'] = 0
118
118
119 self.close_connection = True
119 self.close_connection = True
120 self.saved_status = None
120 self.saved_status = None
121 self.saved_headers = []
121 self.saved_headers = []
122 self.sent_headers = False
122 self.sent_headers = False
123 self.length = None
123 self.length = None
124 self.server.application(env, self._start_response)
124 self.server.application(env, self._start_response)
125
125
126 def send_headers(self):
126 def send_headers(self):
127 if not self.saved_status:
127 if not self.saved_status:
128 raise AssertionError("Sending headers before start_response() called")
128 raise AssertionError("Sending headers before start_response() called")
129 saved_status = self.saved_status.split(None, 1)
129 saved_status = self.saved_status.split(None, 1)
130 saved_status[0] = int(saved_status[0])
130 saved_status[0] = int(saved_status[0])
131 self.send_response(*saved_status)
131 self.send_response(*saved_status)
132 should_close = True
132 should_close = True
133 for h in self.saved_headers:
133 for h in self.saved_headers:
134 self.send_header(*h)
134 self.send_header(*h)
135 if h[0].lower() == 'content-length':
135 if h[0].lower() == 'content-length':
136 should_close = False
136 should_close = False
137 self.length = int(h[1])
137 self.length = int(h[1])
138 # The value of the Connection header is a list of case-insensitive
138 # The value of the Connection header is a list of case-insensitive
139 # tokens separated by commas and optional whitespace.
139 # tokens separated by commas and optional whitespace.
140 if 'close' in [token.strip().lower() for token in
140 if 'close' in [token.strip().lower() for token in
141 self.headers.get('connection', '').split(',')]:
141 self.headers.get('connection', '').split(',')]:
142 should_close = True
142 should_close = True
143 if should_close:
143 if should_close:
144 self.send_header('Connection', 'close')
144 self.send_header('Connection', 'close')
145 self.close_connection = should_close
145 self.close_connection = should_close
146 self.end_headers()
146 self.end_headers()
147 self.sent_headers = True
147 self.sent_headers = True
148
148
149 def _start_response(self, http_status, headers, exc_info=None):
149 def _start_response(self, http_status, headers, exc_info=None):
150 code, msg = http_status.split(None, 1)
150 code, msg = http_status.split(None, 1)
151 code = int(code)
151 code = int(code)
152 self.saved_status = http_status
152 self.saved_status = http_status
153 bad_headers = ('connection', 'transfer-encoding')
153 bad_headers = ('connection', 'transfer-encoding')
154 self.saved_headers = [h for h in headers
154 self.saved_headers = [h for h in headers
155 if h[0].lower() not in bad_headers]
155 if h[0].lower() not in bad_headers]
156 return self._write
156 return self._write
157
157
158 def _write(self, data):
158 def _write(self, data):
159 if not self.saved_status:
159 if not self.saved_status:
160 raise AssertionError("data written before start_response() called")
160 raise AssertionError("data written before start_response() called")
161 elif not self.sent_headers:
161 elif not self.sent_headers:
162 self.send_headers()
162 self.send_headers()
163 if self.length is not None:
163 if self.length is not None:
164 if len(data) > self.length:
164 if len(data) > self.length:
165 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
165 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
166 self.length = self.length - len(data)
166 self.length = self.length - len(data)
167 self.wfile.write(data)
167 self.wfile.write(data)
168 self.wfile.flush()
168 self.wfile.flush()
169
169
170 class _shgwebhandler(_hgwebhandler):
170 class _shgwebhandler(_hgwebhandler):
171
171
172 url_scheme = 'https'
172 url_scheme = 'https'
173
173
174 def setup(self):
174 def setup(self):
175 self.connection = self.request
175 self.connection = self.request
176 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
176 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
177 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
177 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
178
178
179 def do_write(self):
179 def do_write(self):
180 from OpenSSL.SSL import SysCallError
180 from OpenSSL.SSL import SysCallError
181 try:
181 try:
182 super(_shgwebhandler, self).do_write()
182 super(_shgwebhandler, self).do_write()
183 except SysCallError, inst:
183 except SysCallError, inst:
184 if inst.args[0] != errno.EPIPE:
184 if inst.args[0] != errno.EPIPE:
185 raise
185 raise
186
186
187 def handle_one_request(self):
187 def handle_one_request(self):
188 from OpenSSL.SSL import SysCallError, ZeroReturnError
188 from OpenSSL.SSL import SysCallError, ZeroReturnError
189 try:
189 try:
190 super(_shgwebhandler, self).handle_one_request()
190 super(_shgwebhandler, self).handle_one_request()
191 except (SysCallError, ZeroReturnError):
191 except (SysCallError, ZeroReturnError):
192 self.close_connection = True
192 self.close_connection = True
193 pass
193 pass
194
194
195 def create_server(ui, repo):
195 def create_server(ui, repo):
196 use_threads = True
196 use_threads = True
197
197
198 def openlog(opt, default):
198 def openlog(opt, default):
199 if opt and opt != '-':
199 if opt and opt != '-':
200 return open(opt, 'w')
200 return open(opt, 'a')
201 return default
201 return default
202
202
203 if repo is None:
203 if repo is None:
204 myui = ui
204 myui = ui
205 else:
205 else:
206 myui = repo.ui
206 myui = repo.ui
207 address = myui.config("web", "address", "")
207 address = myui.config("web", "address", "")
208 port = int(myui.config("web", "port", 8000))
208 port = int(myui.config("web", "port", 8000))
209 use_ipv6 = myui.configbool("web", "ipv6")
209 use_ipv6 = myui.configbool("web", "ipv6")
210 webdir_conf = myui.config("web", "webdir_conf")
210 webdir_conf = myui.config("web", "webdir_conf")
211 ssl_cert = myui.config("web", "certificate")
211 ssl_cert = myui.config("web", "certificate")
212 accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
212 accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
213 errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
213 errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
214
214
215 if use_threads:
215 if use_threads:
216 try:
216 try:
217 from threading import activeCount
217 from threading import activeCount
218 except ImportError:
218 except ImportError:
219 use_threads = False
219 use_threads = False
220
220
221 if use_threads:
221 if use_threads:
222 _mixin = SocketServer.ThreadingMixIn
222 _mixin = SocketServer.ThreadingMixIn
223 else:
223 else:
224 if hasattr(os, "fork"):
224 if hasattr(os, "fork"):
225 _mixin = SocketServer.ForkingMixIn
225 _mixin = SocketServer.ForkingMixIn
226 else:
226 else:
227 class _mixin:
227 class _mixin:
228 pass
228 pass
229
229
230 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
230 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
231
231
232 # SO_REUSEADDR has broken semantics on windows
232 # SO_REUSEADDR has broken semantics on windows
233 if os.name == 'nt':
233 if os.name == 'nt':
234 allow_reuse_address = 0
234 allow_reuse_address = 0
235
235
236 def __init__(self, *args, **kargs):
236 def __init__(self, *args, **kargs):
237 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
237 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
238 self.accesslog = accesslog
238 self.accesslog = accesslog
239 self.errorlog = errorlog
239 self.errorlog = errorlog
240 self.daemon_threads = True
240 self.daemon_threads = True
241 def make_handler():
241 def make_handler():
242 if webdir_conf:
242 if webdir_conf:
243 hgwebobj = hgwebdir(webdir_conf, ui)
243 hgwebobj = hgwebdir(webdir_conf, ui)
244 elif repo is not None:
244 elif repo is not None:
245 hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
245 hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
246 else:
246 else:
247 raise hg.RepoError(_("There is no Mercurial repository here"
247 raise hg.RepoError(_("There is no Mercurial repository here"
248 " (.hg not found)"))
248 " (.hg not found)"))
249 return hgwebobj
249 return hgwebobj
250 self.application = make_handler()
250 self.application = make_handler()
251
251
252 addr = address
252 addr = address
253 if addr in ('', '::'):
253 if addr in ('', '::'):
254 addr = socket.gethostname()
254 addr = socket.gethostname()
255
255
256 self.addr, self.port = addr, port
256 self.addr, self.port = addr, port
257
257
258 if ssl_cert:
258 if ssl_cert:
259 try:
259 try:
260 from OpenSSL import SSL
260 from OpenSSL import SSL
261 ctx = SSL.Context(SSL.SSLv23_METHOD)
261 ctx = SSL.Context(SSL.SSLv23_METHOD)
262 except ImportError:
262 except ImportError:
263 raise util.Abort("SSL support is unavailable")
263 raise util.Abort("SSL support is unavailable")
264 ctx.use_privatekey_file(ssl_cert)
264 ctx.use_privatekey_file(ssl_cert)
265 ctx.use_certificate_file(ssl_cert)
265 ctx.use_certificate_file(ssl_cert)
266 sock = socket.socket(self.address_family, self.socket_type)
266 sock = socket.socket(self.address_family, self.socket_type)
267 self.socket = SSL.Connection(ctx, sock)
267 self.socket = SSL.Connection(ctx, sock)
268 self.server_bind()
268 self.server_bind()
269 self.server_activate()
269 self.server_activate()
270
270
271 class IPv6HTTPServer(MercurialHTTPServer):
271 class IPv6HTTPServer(MercurialHTTPServer):
272 address_family = getattr(socket, 'AF_INET6', None)
272 address_family = getattr(socket, 'AF_INET6', None)
273
273
274 def __init__(self, *args, **kwargs):
274 def __init__(self, *args, **kwargs):
275 if self.address_family is None:
275 if self.address_family is None:
276 raise hg.RepoError(_('IPv6 not available on this system'))
276 raise hg.RepoError(_('IPv6 not available on this system'))
277 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
277 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
278
278
279 if ssl_cert:
279 if ssl_cert:
280 handler = _shgwebhandler
280 handler = _shgwebhandler
281 else:
281 else:
282 handler = _hgwebhandler
282 handler = _hgwebhandler
283
283
284 try:
284 try:
285 if use_ipv6:
285 if use_ipv6:
286 return IPv6HTTPServer((address, port), handler)
286 return IPv6HTTPServer((address, port), handler)
287 else:
287 else:
288 return MercurialHTTPServer((address, port), handler)
288 return MercurialHTTPServer((address, port), handler)
289 except socket.error, inst:
289 except socket.error, inst:
290 raise util.Abort(_('cannot start server: %s') % inst.args[1])
290 raise util.Abort(_('cannot start server: %s') % inst.args[1])
@@ -1,32 +1,39
1 #!/bin/sh
1 #!/bin/sh
2 # Some tests for hgweb. Tests static files, plain files and different 404's.
2 # Some tests for hgweb. Tests static files, plain files and different 404's.
3
3
4 hg init test
4 hg init test
5 cd test
5 cd test
6 mkdir da
6 mkdir da
7 echo foo > da/foo
7 echo foo > da/foo
8 echo foo > foo
8 echo foo > foo
9 hg ci -Ambase -d '0 0'
9 hg ci -Ambase -d '0 0'
10 hg serve -p $HGPORT -d --pid-file=hg.pid
10 hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
11 cat hg.pid >> $DAEMON_PIDS
11 cat hg.pid >> $DAEMON_PIDS
12 echo % manifest
12 echo % manifest
13 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
13 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
14 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw')
14 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw')
15
15
16 echo % plain file
16 echo % plain file
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw'
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw'
18
18
19 echo % should give a 404 - static file that does not exist
19 echo % should give a 404 - static file that does not exist
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus'
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus'
21
21
22 echo % should give a 404 - bad revision
22 echo % should give a 404 - bad revision
23 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw'
23 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw'
24
24
25 echo % should give a 400 - bad command
25 echo % should give a 400 - bad command
26 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' | sed 's/400.*/400/'
26 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' | sed 's/400.*/400/'
27
27
28 echo % should give a 404 - file does not exist
28 echo % should give a 404 - file does not exist
29 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
29 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
30
30
31 echo % stop and restart
32 kill `cat hg.pid`
33 hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
34 cat hg.pid >> $DAEMON_PIDS
35 # Test the access/error files are opened in append mode
36 python -c "print len(file('access.log').readlines()), 'log lines written'"
37
31 echo % static file
38 echo % static file
32 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
39 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
@@ -1,136 +1,138
1 adding da/foo
1 adding da/foo
2 adding foo
2 adding foo
3 % manifest
3 % manifest
4 200 Script output follows
4 200 Script output follows
5
5
6
6
7 drwxr-xr-x da
7 drwxr-xr-x da
8 -rw-r--r-- 4 foo
8 -rw-r--r-- 4 foo
9
9
10
10
11 200 Script output follows
11 200 Script output follows
12
12
13
13
14 -rw-r--r-- 4 foo
14 -rw-r--r-- 4 foo
15
15
16
16
17 % plain file
17 % plain file
18 200 Script output follows
18 200 Script output follows
19
19
20 foo
20 foo
21 % should give a 404 - static file that does not exist
21 % should give a 404 - static file that does not exist
22 404 Not Found
22 404 Not Found
23
23
24 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
24 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
25 <html>
25 <html>
26 <head>
26 <head>
27 <link rel="icon" href="/static/hgicon.png" type="image/png">
27 <link rel="icon" href="/static/hgicon.png" type="image/png">
28 <meta name="robots" content="index, nofollow" />
28 <meta name="robots" content="index, nofollow" />
29 <link rel="stylesheet" href="/static/style.css" type="text/css" />
29 <link rel="stylesheet" href="/static/style.css" type="text/css" />
30
30
31 <title>Mercurial Error</title>
31 <title>Mercurial Error</title>
32 </head>
32 </head>
33 <body>
33 <body>
34
34
35 <h2>Mercurial Error</h2>
35 <h2>Mercurial Error</h2>
36
36
37 <p>
37 <p>
38 An error occured while processing your request:
38 An error occured while processing your request:
39 </p>
39 </p>
40 <p>
40 <p>
41 Not Found
41 Not Found
42 </p>
42 </p>
43
43
44
44
45 <div class="logo">
45 <div class="logo">
46 powered by<br/>
46 powered by<br/>
47 <a href="http://www.selenic.com/mercurial/">mercurial</a>
47 <a href="http://www.selenic.com/mercurial/">mercurial</a>
48 </div>
48 </div>
49
49
50 </body>
50 </body>
51 </html>
51 </html>
52
52
53 % should give a 404 - bad revision
53 % should give a 404 - bad revision
54 404 Not Found
54 404 Not Found
55
55
56
56
57 error: revision not found: spam
57 error: revision not found: spam
58 % should give a 400 - bad command
58 % should give a 400 - bad command
59 400
59 400
60
60
61
61
62 error: No such method: spam
62 error: No such method: spam
63 % should give a 404 - file does not exist
63 % should give a 404 - file does not exist
64 404 Not Found
64 404 Not Found
65
65
66
66
67 error: Path not found: bork/
67 error: Path not found: bork/
68 % stop and restart
69 7 log lines written
68 % static file
70 % static file
69 200 Script output follows
71 200 Script output follows
70
72
71 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
73 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
72 a { color:#0000cc; }
74 a { color:#0000cc; }
73 a:hover, a:visited, a:active { color:#880000; }
75 a:hover, a:visited, a:active { color:#880000; }
74 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
76 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
75 div.page_header a:visited { color:#0000cc; }
77 div.page_header a:visited { color:#0000cc; }
76 div.page_header a:hover { color:#880000; }
78 div.page_header a:hover { color:#880000; }
77 div.page_nav { padding:8px; }
79 div.page_nav { padding:8px; }
78 div.page_nav a:visited { color:#0000cc; }
80 div.page_nav a:visited { color:#0000cc; }
79 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
81 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
80 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
82 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
81 div.page_footer_text { float:left; color:#555555; font-style:italic; }
83 div.page_footer_text { float:left; color:#555555; font-style:italic; }
82 div.page_body { padding:8px; }
84 div.page_body { padding:8px; }
83 div.title, a.title {
85 div.title, a.title {
84 display:block; padding:6px 8px;
86 display:block; padding:6px 8px;
85 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
87 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
86 }
88 }
87 a.title:hover { background-color: #d9d8d1; }
89 a.title:hover { background-color: #d9d8d1; }
88 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
90 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
89 div.log_body { padding:8px 8px 8px 150px; }
91 div.log_body { padding:8px 8px 8px 150px; }
90 .age { white-space:nowrap; }
92 .age { white-space:nowrap; }
91 span.age { position:relative; float:left; width:142px; font-style:italic; }
93 span.age { position:relative; float:left; width:142px; font-style:italic; }
92 div.log_link {
94 div.log_link {
93 padding:0px 8px;
95 padding:0px 8px;
94 font-size:10px; font-family:sans-serif; font-style:normal;
96 font-size:10px; font-family:sans-serif; font-style:normal;
95 position:relative; float:left; width:136px;
97 position:relative; float:left; width:136px;
96 }
98 }
97 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
99 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
98 a.list { text-decoration:none; color:#000000; }
100 a.list { text-decoration:none; color:#000000; }
99 a.list:hover { text-decoration:underline; color:#880000; }
101 a.list:hover { text-decoration:underline; color:#880000; }
100 table { padding:8px 4px; }
102 table { padding:8px 4px; }
101 th { padding:2px 5px; font-size:12px; text-align:left; }
103 th { padding:2px 5px; font-size:12px; text-align:left; }
102 tr.light:hover, .parity0:hover { background-color:#edece6; }
104 tr.light:hover, .parity0:hover { background-color:#edece6; }
103 tr.dark, .parity1 { background-color:#f6f6f0; }
105 tr.dark, .parity1 { background-color:#f6f6f0; }
104 tr.dark:hover, .parity1:hover { background-color:#edece6; }
106 tr.dark:hover, .parity1:hover { background-color:#edece6; }
105 td { padding:2px 5px; font-size:12px; vertical-align:top; }
107 td { padding:2px 5px; font-size:12px; vertical-align:top; }
106 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
108 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
107 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
109 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
108 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
110 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
109 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
111 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
110 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
112 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
111 .linenr { color:#999999; text-decoration:none }
113 .linenr { color:#999999; text-decoration:none }
112 a.rss_logo {
114 a.rss_logo {
113 float:right; padding:3px 6px; line-height:10px;
115 float:right; padding:3px 6px; line-height:10px;
114 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
116 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
115 color:#ffffff; background-color:#ff6600;
117 color:#ffffff; background-color:#ff6600;
116 font-weight:bold; font-family:sans-serif; font-size:10px;
118 font-weight:bold; font-family:sans-serif; font-size:10px;
117 text-align:center; text-decoration:none;
119 text-align:center; text-decoration:none;
118 }
120 }
119 a.rss_logo:hover { background-color:#ee5500; }
121 a.rss_logo:hover { background-color:#ee5500; }
120 pre { margin: 0; }
122 pre { margin: 0; }
121 span.logtags span {
123 span.logtags span {
122 padding: 0px 4px;
124 padding: 0px 4px;
123 font-size: 10px;
125 font-size: 10px;
124 font-weight: normal;
126 font-weight: normal;
125 border: 1px solid;
127 border: 1px solid;
126 background-color: #ffaaff;
128 background-color: #ffaaff;
127 border-color: #ffccff #ff00ee #ff00ee #ffccff;
129 border-color: #ffccff #ff00ee #ff00ee #ffccff;
128 }
130 }
129 span.logtags span.tagtag {
131 span.logtags span.tagtag {
130 background-color: #ffffaa;
132 background-color: #ffffaa;
131 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
133 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
132 }
134 }
133 span.logtags span.branchtag {
135 span.logtags span.branchtag {
134 background-color: #aaffaa;
136 background-color: #aaffaa;
135 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
137 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
136 }
138 }
General Comments 0
You need to be logged in to leave comments. Login now