|
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
1 from logilab.common.testlib import TestCase, unittest_main, tag, Tags |
18 from logilab.common.testlib import TestCase, unittest_main, tag, Tags |
2 |
19 |
3 from cubicweb.web import StatusResponse |
|
4 from cubicweb.devtools.fake import FakeRequest |
20 from cubicweb.devtools.fake import FakeRequest |
5 |
21 |
6 |
22 |
7 def _test_cache(hin, hout, method='GET'): |
23 def _test_cache(hin, hout, method='GET'): |
8 """forge and process a request |
24 """forge and process an HTTP request using given headers in/out and method, |
9 |
25 then return it once its .is_client_cache_valid() method has been called. |
10 return status code and the request object |
26 |
11 |
27 req.status_out is None if the page should have been calculated. |
12 status is None is no cache is involved |
|
13 """ |
28 """ |
14 # forge request |
29 # forge request |
15 req = FakeRequest(method=method) |
30 req = FakeRequest(method=method) |
16 for key, value in hin: |
31 for key, value in hin: |
17 req._headers_in.addRawHeader(key, str(value)) |
32 req._headers_in.addRawHeader(key, str(value)) |
18 for key, value in hout: |
33 for key, value in hout: |
19 req.headers_out.addRawHeader(key, str(value)) |
34 req.headers_out.addRawHeader(key, str(value)) |
20 # process |
35 # process |
21 status = None |
36 req.status_out = None |
22 try: |
37 req.is_client_cache_valid() |
23 req.validate_cache() |
38 return req |
24 except StatusResponse as ex: |
|
25 status = ex.status |
|
26 return status, req |
|
27 |
39 |
28 class HTTPCache(TestCase): |
40 class HTTPCache(TestCase): |
29 """Check that the http cache logiac work as expected |
41 """Check that the http cache logiac work as expected |
30 (as far as we understood the RFC) |
42 (as far as we understood the RFC) |
31 |
43 |
46 self.fail(msg) |
58 self.fail(msg) |
47 |
59 |
48 def test_IN_none_OUT_none(self): |
60 def test_IN_none_OUT_none(self): |
49 #: test that no caching is requested when not data is available |
61 #: test that no caching is requested when not data is available |
50 #: on any side |
62 #: on any side |
51 status, req =_test_cache((),()) |
63 req =_test_cache((), ()) |
52 self.assertIsNone(status) |
64 self.assertIsNone(req.status_out) |
53 |
65 |
54 def test_IN_Some_OUT_none(self): |
66 def test_IN_Some_OUT_none(self): |
55 #: test that no caching is requested when no data is available |
67 #: test that no caching is requested when no data is available |
56 #: server (origin) side |
68 #: server (origin) side |
57 hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'), |
69 hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'), |
58 ] |
70 ] |
59 status, req = _test_cache(hin, ()) |
71 req = _test_cache(hin, ()) |
60 self.assertIsNone(status) |
72 self.assertIsNone(req.status_out) |
61 hin = [('if-none-match','babar/huitre'), |
73 hin = [('if-none-match','babar/huitre'), |
62 ] |
74 ] |
63 status, req = _test_cache(hin, ()) |
75 req = _test_cache(hin, ()) |
64 self.assertIsNone(status) |
76 self.assertIsNone(req.status_out) |
65 hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'), |
77 hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'), |
66 ('if-none-match','babar/huitre'), |
78 ('if-none-match','babar/huitre'), |
67 ] |
79 ] |
68 status, req = _test_cache(hin, ()) |
80 req = _test_cache(hin, ()) |
69 self.assertIsNone(status) |
81 self.assertIsNone(req.status_out) |
70 |
82 |
71 def test_IN_none_OUT_Some(self): |
83 def test_IN_none_OUT_Some(self): |
72 #: test that no caching is requested when no data is provided |
84 #: test that no caching is requested when no data is provided |
73 #: by the client |
85 #: by the client |
74 hout = [('last-modified','Sat, 14 Apr 2012 14:39:32 GM'), |
86 hout = [('last-modified','Sat, 14 Apr 2012 14:39:32 GM'), |
75 ] |
87 ] |
76 status, req = _test_cache((), hout) |
88 req = _test_cache((), hout) |
77 self.assertIsNone(status) |
89 self.assertIsNone(req.status_out) |
78 hout = [('etag','babar/huitre'), |
90 hout = [('etag','babar/huitre'), |
79 ] |
91 ] |
80 status, req = _test_cache((), hout) |
92 req = _test_cache((), hout) |
81 self.assertIsNone(status) |
93 self.assertIsNone(req.status_out) |
82 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
94 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
83 ('etag','babar/huitre'), |
95 ('etag','babar/huitre'), |
84 ] |
96 ] |
85 status, req = _test_cache((), hout) |
97 req = _test_cache((), hout) |
86 self.assertIsNone(status) |
98 self.assertIsNone(req.status_out) |
87 |
99 |
88 @tag('last_modified') |
100 @tag('last_modified') |
89 def test_last_modified_newer(self): |
101 def test_last_modified_newer(self): |
90 #: test the proper behavior of modification date only |
102 #: test the proper behavior of modification date only |
91 # newer |
103 # newer |
92 hin = [('if-modified-since', 'Sat, 13 Apr 2012 14:39:32 GM'), |
104 hin = [('if-modified-since', 'Sat, 13 Apr 2012 14:39:32 GM'), |
93 ] |
105 ] |
94 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
106 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
95 ] |
107 ] |
96 status, req = _test_cache(hin, hout) |
108 req = _test_cache(hin, hout) |
97 self.assertCache(None, status, 'origin is newer than client') |
109 self.assertCache(None, req.status_out, 'origin is newer than client') |
98 |
110 |
99 @tag('last_modified') |
111 @tag('last_modified') |
100 def test_last_modified_older(self): |
112 def test_last_modified_older(self): |
101 # older |
113 # older |
102 hin = [('if-modified-since', 'Sat, 15 Apr 2012 14:39:32 GM'), |
114 hin = [('if-modified-since', 'Sat, 15 Apr 2012 14:39:32 GM'), |
103 ] |
115 ] |
104 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
116 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
105 ] |
117 ] |
106 status, req = _test_cache(hin, hout) |
118 req = _test_cache(hin, hout) |
107 self.assertCache(304, status, 'origin is older than client') |
119 self.assertCache(304, req.status_out, 'origin is older than client') |
108 |
120 |
109 @tag('last_modified') |
121 @tag('last_modified') |
110 def test_last_modified_same(self): |
122 def test_last_modified_same(self): |
111 # same |
123 # same |
112 hin = [('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
124 hin = [('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
113 ] |
125 ] |
114 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
126 hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'), |
115 ] |
127 ] |
116 status, req = _test_cache(hin, hout) |
128 req = _test_cache(hin, hout) |
117 self.assertCache(304, status, 'origin is equal to client') |
129 self.assertCache(304, req.status_out, 'origin is equal to client') |
118 |
130 |
119 @tag('etag') |
131 @tag('etag') |
120 def test_etag_mismatch(self): |
132 def test_etag_mismatch(self): |
121 #: test the proper behavior of etag only |
133 #: test the proper behavior of etag only |
122 # etag mismatch |
134 # etag mismatch |
123 hin = [('if-none-match', 'babar'), |
135 hin = [('if-none-match', 'babar'), |
124 ] |
136 ] |
125 hout = [('etag', 'celestine'), |
137 hout = [('etag', 'celestine'), |
126 ] |
138 ] |
127 status, req = _test_cache(hin, hout) |
139 req = _test_cache(hin, hout) |
128 self.assertCache(None, status, 'etag mismatch') |
140 self.assertCache(None, req.status_out, 'etag mismatch') |
129 |
141 |
130 @tag('etag') |
142 @tag('etag') |
131 def test_etag_match(self): |
143 def test_etag_match(self): |
132 # etag match |
144 # etag match |
133 hin = [('if-none-match', 'babar'), |
145 hin = [('if-none-match', 'babar'), |
134 ] |
146 ] |
135 hout = [('etag', 'babar'), |
147 hout = [('etag', 'babar'), |
136 ] |
148 ] |
137 status, req = _test_cache(hin, hout) |
149 req = _test_cache(hin, hout) |
138 self.assertCache(304, status, 'etag match') |
150 self.assertCache(304, req.status_out, 'etag match') |
139 # etag match in multiple |
151 # etag match in multiple |
140 hin = [('if-none-match', 'loutre'), |
152 hin = [('if-none-match', 'loutre'), |
141 ('if-none-match', 'babar'), |
153 ('if-none-match', 'babar'), |
142 ] |
154 ] |
143 hout = [('etag', 'babar'), |
155 hout = [('etag', 'babar'), |
144 ] |
156 ] |
145 status, req = _test_cache(hin, hout) |
157 req = _test_cache(hin, hout) |
146 self.assertCache(304, status, 'etag match in multiple') |
158 self.assertCache(304, req.status_out, 'etag match in multiple') |
147 # client use "*" as etag |
159 # client use "*" as etag |
148 hin = [('if-none-match', '*'), |
160 hin = [('if-none-match', '*'), |
149 ] |
161 ] |
150 hout = [('etag', 'babar'), |
162 hout = [('etag', 'babar'), |
151 ] |
163 ] |
152 status, req = _test_cache(hin, hout) |
164 req = _test_cache(hin, hout) |
153 self.assertCache(304, status, 'client use "*" as etag') |
165 self.assertCache(304, req.status_out, 'client use "*" as etag') |
154 |
166 |
155 @tag('etag', 'last_modified') |
167 @tag('etag', 'last_modified') |
156 def test_both(self): |
168 def test_both(self): |
157 #: test the proper behavior of etag only |
169 #: test the proper behavior of etag only |
158 # both wrong |
170 # both wrong |
160 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
172 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
161 ] |
173 ] |
162 hout = [('etag', 'loutre'), |
174 hout = [('etag', 'loutre'), |
163 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'), |
175 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'), |
164 ] |
176 ] |
165 status, req = _test_cache(hin, hout) |
177 req = _test_cache(hin, hout) |
166 self.assertCache(None, status, 'both wrong') |
178 self.assertCache(None, req.status_out, 'both wrong') |
167 |
179 |
168 @tag('etag', 'last_modified') |
180 @tag('etag', 'last_modified') |
169 def test_both_etag_mismatch(self): |
181 def test_both_etag_mismatch(self): |
170 # both etag mismatch |
182 # both etag mismatch |
171 hin = [('if-none-match', 'babar'), |
183 hin = [('if-none-match', 'babar'), |
172 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
184 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
173 ] |
185 ] |
174 hout = [('etag', 'loutre'), |
186 hout = [('etag', 'loutre'), |
175 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'), |
187 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'), |
176 ] |
188 ] |
177 status, req = _test_cache(hin, hout) |
189 req = _test_cache(hin, hout) |
178 self.assertCache(None, status, 'both but etag mismatch') |
190 self.assertCache(None, req.status_out, 'both but etag mismatch') |
179 |
191 |
180 @tag('etag', 'last_modified') |
192 @tag('etag', 'last_modified') |
181 def test_both_but_modified(self): |
193 def test_both_but_modified(self): |
182 # both but modified |
194 # both but modified |
183 hin = [('if-none-match', 'babar'), |
195 hin = [('if-none-match', 'babar'), |
184 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
196 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
185 ] |
197 ] |
186 hout = [('etag', 'babar'), |
198 hout = [('etag', 'babar'), |
187 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'), |
199 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'), |
188 ] |
200 ] |
189 status, req = _test_cache(hin, hout) |
201 req = _test_cache(hin, hout) |
190 self.assertCache(None, status, 'both but modified') |
202 self.assertCache(None, req.status_out, 'both but modified') |
191 |
203 |
192 @tag('etag', 'last_modified') |
204 @tag('etag', 'last_modified') |
193 def test_both_ok(self): |
205 def test_both_ok(self): |
194 # both ok |
206 # both ok |
195 hin = [('if-none-match', 'babar'), |
207 hin = [('if-none-match', 'babar'), |
196 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
208 ('if-modified-since', 'Sat, 14 Apr 2012 14:39:32 GM'), |
197 ] |
209 ] |
198 hout = [('etag', 'babar'), |
210 hout = [('etag', 'babar'), |
199 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'), |
211 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'), |
200 ] |
212 ] |
201 status, req = _test_cache(hin, hout) |
213 req = _test_cache(hin, hout) |
202 self.assertCache(304, status, 'both ok') |
214 self.assertCache(304, req.status_out, 'both ok') |
203 |
215 |
204 @tag('etag', 'HEAD') |
216 @tag('etag', 'HEAD') |
205 def test_head_verb(self): |
217 def test_head_verb(self): |
206 #: check than FOUND 200 is properly raise without content on HEAD request |
218 #: check than FOUND 200 is properly raise without content on HEAD request |
207 #: This logic does not really belong here :-/ |
219 #: This logic does not really belong here :-/ |
208 # modified |
220 # modified |
209 hin = [('if-none-match', 'babar'), |
221 hin = [('if-none-match', 'babar'), |
210 ] |
222 ] |
211 hout = [('etag', 'rhino/really-not-babar'), |
223 hout = [('etag', 'rhino/really-not-babar'), |
212 ] |
224 ] |
213 status, req = _test_cache(hin, hout, method='HEAD') |
225 req = _test_cache(hin, hout, method='HEAD') |
214 self.assertCache(200, status, 'modifier HEAD verb') |
226 self.assertCache(200, req.status_out, 'modifier HEAD verb') |
215 # not modified |
227 # not modified |
216 hin = [('if-none-match', 'babar'), |
228 hin = [('if-none-match', 'babar'), |
217 ] |
229 ] |
218 hout = [('etag', 'babar'), |
230 hout = [('etag', 'babar'), |
219 ] |
231 ] |
220 status, req = _test_cache(hin, hout, method='HEAD') |
232 req = _test_cache(hin, hout, method='HEAD') |
221 self.assertCache(304, status, 'not modifier HEAD verb') |
233 self.assertCache(304, req.status_out, 'not modifier HEAD verb') |
222 |
234 |
223 @tag('etag', 'POST') |
235 @tag('etag', 'POST') |
224 def test_post_verb(self): |
236 def test_post_verb(self): |
225 # modified |
237 # modified |
226 hin = [('if-none-match', 'babar'), |
238 hin = [('if-none-match', 'babar'), |
227 ] |
239 ] |
228 hout = [('etag', 'rhino/really-not-babar'), |
240 hout = [('etag', 'rhino/really-not-babar'), |
229 ] |
241 ] |
230 status, req = _test_cache(hin, hout, method='POST') |
242 req = _test_cache(hin, hout, method='POST') |
231 self.assertCache(None, status, 'modifier HEAD verb') |
243 self.assertCache(None, req.status_out, 'modifier HEAD verb') |
232 # not modified |
244 # not modified |
233 hin = [('if-none-match', 'babar'), |
245 hin = [('if-none-match', 'babar'), |
234 ] |
246 ] |
235 hout = [('etag', 'babar'), |
247 hout = [('etag', 'babar'), |
236 ] |
248 ] |
237 status, req = _test_cache(hin, hout, method='POST') |
249 req = _test_cache(hin, hout, method='POST') |
238 self.assertCache(412, status, 'not modifier HEAD verb') |
250 self.assertCache(412, req.status_out, 'not modifier HEAD verb') |
239 |
251 |
240 @tag('expires') |
252 @tag('expires') |
241 def test_expires_added(self): |
253 def test_expires_added(self): |
242 #: Check that Expires header is added: |
254 #: Check that Expires header is added: |
243 #: - when the page is modified |
255 #: - when the page is modified |
244 #: - when none was already present |
256 #: - when none was already present |
245 hin = [('if-none-match', 'babar'), |
257 hin = [('if-none-match', 'babar'), |
246 ] |
258 ] |
247 hout = [('etag', 'rhino/really-not-babar'), |
259 hout = [('etag', 'rhino/really-not-babar'), |
248 ] |
260 ] |
249 status, req = _test_cache(hin, hout) |
261 req = _test_cache(hin, hout) |
250 self.assertCache(None, status, 'modifier HEAD verb') |
262 self.assertCache(None, req.status_out, 'modifier HEAD verb') |
251 value = req.headers_out.getHeader('expires') |
263 value = req.headers_out.getHeader('expires') |
252 self.assertIsNotNone(value) |
264 self.assertIsNotNone(value) |
253 |
265 |
254 @tag('expires') |
266 @tag('expires') |
255 def test_expires_not_added(self): |
267 def test_expires_not_added(self): |
256 #: Check that Expires header is not added if NOT-MODIFIED |
268 #: Check that Expires header is not added if NOT-MODIFIED |
257 hin = [('if-none-match', 'babar'), |
269 hin = [('if-none-match', 'babar'), |
258 ] |
270 ] |
259 hout = [('etag', 'babar'), |
271 hout = [('etag', 'babar'), |
260 ] |
272 ] |
261 status, req = _test_cache(hin, hout) |
273 req = _test_cache(hin, hout) |
262 self.assertCache(304, status, 'not modifier HEAD verb') |
274 self.assertCache(304, req.status_out, 'not modifier HEAD verb') |
263 value = req.headers_out.getHeader('expires') |
275 value = req.headers_out.getHeader('expires') |
264 self.assertIsNone(value) |
276 self.assertIsNone(value) |
265 |
277 |
266 @tag('expires') |
278 @tag('expires') |
267 def test_expires_no_overwrite(self): |
279 def test_expires_no_overwrite(self): |