##// END OF EJS Templates
hgweb: explicit response status
Dirkjan Ochtman -
r5993:948a41e7 default
parent child Browse files
Show More
@@ -8,6 +8,11 b''
8 8
9 9 import errno, mimetypes, os
10 10
11 HTTP_OK = 200
12 HTTP_BAD_REQUEST = 400
13 HTTP_NOT_FOUND = 404
14 HTTP_SERVER_ERROR = 500
15
11 16 class ErrorResponse(Exception):
12 17 def __init__(self, code, message=None):
13 18 Exception.__init__(self)
@@ -54,18 +59,15 b' def staticfile(directory, fname, req):'
54 59 try:
55 60 os.stat(path)
56 61 ct = mimetypes.guess_type(path)[0] or "text/plain"
57 req.header([
58 ('Content-Type', ct),
59 ('Content-Length', str(os.path.getsize(path)))
60 ])
62 req.respond(HTTP_OK, ct, length = os.path.getsize(path))
61 63 return file(path, 'rb').read()
62 64 except TypeError:
63 raise ErrorResponse(500, 'illegal file name')
65 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal file name')
64 66 except OSError, err:
65 67 if err.errno == errno.ENOENT:
66 raise ErrorResponse(404)
68 raise ErrorResponse(HTTP_NOT_FOUND)
67 69 else:
68 raise ErrorResponse(500, err.strerror)
70 raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
69 71
70 72 def style_map(templatepath, style):
71 73 """Return path to mapfile for a given style.
@@ -11,6 +11,7 b' from mercurial.node import *'
11 11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 12 from mercurial import revlog, templater, templatefilters
13 13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
14 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 15 from request import wsgirequest
15 16 import webcommands, protocol
16 17
@@ -207,27 +208,35 b' class hgweb(object):'
207 208 method(self, req)
208 209 else:
209 210 tmpl = self.templater(req)
211 ctype = tmpl('mimetype', encoding=self.encoding)
212 ctype = templater.stringify(ctype)
213
210 214 if cmd == '':
211 215 req.form['cmd'] = [tmpl.cache['default']]
212 216 cmd = req.form['cmd'][0]
213 217
214 218 if cmd not in webcommands.__all__:
215 raise ErrorResponse(400, 'No such method: ' + cmd)
219 msg = 'No such method: %s' % cmd
220 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
216 221 elif cmd == 'file' and 'raw' in req.form.get('style', []):
222 self.ctype = ctype
217 223 content = webcommands.rawfile(self, req, tmpl)
218 224 else:
219 225 content = getattr(webcommands, cmd)(self, req, tmpl)
226 req.respond(HTTP_OK, ctype)
220 227
221 228 req.write(content)
222 229 del tmpl
223 230
224 231 except revlog.LookupError, err:
225 req.respond(404, tmpl(
226 'error', error='revision not found: %s' % err.name))
232 req.respond(HTTP_NOT_FOUND, ctype)
233 req.write(tmpl('error', error='revision not found: %s' % err.name))
227 234 except (hg.RepoError, revlog.RevlogError), inst:
228 req.respond(500, tmpl('error', error=str(inst)))
235 req.respond(HTTP_SERVER_ERROR, ctype)
236 req.write(tmpl('error', error=str(inst)))
229 237 except ErrorResponse, inst:
230 req.respond(inst.code, tmpl('error', error=inst.message))
238 req.respond(inst.code, ctype)
239 req.write(tmpl('error', error=inst.message))
231 240
232 241 def templater(self, req):
233 242
@@ -252,8 +261,6 b' class hgweb(object):'
252 261 # some functions for the templater
253 262
254 263 def header(**map):
255 ctype = tmpl('mimetype', encoding=self.encoding)
256 req.httphdr(templater.stringify(ctype))
257 264 yield tmpl('header', encoding=self.encoding, **map)
258 265
259 266 def footer(**map):
@@ -668,7 +675,7 b' class hgweb(object):'
668 675 files[short] = (f, n)
669 676
670 677 if not files:
671 raise ErrorResponse(404, 'Path not found: ' + path)
678 raise ErrorResponse(HTTP_NOT_FOUND, 'Path not found: ' + path)
672 679
673 680 def filelist(**map):
674 681 fl = files.keys()
@@ -846,6 +853,7 b' class hgweb(object):'
846 853 if encoding:
847 854 headers.append(('Content-Encoding', encoding))
848 855 req.header(headers)
856 req.respond(HTTP_OK)
849 857 archival.archive(self.repo, req, cnode, artype, prefix=name)
850 858
851 859 # add tags to things
@@ -10,7 +10,7 b' import os'
10 10 from mercurial.i18n import gettext as _
11 11 from mercurial import ui, hg, util, templater, templatefilters
12 12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\
13 get_contact
13 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 14 from hgweb_mod import hgweb
15 15 from request import wsgirequest
16 16
@@ -76,6 +76,9 b' class hgwebdir(object):'
76 76 try:
77 77
78 78 virtual = req.env.get("PATH_INFO", "").strip('/')
79 tmpl = self.templater(req)
80 ctype = tmpl('mimetype', encoding=util._encoding)
81 ctype = templater.stringify(ctype)
79 82
80 83 # a static file
81 84 if virtual.startswith('static/') or 'static' in req.form:
@@ -89,11 +92,12 b' class hgwebdir(object):'
89 92
90 93 # top-level index
91 94 elif not virtual:
92 tmpl = self.templater(req)
95 req.respond(HTTP_OK, ctype)
93 96 req.write(self.makeindex(req, tmpl))
94 97 return
95 98
96 99 # nested indexes and hgwebs
100
97 101 repos = dict(self.repos)
98 102 while virtual:
99 103 real = repos.get(virtual)
@@ -104,14 +108,15 b' class hgwebdir(object):'
104 108 hgweb(repo).run_wsgi(req)
105 109 return
106 110 except IOError, inst:
107 raise ErrorResponse(500, inst.strerror)
111 msg = inst.strerror
112 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
108 113 except hg.RepoError, inst:
109 raise ErrorResponse(500, str(inst))
114 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
110 115
111 116 # browse subdirectories
112 117 subdir = virtual + '/'
113 118 if [r for r in repos if r.startswith(subdir)]:
114 tmpl = self.templater(req)
119 req.respond(HTTP_OK, ctype)
115 120 req.write(self.makeindex(req, tmpl, subdir))
116 121 return
117 122
@@ -121,12 +126,12 b' class hgwebdir(object):'
121 126 virtual = virtual[:up]
122 127
123 128 # prefixes not found
124 tmpl = self.templater(req)
125 req.respond(404, tmpl("notfound", repo=virtual))
129 req.respond(HTTP_NOT_FOUND, ctype)
130 req.write(tmpl("notfound", repo=virtual))
126 131
127 132 except ErrorResponse, err:
128 tmpl = self.templater(req)
129 req.respond(err.code, tmpl('error', error=err.message or ''))
133 req.respond(err.code, ctype)
134 req.write(tmpl('error', error=err.message or ''))
130 135 finally:
131 136 tmpl = None
132 137
@@ -234,8 +239,6 b' class hgwebdir(object):'
234 239 def templater(self, req):
235 240
236 241 def header(**map):
237 ctype = tmpl('mimetype', encoding=util._encoding)
238 req.httphdr(templater.stringify(ctype))
239 242 yield tmpl('header', encoding=util._encoding, **map)
240 243
241 244 def footer(**map):
@@ -9,6 +9,7 b' import cStringIO, zlib, bz2, tempfile, e'
9 9 from mercurial import util, streamclone
10 10 from mercurial.i18n import gettext as _
11 11 from mercurial.node import *
12 from common import HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
12 13
13 14 # __all__ is populated with the allowed commands. Be sure to add to it if
14 15 # you're adding a new command, or the new command won't work.
@@ -18,6 +19,8 b' from mercurial.node import *'
18 19 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
19 20 ]
20 21
22 HGTYPE = 'application/mercurial-0.1'
23
21 24 def lookup(web, req):
22 25 try:
23 26 r = hex(web.repo.lookup(req.form['key'][0]))
@@ -26,12 +29,12 b' def lookup(web, req):'
26 29 r = str(inst)
27 30 success = 0
28 31 resp = "%s %s\n" % (success, r)
29 req.httphdr("application/mercurial-0.1", length=len(resp))
32 req.respond(HTTP_OK, HGTYPE, length=len(resp))
30 33 req.write(resp)
31 34
32 35 def heads(web, req):
33 36 resp = " ".join(map(hex, web.repo.heads())) + "\n"
34 req.httphdr("application/mercurial-0.1", length=len(resp))
37 req.respond(HTTP_OK, HGTYPE, length=len(resp))
35 38 req.write(resp)
36 39
37 40 def branches(web, req):
@@ -42,7 +45,7 b' def branches(web, req):'
42 45 for b in web.repo.branches(nodes):
43 46 resp.write(" ".join(map(hex, b)) + "\n")
44 47 resp = resp.getvalue()
45 req.httphdr("application/mercurial-0.1", length=len(resp))
48 req.respond(HTTP_OK, HGTYPE, length=len(resp))
46 49 req.write(resp)
47 50
48 51 def between(web, req):
@@ -53,11 +56,11 b' def between(web, req):'
53 56 for b in web.repo.between(pairs):
54 57 resp.write(" ".join(map(hex, b)) + "\n")
55 58 resp = resp.getvalue()
56 req.httphdr("application/mercurial-0.1", length=len(resp))
59 req.respond(HTTP_OK, HGTYPE, length=len(resp))
57 60 req.write(resp)
58 61
59 62 def changegroup(web, req):
60 req.httphdr("application/mercurial-0.1")
63 req.respond(HTTP_OK, HGTYPE)
61 64 nodes = []
62 65 if not web.allowpull:
63 66 return
@@ -76,7 +79,7 b' def changegroup(web, req):'
76 79 req.write(z.flush())
77 80
78 81 def changegroupsubset(web, req):
79 req.httphdr("application/mercurial-0.1")
82 req.respond(HTTP_OK, HGTYPE)
80 83 bases = []
81 84 heads = []
82 85 if not web.allowpull:
@@ -106,7 +109,7 b' def capabilities(web, req):'
106 109 if unbundleversions:
107 110 caps.append('unbundle=%s' % ','.join(unbundleversions))
108 111 resp = ' '.join(caps)
109 req.httphdr("application/mercurial-0.1", length=len(resp))
112 req.respond(HTTP_OK, HGTYPE, length=len(resp))
110 113 req.write(resp)
111 114
112 115 def unbundle(web, req):
@@ -116,7 +119,8 b' def unbundle(web, req):'
116 119 # drain incoming bundle, else client will not see
117 120 # response when run outside cgi script
118 121 pass
119 req.httphdr("application/mercurial-0.1", headers=headers)
122 req.header(headers.items())
123 req.respond(HTTP_OK, HGTYPE)
120 124 req.write('0\n')
121 125 req.write(response)
122 126
@@ -148,7 +152,7 b' def unbundle(web, req):'
148 152 bail(_('unsynced changes\n'))
149 153 return
150 154
151 req.httphdr("application/mercurial-0.1")
155 req.respond(HTTP_OK, HGTYPE)
152 156
153 157 # do not lock repo until all changegroup data is
154 158 # streamed. save to temporary file.
@@ -232,14 +236,15 b' def unbundle(web, req):'
232 236 filename = ''
233 237 error = getattr(inst, 'strerror', 'Unknown error')
234 238 if inst.errno == errno.ENOENT:
235 code = 404
239 code = HTTP_NOT_FOUND
236 240 else:
237 code = 500
238 req.respond(code, '%s: %s\n' % (error, filename))
241 code = HTTP_SERVER_ERROR
242 req.respond(code)
243 req.write('%s: %s\n' % (error, filename))
239 244 finally:
240 245 fp.close()
241 246 os.unlink(tempname)
242 247
243 248 def stream_out(web, req):
244 req.httphdr("application/mercurial-0.1")
249 req.respond(HTTP_OK, HGTYPE)
245 250 streamclone.stream_out(web.repo, req, untrusted=True)
@@ -17,7 +17,6 b' class wsgirequest(object):'
17 17 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
18 18 % version)
19 19 self.inp = wsgienv['wsgi.input']
20 self.server_write = None
21 20 self.err = wsgienv['wsgi.errors']
22 21 self.threaded = wsgienv['wsgi.multithread']
23 22 self.multiprocess = wsgienv['wsgi.multiprocess']
@@ -25,6 +24,7 b' class wsgirequest(object):'
25 24 self.env = wsgienv
26 25 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
27 26 self._start_response = start_response
27 self.server_write = None
28 28 self.headers = []
29 29
30 30 def __iter__(self):
@@ -33,8 +33,10 b' class wsgirequest(object):'
33 33 def read(self, count=-1):
34 34 return self.inp.read(count)
35 35
36 def start_response(self, status):
36 def respond(self, status, type=None, filename=None, length=0):
37 37 if self._start_response is not None:
38
39 self.httphdr(type, filename, length)
38 40 if not self.headers:
39 41 raise RuntimeError("request.write called before headers sent")
40 42
@@ -44,6 +46,8 b' class wsgirequest(object):'
44 46
45 47 if isinstance(status, ErrorResponse):
46 48 status = statusmessage(status.code)
49 elif status == 200:
50 status = '200 Script output follows'
47 51 elif isinstance(status, int):
48 52 status = statusmessage(status)
49 53
@@ -51,24 +55,17 b' class wsgirequest(object):'
51 55 self._start_response = None
52 56 self.headers = []
53 57
54 def respond(self, status, *things):
55 if not things:
56 self.start_response(status)
57 for thing in things:
58 if hasattr(thing, "__iter__"):
59 for part in thing:
60 self.respond(status, part)
61 else:
62 thing = str(thing)
63 self.start_response(status)
64 try:
65 self.server_write(thing)
66 except socket.error, inst:
67 if inst[0] != errno.ECONNRESET:
68 raise
69
70 def write(self, *things):
71 self.respond('200 Script output follows', *things)
58 def write(self, thing):
59 if hasattr(thing, "__iter__"):
60 for part in thing:
61 self.write(part)
62 else:
63 thing = str(thing)
64 try:
65 self.server_write(thing)
66 except socket.error, inst:
67 if inst[0] != errno.ECONNRESET:
68 raise
72 69
73 70 def writelines(self, lines):
74 71 for line in lines:
@@ -83,9 +80,10 b' class wsgirequest(object):'
83 80 def header(self, headers=[('Content-Type','text/html')]):
84 81 self.headers.extend(headers)
85 82
86 def httphdr(self, type, filename=None, length=0, headers={}):
83 def httphdr(self, type=None, filename=None, length=0, headers={}):
87 84 headers = headers.items()
88 headers.append(('Content-Type', type))
85 if type is not None:
86 headers.append(('Content-Type', type))
89 87 if filename:
90 88 headers.append(('Content-Disposition', 'inline; filename=%s' %
91 89 filename))
@@ -7,7 +7,7 b''
7 7
8 8 import os, mimetypes
9 9 from mercurial import revlog, util, hg
10 from common import staticfile, ErrorResponse
10 from common import staticfile, ErrorResponse, HTTP_OK, HTTP_NOT_FOUND
11 11
12 12 # __all__ is populated with the allowed commands. Be sure to add to it if
13 13 # you're adding a new command, or the new command won't work.
@@ -27,12 +27,16 b' def log(web, req, tmpl):'
27 27 def rawfile(web, req, tmpl):
28 28 path = web.cleanpath(req.form.get('file', [''])[0])
29 29 if not path:
30 return web.manifest(tmpl, web.changectx(req), path)
30 content = web.manifest(tmpl, web.changectx(req), path)
31 req.respond(HTTP_OK, web.ctype)
32 return content
31 33
32 34 try:
33 35 fctx = web.filectx(req)
34 36 except revlog.LookupError:
35 return web.manifest(tmpl, web.changectx(req), path)
37 content = web.manifest(tmpl, web.changectx(req), path)
38 req.respond(HTTP_OK, web.ctype)
39 return content
36 40
37 41 path = fctx.path()
38 42 text = fctx.data()
@@ -40,7 +44,7 b' def rawfile(web, req, tmpl):'
40 44 if mt is None or util.binary(text):
41 45 mt = mt or 'application/octet-stream'
42 46
43 req.httphdr(mt, path, len(text))
47 req.respond(HTTP_OK, mt, path, len(text))
44 48 return [text]
45 49
46 50 def file(web, req, tmpl):
@@ -104,8 +108,7 b' def archive(web, req, tmpl):'
104 108 web.configbool("web", "allow" + type_, False))):
105 109 web.archive(tmpl, req, req.form['node'][0], type_)
106 110 return []
107
108 raise ErrorResponse(400, 'Unsupported archive type: %s' % type_)
111 raise ErrorResponse(HTTP_NOT_FOUND, 'Unsupported archive type: %s' % type_)
109 112
110 113 def static(web, req, tmpl):
111 114 fname = req.form['file'][0]
General Comments 0
You need to be logged in to leave comments. Login now