##// END OF EJS Templates
request: use trivial iterator over dictionary keys...
Augie Fackler -
r34514:34fcb0f6 default
parent child Browse files
Show More
@@ -1,152 +1,152 b''
1 # hgweb/request.py - An http request from either CGI or the standalone server.
1 # hgweb/request.py - An http request from either CGI or the standalone server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import cgi
11 import cgi
12 import errno
12 import errno
13 import socket
13 import socket
14
14
15 from .common import (
15 from .common import (
16 ErrorResponse,
16 ErrorResponse,
17 HTTP_NOT_MODIFIED,
17 HTTP_NOT_MODIFIED,
18 statusmessage,
18 statusmessage,
19 )
19 )
20
20
21 from .. import (
21 from .. import (
22 util,
22 util,
23 )
23 )
24
24
25 shortcuts = {
25 shortcuts = {
26 'cl': [('cmd', ['changelog']), ('rev', None)],
26 'cl': [('cmd', ['changelog']), ('rev', None)],
27 'sl': [('cmd', ['shortlog']), ('rev', None)],
27 'sl': [('cmd', ['shortlog']), ('rev', None)],
28 'cs': [('cmd', ['changeset']), ('node', None)],
28 'cs': [('cmd', ['changeset']), ('node', None)],
29 'f': [('cmd', ['file']), ('filenode', None)],
29 'f': [('cmd', ['file']), ('filenode', None)],
30 'fl': [('cmd', ['filelog']), ('filenode', None)],
30 'fl': [('cmd', ['filelog']), ('filenode', None)],
31 'fd': [('cmd', ['filediff']), ('node', None)],
31 'fd': [('cmd', ['filediff']), ('node', None)],
32 'fa': [('cmd', ['annotate']), ('filenode', None)],
32 'fa': [('cmd', ['annotate']), ('filenode', None)],
33 'mf': [('cmd', ['manifest']), ('manifest', None)],
33 'mf': [('cmd', ['manifest']), ('manifest', None)],
34 'ca': [('cmd', ['archive']), ('node', None)],
34 'ca': [('cmd', ['archive']), ('node', None)],
35 'tags': [('cmd', ['tags'])],
35 'tags': [('cmd', ['tags'])],
36 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
36 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
37 'static': [('cmd', ['static']), ('file', None)]
37 'static': [('cmd', ['static']), ('file', None)]
38 }
38 }
39
39
40 def normalize(form):
40 def normalize(form):
41 # first expand the shortcuts
41 # first expand the shortcuts
42 for k in shortcuts.iterkeys():
42 for k in shortcuts:
43 if k in form:
43 if k in form:
44 for name, value in shortcuts[k]:
44 for name, value in shortcuts[k]:
45 if value is None:
45 if value is None:
46 value = form[k]
46 value = form[k]
47 form[name] = value
47 form[name] = value
48 del form[k]
48 del form[k]
49 # And strip the values
49 # And strip the values
50 for k, v in form.iteritems():
50 for k, v in form.iteritems():
51 form[k] = [i.strip() for i in v]
51 form[k] = [i.strip() for i in v]
52 return form
52 return form
53
53
54 class wsgirequest(object):
54 class wsgirequest(object):
55 """Higher-level API for a WSGI request.
55 """Higher-level API for a WSGI request.
56
56
57 WSGI applications are invoked with 2 arguments. They are used to
57 WSGI applications are invoked with 2 arguments. They are used to
58 instantiate instances of this class, which provides higher-level APIs
58 instantiate instances of this class, which provides higher-level APIs
59 for obtaining request parameters, writing HTTP output, etc.
59 for obtaining request parameters, writing HTTP output, etc.
60 """
60 """
61 def __init__(self, wsgienv, start_response):
61 def __init__(self, wsgienv, start_response):
62 version = wsgienv[r'wsgi.version']
62 version = wsgienv[r'wsgi.version']
63 if (version < (1, 0)) or (version >= (2, 0)):
63 if (version < (1, 0)) or (version >= (2, 0)):
64 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
64 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
65 % version)
65 % version)
66 self.inp = wsgienv[r'wsgi.input']
66 self.inp = wsgienv[r'wsgi.input']
67 self.err = wsgienv[r'wsgi.errors']
67 self.err = wsgienv[r'wsgi.errors']
68 self.threaded = wsgienv[r'wsgi.multithread']
68 self.threaded = wsgienv[r'wsgi.multithread']
69 self.multiprocess = wsgienv[r'wsgi.multiprocess']
69 self.multiprocess = wsgienv[r'wsgi.multiprocess']
70 self.run_once = wsgienv[r'wsgi.run_once']
70 self.run_once = wsgienv[r'wsgi.run_once']
71 self.env = wsgienv
71 self.env = wsgienv
72 self.form = normalize(cgi.parse(self.inp,
72 self.form = normalize(cgi.parse(self.inp,
73 self.env,
73 self.env,
74 keep_blank_values=1))
74 keep_blank_values=1))
75 self._start_response = start_response
75 self._start_response = start_response
76 self.server_write = None
76 self.server_write = None
77 self.headers = []
77 self.headers = []
78
78
79 def __iter__(self):
79 def __iter__(self):
80 return iter([])
80 return iter([])
81
81
82 def read(self, count=-1):
82 def read(self, count=-1):
83 return self.inp.read(count)
83 return self.inp.read(count)
84
84
85 def drain(self):
85 def drain(self):
86 '''need to read all data from request, httplib is half-duplex'''
86 '''need to read all data from request, httplib is half-duplex'''
87 length = int(self.env.get('CONTENT_LENGTH') or 0)
87 length = int(self.env.get('CONTENT_LENGTH') or 0)
88 for s in util.filechunkiter(self.inp, limit=length):
88 for s in util.filechunkiter(self.inp, limit=length):
89 pass
89 pass
90
90
91 def respond(self, status, type, filename=None, body=None):
91 def respond(self, status, type, filename=None, body=None):
92 if self._start_response is not None:
92 if self._start_response is not None:
93 self.headers.append(('Content-Type', type))
93 self.headers.append(('Content-Type', type))
94 if filename:
94 if filename:
95 filename = (filename.rpartition('/')[-1]
95 filename = (filename.rpartition('/')[-1]
96 .replace('\\', '\\\\').replace('"', '\\"'))
96 .replace('\\', '\\\\').replace('"', '\\"'))
97 self.headers.append(('Content-Disposition',
97 self.headers.append(('Content-Disposition',
98 'inline; filename="%s"' % filename))
98 'inline; filename="%s"' % filename))
99 if body is not None:
99 if body is not None:
100 self.headers.append(('Content-Length', str(len(body))))
100 self.headers.append(('Content-Length', str(len(body))))
101
101
102 for k, v in self.headers:
102 for k, v in self.headers:
103 if not isinstance(v, str):
103 if not isinstance(v, str):
104 raise TypeError('header value must be string: %r' % (v,))
104 raise TypeError('header value must be string: %r' % (v,))
105
105
106 if isinstance(status, ErrorResponse):
106 if isinstance(status, ErrorResponse):
107 self.headers.extend(status.headers)
107 self.headers.extend(status.headers)
108 if status.code == HTTP_NOT_MODIFIED:
108 if status.code == HTTP_NOT_MODIFIED:
109 # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
109 # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
110 # it MUST NOT include any headers other than these and no
110 # it MUST NOT include any headers other than these and no
111 # body
111 # body
112 self.headers = [(k, v) for (k, v) in self.headers if
112 self.headers = [(k, v) for (k, v) in self.headers if
113 k in ('Date', 'ETag', 'Expires',
113 k in ('Date', 'ETag', 'Expires',
114 'Cache-Control', 'Vary')]
114 'Cache-Control', 'Vary')]
115 status = statusmessage(status.code, str(status))
115 status = statusmessage(status.code, str(status))
116 elif status == 200:
116 elif status == 200:
117 status = '200 Script output follows'
117 status = '200 Script output follows'
118 elif isinstance(status, int):
118 elif isinstance(status, int):
119 status = statusmessage(status)
119 status = statusmessage(status)
120
120
121 self.server_write = self._start_response(status, self.headers)
121 self.server_write = self._start_response(status, self.headers)
122 self._start_response = None
122 self._start_response = None
123 self.headers = []
123 self.headers = []
124 if body is not None:
124 if body is not None:
125 self.write(body)
125 self.write(body)
126 self.server_write = None
126 self.server_write = None
127
127
128 def write(self, thing):
128 def write(self, thing):
129 if thing:
129 if thing:
130 try:
130 try:
131 self.server_write(thing)
131 self.server_write(thing)
132 except socket.error as inst:
132 except socket.error as inst:
133 if inst[0] != errno.ECONNRESET:
133 if inst[0] != errno.ECONNRESET:
134 raise
134 raise
135
135
136 def writelines(self, lines):
136 def writelines(self, lines):
137 for line in lines:
137 for line in lines:
138 self.write(line)
138 self.write(line)
139
139
140 def flush(self):
140 def flush(self):
141 return None
141 return None
142
142
143 def close(self):
143 def close(self):
144 return None
144 return None
145
145
146 def wsgiapplication(app_maker):
146 def wsgiapplication(app_maker):
147 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
147 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
148 can and should now be used as a WSGI application.'''
148 can and should now be used as a WSGI application.'''
149 application = app_maker()
149 application = app_maker()
150 def run_wsgi(env, respond):
150 def run_wsgi(env, respond):
151 return application(env, respond)
151 return application(env, respond)
152 return run_wsgi
152 return run_wsgi
General Comments 0
You need to be logged in to leave comments. Login now