##// END OF EJS Templates
serve: don't send any content headers with 304 responses...
Mads Kiilerich -
r18380:a4d7fd7a default
parent child Browse files
Show More
@@ -1,329 +1,330
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 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 10 from mercurial import util, error
11 11 from mercurial.hgweb import common
12 12 from mercurial.i18n import _
13 13
14 14 def _splitURI(uri):
15 15 """Return path and query that has been split from uri
16 16
17 17 Just like CGI environment, the path is unquoted, the query is
18 18 not.
19 19 """
20 20 if '?' in uri:
21 21 path, query = uri.split('?', 1)
22 22 else:
23 23 path, query = uri, ''
24 24 return urllib.unquote(path), query
25 25
26 26 class _error_logger(object):
27 27 def __init__(self, handler):
28 28 self.handler = handler
29 29 def flush(self):
30 30 pass
31 31 def write(self, str):
32 32 self.writelines(str.split('\n'))
33 33 def writelines(self, seq):
34 34 for msg in seq:
35 35 self.handler.log_error("HG error: %s", msg)
36 36
37 37 class _httprequesthandler(BaseHTTPServer.BaseHTTPRequestHandler):
38 38
39 39 url_scheme = 'http'
40 40
41 41 @staticmethod
42 42 def preparehttpserver(httpserver, ssl_cert):
43 43 """Prepare .socket of new HTTPServer instance"""
44 44 pass
45 45
46 46 def __init__(self, *args, **kargs):
47 47 self.protocol_version = 'HTTP/1.1'
48 48 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
49 49
50 50 def _log_any(self, fp, format, *args):
51 51 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
52 52 self.log_date_time_string(),
53 53 format % args))
54 54 fp.flush()
55 55
56 56 def log_error(self, format, *args):
57 57 self._log_any(self.server.errorlog, format, *args)
58 58
59 59 def log_message(self, format, *args):
60 60 self._log_any(self.server.accesslog, format, *args)
61 61
62 62 def log_request(self, code='-', size='-'):
63 63 xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
64 64 self.log_message('"%s" %s %s%s',
65 65 self.requestline, str(code), str(size),
66 66 ''.join([' %s:%s' % h for h in sorted(xheaders)]))
67 67
68 68 def do_write(self):
69 69 try:
70 70 self.do_hgweb()
71 71 except socket.error, inst:
72 72 if inst[0] != errno.EPIPE:
73 73 raise
74 74
75 75 def do_POST(self):
76 76 try:
77 77 self.do_write()
78 78 except Exception:
79 79 self._start_response("500 Internal Server Error", [])
80 80 self._write("Internal Server Error")
81 81 tb = "".join(traceback.format_exception(*sys.exc_info()))
82 82 self.log_error("Exception happened during processing "
83 83 "request '%s':\n%s", self.path, tb)
84 84
85 85 def do_GET(self):
86 86 self.do_POST()
87 87
88 88 def do_hgweb(self):
89 89 path, query = _splitURI(self.path)
90 90
91 91 env = {}
92 92 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
93 93 env['REQUEST_METHOD'] = self.command
94 94 env['SERVER_NAME'] = self.server.server_name
95 95 env['SERVER_PORT'] = str(self.server.server_port)
96 96 env['REQUEST_URI'] = self.path
97 97 env['SCRIPT_NAME'] = self.server.prefix
98 98 env['PATH_INFO'] = path[len(self.server.prefix):]
99 99 env['REMOTE_HOST'] = self.client_address[0]
100 100 env['REMOTE_ADDR'] = self.client_address[0]
101 101 if query:
102 102 env['QUERY_STRING'] = query
103 103
104 104 if self.headers.typeheader is None:
105 105 env['CONTENT_TYPE'] = self.headers.type
106 106 else:
107 107 env['CONTENT_TYPE'] = self.headers.typeheader
108 108 length = self.headers.getheader('content-length')
109 109 if length:
110 110 env['CONTENT_LENGTH'] = length
111 111 for header in [h for h in self.headers.keys()
112 112 if h not in ('content-type', 'content-length')]:
113 113 hkey = 'HTTP_' + header.replace('-', '_').upper()
114 114 hval = self.headers.getheader(header)
115 115 hval = hval.replace('\n', '').strip()
116 116 if hval:
117 117 env[hkey] = hval
118 118 env['SERVER_PROTOCOL'] = self.request_version
119 119 env['wsgi.version'] = (1, 0)
120 120 env['wsgi.url_scheme'] = self.url_scheme
121 121 if env.get('HTTP_EXPECT', '').lower() == '100-continue':
122 122 self.rfile = common.continuereader(self.rfile, self.wfile.write)
123 123
124 124 env['wsgi.input'] = self.rfile
125 125 env['wsgi.errors'] = _error_logger(self)
126 126 env['wsgi.multithread'] = isinstance(self.server,
127 127 SocketServer.ThreadingMixIn)
128 128 env['wsgi.multiprocess'] = isinstance(self.server,
129 129 SocketServer.ForkingMixIn)
130 130 env['wsgi.run_once'] = 0
131 131
132 132 self.saved_status = None
133 133 self.saved_headers = []
134 134 self.sent_headers = False
135 135 self.length = None
136 136 self._chunked = None
137 137 for chunk in self.server.application(env, self._start_response):
138 138 self._write(chunk)
139 139 if not self.sent_headers:
140 140 self.send_headers()
141 141 self._done()
142 142
143 143 def send_headers(self):
144 144 if not self.saved_status:
145 145 raise AssertionError("Sending headers before "
146 146 "start_response() called")
147 147 saved_status = self.saved_status.split(None, 1)
148 148 saved_status[0] = int(saved_status[0])
149 149 self.send_response(*saved_status)
150 150 self.length = None
151 151 self._chunked = False
152 152 for h in self.saved_headers:
153 153 self.send_header(*h)
154 154 if h[0].lower() == 'content-length':
155 155 self.length = int(h[1])
156 if self.length is None:
156 if (self.length is None and
157 saved_status[0] != common.HTTP_NOT_MODIFIED):
157 158 self._chunked = (not self.close_connection and
158 159 self.request_version == "HTTP/1.1")
159 160 if self._chunked:
160 161 self.send_header('Transfer-Encoding', 'chunked')
161 162 else:
162 163 self.send_header('Connection', 'close')
163 164 self.end_headers()
164 165 self.sent_headers = True
165 166
166 167 def _start_response(self, http_status, headers, exc_info=None):
167 168 code, msg = http_status.split(None, 1)
168 169 code = int(code)
169 170 self.saved_status = http_status
170 171 bad_headers = ('connection', 'transfer-encoding')
171 172 self.saved_headers = [h for h in headers
172 173 if h[0].lower() not in bad_headers]
173 174 return self._write
174 175
175 176 def _write(self, data):
176 177 if not self.saved_status:
177 178 raise AssertionError("data written before start_response() called")
178 179 elif not self.sent_headers:
179 180 self.send_headers()
180 181 if self.length is not None:
181 182 if len(data) > self.length:
182 183 raise AssertionError("Content-length header sent, but more "
183 184 "bytes than specified are being written.")
184 185 self.length = self.length - len(data)
185 186 elif self._chunked and data:
186 187 data = '%x\r\n%s\r\n' % (len(data), data)
187 188 self.wfile.write(data)
188 189 self.wfile.flush()
189 190
190 191 def _done(self):
191 192 if self._chunked:
192 193 self.wfile.write('0\r\n\r\n')
193 194 self.wfile.flush()
194 195
195 196 class _httprequesthandleropenssl(_httprequesthandler):
196 197 """HTTPS handler based on pyOpenSSL"""
197 198
198 199 url_scheme = 'https'
199 200
200 201 @staticmethod
201 202 def preparehttpserver(httpserver, ssl_cert):
202 203 try:
203 204 import OpenSSL
204 205 OpenSSL.SSL.Context
205 206 except ImportError:
206 207 raise util.Abort(_("SSL support is unavailable"))
207 208 ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
208 209 ctx.use_privatekey_file(ssl_cert)
209 210 ctx.use_certificate_file(ssl_cert)
210 211 sock = socket.socket(httpserver.address_family, httpserver.socket_type)
211 212 httpserver.socket = OpenSSL.SSL.Connection(ctx, sock)
212 213 httpserver.server_bind()
213 214 httpserver.server_activate()
214 215
215 216 def setup(self):
216 217 self.connection = self.request
217 218 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
218 219 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
219 220
220 221 def do_write(self):
221 222 import OpenSSL
222 223 try:
223 224 _httprequesthandler.do_write(self)
224 225 except OpenSSL.SSL.SysCallError, inst:
225 226 if inst.args[0] != errno.EPIPE:
226 227 raise
227 228
228 229 def handle_one_request(self):
229 230 import OpenSSL
230 231 try:
231 232 _httprequesthandler.handle_one_request(self)
232 233 except (OpenSSL.SSL.SysCallError, OpenSSL.SSL.ZeroReturnError):
233 234 self.close_connection = True
234 235 pass
235 236
236 237 class _httprequesthandlerssl(_httprequesthandler):
237 238 """HTTPS handler based on Pythons ssl module (introduced in 2.6)"""
238 239
239 240 url_scheme = 'https'
240 241
241 242 @staticmethod
242 243 def preparehttpserver(httpserver, ssl_cert):
243 244 try:
244 245 import ssl
245 246 ssl.wrap_socket
246 247 except ImportError:
247 248 raise util.Abort(_("SSL support is unavailable"))
248 249 httpserver.socket = ssl.wrap_socket(httpserver.socket, server_side=True,
249 250 certfile=ssl_cert, ssl_version=ssl.PROTOCOL_SSLv23)
250 251
251 252 def setup(self):
252 253 self.connection = self.request
253 254 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
254 255 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
255 256
256 257 try:
257 258 from threading import activeCount
258 259 activeCount() # silence pyflakes
259 260 _mixin = SocketServer.ThreadingMixIn
260 261 except ImportError:
261 262 if util.safehasattr(os, "fork"):
262 263 _mixin = SocketServer.ForkingMixIn
263 264 else:
264 265 class _mixin(object):
265 266 pass
266 267
267 268 def openlog(opt, default):
268 269 if opt and opt != '-':
269 270 return open(opt, 'a')
270 271 return default
271 272
272 273 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
273 274
274 275 # SO_REUSEADDR has broken semantics on windows
275 276 if os.name == 'nt':
276 277 allow_reuse_address = 0
277 278
278 279 def __init__(self, ui, app, addr, handler, **kwargs):
279 280 BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
280 281 self.daemon_threads = True
281 282 self.application = app
282 283
283 284 handler.preparehttpserver(self, ui.config('web', 'certificate'))
284 285
285 286 prefix = ui.config('web', 'prefix', '')
286 287 if prefix:
287 288 prefix = '/' + prefix.strip('/')
288 289 self.prefix = prefix
289 290
290 291 alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
291 292 elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
292 293 self.accesslog = alog
293 294 self.errorlog = elog
294 295
295 296 self.addr, self.port = self.socket.getsockname()[0:2]
296 297 self.fqaddr = socket.getfqdn(addr[0])
297 298
298 299 class IPv6HTTPServer(MercurialHTTPServer):
299 300 address_family = getattr(socket, 'AF_INET6', None)
300 301 def __init__(self, *args, **kwargs):
301 302 if self.address_family is None:
302 303 raise error.RepoError(_('IPv6 is not available on this system'))
303 304 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
304 305
305 306 def create_server(ui, app):
306 307
307 308 if ui.config('web', 'certificate'):
308 309 if sys.version_info >= (2, 6):
309 310 handler = _httprequesthandlerssl
310 311 else:
311 312 handler = _httprequesthandleropenssl
312 313 else:
313 314 handler = _httprequesthandler
314 315
315 316 if ui.configbool('web', 'ipv6'):
316 317 cls = IPv6HTTPServer
317 318 else:
318 319 cls = MercurialHTTPServer
319 320
320 321 # ugly hack due to python issue5853 (for threaded use)
321 322 import mimetypes; mimetypes.init()
322 323
323 324 address = ui.config('web', 'address', '')
324 325 port = util.getport(ui.config('web', 'port', 8000))
325 326 try:
326 327 return cls(ui, app, (address, port), handler)
327 328 except socket.error, inst:
328 329 raise util.Abort(_("cannot start server at '%s:%d': %s")
329 330 % (address, port, inst.args[1]))
@@ -1,53 +1,55
1 1 #!/usr/bin/env python
2 2
3 3 """This does HTTP GET requests given a host:port and path and returns
4 4 a subset of the headers plus the body of the result."""
5 5
6 6 import httplib, sys
7 7
8 8 try:
9 9 import msvcrt, os
10 10 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
11 11 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
12 12 except ImportError:
13 13 pass
14 14
15 15 twice = False
16 16 if '--twice' in sys.argv:
17 17 sys.argv.remove('--twice')
18 18 twice = True
19 19
20 20 reasons = {'Not modified': 'Not Modified'} # python 2.4
21 21
22 22 tag = None
23 23 def request(host, path, show):
24 24 assert not path.startswith('/'), path
25 25 global tag
26 26 headers = {}
27 27 if tag:
28 28 headers['If-None-Match'] = tag
29 29
30 30 conn = httplib.HTTPConnection(host)
31 31 conn.request("GET", '/' + path, None, headers)
32 32 response = conn.getresponse()
33 33 print response.status, reasons.get(response.reason, response.reason)
34 if show[:1] == ['-']:
35 show = [h for h, v in response.getheaders() if h.lower() not in show]
34 36 for h in [h.lower() for h in show]:
35 37 if response.getheader(h, None) is not None:
36 38 print "%s: %s" % (h, response.getheader(h))
37 39
38 40 print
39 41 data = response.read()
40 42 sys.stdout.write(data)
41 43
42 44 if twice and response.getheader('ETag', None):
43 45 tag = response.getheader('ETag')
44 46
45 47 return response.status
46 48
47 49 status = request(sys.argv[1], sys.argv[2], sys.argv[3:])
48 50 if twice:
49 51 status = request(sys.argv[1], sys.argv[2], sys.argv[3:])
50 52
51 53 if 200 <= status <= 305:
52 54 sys.exit(0)
53 55 sys.exit(1)
@@ -1,492 +1,494
1 1 $ "$TESTDIR/hghave" serve || exit 80
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 serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
14 14 $ cat hg.pid >> $DAEMON_PIDS
15 15
16 16 manifest
17 17
18 18 $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/?style=raw')
19 19 200 Script output follows
20 20
21 21
22 22 drwxr-xr-x da
23 23 -rw-r--r-- 4 foo
24 24
25 25
26 26 $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/da?style=raw')
27 27 200 Script output follows
28 28
29 29
30 30 -rw-r--r-- 4 foo
31 31
32 32
33 33
34 34 plain file
35 35
36 36 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/foo?style=raw'
37 37 200 Script output follows
38 38
39 39 foo
40 40
41 41 should give a 404 - static file that does not exist
42 42
43 43 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'static/bogus'
44 44 404 Not Found
45 45
46 46 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
47 47 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
48 48 <head>
49 49 <link rel="icon" href="/static/hgicon.png" type="image/png" />
50 50 <meta name="robots" content="index, nofollow" />
51 51 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
52 52 <script type="text/javascript" src="/static/mercurial.js"></script>
53 53
54 54 <title>test: error</title>
55 55 </head>
56 56 <body>
57 57
58 58 <div class="container">
59 59 <div class="menu">
60 60 <div class="logo">
61 61 <a href="http://mercurial.selenic.com/">
62 62 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
63 63 </div>
64 64 <ul>
65 65 <li><a href="/shortlog">log</a></li>
66 66 <li><a href="/graph">graph</a></li>
67 67 <li><a href="/tags">tags</a></li>
68 68 <li><a href="/bookmarks">bookmarks</a></li>
69 69 <li><a href="/branches">branches</a></li>
70 70 </ul>
71 71 <ul>
72 72 <li><a href="/help">help</a></li>
73 73 </ul>
74 74 </div>
75 75
76 76 <div class="main">
77 77
78 78 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
79 79 <h3>error</h3>
80 80
81 81 <form class="search" action="/log">
82 82
83 83 <p><input name="rev" id="search1" type="text" size="30"></p>
84 84 <div id="hint">find changesets by author, revision,
85 85 files, or words in the commit message</div>
86 86 </form>
87 87
88 88 <div class="description">
89 89 <p>
90 90 An error occurred while processing your request:
91 91 </p>
92 92 <p>
93 93 Not Found
94 94 </p>
95 95 </div>
96 96 </div>
97 97 </div>
98 98
99 99 <script type="text/javascript">process_dates()</script>
100 100
101 101
102 102 </body>
103 103 </html>
104 104
105 105 [1]
106 106
107 107 should give a 404 - bad revision
108 108
109 109 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/spam/foo?style=raw'
110 110 404 Not Found
111 111
112 112
113 113 error: revision not found: spam
114 114 [1]
115 115
116 116 should give a 400 - bad command
117 117
118 118 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw'
119 119 400* (glob)
120 120
121 121
122 122 error: no such method: spam
123 123 [1]
124 124
125 125 should give a 404 - file does not exist
126 126
127 127 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/bork?style=raw'
128 128 404 Not Found
129 129
130 130
131 131 error: bork@2ef0ac749a14: not found in manifest
132 132 [1]
133 133 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/bork'
134 134 404 Not Found
135 135
136 136 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
137 137 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
138 138 <head>
139 139 <link rel="icon" href="/static/hgicon.png" type="image/png" />
140 140 <meta name="robots" content="index, nofollow" />
141 141 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
142 142 <script type="text/javascript" src="/static/mercurial.js"></script>
143 143
144 144 <title>test: error</title>
145 145 </head>
146 146 <body>
147 147
148 148 <div class="container">
149 149 <div class="menu">
150 150 <div class="logo">
151 151 <a href="http://mercurial.selenic.com/">
152 152 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
153 153 </div>
154 154 <ul>
155 155 <li><a href="/shortlog">log</a></li>
156 156 <li><a href="/graph">graph</a></li>
157 157 <li><a href="/tags">tags</a></li>
158 158 <li><a href="/bookmarks">bookmarks</a></li>
159 159 <li><a href="/branches">branches</a></li>
160 160 </ul>
161 161 <ul>
162 162 <li><a href="/help">help</a></li>
163 163 </ul>
164 164 </div>
165 165
166 166 <div class="main">
167 167
168 168 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
169 169 <h3>error</h3>
170 170
171 171 <form class="search" action="/log">
172 172
173 173 <p><input name="rev" id="search1" type="text" size="30"></p>
174 174 <div id="hint">find changesets by author, revision,
175 175 files, or words in the commit message</div>
176 176 </form>
177 177
178 178 <div class="description">
179 179 <p>
180 180 An error occurred while processing your request:
181 181 </p>
182 182 <p>
183 183 bork@2ef0ac749a14: not found in manifest
184 184 </p>
185 185 </div>
186 186 </div>
187 187 </div>
188 188
189 189 <script type="text/javascript">process_dates()</script>
190 190
191 191
192 192 </body>
193 193 </html>
194 194
195 195 [1]
196 196 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/tip/bork?style=raw'
197 197 404 Not Found
198 198
199 199
200 200 error: bork@2ef0ac749a14: not found in manifest
201 201 [1]
202 202
203 203 try bad style
204 204
205 205 $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/?style=foobar')
206 206 200 Script output follows
207 207
208 208 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
209 209 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
210 210 <head>
211 211 <link rel="icon" href="/static/hgicon.png" type="image/png" />
212 212 <meta name="robots" content="index, nofollow" />
213 213 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
214 214 <script type="text/javascript" src="/static/mercurial.js"></script>
215 215
216 216 <title>test: 2ef0ac749a14 /</title>
217 217 </head>
218 218 <body>
219 219
220 220 <div class="container">
221 221 <div class="menu">
222 222 <div class="logo">
223 223 <a href="http://mercurial.selenic.com/">
224 224 <img src="/static/hglogo.png" alt="mercurial" /></a>
225 225 </div>
226 226 <ul>
227 227 <li><a href="/shortlog/2ef0ac749a14">log</a></li>
228 228 <li><a href="/graph/2ef0ac749a14">graph</a></li>
229 229 <li><a href="/tags">tags</a></li>
230 230 <li><a href="/bookmarks">bookmarks</a></li>
231 231 <li><a href="/branches">branches</a></li>
232 232 </ul>
233 233 <ul>
234 234 <li><a href="/rev/2ef0ac749a14">changeset</a></li>
235 235 <li class="active">browse</li>
236 236 </ul>
237 237 <ul>
238 238
239 239 </ul>
240 240 <ul>
241 241 <li><a href="/help">help</a></li>
242 242 </ul>
243 243 </div>
244 244
245 245 <div class="main">
246 246 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
247 247 <h3>directory / @ 0:2ef0ac749a14 <span class="tag">tip</span> </h3>
248 248
249 249 <form class="search" action="/log">
250 250
251 251 <p><input name="rev" id="search1" type="text" size="30" /></p>
252 252 <div id="hint">find changesets by author, revision,
253 253 files, or words in the commit message</div>
254 254 </form>
255 255
256 256 <table class="bigtable">
257 257 <tr>
258 258 <th class="name">name</th>
259 259 <th class="size">size</th>
260 260 <th class="permissions">permissions</th>
261 261 </tr>
262 262 <tr class="fileline parity0">
263 263 <td class="name"><a href="/file/2ef0ac749a14/">[up]</a></td>
264 264 <td class="size"></td>
265 265 <td class="permissions">drwxr-xr-x</td>
266 266 </tr>
267 267
268 268 <tr class="fileline parity1">
269 269 <td class="name">
270 270 <a href="/file/2ef0ac749a14/da">
271 271 <img src="/static/coal-folder.png" alt="dir."/> da/
272 272 </a>
273 273 <a href="/file/2ef0ac749a14/da/">
274 274
275 275 </a>
276 276 </td>
277 277 <td class="size"></td>
278 278 <td class="permissions">drwxr-xr-x</td>
279 279 </tr>
280 280
281 281 <tr class="fileline parity0">
282 282 <td class="filename">
283 283 <a href="/file/2ef0ac749a14/foo">
284 284 <img src="/static/coal-file.png" alt="file"/> foo
285 285 </a>
286 286 </td>
287 287 <td class="size">4</td>
288 288 <td class="permissions">-rw-r--r--</td>
289 289 </tr>
290 290 </table>
291 291 </div>
292 292 </div>
293 293 <script type="text/javascript">process_dates()</script>
294 294
295 295
296 296 </body>
297 297 </html>
298 298
299 299
300 300 stop and restart
301 301
302 302 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
303 303 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
304 304 $ cat hg.pid >> $DAEMON_PIDS
305 305
306 306 Test the access/error files are opened in append mode
307 307
308 308 $ python -c "print len(file('access.log').readlines()), 'log lines written'"
309 309 10 log lines written
310 310
311 311 static file
312 312
313 $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT 'static/style-gitweb.css'
313 $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
314 314 200 Script output follows
315 content-length: 4619
316 content-type: text/css
315 317
316 318 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
317 319 a { color:#0000cc; }
318 320 a:hover, a:visited, a:active { color:#880000; }
319 321 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
320 322 div.page_header a:visited { color:#0000cc; }
321 323 div.page_header a:hover { color:#880000; }
322 324 div.page_nav { padding:8px; }
323 325 div.page_nav a:visited { color:#0000cc; }
324 326 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
325 327 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
326 328 div.page_footer_text { float:left; color:#555555; font-style:italic; }
327 329 div.page_body { padding:8px; }
328 330 div.title, a.title {
329 331 display:block; padding:6px 8px;
330 332 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
331 333 }
332 334 a.title:hover { background-color: #d9d8d1; }
333 335 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
334 336 div.log_body { padding:8px 8px 8px 150px; }
335 337 .age { white-space:nowrap; }
336 338 span.age { position:relative; float:left; width:142px; font-style:italic; }
337 339 div.log_link {
338 340 padding:0px 8px;
339 341 font-size:10px; font-family:sans-serif; font-style:normal;
340 342 position:relative; float:left; width:136px;
341 343 }
342 344 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
343 345 a.list { text-decoration:none; color:#000000; }
344 346 a.list:hover { text-decoration:underline; color:#880000; }
345 347 table { padding:8px 4px; }
346 348 th { padding:2px 5px; font-size:12px; text-align:left; }
347 349 tr.light:hover, .parity0:hover { background-color:#edece6; }
348 350 tr.dark, .parity1 { background-color:#f6f6f0; }
349 351 tr.dark:hover, .parity1:hover { background-color:#edece6; }
350 352 td { padding:2px 5px; font-size:12px; vertical-align:top; }
351 353 td.closed { background-color: #99f; }
352 354 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
353 355 td.indexlinks { white-space: nowrap; }
354 356 td.indexlinks a {
355 357 padding: 2px 5px; line-height: 10px;
356 358 border: 1px solid;
357 359 color: #ffffff; background-color: #7777bb;
358 360 border-color: #aaaadd #333366 #333366 #aaaadd;
359 361 font-weight: bold; text-align: center; text-decoration: none;
360 362 font-size: 10px;
361 363 }
362 364 td.indexlinks a:hover { background-color: #6666aa; }
363 365 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
364 366 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
365 367 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
366 368 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
367 369 .linenr { color:#999999; text-decoration:none }
368 370 div.rss_logo { float: right; white-space: nowrap; }
369 371 div.rss_logo a {
370 372 padding:3px 6px; line-height:10px;
371 373 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
372 374 color:#ffffff; background-color:#ff6600;
373 375 font-weight:bold; font-family:sans-serif; font-size:10px;
374 376 text-align:center; text-decoration:none;
375 377 }
376 378 div.rss_logo a:hover { background-color:#ee5500; }
377 379 pre { margin: 0; }
378 380 span.logtags span {
379 381 padding: 0px 4px;
380 382 font-size: 10px;
381 383 font-weight: normal;
382 384 border: 1px solid;
383 385 background-color: #ffaaff;
384 386 border-color: #ffccff #ff00ee #ff00ee #ffccff;
385 387 }
386 388 span.logtags span.tagtag {
387 389 background-color: #ffffaa;
388 390 border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
389 391 }
390 392 span.logtags span.branchtag {
391 393 background-color: #aaffaa;
392 394 border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
393 395 }
394 396 span.logtags span.inbranchtag {
395 397 background-color: #d5dde6;
396 398 border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
397 399 }
398 400 span.logtags span.bookmarktag {
399 401 background-color: #afdffa;
400 402 border-color: #ccecff #46ace6 #46ace6 #ccecff;
401 403 }
402 404
403 405 /* Graph */
404 406 div#wrapper {
405 407 position: relative;
406 408 margin: 0;
407 409 padding: 0;
408 410 margin-top: 3px;
409 411 }
410 412
411 413 canvas {
412 414 position: absolute;
413 415 z-index: 5;
414 416 top: -0.9em;
415 417 margin: 0;
416 418 }
417 419
418 420 ul#nodebgs {
419 421 list-style: none inside none;
420 422 padding: 0;
421 423 margin: 0;
422 424 top: -0.7em;
423 425 }
424 426
425 427 ul#graphnodes li, ul#nodebgs li {
426 428 height: 39px;
427 429 }
428 430
429 431 ul#graphnodes {
430 432 position: absolute;
431 433 z-index: 10;
432 434 top: -0.8em;
433 435 list-style: none inside none;
434 436 padding: 0;
435 437 }
436 438
437 439 ul#graphnodes li .info {
438 440 display: block;
439 441 font-size: 100%;
440 442 position: relative;
441 443 top: -3px;
442 444 font-style: italic;
443 445 }
444 446
445 447 /* Comparison */
446 448 .legend {
447 449 padding: 1.5% 0 1.5% 0;
448 450 }
449 451
450 452 .legendinfo {
451 453 border: 1px solid #d9d8d1;
452 454 font-size: 80%;
453 455 text-align: center;
454 456 padding: 0.5%;
455 457 }
456 458
457 459 .equal {
458 460 background-color: #ffffff;
459 461 }
460 462
461 463 .delete {
462 464 background-color: #faa;
463 465 color: #333;
464 466 }
465 467
466 468 .insert {
467 469 background-color: #ffa;
468 470 }
469 471
470 472 .replace {
471 473 background-color: #e8e8e8;
472 474 }
473 475
474 476 .comparison {
475 477 overflow-x: auto;
476 478 }
477 479
478 480 .header th {
479 481 text-align: center;
480 482 }
481 483
482 484 .block {
483 485 border-top: 1px solid #d9d8d1;
484 486 }
485 487 304 Not Modified
486 488
487 489
488 490 errors
489 491
490 492 $ cat errors.log
491 493
492 494 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now