etwist/request.py
changeset 0 b97547f5f1fa
child 1016 26387b836099
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """Twisted request handler for CubicWeb
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from twisted.web2 import http, http_headers
       
    10 
       
    11 from mx.DateTime import DateTimeFromTicks
       
    12 
       
    13 from cubicweb.web import DirectResponse
       
    14 from cubicweb.web.request import CubicWebRequestBase
       
    15 from cubicweb.web.httpcache import GMTOFFSET
       
    16 
       
    17 def cleanup_files(dct, encoding):
       
    18     d = {}
       
    19     for k, infos in dct.items():
       
    20         for (filename, mt, stream) in infos:
       
    21             if filename:
       
    22                 # XXX: suppose that no file submitted <-> no filename
       
    23                 filename = unicode(filename, encoding)
       
    24                 mt = u'%s/%s' % (mt.mediaType, mt.mediaSubtype)
       
    25                 d[k] = (filename, mt, stream)
       
    26     return d
       
    27 
       
    28 
       
    29 class CubicWebTwistedRequestAdapter(CubicWebRequestBase):
       
    30     def __init__(self, req, vreg, https, base_url):
       
    31         self._twreq = req
       
    32         self._base_url = base_url
       
    33         super(CubicWebTwistedRequestAdapter, self).__init__(vreg, https, req.args)
       
    34         self.form.update(cleanup_files(req.files, self.encoding))
       
    35         # prepare output headers
       
    36         self.headers_out = http_headers.Headers()
       
    37         self._headers = req.headers
       
    38 
       
    39     def base_url(self):
       
    40         """return the root url of the application"""
       
    41         return self._base_url
       
    42     
       
    43     def http_method(self):
       
    44         """returns 'POST', 'GET', 'HEAD', etc."""
       
    45         return self._twreq.method
       
    46     
       
    47     def relative_path(self, includeparams=True):
       
    48         """return the normalized path of the request (ie at least relative
       
    49         to the application's root, but some other normalization may be needed
       
    50         so that the returned path may be used to compare to generated urls
       
    51 
       
    52         :param includeparams:
       
    53            boolean indicating if GET form parameters should be kept in the path
       
    54         """
       
    55         path = self._twreq.uri[1:] # remove the root '/'
       
    56         if not includeparams:
       
    57             path = path.split('?', 1)[0]
       
    58         return path
       
    59 
       
    60     def get_header(self, header, default=None, raw=True):
       
    61         """return the value associated with the given input header,
       
    62         raise KeyError if the header is not set
       
    63         """
       
    64         if raw:
       
    65             return self._twreq.headers.getRawHeaders(header, [default])[0]
       
    66         return self._twreq.headers.getHeader(header, default)
       
    67 
       
    68     def set_header(self, header, value, raw=True):
       
    69         """set an output HTTP header"""
       
    70         if raw:
       
    71             # adding encoded header is important, else page content
       
    72             # will be reconverted back to unicode and apart unefficiency, this
       
    73             # may cause decoding problem (e.g. when downloading a file)
       
    74             self.headers_out.setRawHeaders(header, [str(value)])
       
    75         else:
       
    76             self.headers_out.setHeader(header, value)
       
    77 
       
    78     def add_header(self, header, value):
       
    79         """add an output HTTP header"""
       
    80         # adding encoded header is important, else page content
       
    81         # will be reconverted back to unicode and apart unefficiency, this
       
    82         # may cause decoding problem (e.g. when downloading a file)
       
    83         self.headers_out.addRawHeader(header, str(value))
       
    84 
       
    85     def remove_header(self, header):
       
    86         """remove an output HTTP header"""
       
    87         self.headers_out.removeHeader(header)
       
    88 
       
    89     def _validate_cache(self):
       
    90         """raise a `DirectResponse` exception if a cached page along the way
       
    91         exists and is still usable
       
    92         """
       
    93         if self.get_header('Cache-Control') in ('max-age=0', 'no-cache'):
       
    94             # Expires header seems to be required by IE7
       
    95             self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
       
    96             return
       
    97         try:
       
    98             http.checkPreconditions(self._twreq, _PreResponse(self))
       
    99         except http.HTTPError, ex:
       
   100             self.info('valid http cache, no actual rendering')
       
   101             raise DirectResponse(ex.response)
       
   102         # Expires header seems to be required by IE7
       
   103         self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
       
   104 
       
   105     def header_accept_language(self):
       
   106         """returns an ordered list of preferred languages"""
       
   107         acceptedlangs = self.get_header('Accept-Language', raw=False) or {}
       
   108         for lang, _ in sorted(acceptedlangs.iteritems(), key=lambda x: x[1],
       
   109                               reverse=True):
       
   110             lang = lang.split('-')[0]
       
   111             yield lang
       
   112 
       
   113     def header_if_modified_since(self):
       
   114         """If the HTTP header If-modified-since is set, return the equivalent
       
   115         mx date time value (GMT), else return None
       
   116         """
       
   117         mtime = self.get_header('If-modified-since', raw=False)
       
   118         if mtime:
       
   119             # :/ twisted is returned a localized time stamp
       
   120             return DateTimeFromTicks(mtime) + GMTOFFSET
       
   121         return None
       
   122 
       
   123 
       
   124 class _PreResponse(object):
       
   125     def __init__(self, request):
       
   126         self.headers = request.headers_out
       
   127         self.code = 200