##// END OF EJS Templates
server: skip logging of ECONNRESET...
Augie Fackler -
r42005:6bbb12cb default
parent child Browse files
Show More
@@ -1,383 +1,385
1 1 # hgweb/server.py - The standalone hg web server.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from __future__ import absolute_import
10 10
11 11 import errno
12 12 import os
13 13 import socket
14 14 import sys
15 15 import traceback
16 16 import wsgiref.validate
17 17
18 18 from ..i18n import _
19 19
20 20 from .. import (
21 21 encoding,
22 22 error,
23 23 pycompat,
24 24 util,
25 25 )
26 26
27 27 httpservermod = util.httpserver
28 28 socketserver = util.socketserver
29 29 urlerr = util.urlerr
30 30 urlreq = util.urlreq
31 31
32 32 from . import (
33 33 common,
34 34 )
35 35
36 36 def _splitURI(uri):
37 37 """Return path and query that has been split from uri
38 38
39 39 Just like CGI environment, the path is unquoted, the query is
40 40 not.
41 41 """
42 42 if r'?' in uri:
43 43 path, query = uri.split(r'?', 1)
44 44 else:
45 45 path, query = uri, r''
46 46 return urlreq.unquote(path), query
47 47
48 48 class _error_logger(object):
49 49 def __init__(self, handler):
50 50 self.handler = handler
51 51 def flush(self):
52 52 pass
53 53 def write(self, str):
54 54 self.writelines(str.split('\n'))
55 55 def writelines(self, seq):
56 56 for msg in seq:
57 57 self.handler.log_error(r"HG error: %s", encoding.strfromlocal(msg))
58 58
59 59 class _httprequesthandler(httpservermod.basehttprequesthandler):
60 60
61 61 url_scheme = 'http'
62 62
63 63 @staticmethod
64 64 def preparehttpserver(httpserver, ui):
65 65 """Prepare .socket of new HTTPServer instance"""
66 66
67 67 def __init__(self, *args, **kargs):
68 68 self.protocol_version = r'HTTP/1.1'
69 69 httpservermod.basehttprequesthandler.__init__(self, *args, **kargs)
70 70
71 71 def _log_any(self, fp, format, *args):
72 72 fp.write(pycompat.sysbytes(
73 73 r"%s - - [%s] %s" % (self.client_address[0],
74 74 self.log_date_time_string(),
75 75 format % args)) + '\n')
76 76 fp.flush()
77 77
78 78 def log_error(self, format, *args):
79 79 self._log_any(self.server.errorlog, format, *args)
80 80
81 81 def log_message(self, format, *args):
82 82 self._log_any(self.server.accesslog, format, *args)
83 83
84 84 def log_request(self, code=r'-', size=r'-'):
85 85 xheaders = []
86 86 if util.safehasattr(self, 'headers'):
87 87 xheaders = [h for h in self.headers.items()
88 88 if h[0].startswith(r'x-')]
89 89 self.log_message(r'"%s" %s %s%s',
90 90 self.requestline, str(code), str(size),
91 91 r''.join([r' %s:%s' % h for h in sorted(xheaders)]))
92 92
93 93 def do_write(self):
94 94 try:
95 95 self.do_hgweb()
96 96 except socket.error as inst:
97 97 if inst.errno != errno.EPIPE:
98 98 raise
99 99
100 100 def do_POST(self):
101 101 try:
102 102 self.do_write()
103 except Exception:
103 except Exception as e:
104 104 # I/O below could raise another exception. So log the original
105 105 # exception first to ensure it is recorded.
106 if not (isinstance(e, (OSError, socket.error))
107 and e.errno == errno.ECONNRESET):
106 108 tb = r"".join(traceback.format_exception(*sys.exc_info()))
107 109 # We need a native-string newline to poke in the log
108 110 # message, because we won't get a newline when using an
109 111 # r-string. This is the easy way out.
110 112 newline = chr(10)
111 113 self.log_error(r"Exception happened during processing "
112 114 r"request '%s':%s%s", self.path, newline, tb)
113 115
114 116 self._start_response(r"500 Internal Server Error", [])
115 117 self._write(b"Internal Server Error")
116 118 self._done()
117 119
118 120 def do_PUT(self):
119 121 self.do_POST()
120 122
121 123 def do_GET(self):
122 124 self.do_POST()
123 125
124 126 def do_hgweb(self):
125 127 self.sent_headers = False
126 128 path, query = _splitURI(self.path)
127 129
128 130 # Ensure the slicing of path below is valid
129 131 if (path != self.server.prefix
130 132 and not path.startswith(self.server.prefix + b'/')):
131 133 self._start_response(pycompat.strurl(common.statusmessage(404)),
132 134 [])
133 135 if self.command == 'POST':
134 136 # Paranoia: tell the client we're going to close the
135 137 # socket so they don't try and reuse a socket that
136 138 # might have a POST body waiting to confuse us. We do
137 139 # this by directly munging self.saved_headers because
138 140 # self._start_response ignores Connection headers.
139 141 self.saved_headers = [(r'Connection', r'Close')]
140 142 self._write(b"Not Found")
141 143 self._done()
142 144 return
143 145
144 146 env = {}
145 147 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
146 148 env[r'REQUEST_METHOD'] = self.command
147 149 env[r'SERVER_NAME'] = self.server.server_name
148 150 env[r'SERVER_PORT'] = str(self.server.server_port)
149 151 env[r'REQUEST_URI'] = self.path
150 152 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
151 153 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix):])
152 154 env[r'REMOTE_HOST'] = self.client_address[0]
153 155 env[r'REMOTE_ADDR'] = self.client_address[0]
154 156 env[r'QUERY_STRING'] = query or r''
155 157
156 158 if pycompat.ispy3:
157 159 if self.headers.get_content_type() is None:
158 160 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
159 161 else:
160 162 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
161 163 length = self.headers.get(r'content-length')
162 164 else:
163 165 if self.headers.typeheader is None:
164 166 env[r'CONTENT_TYPE'] = self.headers.type
165 167 else:
166 168 env[r'CONTENT_TYPE'] = self.headers.typeheader
167 169 length = self.headers.getheader(r'content-length')
168 170 if length:
169 171 env[r'CONTENT_LENGTH'] = length
170 172 for header in [h for h in self.headers.keys()
171 173 if h.lower() not in (r'content-type', r'content-length')]:
172 174 hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
173 175 hval = self.headers.get(header)
174 176 hval = hval.replace(r'\n', r'').strip()
175 177 if hval:
176 178 env[hkey] = hval
177 179 env[r'SERVER_PROTOCOL'] = self.request_version
178 180 env[r'wsgi.version'] = (1, 0)
179 181 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
180 182 if env.get(r'HTTP_EXPECT', '').lower() == '100-continue':
181 183 self.rfile = common.continuereader(self.rfile, self.wfile.write)
182 184
183 185 env[r'wsgi.input'] = self.rfile
184 186 env[r'wsgi.errors'] = _error_logger(self)
185 187 env[r'wsgi.multithread'] = isinstance(self.server,
186 188 socketserver.ThreadingMixIn)
187 189 if util.safehasattr(socketserver, 'ForkingMixIn'):
188 190 env[r'wsgi.multiprocess'] = isinstance(self.server,
189 191 socketserver.ForkingMixIn)
190 192 else:
191 193 env[r'wsgi.multiprocess'] = False
192 194
193 195 env[r'wsgi.run_once'] = 0
194 196
195 197 wsgiref.validate.check_environ(env)
196 198
197 199 self.saved_status = None
198 200 self.saved_headers = []
199 201 self.length = None
200 202 self._chunked = None
201 203 for chunk in self.server.application(env, self._start_response):
202 204 self._write(chunk)
203 205 if not self.sent_headers:
204 206 self.send_headers()
205 207 self._done()
206 208
207 209 def send_headers(self):
208 210 if not self.saved_status:
209 211 raise AssertionError("Sending headers before "
210 212 "start_response() called")
211 213 saved_status = self.saved_status.split(None, 1)
212 214 saved_status[0] = int(saved_status[0])
213 215 self.send_response(*saved_status)
214 216 self.length = None
215 217 self._chunked = False
216 218 for h in self.saved_headers:
217 219 self.send_header(*h)
218 220 if h[0].lower() == r'content-length':
219 221 self.length = int(h[1])
220 222 if (self.length is None and
221 223 saved_status[0] != common.HTTP_NOT_MODIFIED):
222 224 self._chunked = (not self.close_connection and
223 225 self.request_version == r'HTTP/1.1')
224 226 if self._chunked:
225 227 self.send_header(r'Transfer-Encoding', r'chunked')
226 228 else:
227 229 self.send_header(r'Connection', r'close')
228 230 self.end_headers()
229 231 self.sent_headers = True
230 232
231 233 def _start_response(self, http_status, headers, exc_info=None):
232 234 assert isinstance(http_status, str)
233 235 code, msg = http_status.split(None, 1)
234 236 code = int(code)
235 237 self.saved_status = http_status
236 238 bad_headers = (r'connection', r'transfer-encoding')
237 239 self.saved_headers = [h for h in headers
238 240 if h[0].lower() not in bad_headers]
239 241 return self._write
240 242
241 243 def _write(self, data):
242 244 if not self.saved_status:
243 245 raise AssertionError("data written before start_response() called")
244 246 elif not self.sent_headers:
245 247 self.send_headers()
246 248 if self.length is not None:
247 249 if len(data) > self.length:
248 250 raise AssertionError("Content-length header sent, but more "
249 251 "bytes than specified are being written.")
250 252 self.length = self.length - len(data)
251 253 elif self._chunked and data:
252 254 data = '%x\r\n%s\r\n' % (len(data), data)
253 255 self.wfile.write(data)
254 256 self.wfile.flush()
255 257
256 258 def _done(self):
257 259 if self._chunked:
258 260 self.wfile.write('0\r\n\r\n')
259 261 self.wfile.flush()
260 262
261 263 def version_string(self):
262 264 if self.server.serverheader:
263 265 return encoding.strfromlocal(self.server.serverheader)
264 266 return httpservermod.basehttprequesthandler.version_string(self)
265 267
266 268 class _httprequesthandlerssl(_httprequesthandler):
267 269 """HTTPS handler based on Python's ssl module"""
268 270
269 271 url_scheme = 'https'
270 272
271 273 @staticmethod
272 274 def preparehttpserver(httpserver, ui):
273 275 try:
274 276 from .. import sslutil
275 277 sslutil.modernssl
276 278 except ImportError:
277 279 raise error.Abort(_("SSL support is unavailable"))
278 280
279 281 certfile = ui.config('web', 'certificate')
280 282
281 283 # These config options are currently only meant for testing. Use
282 284 # at your own risk.
283 285 cafile = ui.config('devel', 'servercafile')
284 286 reqcert = ui.configbool('devel', 'serverrequirecert')
285 287
286 288 httpserver.socket = sslutil.wrapserversocket(httpserver.socket,
287 289 ui,
288 290 certfile=certfile,
289 291 cafile=cafile,
290 292 requireclientcert=reqcert)
291 293
292 294 def setup(self):
293 295 self.connection = self.request
294 296 self.rfile = self.request.makefile(r"rb", self.rbufsize)
295 297 self.wfile = self.request.makefile(r"wb", self.wbufsize)
296 298
297 299 try:
298 300 import threading
299 301 threading.activeCount() # silence pyflakes and bypass demandimport
300 302 _mixin = socketserver.ThreadingMixIn
301 303 except ImportError:
302 304 if util.safehasattr(os, "fork"):
303 305 _mixin = socketserver.ForkingMixIn
304 306 else:
305 307 class _mixin(object):
306 308 pass
307 309
308 310 def openlog(opt, default):
309 311 if opt and opt != '-':
310 312 return open(opt, 'ab')
311 313 return default
312 314
313 315 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
314 316
315 317 # SO_REUSEADDR has broken semantics on windows
316 318 if pycompat.iswindows:
317 319 allow_reuse_address = 0
318 320
319 321 def __init__(self, ui, app, addr, handler, **kwargs):
320 322 httpservermod.httpserver.__init__(self, addr, handler, **kwargs)
321 323 self.daemon_threads = True
322 324 self.application = app
323 325
324 326 handler.preparehttpserver(self, ui)
325 327
326 328 prefix = ui.config('web', 'prefix')
327 329 if prefix:
328 330 prefix = '/' + prefix.strip('/')
329 331 self.prefix = prefix
330 332
331 333 alog = openlog(ui.config('web', 'accesslog'), ui.fout)
332 334 elog = openlog(ui.config('web', 'errorlog'), ui.ferr)
333 335 self.accesslog = alog
334 336 self.errorlog = elog
335 337
336 338 self.addr, self.port = self.socket.getsockname()[0:2]
337 339 self.fqaddr = socket.getfqdn(addr[0])
338 340
339 341 self.serverheader = ui.config('web', 'server-header')
340 342
341 343 class IPv6HTTPServer(MercurialHTTPServer):
342 344 address_family = getattr(socket, 'AF_INET6', None)
343 345 def __init__(self, *args, **kwargs):
344 346 if self.address_family is None:
345 347 raise error.RepoError(_('IPv6 is not available on this system'))
346 348 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
347 349
348 350 def create_server(ui, app):
349 351
350 352 if ui.config('web', 'certificate'):
351 353 handler = _httprequesthandlerssl
352 354 else:
353 355 handler = _httprequesthandler
354 356
355 357 if ui.configbool('web', 'ipv6'):
356 358 cls = IPv6HTTPServer
357 359 else:
358 360 cls = MercurialHTTPServer
359 361
360 362 # ugly hack due to python issue5853 (for threaded use)
361 363 try:
362 364 import mimetypes
363 365 mimetypes.init()
364 366 except UnicodeDecodeError:
365 367 # Python 2.x's mimetypes module attempts to decode strings
366 368 # from Windows' ANSI APIs as ascii (fail), then re-encode them
367 369 # as ascii (clown fail), because the default Python Unicode
368 370 # codec is hardcoded as ascii.
369 371
370 372 sys.argv # unwrap demand-loader so that reload() works
371 373 reload(sys) # resurrect sys.setdefaultencoding()
372 374 oldenc = sys.getdefaultencoding()
373 375 sys.setdefaultencoding("latin1") # or any full 8-bit encoding
374 376 mimetypes.init()
375 377 sys.setdefaultencoding(oldenc)
376 378
377 379 address = ui.config('web', 'address')
378 380 port = util.getport(ui.config('web', 'port'))
379 381 try:
380 382 return cls(ui, app, (address, port), handler)
381 383 except socket.error as inst:
382 384 raise error.Abort(_("cannot start server at '%s:%d': %s")
383 385 % (address, port, encoding.strtolocal(inst.args[1])))
@@ -1,979 +1,967
1 1 #require serve
2 2
3 3 Some tests for hgweb. Tests static files, plain files and different 404's.
4 4
5 5 $ hg init test
6 6 $ cd test
7 7 $ mkdir da
8 8 $ echo foo > da/foo
9 9 $ echo foo > foo
10 10 $ hg ci -Ambase
11 11 adding da/foo
12 12 adding foo
13 13 $ hg bookmark -r0 '@'
14 14 $ hg bookmark -r0 'a b c'
15 15 $ hg bookmark -r0 'd/e/f'
16 16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
17 17 $ cat hg.pid >> $DAEMON_PIDS
18 18
19 19 manifest
20 20
21 21 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=raw')
22 22 200 Script output follows
23 23
24 24
25 25 drwxr-xr-x da
26 26 -rw-r--r-- 4 foo
27 27
28 28
29 29 $ (get-with-headers.py localhost:$HGPORT 'file/tip/da?style=raw')
30 30 200 Script output follows
31 31
32 32
33 33 -rw-r--r-- 4 foo
34 34
35 35
36 36
37 37 plain file
38 38
39 39 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?style=raw'
40 40 200 Script output follows
41 41
42 42 foo
43 43
44 44 should give a 404 - static file that does not exist
45 45
46 46 $ get-with-headers.py localhost:$HGPORT 'static/bogus'
47 47 404 Not Found
48 48
49 49 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
50 50 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
51 51 <head>
52 52 <link rel="icon" href="/static/hgicon.png" type="image/png" />
53 53 <meta name="robots" content="index, nofollow" />
54 54 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
55 55 <script type="text/javascript" src="/static/mercurial.js"></script>
56 56
57 57 <title>test: error</title>
58 58 </head>
59 59 <body>
60 60
61 61 <div class="container">
62 62 <div class="menu">
63 63 <div class="logo">
64 64 <a href="https://mercurial-scm.org/">
65 65 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
66 66 </div>
67 67 <ul>
68 68 <li><a href="/shortlog">log</a></li>
69 69 <li><a href="/graph">graph</a></li>
70 70 <li><a href="/tags">tags</a></li>
71 71 <li><a href="/bookmarks">bookmarks</a></li>
72 72 <li><a href="/branches">branches</a></li>
73 73 </ul>
74 74 <ul>
75 75 <li><a href="/help">help</a></li>
76 76 </ul>
77 77 </div>
78 78
79 79 <div class="main">
80 80
81 81 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
82 82 <h3>error</h3>
83 83
84 84
85 85 <form class="search" action="/log">
86 86
87 87 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
88 88 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
89 89 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
90 90 </form>
91 91
92 92 <div class="description">
93 93 <p>
94 94 An error occurred while processing your request:
95 95 </p>
96 96 <p>
97 97 Not Found
98 98 </p>
99 99 </div>
100 100 </div>
101 101 </div>
102 102
103 103
104 104
105 105 </body>
106 106 </html>
107 107
108 108 [1]
109 109
110 110 should give a 404 - bad revision
111 111
112 112 $ get-with-headers.py localhost:$HGPORT 'file/spam/foo?style=raw'
113 113 404 Not Found
114 114
115 115
116 116 error: revision not found: spam
117 117 [1]
118 118
119 119 should give a 400 - bad command
120 120
121 121 $ get-with-headers.py localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
122 122 400* (glob)
123 123
124 124
125 125 error: no such method: spam
126 126 [1]
127 127
128 128 $ get-with-headers.py --headeronly localhost:$HGPORT '?cmd=spam'
129 129 400 no such method: spam
130 130 [1]
131 131
132 132 should give a 400 - bad command as a part of url path (issue4071)
133 133
134 134 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam'
135 135 400 no such method: spam
136 136 [1]
137 137
138 138 $ get-with-headers.py --headeronly localhost:$HGPORT 'raw-spam'
139 139 400 no such method: spam
140 140 [1]
141 141
142 142 $ get-with-headers.py --headeronly localhost:$HGPORT 'spam/tip/foo'
143 143 400 no such method: spam
144 144 [1]
145 145
146 146 should give a 404 - file does not exist
147 147
148 148 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork?style=raw'
149 149 404 Not Found
150 150
151 151
152 152 error: bork@2ef0ac749a14: not found in manifest
153 153 [1]
154 154 $ get-with-headers.py localhost:$HGPORT 'file/tip/bork'
155 155 404 Not Found
156 156
157 157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
158 158 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
159 159 <head>
160 160 <link rel="icon" href="/static/hgicon.png" type="image/png" />
161 161 <meta name="robots" content="index, nofollow" />
162 162 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
163 163 <script type="text/javascript" src="/static/mercurial.js"></script>
164 164
165 165 <title>test: error</title>
166 166 </head>
167 167 <body>
168 168
169 169 <div class="container">
170 170 <div class="menu">
171 171 <div class="logo">
172 172 <a href="https://mercurial-scm.org/">
173 173 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
174 174 </div>
175 175 <ul>
176 176 <li><a href="/shortlog">log</a></li>
177 177 <li><a href="/graph">graph</a></li>
178 178 <li><a href="/tags">tags</a></li>
179 179 <li><a href="/bookmarks">bookmarks</a></li>
180 180 <li><a href="/branches">branches</a></li>
181 181 </ul>
182 182 <ul>
183 183 <li><a href="/help">help</a></li>
184 184 </ul>
185 185 </div>
186 186
187 187 <div class="main">
188 188
189 189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
190 190 <h3>error</h3>
191 191
192 192
193 193 <form class="search" action="/log">
194 194
195 195 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
196 196 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
197 197 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
198 198 </form>
199 199
200 200 <div class="description">
201 201 <p>
202 202 An error occurred while processing your request:
203 203 </p>
204 204 <p>
205 205 bork@2ef0ac749a14: not found in manifest
206 206 </p>
207 207 </div>
208 208 </div>
209 209 </div>
210 210
211 211
212 212
213 213 </body>
214 214 </html>
215 215
216 216 [1]
217 217 $ get-with-headers.py localhost:$HGPORT 'diff/tip/bork?style=raw'
218 218 404 Not Found
219 219
220 220
221 221 error: bork@2ef0ac749a14: not found in manifest
222 222 [1]
223 223
224 224 try bad style
225 225
226 226 $ (get-with-headers.py localhost:$HGPORT 'file/tip/?style=foobar')
227 227 200 Script output follows
228 228
229 229 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
230 230 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
231 231 <head>
232 232 <link rel="icon" href="/static/hgicon.png" type="image/png" />
233 233 <meta name="robots" content="index, nofollow" />
234 234 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
235 235 <script type="text/javascript" src="/static/mercurial.js"></script>
236 236
237 237 <title>test: 2ef0ac749a14 /</title>
238 238 </head>
239 239 <body>
240 240
241 241 <div class="container">
242 242 <div class="menu">
243 243 <div class="logo">
244 244 <a href="https://mercurial-scm.org/">
245 245 <img src="/static/hglogo.png" alt="mercurial" /></a>
246 246 </div>
247 247 <ul>
248 248 <li><a href="/shortlog/tip">log</a></li>
249 249 <li><a href="/graph/tip">graph</a></li>
250 250 <li><a href="/tags">tags</a></li>
251 251 <li><a href="/bookmarks">bookmarks</a></li>
252 252 <li><a href="/branches">branches</a></li>
253 253 </ul>
254 254 <ul>
255 255 <li><a href="/rev/tip">changeset</a></li>
256 256 <li class="active">browse</li>
257 257 </ul>
258 258 <ul>
259 259
260 260 </ul>
261 261 <ul>
262 262 <li><a href="/help">help</a></li>
263 263 </ul>
264 264 </div>
265 265
266 266 <div class="main">
267 267 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
268 268 <h3>
269 269 directory / @ 0:<a href="/rev/2ef0ac749a14">2ef0ac749a14</a>
270 270 <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> <span class="tag">@</span> <span class="tag">a b c</span> <span class="tag">d/e/f</span>
271 271 </h3>
272 272
273 273
274 274 <form class="search" action="/log">
275 275
276 276 <p><input name="rev" id="search1" type="text" size="30" value="" /></p>
277 277 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
278 278 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
279 279 </form>
280 280
281 281 <table class="bigtable">
282 282 <thead>
283 283 <tr>
284 284 <th class="name">name</th>
285 285 <th class="size">size</th>
286 286 <th class="permissions">permissions</th>
287 287 </tr>
288 288 </thead>
289 289 <tbody class="stripes2">
290 290
291 291
292 292 <tr class="fileline">
293 293 <td class="name">
294 294 <a href="/file/tip/da">
295 295 <img src="/static/coal-folder.png" alt="dir."/> da/
296 296 </a>
297 297 <a href="/file/tip/da/">
298 298
299 299 </a>
300 300 </td>
301 301 <td class="size"></td>
302 302 <td class="permissions">drwxr-xr-x</td>
303 303 </tr>
304 304
305 305 <tr class="fileline">
306 306 <td class="filename">
307 307 <a href="/file/tip/foo">
308 308 <img src="/static/coal-file.png" alt="file"/> foo
309 309 </a>
310 310 </td>
311 311 <td class="size">4</td>
312 312 <td class="permissions">-rw-r--r--</td>
313 313 </tr>
314 314 </tbody>
315 315 </table>
316 316 </div>
317 317 </div>
318 318
319 319
320 320 </body>
321 321 </html>
322 322
323 323
324 324 stop and restart
325 325
326 326 $ killdaemons.py
327 327 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
328 328 $ cat hg.pid >> $DAEMON_PIDS
329 329
330 330 Test the access/error files are opened in append mode
331 331
332 332 $ "$PYTHON" -c "from __future__ import print_function; print(len(open('access.log', 'rb').readlines()), 'log lines written')"
333 333 14 log lines written
334 334
335 335 static file
336 336
337 337 $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
338 338 200 Script output follows
339 339 content-length: 9074
340 340 content-type: text/css
341 341
342 342 body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; }
343 343 a { color:#0000cc; }
344 344 a:hover, a:visited, a:active { color:#880000; }
345 345 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
346 346 div.page_header a:visited { color:#0000cc; }
347 347 div.page_header a:hover { color:#880000; }
348 348 div.page_nav {
349 349 padding:8px;
350 350 display: flex;
351 351 justify-content: space-between;
352 352 align-items: center;
353 353 }
354 354 div.page_nav a:visited { color:#0000cc; }
355 355 div.extra_nav {
356 356 padding: 8px;
357 357 }
358 358 div.extra_nav a:visited {
359 359 color: #0000cc;
360 360 }
361 361 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
362 362 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
363 363 div.page_footer_text { float:left; color:#555555; font-style:italic; }
364 364 div.page_body { padding:8px; }
365 365 div.title, a.title {
366 366 display:block; padding:6px 8px;
367 367 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
368 368 }
369 369 a.title:hover { background-color: #d9d8d1; }
370 370 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
371 371 div.log_body { padding:8px 8px 8px 150px; }
372 372 .age { white-space:nowrap; }
373 373 a.title span.age { position:relative; float:left; width:142px; font-style:italic; }
374 374 div.log_link {
375 375 padding:0px 8px;
376 376 font-size:10px; font-family:sans-serif; font-style:normal;
377 377 position:relative; float:left; width:136px;
378 378 }
379 379 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
380 380 a.list { text-decoration:none; color:#000000; }
381 381 a.list:hover { text-decoration:underline; color:#880000; }
382 382 table { padding:8px 4px; }
383 383 th { padding:2px 5px; font-size:12px; text-align:left; }
384 384 .parity0 { background-color:#ffffff; }
385 385 tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+4) { background-color:#f6f6f0; }
386 386 tr.light:hover, .parity0:hover, tr.dark:hover, .parity1:hover,
387 387 pre.sourcelines.stripes > :nth-child(4n+2):hover,
388 388 pre.sourcelines.stripes > :nth-child(4n+4):hover,
389 389 pre.sourcelines.stripes > :nth-child(4n+1):hover + :nth-child(4n+2),
390 390 pre.sourcelines.stripes > :nth-child(4n+3):hover + :nth-child(4n+4) { background-color:#edece6; }
391 391 td { padding:2px 5px; font-size:12px; vertical-align:top; }
392 392 td.closed { background-color: #99f; }
393 393 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
394 394 td.indexlinks { white-space: nowrap; }
395 395 td.indexlinks a {
396 396 padding: 2px 5px; line-height: 10px;
397 397 border: 1px solid;
398 398 color: #ffffff; background-color: #7777bb;
399 399 border-color: #aaaadd #333366 #333366 #aaaadd;
400 400 font-weight: bold; text-align: center; text-decoration: none;
401 401 font-size: 10px;
402 402 }
403 403 td.indexlinks a:hover { background-color: #6666aa; }
404 404 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
405 405
406 406 .search {
407 407 margin-right: 8px;
408 408 }
409 409
410 410 div#hint {
411 411 position: absolute;
412 412 display: none;
413 413 width: 250px;
414 414 padding: 5px;
415 415 background: #ffc;
416 416 border: 1px solid yellow;
417 417 border-radius: 5px;
418 418 z-index: 15;
419 419 }
420 420
421 421 #searchform:hover div#hint { display: block; }
422 422
423 423 tr.thisrev a { color:#999999; text-decoration: none; }
424 424 tr.thisrev pre { color:#009900; }
425 425 td.annotate {
426 426 white-space: nowrap;
427 427 }
428 428 div.annotate-info {
429 429 z-index: 5;
430 430 display: none;
431 431 position: absolute;
432 432 background-color: #FFFFFF;
433 433 border: 1px solid #d9d8d1;
434 434 text-align: left;
435 435 color: #000000;
436 436 padding: 5px;
437 437 }
438 438 div.annotate-info a { color: #0000FF; text-decoration: underline; }
439 439 td.annotate:hover div.annotate-info { display: inline; }
440 440
441 441 #diffopts-form {
442 442 padding-left: 8px;
443 443 display: none;
444 444 }
445 445
446 446 .linenr { color:#999999; text-decoration:none }
447 447 div.rss_logo { float: right; white-space: nowrap; }
448 448 div.rss_logo a {
449 449 padding:3px 6px; line-height:10px;
450 450 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
451 451 color:#ffffff; background-color:#ff6600;
452 452 font-weight:bold; font-family:sans-serif; font-size:10px;
453 453 text-align:center; text-decoration:none;
454 454 }
455 455 div.rss_logo a:hover { background-color:#ee5500; }
456 456 pre { margin: 0; }
457 457 span.logtags span {
458 458 padding: 0px 4px;
459 459 font-size: 10px;
460 460 font-weight: normal;
461 461 border: 1px solid;
462 462 background-color: #ffaaff;
463 463 border-color: #ffccff #ff00ee #ff00ee #ffccff;
464 464 }
465 465 span.logtags span.phasetag {
466 466 background-color: #dfafff;
467 467 border-color: #e2b8ff #ce48ff #ce48ff #e2b8ff;
468 468 }
469 469 span.logtags span.obsoletetag {
470 470 background-color: #dddddd;
471 471 border-color: #e4e4e4 #a3a3a3 #a3a3a3 #e4e4e4;
472 472 }
473 473 span.logtags span.instabilitytag {
474 474 background-color: #ffb1c0;
475 475 border-color: #ffbbc8 #ff4476 #ff4476 #ffbbc8;
476 476 }
477 477 span.logtags span.tagtag {
478 478 background-color: #ffffaa;
479 479 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
480 480 }
481 481 span.logtags span.branchtag {
482 482 background-color: #aaffaa;
483 483 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
484 484 }
485 485 span.logtags span.inbranchtag {
486 486 background-color: #d5dde6;
487 487 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
488 488 }
489 489 span.logtags span.bookmarktag {
490 490 background-color: #afdffa;
491 491 border-color: #ccecff #46ace6 #46ace6 #ccecff;
492 492 }
493 493 span.difflineplus { color:#008800; }
494 494 span.difflineminus { color:#cc0000; }
495 495 span.difflineat { color:#990099; }
496 496 div.diffblocks { counter-reset: lineno; }
497 497 div.diffblock { counter-increment: lineno; }
498 498 pre.sourcelines { position: relative; counter-reset: lineno; }
499 499 pre.sourcelines > span {
500 500 display: inline-block;
501 501 box-sizing: border-box;
502 502 width: 100%;
503 503 padding: 0 0 0 5em;
504 504 counter-increment: lineno;
505 505 vertical-align: top;
506 506 }
507 507 pre.sourcelines > span:before {
508 508 -moz-user-select: -moz-none;
509 509 -khtml-user-select: none;
510 510 -webkit-user-select: none;
511 511 -ms-user-select: none;
512 512 user-select: none;
513 513 display: inline-block;
514 514 margin-left: -6em;
515 515 width: 4em;
516 516 color: #999;
517 517 text-align: right;
518 518 content: counters(lineno,".");
519 519 float: left;
520 520 }
521 521 pre.sourcelines > a {
522 522 display: inline-block;
523 523 position: absolute;
524 524 left: 0px;
525 525 width: 4em;
526 526 height: 1em;
527 527 }
528 528 tr:target td,
529 529 pre.sourcelines > span:target,
530 530 pre.sourcelines.stripes > span:target {
531 531 background-color: #bfdfff;
532 532 }
533 533
534 534 .description {
535 535 font-family: monospace;
536 536 white-space: pre;
537 537 }
538 538
539 539 /* Followlines */
540 540 tbody.sourcelines > tr.followlines-selected,
541 541 pre.sourcelines > span.followlines-selected {
542 542 background-color: #99C7E9 !important;
543 543 }
544 544
545 545 div#followlines {
546 546 background-color: #FFF;
547 547 border: 1px solid #d9d8d1;
548 548 padding: 5px;
549 549 position: fixed;
550 550 }
551 551
552 552 div.followlines-cancel {
553 553 text-align: right;
554 554 }
555 555
556 556 div.followlines-cancel > button {
557 557 line-height: 80%;
558 558 padding: 0;
559 559 border: 0;
560 560 border-radius: 2px;
561 561 background-color: inherit;
562 562 font-weight: bold;
563 563 }
564 564
565 565 div.followlines-cancel > button:hover {
566 566 color: #FFFFFF;
567 567 background-color: #CF1F1F;
568 568 }
569 569
570 570 div.followlines-link {
571 571 margin: 2px;
572 572 margin-top: 4px;
573 573 font-family: sans-serif;
574 574 }
575 575
576 576 .btn-followlines {
577 577 position: absolute;
578 578 display: none;
579 579 cursor: pointer;
580 580 box-sizing: content-box;
581 581 font-size: 11px;
582 582 width: 13px;
583 583 height: 13px;
584 584 border-radius: 3px;
585 585 margin: 0px;
586 586 margin-top: -2px;
587 587 padding: 0px;
588 588 background-color: #E5FDE5;
589 589 border: 1px solid #9BC19B;
590 590 font-family: monospace;
591 591 text-align: center;
592 592 line-height: 5px;
593 593 }
594 594
595 595 span.followlines-select .btn-followlines {
596 596 margin-left: -1.6em;
597 597 }
598 598
599 599 .btn-followlines:hover {
600 600 transform: scale(1.1, 1.1);
601 601 }
602 602
603 603 .btn-followlines .followlines-plus {
604 604 color: green;
605 605 }
606 606
607 607 .btn-followlines .followlines-minus {
608 608 color: red;
609 609 }
610 610
611 611 .btn-followlines-end {
612 612 background-color: #ffdcdc;
613 613 }
614 614
615 615 .sourcelines tr:hover .btn-followlines,
616 616 .sourcelines span.followlines-select:hover > .btn-followlines {
617 617 display: inline;
618 618 }
619 619
620 620 .btn-followlines-hidden,
621 621 .sourcelines tr:hover .btn-followlines-hidden {
622 622 display: none;
623 623 }
624 624
625 625 /* Graph */
626 626 div#wrapper {
627 627 position: relative;
628 628 margin: 0;
629 629 padding: 0;
630 630 margin-top: 3px;
631 631 }
632 632
633 633 canvas {
634 634 position: absolute;
635 635 z-index: 5;
636 636 top: -0.9em;
637 637 margin: 0;
638 638 }
639 639
640 640 ul#graphnodes {
641 641 list-style: none inside none;
642 642 padding: 0;
643 643 margin: 0;
644 644 }
645 645
646 646 ul#graphnodes li {
647 647 position: relative;
648 648 height: 37px;
649 649 overflow: visible;
650 650 padding-top: 2px;
651 651 }
652 652
653 653 ul#graphnodes li .fg {
654 654 position: absolute;
655 655 z-index: 10;
656 656 }
657 657
658 658 ul#graphnodes li .info {
659 659 font-size: 100%;
660 660 font-style: italic;
661 661 }
662 662
663 663 /* Comparison */
664 664 .legend {
665 665 padding: 1.5% 0 1.5% 0;
666 666 }
667 667
668 668 .legendinfo {
669 669 border: 1px solid #d9d8d1;
670 670 font-size: 80%;
671 671 text-align: center;
672 672 padding: 0.5%;
673 673 }
674 674
675 675 .equal {
676 676 background-color: #ffffff;
677 677 }
678 678
679 679 .delete {
680 680 background-color: #faa;
681 681 color: #333;
682 682 }
683 683
684 684 .insert {
685 685 background-color: #ffa;
686 686 }
687 687
688 688 .replace {
689 689 background-color: #e8e8e8;
690 690 }
691 691
692 692 .comparison {
693 693 overflow-x: auto;
694 694 }
695 695
696 696 .header th {
697 697 text-align: center;
698 698 }
699 699
700 700 .block {
701 701 border-top: 1px solid #d9d8d1;
702 702 }
703 703
704 704 .scroll-loading {
705 705 -webkit-animation: change_color 1s linear 0s infinite alternate;
706 706 -moz-animation: change_color 1s linear 0s infinite alternate;
707 707 -o-animation: change_color 1s linear 0s infinite alternate;
708 708 animation: change_color 1s linear 0s infinite alternate;
709 709 }
710 710
711 711 @-webkit-keyframes change_color {
712 712 from { background-color: #A0CEFF; } to { }
713 713 }
714 714 @-moz-keyframes change_color {
715 715 from { background-color: #A0CEFF; } to { }
716 716 }
717 717 @-o-keyframes change_color {
718 718 from { background-color: #A0CEFF; } to { }
719 719 }
720 720 @keyframes change_color {
721 721 from { background-color: #A0CEFF; } to { }
722 722 }
723 723
724 724 .scroll-loading-error {
725 725 background-color: #FFCCCC !important;
726 726 }
727 727
728 728 #doc {
729 729 margin: 0 8px;
730 730 }
731 731 304 Not Modified
732 732
733 733
734 734 phase changes are refreshed (issue4061)
735 735
736 736 $ echo bar >> foo
737 737 $ hg ci -msecret --secret
738 738 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
739 739 200 Script output follows
740 740
741 741
742 742 # HG changelog
743 743 # Node ID 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
744 744
745 745 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
746 746 revision: 0
747 747 user: test
748 748 date: Thu, 01 Jan 1970 00:00:00 +0000
749 749 summary: base
750 750 branch: default
751 751 tag: tip
752 752 bookmark: @
753 753 bookmark: a b c
754 754 bookmark: d/e/f
755 755
756 756
757 757 $ hg phase --draft tip
758 758 $ get-with-headers.py localhost:$HGPORT 'log?style=raw'
759 759 200 Script output follows
760 760
761 761
762 762 # HG changelog
763 763 # Node ID a084749e708a9c4c0a5b652a2a446322ce290e04
764 764
765 765 changeset: a084749e708a9c4c0a5b652a2a446322ce290e04
766 766 revision: 1
767 767 user: test
768 768 date: Thu, 01 Jan 1970 00:00:00 +0000
769 769 summary: secret
770 770 branch: default
771 771 tag: tip
772 772
773 773 changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
774 774 revision: 0
775 775 user: test
776 776 date: Thu, 01 Jan 1970 00:00:00 +0000
777 777 summary: base
778 778 bookmark: @
779 779 bookmark: a b c
780 780 bookmark: d/e/f
781 781
782 782
783 783
784 784 access bookmarks
785 785
786 786 $ get-with-headers.py localhost:$HGPORT 'rev/@?style=paper' | egrep '^200|changeset 0:'
787 787 200 Script output follows
788 788 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
789 789
790 790 $ get-with-headers.py localhost:$HGPORT 'rev/%40?style=paper' | egrep '^200|changeset 0:'
791 791 200 Script output follows
792 792 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
793 793
794 794 $ get-with-headers.py localhost:$HGPORT 'rev/a%20b%20c?style=paper' | egrep '^200|changeset 0:'
795 795 200 Script output follows
796 796 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
797 797
798 798 $ get-with-headers.py localhost:$HGPORT 'rev/d%252Fe%252Ff?style=paper' | egrep '^200|changeset 0:'
799 799 200 Script output follows
800 800 changeset 0:<a href="/rev/2ef0ac749a14?style=paper">2ef0ac749a14</a>
801 801
802 802 no '[up]' entry in file view when in root directory
803 803
804 804 $ get-with-headers.py localhost:$HGPORT 'file/tip?style=paper' | grep -F '[up]'
805 805 [1]
806 806 $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=paper' | grep -F '[up]'
807 807 <a href="/file/tip/?style=paper">[up]</a>
808 808 $ get-with-headers.py localhost:$HGPORT 'file/tip?style=coal' | grep -F '[up]'
809 809 [1]
810 810 $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=coal' | grep -F '[up]'
811 811 <a href="/file/tip/?style=coal">[up]</a>
812 812 $ get-with-headers.py localhost:$HGPORT 'file/tip?style=gitweb' | grep -F '[up]'
813 813 [1]
814 814 $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=gitweb' | grep -F '[up]'
815 815 <a href="/file/tip/?style=gitweb">[up]</a>
816 816 $ get-with-headers.py localhost:$HGPORT 'file/tip?style=monoblue' | grep -F '[up]'
817 817 [1]
818 818 $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=monoblue' | grep -F '[up]'
819 819 <a href="/file/tip/?style=monoblue">[up]</a>
820 820 $ get-with-headers.py localhost:$HGPORT 'file/tip?style=spartan' | grep -F '[up]'
821 821 [1]
822 822 $ get-with-headers.py localhost:$HGPORT 'file/tip/da?style=spartan' | grep -F '[up]'
823 823 <a href="/file/tip/?style=spartan">[up]</a>
824 824
825 825 no style can be loaded from directories other than the specified paths
826 826
827 827 $ mkdir -p x/templates/fallback
828 828 $ cat <<EOF > x/templates/fallback/map
829 829 > default = 'shortlog'
830 830 > shortlog = 'fall back to default\n'
831 831 > mimetype = 'text/plain'
832 832 > EOF
833 833 $ cat <<EOF > x/map
834 834 > default = 'shortlog'
835 835 > shortlog = 'access to outside of templates directory\n'
836 836 > mimetype = 'text/plain'
837 837 > EOF
838 838
839 839 $ killdaemons.py
840 840 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log \
841 841 > --config web.style=fallback --config web.templates=x/templates
842 842 $ cat hg.pid >> $DAEMON_PIDS
843 843
844 844 $ get-with-headers.py localhost:$HGPORT "?style=`pwd`/x"
845 845 200 Script output follows
846 846
847 847 fall back to default
848 848
849 849 $ get-with-headers.py localhost:$HGPORT '?style=..'
850 850 200 Script output follows
851 851
852 852 fall back to default
853 853
854 854 $ get-with-headers.py localhost:$HGPORT '?style=./..'
855 855 200 Script output follows
856 856
857 857 fall back to default
858 858
859 859 $ get-with-headers.py localhost:$HGPORT '?style=.../.../'
860 860 200 Script output follows
861 861
862 862 fall back to default
863 863
864 864 $ killdaemons.py
865 865
866 866 Test signal-safe-lock in web and non-web processes
867 867
868 868 $ cat <<'EOF' > disablesig.py
869 869 > import signal
870 870 > from mercurial import error, extensions
871 871 > def disabledsig(orig, signalnum, handler):
872 872 > if signalnum == signal.SIGTERM:
873 873 > raise error.Abort(b'SIGTERM cannot be replaced')
874 874 > try:
875 875 > return orig(signalnum, handler)
876 876 > except ValueError:
877 877 > raise error.Abort(b'signal.signal() called in thread?')
878 878 > def uisetup(ui):
879 879 > extensions.wrapfunction(signal, b'signal', disabledsig)
880 880 > EOF
881 881
882 882 by default, signal interrupt should be disabled while making a lock file
883 883
884 884 $ hg debuglock -s --config extensions.disablesig=disablesig.py
885 885 abort: SIGTERM cannot be replaced
886 886 [255]
887 887
888 888 but in hgweb, it isn't disabled since some WSGI servers complains about
889 889 unsupported signal.signal() calls (see issue5889)
890 890
891 891 $ hg serve --config extensions.disablesig=disablesig.py \
892 892 > --config web.allow-push='*' --config web.push_ssl=False \
893 893 > -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
894 894 $ cat hg.pid >> $DAEMON_PIDS
895 895
896 896 $ hg clone -q http://localhost:$HGPORT/ repo
897 897 $ hg bookmark -R repo foo
898 898
899 899 push would fail if signal.signal() were called
900 900
901 901 $ hg push -R repo -B foo
902 902 pushing to http://localhost:$HGPORT/
903 903 searching for changes
904 904 no changes found
905 905 exporting bookmark foo
906 906 [1]
907 907
908 908 $ rm -R repo
909 909 $ killdaemons.py
910 910
911 911 errors
912 912
913 913 $ cat errors.log | "$PYTHON" $TESTDIR/filtertraceback.py
914 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=spam': (glob)
915 Traceback (most recent call last):
916 error: [Errno 104] $ECONNRESET$
917
918 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/spam': (glob)
919 Traceback (most recent call last):
920 error: [Errno 104] $ECONNRESET$
921
922 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/spam/tip/foo': (glob)
923 Traceback (most recent call last):
924 error: [Errno 104] $ECONNRESET$
925
926 914 $ rm -f errors.log
927 915
928 916 Uncaught exceptions result in a logged error and canned HTTP response
929 917
930 918 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
931 919 $ cat hg.pid >> $DAEMON_PIDS
932 920
933 921 $ get-with-headers.py localhost:$HGPORT 'raiseerror' transfer-encoding content-type
934 922 500 Internal Server Error
935 923 transfer-encoding: chunked
936 924
937 925 Internal Server Error (no-eol)
938 926 [1]
939 927
940 928 $ killdaemons.py
941 929 $ cat errors.log | "$PYTHON" $TESTDIR/filtertraceback.py
942 930 .* Exception happened during processing request '/raiseerror': (re)
943 931 Traceback (most recent call last):
944 932 AttributeError: I am an uncaught error!
945 933
946 934
947 935 Uncaught exception after partial content sent
948 936
949 937 $ hg serve --config extensions.hgweberror=$TESTDIR/hgweberror.py -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
950 938 $ cat hg.pid >> $DAEMON_PIDS
951 939 $ get-with-headers.py localhost:$HGPORT 'raiseerror?partialresponse=1' transfer-encoding content-type
952 940 200 Script output follows
953 941 transfer-encoding: chunked
954 942 content-type: text/plain
955 943
956 944 partial content
957 945 Internal Server Error (no-eol)
958 946
959 947 $ killdaemons.py
960 948
961 949 HTTP 304 works with hgwebdir (issue5844)
962 950
963 951 $ cat > hgweb.conf << EOF
964 952 > [paths]
965 953 > /repo = $TESTTMP/test
966 954 > EOF
967 955
968 956 $ hg serve --web-conf hgweb.conf -p $HGPORT -d --pid-file hg.pid -E error.log
969 957 $ cat hg.pid >> $DAEMON_PIDS
970 958
971 959 $ get-with-headers.py --twice --headeronly localhost:$HGPORT 'repo/static/style.css' - date etag server
972 960 200 Script output follows
973 961 content-length: 2677
974 962 content-type: text/css
975 963 304 Not Modified
976 964
977 965 $ killdaemons.py
978 966
979 967 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now