etwist/request.py
changeset 5155 1dea6e0fdfc1
parent 4425 b9913205d91e
child 5423 e15abfdcce38
equal deleted inserted replaced
5125:eaec839ad3fe 5155:1dea6e0fdfc1
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 
     9 
    10 from datetime import datetime
    10 from datetime import datetime
    11 
    11 
    12 from twisted.web2 import http, http_headers
    12 from twisted.web import http
    13 
    13 
    14 from cubicweb.web import DirectResponse
    14 from cubicweb.web import DirectResponse
    15 from cubicweb.web.request import CubicWebRequestBase
    15 from cubicweb.web.request import CubicWebRequestBase
    16 from cubicweb.web.httpcache import GMTOFFSET
    16 from cubicweb.web.httpcache import GMTOFFSET
    17 
    17 from cubicweb.web.http_headers import Headers
    18 def cleanup_files(dct, encoding):
    18 from cubicweb.etwist.http import not_modified_response
    19     d = {}
       
    20     for k, infos in dct.items():
       
    21         for (filename, mt, stream) in infos:
       
    22             if filename:
       
    23                 # XXX: suppose that no file submitted <-> no filename
       
    24                 filename = unicode(filename, encoding)
       
    25                 mt = u'%s/%s' % (mt.mediaType, mt.mediaSubtype)
       
    26                 d[k] = (filename, mt, stream)
       
    27     return d
       
    28 
    19 
    29 
    20 
    30 class CubicWebTwistedRequestAdapter(CubicWebRequestBase):
    21 class CubicWebTwistedRequestAdapter(CubicWebRequestBase):
    31     def __init__(self, req, vreg, https, base_url):
    22     def __init__(self, req, vreg, https, base_url):
    32         self._twreq = req
    23         self._twreq = req
    33         self._base_url = base_url
    24         self._base_url = base_url
    34         super(CubicWebTwistedRequestAdapter, self).__init__(vreg, https, req.args)
    25         super(CubicWebTwistedRequestAdapter, self).__init__(vreg, https, req.args)
    35         self.form.update(cleanup_files(req.files, self.encoding))
    26         for key, (name, stream) in req.files.iteritems():
    36         # prepare output headers
    27             if name is None:
    37         self.headers_out = http_headers.Headers()
    28                 self.form[key] = (name, stream)
    38         self._headers = req.headers
    29             else:
       
    30                 self.form[key] = (unicode(name, self.encoding), stream)
       
    31         # XXX can't we keep received_headers?
       
    32         self._headers_in = Headers()
       
    33         for k, v in req.received_headers.iteritems():
       
    34             self._headers_in.addRawHeader(k, v)
    39 
    35 
    40     def base_url(self):
    36     def base_url(self):
    41         """return the root url of the instance"""
    37         """return the root url of the instance"""
    42         return self._base_url
    38         return self._base_url
    43 
    39 
    61     def get_header(self, header, default=None, raw=True):
    57     def get_header(self, header, default=None, raw=True):
    62         """return the value associated with the given input header,
    58         """return the value associated with the given input header,
    63         raise KeyError if the header is not set
    59         raise KeyError if the header is not set
    64         """
    60         """
    65         if raw:
    61         if raw:
    66             return self._twreq.headers.getRawHeaders(header, [default])[0]
    62             return self._headers_in.getRawHeaders(header, [default])[0]
    67         return self._twreq.headers.getHeader(header, default)
    63         return self._headers_in.getHeader(header, default)
    68 
       
    69     def set_header(self, header, value, raw=True):
       
    70         """set an output HTTP header"""
       
    71         if raw:
       
    72             # adding encoded header is important, else page content
       
    73             # will be reconverted back to unicode and apart unefficiency, this
       
    74             # may cause decoding problem (e.g. when downloading a file)
       
    75             self.headers_out.setRawHeaders(header, [str(value)])
       
    76         else:
       
    77             self.headers_out.setHeader(header, value)
       
    78 
       
    79     def add_header(self, header, value):
       
    80         """add an output HTTP header"""
       
    81         # adding encoded header is important, else page content
       
    82         # will be reconverted back to unicode and apart unefficiency, this
       
    83         # may cause decoding problem (e.g. when downloading a file)
       
    84         self.headers_out.addRawHeader(header, str(value))
       
    85 
       
    86     def remove_header(self, header):
       
    87         """remove an output HTTP header"""
       
    88         self.headers_out.removeHeader(header)
       
    89 
    64 
    90     def _validate_cache(self):
    65     def _validate_cache(self):
    91         """raise a `DirectResponse` exception if a cached page along the way
    66         """raise a `DirectResponse` exception if a cached page along the way
    92         exists and is still usable
    67         exists and is still usable
    93         """
    68         """
    94         if self.get_header('Cache-Control') in ('max-age=0', 'no-cache'):
    69         if self.get_header('Cache-Control') in ('max-age=0', 'no-cache'):
    95             # Expires header seems to be required by IE7
    70             # Expires header seems to be required by IE7
    96             self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
    71             self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
    97             return
    72             return
    98         try:
    73 
    99             http.checkPreconditions(self._twreq, _PreResponse(self))
    74         # when using both 'Last-Modified' and 'ETag' response headers
   100         except http.HTTPError, ex:
    75         # (i.e. using respectively If-Modified-Since and If-None-Match request
   101             self.info('valid http cache, no actual rendering')
    76         # headers, see
   102             raise DirectResponse(ex.response)
    77         # http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 for
       
    78         # reference
       
    79 
       
    80         cached_because_not_modified_since = False
       
    81 
       
    82         last_modified = self.headers_out.getHeader('last-modified')
       
    83         if last_modified is not None:
       
    84             cached_because_not_modified_since = (self._twreq.setLastModified(last_modified)
       
    85                                                  == http.CACHED)
       
    86 
       
    87         if not cached_because_not_modified_since:
       
    88             return
       
    89 
       
    90         cached_because_etag_is_same = False
       
    91         etag = self.headers_out.getRawHeaders('etag')
       
    92         if etag is not None:
       
    93             cached_because_etag_is_same = self._twreq.setETag(etag[0]) == http.CACHED
       
    94 
       
    95         if cached_because_etag_is_same:
       
    96             response = not_modified_response(self._twreq, self._headers_in)
       
    97             raise DirectResponse(response)
       
    98 
   103         # Expires header seems to be required by IE7
    99         # Expires header seems to be required by IE7
   104         self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
   100         self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
   105 
   101 
   106     def header_accept_language(self):
   102     def header_accept_language(self):
   107         """returns an ordered list of preferred languages"""
   103         """returns an ordered list of preferred languages"""
   118         mtime = self.get_header('If-modified-since', raw=False)
   114         mtime = self.get_header('If-modified-since', raw=False)
   119         if mtime:
   115         if mtime:
   120             # :/ twisted is returned a localized time stamp
   116             # :/ twisted is returned a localized time stamp
   121             return datetime.fromtimestamp(mtime) + GMTOFFSET
   117             return datetime.fromtimestamp(mtime) + GMTOFFSET
   122         return None
   118         return None
   123 
       
   124 
       
   125 class _PreResponse(object):
       
   126     def __init__(self, request):
       
   127         self.headers = request.headers_out
       
   128         self.code = 200