##// END OF EJS Templates
hgweb: expose input stream on parsed WSGI request object...
Gregory Szorc -
r36873:da4e2f87 default
parent child Browse files
Show More
@@ -291,7 +291,8 b' class hgwebdir(object):'
291 291 # variable.
292 292 # TODO this is kind of hacky and we should have a better
293 293 # way of doing this than with REPO_NAME side-effects.
294 wsgireq.req = requestmod.parserequestfromenv(wsgireq.env)
294 wsgireq.req = requestmod.parserequestfromenv(
295 wsgireq.env, wsgireq.req.bodyfh)
295 296 try:
296 297 # ensure caller gets private copy of ui
297 298 repo = hg.repository(self.ui.copy(), real)
@@ -61,7 +61,10 b' def normalize(form):'
61 61
62 62 @attr.s(frozen=True)
63 63 class parsedrequest(object):
64 """Represents a parsed WSGI request / static HTTP request parameters."""
64 """Represents a parsed WSGI request.
65
66 Contains both parsed parameters as well as a handle on the input stream.
67 """
65 68
66 69 # Request method.
67 70 method = attr.ib()
@@ -91,8 +94,10 b' class parsedrequest(object):'
91 94 # wsgiref.headers.Headers instance. Operates like a dict with case
92 95 # insensitive keys.
93 96 headers = attr.ib()
97 # Request body input stream.
98 bodyfh = attr.ib()
94 99
95 def parserequestfromenv(env):
100 def parserequestfromenv(env, bodyfh):
96 101 """Parse URL components from environment variables.
97 102
98 103 WSGI defines request attributes via environment variables. This function
@@ -209,6 +214,12 b' def parserequestfromenv(env):'
209 214 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
210 215 headers['Content-Length'] = env['CONTENT_LENGTH']
211 216
217 # TODO do this once we remove wsgirequest.inp, otherwise we could have
218 # multiple readers from the underlying input stream.
219 #bodyfh = env['wsgi.input']
220 #if 'Content-Length' in headers:
221 # bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
222
212 223 return parsedrequest(method=env['REQUEST_METHOD'],
213 224 url=fullurl, baseurl=baseurl,
214 225 advertisedurl=advertisedfullurl,
@@ -219,7 +230,8 b' def parserequestfromenv(env):'
219 230 querystring=querystring,
220 231 querystringlist=querystringlist,
221 232 querystringdict=querystringdict,
222 headers=headers)
233 headers=headers,
234 bodyfh=bodyfh)
223 235
224 236 class wsgirequest(object):
225 237 """Higher-level API for a WSGI request.
@@ -233,28 +245,27 b' class wsgirequest(object):'
233 245 if (version < (1, 0)) or (version >= (2, 0)):
234 246 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
235 247 % version)
236 self.inp = wsgienv[r'wsgi.input']
248
249 inp = wsgienv[r'wsgi.input']
237 250
238 251 if r'HTTP_CONTENT_LENGTH' in wsgienv:
239 self.inp = util.cappedreader(self.inp,
240 int(wsgienv[r'HTTP_CONTENT_LENGTH']))
252 inp = util.cappedreader(inp, int(wsgienv[r'HTTP_CONTENT_LENGTH']))
241 253 elif r'CONTENT_LENGTH' in wsgienv:
242 self.inp = util.cappedreader(self.inp,
243 int(wsgienv[r'CONTENT_LENGTH']))
254 inp = util.cappedreader(inp, int(wsgienv[r'CONTENT_LENGTH']))
244 255
245 256 self.err = wsgienv[r'wsgi.errors']
246 257 self.threaded = wsgienv[r'wsgi.multithread']
247 258 self.multiprocess = wsgienv[r'wsgi.multiprocess']
248 259 self.run_once = wsgienv[r'wsgi.run_once']
249 260 self.env = wsgienv
250 self.form = normalize(cgi.parse(self.inp,
261 self.form = normalize(cgi.parse(inp,
251 262 self.env,
252 263 keep_blank_values=1))
253 264 self._start_response = start_response
254 265 self.server_write = None
255 266 self.headers = []
256 267
257 self.req = parserequestfromenv(wsgienv)
268 self.req = parserequestfromenv(wsgienv, inp)
258 269
259 270 def respond(self, status, type, filename=None, body=None):
260 271 if not isinstance(type, str):
@@ -315,7 +326,7 b' class wsgirequest(object):'
315 326 # input stream doesn't overrun the actual request. So there's
316 327 # no guarantee that reading until EOF won't corrupt the stream
317 328 # state.
318 if not isinstance(self.inp, util.cappedreader):
329 if not isinstance(self.req.bodyfh, util.cappedreader):
319 330 close = True
320 331 else:
321 332 # We /could/ only drain certain HTTP response codes. But 200
@@ -329,9 +340,9 b' class wsgirequest(object):'
329 340 self.headers.append((r'Connection', r'Close'))
330 341
331 342 if drain:
332 assert isinstance(self.inp, util.cappedreader)
343 assert isinstance(self.req.bodyfh, util.cappedreader)
333 344 while True:
334 chunk = self.inp.read(32768)
345 chunk = self.req.bodyfh.read(32768)
335 346 if not chunk:
336 347 break
337 348
@@ -83,7 +83,7 b' class httpv1protocolhandler(wireprototyp'
83 83 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
84 84 if postlen:
85 85 args.update(urlreq.parseqs(
86 self._wsgireq.inp.read(postlen), keep_blank_values=True))
86 self._req.bodyfh.read(postlen), keep_blank_values=True))
87 87 return args
88 88
89 89 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
@@ -97,7 +97,7 b' class httpv1protocolhandler(wireprototyp'
97 97 # If httppostargs is used, we need to read Content-Length
98 98 # minus the amount that was consumed by args.
99 99 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
100 for s in util.filechunkiter(self._wsgireq.inp, limit=length):
100 for s in util.filechunkiter(self._req.bodyfh, limit=length):
101 101 fp.write(s)
102 102
103 103 @contextlib.contextmanager
General Comments 0
You need to be logged in to leave comments. Login now