2 # Copyright 2011 Google Inc. All Rights Reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
22 def create_request(headers
):
23 return httparchive
.ArchivedHttpRequest(
24 'GET', 'www.test.com', '/', None, headers
)
26 def create_response(headers
):
27 return httparchive
.ArchivedHttpResponse(
28 11, 200, 'OK', headers
, '')
31 class HttpArchiveTest(unittest
.TestCase
):
34 REQUEST
= create_request(REQUEST_HEADERS
)
36 # Used for if-(un)modified-since checks
37 DATE_PAST
= 'Wed, 13 Jul 2011 03:58:08 GMT'
38 DATE_PRESENT
= 'Wed, 20 Jul 2011 04:58:08 GMT'
39 DATE_FUTURE
= 'Wed, 27 Jul 2011 05:58:08 GMT'
40 DATE_INVALID
= 'This is an invalid date!!'
44 ETAG_INVALID
= 'This is an invalid etag value!!'
46 RESPONSE_HEADERS
= [('last-modified', DATE_PRESENT
), ('etag', ETAG_VALID
)]
47 RESPONSE
= create_response(RESPONSE_HEADERS
)
50 self
.archive
= httparchive
.HttpArchive()
51 self
.archive
[self
.REQUEST
] = self
.RESPONSE
53 # Also add an identical POST request for testing
54 request
= httparchive
.ArchivedHttpRequest(
55 'POST', 'www.test.com', '/', None, self
.REQUEST_HEADERS
)
56 self
.archive
[request
] = self
.RESPONSE
62 archive
= httparchive
.HttpArchive()
63 self
.assertEqual(len(archive
), 0)
65 def test__TrimHeaders(self
):
66 request
= httparchive
.ArchivedHttpRequest
67 header1
= {'accept-encoding': 'gzip,deflate'}
68 self
.assertEqual(request
._TrimHeaders
(header1
),
69 [(k
, v
) for k
, v
in header1
.items()])
71 header2
= {'referer': 'www.google.com'}
72 self
.assertEqual(request
._TrimHeaders
(header2
), [])
74 header3
= {'referer': 'www.google.com', 'cookie': 'cookie_monster!',
76 self
.assertEqual(request
._TrimHeaders
(header3
), [('hello', 'world')])
78 # Tests that spaces and trailing comma get stripped.
79 header4
= {'accept-encoding': 'gzip, deflate,, '}
80 self
.assertEqual(request
._TrimHeaders
(header4
),
81 [('accept-encoding', 'gzip,deflate')])
83 def test_matches(self
):
85 request1
= httparchive
.ArchivedHttpRequest(
86 'GET', 'www.test.com', '/index.html?hello=world', None, headers
)
87 request2
= httparchive
.ArchivedHttpRequest(
88 'GET', 'www.test.com', '/index.html?foo=bar', None, headers
)
90 self
.assert_(not request1
.matches(
91 request2
.command
, request2
.host
, request2
.full_path
, use_query
=True))
92 self
.assert_(request1
.matches(
93 request2
.command
, request2
.host
, request2
.full_path
, use_query
=False))
95 self
.assert_(request1
.matches(
96 request2
.command
, request2
.host
, None, use_query
=True))
97 self
.assert_(request1
.matches(
98 request2
.command
, None, request2
.full_path
, use_query
=False))
100 empty_request
= httparchive
.ArchivedHttpRequest(
101 None, None, None, None, headers
)
102 self
.assert_(not empty_request
.matches(
103 request2
.command
, request2
.host
, None, use_query
=True))
104 self
.assert_(not empty_request
.matches(
105 request2
.command
, None, request2
.full_path
, use_query
=False))
107 def setup_find_closest_request(self
):
109 request1
= httparchive
.ArchivedHttpRequest(
110 'GET', 'www.test.com', '/a?hello=world', None, headers
)
111 request2
= httparchive
.ArchivedHttpRequest(
112 'GET', 'www.test.com', '/a?foo=bar', None, headers
)
113 request3
= httparchive
.ArchivedHttpRequest(
114 'GET', 'www.test.com', '/b?hello=world', None, headers
)
116 archive
= httparchive
.HttpArchive()
117 # Add requests 2 and 3 and find closest match with request1
118 archive
[request2
] = self
.RESPONSE
119 archive
[request3
] = self
.RESPONSE
121 return archive
, request1
, request2
, request3
123 def test_find_closest_request(self
):
124 archive
, request1
, request2
, request3
= self
.setup_find_closest_request()
126 # Request 3 is the closest match to request 1
128 request3
, archive
.find_closest_request(request1
, use_path
=False))
129 # However, if we match strictly on path, request2 is the only match
131 request2
, archive
.find_closest_request(request1
, use_path
=True))
133 def test_find_closest_request_delete_simple(self
):
134 archive
, request1
, request2
, request3
= self
.setup_find_closest_request()
136 del archive
[request3
]
138 request2
, archive
.find_closest_request(request1
, use_path
=False))
140 request2
, archive
.find_closest_request(request1
, use_path
=True))
142 def test_find_closest_request_delete_complex(self
):
143 archive
, request1
, request2
, request3
= self
.setup_find_closest_request()
145 del archive
[request2
]
147 request3
, archive
.find_closest_request(request1
, use_path
=False))
149 None, archive
.find_closest_request(request1
, use_path
=True))
151 def test_get_simple(self
):
152 request
= self
.REQUEST
153 response
= self
.RESPONSE
154 archive
= self
.archive
156 self
.assertEqual(archive
.get(request
), response
)
158 false_request_headers
= {'foo': 'bar'}
159 false_request
= create_request(false_request_headers
)
160 self
.assertEqual(archive
.get(false_request
, default
=None), None)
162 def test_get_modified_headers(self
):
163 request
= self
.REQUEST
164 response
= self
.RESPONSE
165 archive
= self
.archive
166 not_modified_response
= httparchive
.create_response(304)
168 # Fail check and return response again
169 request_headers
= {'if-modified-since': self
.DATE_PAST
}
170 request
= create_request(request_headers
)
171 self
.assertEqual(archive
.get(request
), response
)
173 # Succeed check and return 304 Not Modified
174 request_headers
= {'if-modified-since': self
.DATE_FUTURE
}
175 request
= create_request(request_headers
)
176 self
.assertEqual(archive
.get(request
), not_modified_response
)
178 # Succeed check and return 304 Not Modified
179 request_headers
= {'if-modified-since': self
.DATE_PRESENT
}
180 request
= create_request(request_headers
)
181 self
.assertEqual(archive
.get(request
), not_modified_response
)
183 # Invalid date, fail check and return response again
184 request_headers
= {'if-modified-since': self
.DATE_INVALID
}
185 request
= create_request(request_headers
)
186 self
.assertEqual(archive
.get(request
), response
)
188 # fail check since the request is not a GET or HEAD request (as per RFC)
189 request_headers
= {'if-modified-since': self
.DATE_FUTURE
}
190 request
= httparchive
.ArchivedHttpRequest(
191 'POST', 'www.test.com', '/', None, request_headers
)
192 self
.assertEqual(archive
.get(request
), response
)
194 def test_get_unmodified_headers(self
):
195 request
= self
.REQUEST
196 response
= self
.RESPONSE
197 archive
= self
.archive
198 not_modified_response
= httparchive
.create_response(304)
201 request_headers
= {'if-unmodified-since': self
.DATE_PAST
}
202 request
= create_request(request_headers
)
203 self
.assertEqual(archive
.get(request
), not_modified_response
)
206 request_headers
= {'if-unmodified-since': self
.DATE_FUTURE
}
207 request
= create_request(request_headers
)
208 self
.assertEqual(archive
.get(request
), response
)
211 request_headers
= {'if-unmodified-since': self
.DATE_PRESENT
}
212 request
= create_request(request_headers
)
213 self
.assertEqual(archive
.get(request
), not_modified_response
)
216 request_headers
= {'if-unmodified-since': self
.DATE_INVALID
}
217 request
= create_request(request_headers
)
218 self
.assertEqual(archive
.get(request
), response
)
220 # Fail check since the request is not a GET or HEAD request (as per RFC)
221 request_headers
= {'if-modified-since': self
.DATE_PAST
}
222 request
= httparchive
.ArchivedHttpRequest(
223 'POST', 'www.test.com', '/', None, request_headers
)
224 self
.assertEqual(archive
.get(request
), response
)
226 def test_get_etags(self
):
227 request
= self
.REQUEST
228 response
= self
.RESPONSE
229 archive
= self
.archive
230 not_modified_response
= httparchive
.create_response(304)
231 precondition_failed_response
= httparchive
.create_response(412)
234 request_headers
= {'if-match': self
.ETAG_VALID
}
235 request
= create_request(request_headers
)
236 self
.assertEqual(archive
.get(request
), response
)
238 request_headers
= {'if-match': self
.ETAG_INVALID
}
239 request
= create_request(request_headers
)
240 self
.assertEqual(archive
.get(request
), precondition_failed_response
)
242 # if-none-match headers
243 request_headers
= {'if-none-match': self
.ETAG_VALID
}
244 request
= create_request(request_headers
)
245 self
.assertEqual(archive
.get(request
), not_modified_response
)
247 request_headers
= {'if-none-match': self
.ETAG_INVALID
}
248 request
= create_request(request_headers
)
249 self
.assertEqual(archive
.get(request
), response
)
251 def test_get_multiple_match_headers(self
):
252 request
= self
.REQUEST
253 response
= self
.RESPONSE
254 archive
= self
.archive
255 not_modified_response
= httparchive
.create_response(304)
256 precondition_failed_response
= httparchive
.create_response(412)
259 # If the request would, without the If-Match header field,
260 # result in anything other than a 2xx or 412 status,
261 # then the If-Match header MUST be ignored.
264 'if-match': self
.ETAG_VALID
,
265 'if-modified-since': self
.DATE_PAST
,
267 request
= create_request(request_headers
)
268 self
.assertEqual(archive
.get(request
), response
)
270 # Invalid etag, precondition failed
272 'if-match': self
.ETAG_INVALID
,
273 'if-modified-since': self
.DATE_PAST
,
275 request
= create_request(request_headers
)
276 self
.assertEqual(archive
.get(request
), precondition_failed_response
)
278 # 304 response; ignore if-match header
280 'if-match': self
.ETAG_VALID
,
281 'if-modified-since': self
.DATE_FUTURE
,
283 request
= create_request(request_headers
)
284 self
.assertEqual(archive
.get(request
), not_modified_response
)
286 # 304 response; ignore if-match header
288 'if-match': self
.ETAG_INVALID
,
289 'if-modified-since': self
.DATE_PRESENT
,
291 request
= create_request(request_headers
)
292 self
.assertEqual(archive
.get(request
), not_modified_response
)
294 # Invalid etag, precondition failed
296 'if-match': self
.ETAG_INVALID
,
297 'if-modified-since': self
.DATE_INVALID
,
299 request
= create_request(request_headers
)
300 self
.assertEqual(archive
.get(request
), precondition_failed_response
)
302 def test_get_multiple_none_match_headers(self
):
303 request
= self
.REQUEST
304 response
= self
.RESPONSE
305 archive
= self
.archive
306 not_modified_response
= httparchive
.create_response(304)
307 precondition_failed_response
= httparchive
.create_response(412)
309 # if-none-match headers
310 # If the request would, without the If-None-Match header field,
311 # result in anything other than a 2xx or 304 status,
312 # then the If-None-Match header MUST be ignored.
315 'if-none-match': self
.ETAG_VALID
,
316 'if-modified-since': self
.DATE_PAST
,
318 request
= create_request(request_headers
)
319 self
.assertEqual(archive
.get(request
), response
)
322 'if-none-match': self
.ETAG_INVALID
,
323 'if-modified-since': self
.DATE_PAST
,
325 request
= create_request(request_headers
)
326 self
.assertEqual(archive
.get(request
), response
)
328 # etag match, precondition failed
330 'if-none-match': self
.ETAG_VALID
,
331 'if-modified-since': self
.DATE_FUTURE
,
333 request
= create_request(request_headers
)
334 self
.assertEqual(archive
.get(request
), not_modified_response
)
337 'if-none-match': self
.ETAG_INVALID
,
338 'if-modified-since': self
.DATE_PRESENT
,
340 request
= create_request(request_headers
)
341 self
.assertEqual(archive
.get(request
), not_modified_response
)
344 'if-none-match': self
.ETAG_INVALID
,
345 'if-modified-since': self
.DATE_INVALID
,
347 request
= create_request(request_headers
)
348 self
.assertEqual(archive
.get(request
), response
)
351 class ArchivedHttpResponse(unittest
.TestCase
):
352 PAST_DATE_A
= 'Tue, 13 Jul 2010 03:47:07 GMT'
353 PAST_DATE_B
= 'Tue, 13 Jul 2010 02:47:07 GMT' # PAST_DATE_A -1 hour
354 PAST_DATE_C
= 'Tue, 13 Jul 2010 04:47:07 GMT' # PAST_DATE_A +1 hour
355 NOW_DATE_A
= 'Wed, 20 Jul 2011 04:58:08 GMT'
356 NOW_DATE_B
= 'Wed, 20 Jul 2011 03:58:08 GMT' # NOW_DATE_A -1 hour
357 NOW_DATE_C
= 'Wed, 20 Jul 2011 05:58:08 GMT' # NOW_DATE_A +1 hour
358 NOW_SECONDS
= calendar
.timegm(email
.utils
.parsedate(NOW_DATE_A
))
361 self
.response
= create_response([('date', self
.PAST_DATE_A
)])
363 def test_update_date_same_date(self
):
365 self
.response
.update_date(self
.PAST_DATE_A
, now
=self
.NOW_SECONDS
),
368 def test_update_date_before_date(self
):
370 self
.response
.update_date(self
.PAST_DATE_B
, now
=self
.NOW_SECONDS
),
373 def test_update_date_after_date(self
):
375 self
.response
.update_date(self
.PAST_DATE_C
, now
=self
.NOW_SECONDS
),
378 def test_update_date_bad_date_param(self
):
380 self
.response
.update_date('garbage date', now
=self
.NOW_SECONDS
),
383 def test_update_date_bad_date_header(self
):
384 self
.response
.set_header('date', 'garbage date')
386 self
.response
.update_date(self
.PAST_DATE_B
, now
=self
.NOW_SECONDS
),
390 if __name__
== '__main__':