author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Thu, 23 Jun 2011 07:41:25 +0200 | |
changeset 7554 | fdace9d67d96 |
parent 5867 | 7ef636d24ab2 |
child 8176 | eff4fe02ec64 |
permissions | -rw-r--r-- |
5421
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
1 |
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
2 |
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
3 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
4 |
# This file is part of CubicWeb. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
5 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
6 |
# CubicWeb is free software: you can redistribute it and/or modify it under the |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
7 |
# terms of the GNU Lesser General Public License as published by the Free |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
8 |
# Software Foundation, either version 2.1 of the License, or (at your option) |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
9 |
# any later version. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
10 |
# |
5424
8ecbcbff9777
replace logilab-common by CubicWeb in disclaimer
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5421
diff
changeset
|
11 |
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
5421
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
12 |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
13 |
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
14 |
# details. |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
15 |
# |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
16 |
# You should have received a copy of the GNU Lesser General Public License along |
8167de96c523
proper licensing information (LGPL-2.1). Hope I get it right this time.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4436
diff
changeset
|
17 |
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
5867 | 18 |
"""HTTP cache managers""" |
0 | 19 |
|
20 |
__docformat__ = "restructuredtext en" |
|
21 |
||
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
22 |
from time import mktime |
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
762
diff
changeset
|
23 |
from datetime import datetime |
0 | 24 |
|
25 |
# time delta usable to convert localized time to GMT time |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
762
diff
changeset
|
26 |
GMTOFFSET = - (datetime.now() - datetime.utcnow()) |
0 | 27 |
|
28 |
class NoHTTPCacheManager(object): |
|
29 |
"""default cache manager: set no-cache cache control policy""" |
|
30 |
def __init__(self, view): |
|
31 |
self.view = view |
|
3460
e4843535db25
[api] some more _cw / __regid__, automatic tests now pass again
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3451
diff
changeset
|
32 |
self.req = view._cw |
3462
3a79fecdd2b4
[magicsearch] make tests pass again: base preprocessor must have access to vreg
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3460
diff
changeset
|
33 |
self.cw_rset = view.cw_rset |
0 | 34 |
|
35 |
def set_headers(self): |
|
36 |
self.req.set_header('Cache-control', 'no-cache') |
|
37 |
||
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
38 |
|
0 | 39 |
class MaxAgeHTTPCacheManager(NoHTTPCacheManager): |
40 |
"""max-age cache manager: set max-age cache control policy, with max-age |
|
41 |
specified with the `cache_max_age` attribute of the view |
|
42 |
""" |
|
43 |
def set_headers(self): |
|
44 |
self.req.set_header('Cache-control', |
|
45 |
'max-age=%s' % self.view.cache_max_age) |
|
46 |
||
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
47 |
|
0 | 48 |
class EtagHTTPCacheManager(NoHTTPCacheManager): |
49 |
"""etag based cache manager for startup views |
|
50 |
||
51 |
* etag is generated using the view name and the user's groups |
|
52 |
* set policy to 'must-revalidate' and expires to the current time to force |
|
53 |
revalidation on each request |
|
54 |
""" |
|
55 |
||
56 |
def etag(self): |
|
5244
5467674ad101
[web] put a fake object that raise Unauthorized on any attribute access as req.cnx and req._user, so we are properly asked to authenticated on any view that tries to do something with one of those attributes (instead of doing defensive programming everywhere we're doing that)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5223
diff
changeset
|
57 |
if not self.req.cnx: # session without established connection to the repo |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
58 |
return self.view.__regid__ |
3460
e4843535db25
[api] some more _cw / __regid__, automatic tests now pass again
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3451
diff
changeset
|
59 |
return self.view.__regid__ + '/' + ','.join(sorted(self.req.user.groups)) |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1149
diff
changeset
|
60 |
|
0 | 61 |
def max_age(self): |
62 |
# 0 to actually force revalidation |
|
63 |
return 0 |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1149
diff
changeset
|
64 |
|
0 | 65 |
def last_modified(self): |
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
66 |
"""return view's last modified GMT time""" |
0 | 67 |
return self.view.last_modified() |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1149
diff
changeset
|
68 |
|
0 | 69 |
def set_headers(self): |
70 |
req = self.req |
|
71 |
try: |
|
72 |
req.set_header('Etag', '"%s"' % self.etag()) |
|
73 |
except NoEtag: |
|
74 |
self.req.set_header('Cache-control', 'no-cache') |
|
75 |
return |
|
76 |
req.set_header('Cache-control', |
|
77 |
'must-revalidate;max-age=%s' % self.max_age()) |
|
78 |
mdate = self.last_modified() |
|
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
79 |
# use a timestamp, not a formatted raw header, and let |
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
80 |
# the front-end correctly generate it |
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
81 |
# ("%a, %d %b %Y %H:%M:%S GMT" return localized date that |
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
82 |
# twisted don't parse correctly) |
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
83 |
req.set_header('Last-modified', mktime(mdate.timetuple()), raw=False) |
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
84 |
|
0 | 85 |
|
86 |
class EntityHTTPCacheManager(EtagHTTPCacheManager): |
|
87 |
"""etag based cache manager for view displaying a single entity |
|
88 |
||
89 |
* etag is generated using entity's eid, the view name and the user's groups |
|
90 |
* get last modified time from the entity definition (this may not be the |
|
91 |
entity's modification time since a view may include some related entities |
|
92 |
with a modification time to consider) using the `last_modified` method |
|
93 |
""" |
|
94 |
def etag(self): |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1977
diff
changeset
|
95 |
if self.cw_rset is None or len(self.cw_rset) == 0: # entity startup view for instance |
0 | 96 |
return super(EntityHTTPCacheManager, self).etag() |
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1977
diff
changeset
|
97 |
if len(self.cw_rset) > 1: |
0 | 98 |
raise NoEtag() |
99 |
etag = super(EntityHTTPCacheManager, self).etag() |
|
3451
6b46d73823f5
[api] work in progress, use __regid__, cw_*, etc.
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1977
diff
changeset
|
100 |
eid = self.cw_rset[0][0] |
0 | 101 |
if self.req.user.owns(eid): |
102 |
etag += ',owners' |
|
103 |
return str(eid) + '/' + etag |
|
104 |
||
105 |
||
106 |
class NoEtag(Exception): |
|
107 |
"""an etag can't be generated""" |
|
108 |
||
109 |
__all__ = ('GMTOFFSET', |
|
110 |
'NoHTTPCacheManager', 'MaxAgeHTTPCacheManager', |
|
111 |
'EtagHTTPCacheManager', 'EntityHTTPCacheManager') |
|
112 |
||
113 |
# monkey patching, so view doesn't depends on this module and we have all |
|
114 |
# http cache related logic here |
|
115 |
||
1149 | 116 |
from cubicweb import view as viewmod |
0 | 117 |
|
118 |
def set_http_cache_headers(self): |
|
119 |
self.http_cache_manager(self).set_headers() |
|
1149 | 120 |
viewmod.View.set_http_cache_headers = set_http_cache_headers |
0 | 121 |
|
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
122 |
|
0 | 123 |
def last_modified(self): |
124 |
"""return the date/time where this view should be considered as |
|
125 |
modified. Take care of possible related objects modifications. |
|
126 |
||
127 |
/!\ must return GMT time /!\ |
|
128 |
""" |
|
129 |
# XXX check view module's file modification time in dev mod ? |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
762
diff
changeset
|
130 |
ctime = datetime.utcnow() |
0 | 131 |
if self.cache_max_age: |
3462
3a79fecdd2b4
[magicsearch] make tests pass again: base preprocessor must have access to vreg
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3460
diff
changeset
|
132 |
mtime = self._cw.header_if_modified_since() |
0 | 133 |
if mtime: |
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
762
diff
changeset
|
134 |
tdelta = (ctime - mtime) |
4423
c1850ef961fc
simpler last_modified implementation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4212
diff
changeset
|
135 |
if tdelta.days * 24*60*60 + tdelta.seconds <= self.cache_max_age: |
c1850ef961fc
simpler last_modified implementation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4212
diff
changeset
|
136 |
return mtime |
0 | 137 |
# mtime = ctime will force page rerendering |
4423
c1850ef961fc
simpler last_modified implementation
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4212
diff
changeset
|
138 |
return ctime |
1149 | 139 |
viewmod.View.last_modified = last_modified |
0 | 140 |
|
4424
5a5cd7591706
fix spurious http cache bug: sometimes last-modified headers is generated using non-english local, which ends up in a date that twisted can't parse and make it feels the page may be cached while it may not
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4423
diff
changeset
|
141 |
|
0 | 142 |
# configure default caching |
1149 | 143 |
viewmod.View.http_cache_manager = NoHTTPCacheManager |
0 | 144 |
# max-age=0 to actually force revalidation when needed |
1149 | 145 |
viewmod.View.cache_max_age = 0 |
0 | 146 |
|
1149 | 147 |
viewmod.StartupView.http_cache_manager = MaxAgeHTTPCacheManager |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1149
diff
changeset
|
148 |
viewmod.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default |