mercurial/hgweb/request.py
changeset 36861 a88d68dc3ee8
parent 36859 422be99519e5
child 36862 ec0af9c59270
equal deleted inserted replaced
36860:2859c6fa4fc2 36861:a88d68dc3ee8
    21 
    21 
    22 from ..thirdparty import (
    22 from ..thirdparty import (
    23     attr,
    23     attr,
    24 )
    24 )
    25 from .. import (
    25 from .. import (
       
    26     error,
    26     pycompat,
    27     pycompat,
    27     util,
    28     util,
    28 )
    29 )
    29 
    30 
    30 @attr.s(frozen=True)
    31 @attr.s(frozen=True)
   199                          querystringlist=querystringlist,
   200                          querystringlist=querystringlist,
   200                          querystringdict=querystringdict,
   201                          querystringdict=querystringdict,
   201                          headers=headers,
   202                          headers=headers,
   202                          bodyfh=bodyfh)
   203                          bodyfh=bodyfh)
   203 
   204 
       
   205 class wsgiresponse(object):
       
   206     """Represents a response to a WSGI request.
       
   207 
       
   208     A response consists of a status line, headers, and a body.
       
   209 
       
   210     Consumers must populate the ``status`` and ``headers`` fields and
       
   211     make a call to a ``setbody*()`` method before the response can be
       
   212     issued.
       
   213 
       
   214     When it is time to start sending the response over the wire,
       
   215     ``sendresponse()`` is called. It handles emitting the header portion
       
   216     of the response message. It then yields chunks of body data to be
       
   217     written to the peer. Typically, the WSGI application itself calls
       
   218     and returns the value from ``sendresponse()``.
       
   219     """
       
   220 
       
   221     def __init__(self, req, startresponse):
       
   222         """Create an empty response tied to a specific request.
       
   223 
       
   224         ``req`` is a ``parsedrequest``. ``startresponse`` is the
       
   225         ``start_response`` function passed to the WSGI application.
       
   226         """
       
   227         self._req = req
       
   228         self._startresponse = startresponse
       
   229 
       
   230         self.status = None
       
   231         self.headers = wsgiheaders.Headers([])
       
   232 
       
   233         self._bodybytes = None
       
   234         self._bodygen = None
       
   235         self._started = False
       
   236 
       
   237     def setbodybytes(self, b):
       
   238         """Define the response body as static bytes."""
       
   239         if self._bodybytes is not None or self._bodygen is not None:
       
   240             raise error.ProgrammingError('cannot define body multiple times')
       
   241 
       
   242         self._bodybytes = b
       
   243         self.headers['Content-Length'] = '%d' % len(b)
       
   244 
       
   245     def setbodygen(self, gen):
       
   246         """Define the response body as a generator of bytes."""
       
   247         if self._bodybytes is not None or self._bodygen is not None:
       
   248             raise error.ProgrammingError('cannot define body multiple times')
       
   249 
       
   250         self._bodygen = gen
       
   251 
       
   252     def sendresponse(self):
       
   253         """Send the generated response to the client.
       
   254 
       
   255         Before this is called, ``status`` must be set and one of
       
   256         ``setbodybytes()`` or ``setbodygen()`` must be called.
       
   257 
       
   258         Calling this method multiple times is not allowed.
       
   259         """
       
   260         if self._started:
       
   261             raise error.ProgrammingError('sendresponse() called multiple times')
       
   262 
       
   263         self._started = True
       
   264 
       
   265         if not self.status:
       
   266             raise error.ProgrammingError('status line not defined')
       
   267 
       
   268         if self._bodybytes is None and self._bodygen is None:
       
   269             raise error.ProgrammingError('response body not defined')
       
   270 
       
   271         # Various HTTP clients (notably httplib) won't read the HTTP response
       
   272         # until the HTTP request has been sent in full. If servers (us) send a
       
   273         # response before the HTTP request has been fully sent, the connection
       
   274         # may deadlock because neither end is reading.
       
   275         #
       
   276         # We work around this by "draining" the request data before
       
   277         # sending any response in some conditions.
       
   278         drain = False
       
   279         close = False
       
   280 
       
   281         # If the client sent Expect: 100-continue, we assume it is smart enough
       
   282         # to deal with the server sending a response before reading the request.
       
   283         # (httplib doesn't do this.)
       
   284         if self._req.headers.get('Expect', '').lower() == '100-continue':
       
   285             pass
       
   286         # Only tend to request methods that have bodies. Strictly speaking,
       
   287         # we should sniff for a body. But this is fine for our existing
       
   288         # WSGI applications.
       
   289         elif self._req.method not in ('POST', 'PUT'):
       
   290             pass
       
   291         else:
       
   292             # If we don't know how much data to read, there's no guarantee
       
   293             # that we can drain the request responsibly. The WSGI
       
   294             # specification only says that servers *should* ensure the
       
   295             # input stream doesn't overrun the actual request. So there's
       
   296             # no guarantee that reading until EOF won't corrupt the stream
       
   297             # state.
       
   298             if not isinstance(self._req.bodyfh, util.cappedreader):
       
   299                 close = True
       
   300             else:
       
   301                 # We /could/ only drain certain HTTP response codes. But 200 and
       
   302                 # non-200 wire protocol responses both require draining. Since
       
   303                 # we have a capped reader in place for all situations where we
       
   304                 # drain, it is safe to read from that stream. We'll either do
       
   305                 # a drain or no-op if we're already at EOF.
       
   306                 drain = True
       
   307 
       
   308         if close:
       
   309             self.headers['Connection'] = 'Close'
       
   310 
       
   311         if drain:
       
   312             assert isinstance(self._req.bodyfh, util.cappedreader)
       
   313             while True:
       
   314                 chunk = self._req.bodyfh.read(32768)
       
   315                 if not chunk:
       
   316                     break
       
   317 
       
   318         self._startresponse(pycompat.sysstr(self.status), self.headers.items())
       
   319         if self._bodybytes:
       
   320             yield self._bodybytes
       
   321         elif self._bodygen:
       
   322             for chunk in self._bodygen:
       
   323                 yield chunk
       
   324         else:
       
   325             error.ProgrammingError('do not know how to send body')
       
   326 
   204 class wsgirequest(object):
   327 class wsgirequest(object):
   205     """Higher-level API for a WSGI request.
   328     """Higher-level API for a WSGI request.
   206 
   329 
   207     WSGI applications are invoked with 2 arguments. They are used to
   330     WSGI applications are invoked with 2 arguments. They are used to
   208     instantiate instances of this class, which provides higher-level APIs
   331     instantiate instances of this class, which provides higher-level APIs
   226         self.multiprocess = wsgienv[r'wsgi.multiprocess']
   349         self.multiprocess = wsgienv[r'wsgi.multiprocess']
   227         self.run_once = wsgienv[r'wsgi.run_once']
   350         self.run_once = wsgienv[r'wsgi.run_once']
   228         self.env = wsgienv
   351         self.env = wsgienv
   229         self.req = parserequestfromenv(wsgienv, inp)
   352         self.req = parserequestfromenv(wsgienv, inp)
   230         self.form = self.req.querystringdict
   353         self.form = self.req.querystringdict
       
   354         self.res = wsgiresponse(self.req, start_response)
   231         self._start_response = start_response
   355         self._start_response = start_response
   232         self.server_write = None
   356         self.server_write = None
   233         self.headers = []
   357         self.headers = []
   234 
   358 
   235     def respond(self, status, type, filename=None, body=None):
   359     def respond(self, status, type, filename=None, body=None):