121
|
1 # hgweb/request.py - An http request from either CGI or the standalone server.
|
|
2 #
|
|
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
|
|
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
|
|
5 #
|
|
6 # This software may be used and distributed according to the terms of the
|
|
7 # GNU General Public License version 2, incorporated herein by reference.
|
|
8
|
|
9 import socket, cgi, errno
|
122
|
10 from upmana.mercurial import util
|
121
|
11 from common import ErrorResponse, statusmessage
|
|
12
|
|
13 shortcuts = {
|
|
14 'cl': [('cmd', ['changelog']), ('rev', None)],
|
|
15 'sl': [('cmd', ['shortlog']), ('rev', None)],
|
|
16 'cs': [('cmd', ['changeset']), ('node', None)],
|
|
17 'f': [('cmd', ['file']), ('filenode', None)],
|
|
18 'fl': [('cmd', ['filelog']), ('filenode', None)],
|
|
19 'fd': [('cmd', ['filediff']), ('node', None)],
|
|
20 'fa': [('cmd', ['annotate']), ('filenode', None)],
|
|
21 'mf': [('cmd', ['manifest']), ('manifest', None)],
|
|
22 'ca': [('cmd', ['archive']), ('node', None)],
|
|
23 'tags': [('cmd', ['tags'])],
|
|
24 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
|
|
25 'static': [('cmd', ['static']), ('file', None)]
|
|
26 }
|
|
27
|
|
28 def expand(form):
|
|
29 for k in shortcuts.iterkeys():
|
|
30 if k in form:
|
|
31 for name, value in shortcuts[k]:
|
|
32 if value is None:
|
|
33 value = form[k]
|
|
34 form[name] = value
|
|
35 del form[k]
|
|
36 return form
|
|
37
|
|
38 class wsgirequest(object):
|
|
39 def __init__(self, wsgienv, start_response):
|
|
40 version = wsgienv['wsgi.version']
|
|
41 if (version < (1, 0)) or (version >= (2, 0)):
|
|
42 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
|
|
43 % version)
|
|
44 self.inp = wsgienv['wsgi.input']
|
|
45 self.err = wsgienv['wsgi.errors']
|
|
46 self.threaded = wsgienv['wsgi.multithread']
|
|
47 self.multiprocess = wsgienv['wsgi.multiprocess']
|
|
48 self.run_once = wsgienv['wsgi.run_once']
|
|
49 self.env = wsgienv
|
|
50 self.form = expand(cgi.parse(self.inp, self.env, keep_blank_values=1))
|
|
51 self._start_response = start_response
|
|
52 self.server_write = None
|
|
53 self.headers = []
|
|
54
|
|
55 def __iter__(self):
|
|
56 return iter([])
|
|
57
|
|
58 def read(self, count=-1):
|
|
59 return self.inp.read(count)
|
|
60
|
|
61 def drain(self):
|
|
62 '''need to read all data from request, httplib is half-duplex'''
|
|
63 length = int(self.env.get('CONTENT_LENGTH', 0))
|
|
64 for s in util.filechunkiter(self.inp, limit=length):
|
|
65 pass
|
|
66
|
|
67 def respond(self, status, type=None, filename=None, length=0):
|
|
68 if self._start_response is not None:
|
|
69
|
|
70 self.httphdr(type, filename, length)
|
|
71 if not self.headers:
|
|
72 raise RuntimeError("request.write called before headers sent")
|
|
73
|
|
74 for k, v in self.headers:
|
|
75 if not isinstance(v, str):
|
|
76 raise TypeError('header value must be string: %r' % v)
|
|
77
|
|
78 if isinstance(status, ErrorResponse):
|
|
79 self.header(status.headers)
|
|
80 status = statusmessage(status.code)
|
|
81 elif status == 200:
|
|
82 status = '200 Script output follows'
|
|
83 elif isinstance(status, int):
|
|
84 status = statusmessage(status)
|
|
85
|
|
86 self.server_write = self._start_response(status, self.headers)
|
|
87 self._start_response = None
|
|
88 self.headers = []
|
|
89
|
|
90 def write(self, thing):
|
|
91 if hasattr(thing, "__iter__"):
|
|
92 for part in thing:
|
|
93 self.write(part)
|
|
94 else:
|
|
95 thing = str(thing)
|
|
96 try:
|
|
97 self.server_write(thing)
|
|
98 except socket.error, inst:
|
|
99 if inst[0] != errno.ECONNRESET:
|
|
100 raise
|
|
101
|
|
102 def writelines(self, lines):
|
|
103 for line in lines:
|
|
104 self.write(line)
|
|
105
|
|
106 def flush(self):
|
|
107 return None
|
|
108
|
|
109 def close(self):
|
|
110 return None
|
|
111
|
|
112 def header(self, headers=[('Content-Type','text/html')]):
|
|
113 self.headers.extend(headers)
|
|
114
|
|
115 def httphdr(self, type=None, filename=None, length=0, headers={}):
|
|
116 headers = headers.items()
|
|
117 if type is not None:
|
|
118 headers.append(('Content-Type', type))
|
|
119 if filename:
|
|
120 filename = (filename.split('/')[-1]
|
|
121 .replace('\\', '\\\\').replace('"', '\\"'))
|
|
122 headers.append(('Content-Disposition',
|
|
123 'inline; filename="%s"' % filename))
|
|
124 if length:
|
|
125 headers.append(('Content-Length', str(length)))
|
|
126 self.header(headers)
|
|
127
|
|
128 def wsgiapplication(app_maker):
|
|
129 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
|
|
130 can and should now be used as a WSGI application.'''
|
|
131 application = app_maker()
|
|
132 def run_wsgi(env, respond):
|
|
133 return application(env, respond)
|
|
134 return run_wsgi
|