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""" |