##// END OF EJS Templates
hgweb: refactor the request draining code...
Gregory Szorc -
r36871:2cdf47e1 default
parent child Browse files
Show More
@@ -254,12 +254,6 class wsgirequest(object):
254 254 self.server_write = None
255 255 self.headers = []
256 256
257 def drain(self):
258 '''need to read all data from request, httplib is half-duplex'''
259 length = int(self.env.get('CONTENT_LENGTH') or 0)
260 for s in util.filechunkiter(self.inp, limit=length):
261 pass
262
263 257 def respond(self, status, type, filename=None, body=None):
264 258 if not isinstance(type, str):
265 259 type = pycompat.sysstr(type)
@@ -292,6 +286,53 class wsgirequest(object):
292 286 elif isinstance(status, int):
293 287 status = statusmessage(status)
294 288
289 # Various HTTP clients (notably httplib) won't read the HTTP
290 # response until the HTTP request has been sent in full. If servers
291 # (us) send a response before the HTTP request has been fully sent,
292 # the connection may deadlock because neither end is reading.
293 #
294 # We work around this by "draining" the request data before
295 # sending any response in some conditions.
296 drain = False
297 close = False
298
299 # If the client sent Expect: 100-continue, we assume it is smart
300 # enough to deal with the server sending a response before reading
301 # the request. (httplib doesn't do this.)
302 if self.env.get(r'HTTP_EXPECT', r'').lower() == r'100-continue':
303 pass
304 # Only tend to request methods that have bodies. Strictly speaking,
305 # we should sniff for a body. But this is fine for our existing
306 # WSGI applications.
307 elif self.env[r'REQUEST_METHOD'] not in (r'POST', r'PUT'):
308 pass
309 else:
310 # If we don't know how much data to read, there's no guarantee
311 # that we can drain the request responsibly. The WSGI
312 # specification only says that servers *should* ensure the
313 # input stream doesn't overrun the actual request. So there's
314 # no guarantee that reading until EOF won't corrupt the stream
315 # state.
316 if not isinstance(self.inp, util.cappedreader):
317 close = True
318 else:
319 # We /could/ only drain certain HTTP response codes. But 200
320 # and non-200 wire protocol responses both require draining.
321 # Since we have a capped reader in place for all situations
322 # where we drain, it is safe to read from that stream. We'll
323 # either do a drain or no-op if we're already at EOF.
324 drain = True
325
326 if close:
327 self.headers.append((r'Connection', r'Close'))
328
329 if drain:
330 assert isinstance(self.inp, util.cappedreader)
331 while True:
332 chunk = self.inp.read(32768)
333 if not chunk:
334 break
335
295 336 self.server_write = self._start_response(
296 337 pycompat.sysstr(status), self.headers)
297 338 self._start_response = None
@@ -301,9 +301,6 def _callhttp(repo, wsgireq, req, proto,
301 301 wsgireq.respond(HTTP_OK, HGTYPE, body=rsp)
302 302 return []
303 303 elif isinstance(rsp, wireprototypes.pusherr):
304 # This is the httplib workaround documented in _handlehttperror().
305 wsgireq.drain()
306
307 304 rsp = '0\n%s\n' % rsp.res
308 305 wsgireq.respond(HTTP_OK, HGTYPE, body=rsp)
309 306 return []
@@ -316,21 +313,6 def _callhttp(repo, wsgireq, req, proto,
316 313 def _handlehttperror(e, wsgireq, req):
317 314 """Called when an ErrorResponse is raised during HTTP request processing."""
318 315
319 # Clients using Python's httplib are stateful: the HTTP client
320 # won't process an HTTP response until all request data is
321 # sent to the server. The intent of this code is to ensure
322 # we always read HTTP request data from the client, thus
323 # ensuring httplib transitions to a state that allows it to read
324 # the HTTP response. In other words, it helps prevent deadlocks
325 # on clients using httplib.
326
327 if (req.method == 'POST' and
328 # But not if Expect: 100-continue is being used.
329 (req.headers.get('Expect', '').lower() != '100-continue')):
330 wsgireq.drain()
331 else:
332 wsgireq.headers.append((r'Connection', r'Close'))
333
334 316 # TODO This response body assumes the failed command was
335 317 # "unbundle." That assumption is not always valid.
336 318 wsgireq.respond(e, HGTYPE, body='0\n%s\n' % pycompat.bytestr(e))
General Comments 0
You need to be logged in to leave comments. Login now