mercurial/hgweb/request.py
changeset 36878 ccb70a77f746
parent 36876 97f44b0720e2
child 36896 b2a3308d6a21
equal deleted inserted replaced
36877:02bea04b4c54 36878:ccb70a77f746
    13 import wsgiref.headers as wsgiheaders
    13 import wsgiref.headers as wsgiheaders
    14 #import wsgiref.validate
    14 #import wsgiref.validate
    15 
    15 
    16 from .common import (
    16 from .common import (
    17     ErrorResponse,
    17     ErrorResponse,
    18     HTTP_NOT_MODIFIED,
       
    19     statusmessage,
    18     statusmessage,
    20 )
    19 )
    21 
    20 
    22 from ..thirdparty import (
    21 from ..thirdparty import (
    23     attr,
    22     attr,
   359         if (self._bodybytes is not None or self._bodygen is not None
   358         if (self._bodybytes is not None or self._bodygen is not None
   360             or self._bodywillwrite):
   359             or self._bodywillwrite):
   361             raise error.ProgrammingError('cannot define body multiple times')
   360             raise error.ProgrammingError('cannot define body multiple times')
   362 
   361 
   363     def setbodybytes(self, b):
   362     def setbodybytes(self, b):
   364         """Define the response body as static bytes."""
   363         """Define the response body as static bytes.
       
   364 
       
   365         The empty string signals that there is no response body.
       
   366         """
   365         self._verifybody()
   367         self._verifybody()
   366         self._bodybytes = b
   368         self._bodybytes = b
   367         self.headers['Content-Length'] = '%d' % len(b)
   369         self.headers['Content-Length'] = '%d' % len(b)
   368 
   370 
   369     def setbodygen(self, gen):
   371     def setbodygen(self, gen):
   405             raise error.ProgrammingError('status line not defined')
   407             raise error.ProgrammingError('status line not defined')
   406 
   408 
   407         if (self._bodybytes is None and self._bodygen is None
   409         if (self._bodybytes is None and self._bodygen is None
   408             and not self._bodywillwrite):
   410             and not self._bodywillwrite):
   409             raise error.ProgrammingError('response body not defined')
   411             raise error.ProgrammingError('response body not defined')
       
   412 
       
   413         # RFC 7232 Section 4.1 states that a 304 MUST generate one of
       
   414         # {Cache-Control, Content-Location, Date, ETag, Expires, Vary}
       
   415         # and SHOULD NOT generate other headers unless they could be used
       
   416         # to guide cache updates. Furthermore, RFC 7230 Section 3.3.2
       
   417         # states that no response body can be issued. Content-Length can
       
   418         # be sent. But if it is present, it should be the size of the response
       
   419         # that wasn't transferred.
       
   420         if self.status.startswith('304 '):
       
   421             # setbodybytes('') will set C-L to 0. This doesn't conform with the
       
   422             # spec. So remove it.
       
   423             if self.headers.get('Content-Length') == '0':
       
   424                 del self.headers['Content-Length']
       
   425 
       
   426             # Strictly speaking, this is too strict. But until it causes
       
   427             # problems, let's be strict.
       
   428             badheaders = {k for k in self.headers.keys()
       
   429                           if k.lower() not in ('date', 'etag', 'expires',
       
   430                                                'cache-control',
       
   431                                                'content-location',
       
   432                                                'vary')}
       
   433             if badheaders:
       
   434                 raise error.ProgrammingError(
       
   435                     'illegal header on 304 response: %s' %
       
   436                     ', '.join(sorted(badheaders)))
       
   437 
       
   438             if self._bodygen is not None or self._bodywillwrite:
       
   439                 raise error.ProgrammingError("must use setbodybytes('') with "
       
   440                                              "304 responses")
   410 
   441 
   411         # Various HTTP clients (notably httplib) won't read the HTTP response
   442         # Various HTTP clients (notably httplib) won't read the HTTP response
   412         # until the HTTP request has been sent in full. If servers (us) send a
   443         # until the HTTP request has been sent in full. If servers (us) send a
   413         # response before the HTTP request has been fully sent, the connection
   444         # response before the HTTP request has been fully sent, the connection
   414         # may deadlock because neither end is reading.
   445         # may deadlock because neither end is reading.
   537                 if not isinstance(v, str):
   568                 if not isinstance(v, str):
   538                     raise TypeError('header value must be string: %r' % (v,))
   569                     raise TypeError('header value must be string: %r' % (v,))
   539 
   570 
   540             if isinstance(status, ErrorResponse):
   571             if isinstance(status, ErrorResponse):
   541                 self.headers.extend(status.headers)
   572                 self.headers.extend(status.headers)
   542                 if status.code == HTTP_NOT_MODIFIED:
       
   543                     # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
       
   544                     # it MUST NOT include any headers other than these and no
       
   545                     # body
       
   546                     self.headers = [(k, v) for (k, v) in self.headers if
       
   547                                     k in ('Date', 'ETag', 'Expires',
       
   548                                           'Cache-Control', 'Vary')]
       
   549                 status = statusmessage(status.code, pycompat.bytestr(status))
   573                 status = statusmessage(status.code, pycompat.bytestr(status))
   550             elif status == 200:
   574             elif status == 200:
   551                 status = '200 Script output follows'
   575                 status = '200 Script output follows'
   552             elif isinstance(status, int):
   576             elif isinstance(status, int):
   553                 status = statusmessage(status)
   577                 status = statusmessage(status)