|
1 # -*- coding:utf-8 -*- |
|
2 from datetime import datetime, timedelta |
|
3 from calendar import timegm |
|
4 |
|
5 from django.test import TestCase |
|
6 from django.utils.http import parse_etags, quote_etag |
|
7 |
|
8 FULL_RESPONSE = 'Test conditional get response' |
|
9 LAST_MODIFIED = datetime(2007, 10, 21, 23, 21, 47) |
|
10 LAST_MODIFIED_STR = 'Sun, 21 Oct 2007 23:21:47 GMT' |
|
11 EXPIRED_LAST_MODIFIED_STR = 'Sat, 20 Oct 2007 23:21:47 GMT' |
|
12 ETAG = 'b4246ffc4f62314ca13147c9d4f76974' |
|
13 EXPIRED_ETAG = '7fae4cd4b0f81e7d2914700043aa8ed6' |
|
14 |
|
15 class ConditionalGet(TestCase): |
|
16 def assertFullResponse(self, response, check_last_modified=True, check_etag=True): |
|
17 self.assertEquals(response.status_code, 200) |
|
18 self.assertEquals(response.content, FULL_RESPONSE) |
|
19 if check_last_modified: |
|
20 self.assertEquals(response['Last-Modified'], LAST_MODIFIED_STR) |
|
21 if check_etag: |
|
22 self.assertEquals(response['ETag'], '"%s"' % ETAG) |
|
23 |
|
24 def assertNotModified(self, response): |
|
25 self.assertEquals(response.status_code, 304) |
|
26 self.assertEquals(response.content, '') |
|
27 |
|
28 def testWithoutConditions(self): |
|
29 response = self.client.get('/condition/') |
|
30 self.assertFullResponse(response) |
|
31 |
|
32 def testIfModifiedSince(self): |
|
33 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR |
|
34 response = self.client.get('/condition/') |
|
35 self.assertNotModified(response) |
|
36 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = EXPIRED_LAST_MODIFIED_STR |
|
37 response = self.client.get('/condition/') |
|
38 self.assertFullResponse(response) |
|
39 |
|
40 def testIfNoneMatch(self): |
|
41 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % ETAG |
|
42 response = self.client.get('/condition/') |
|
43 self.assertNotModified(response) |
|
44 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % EXPIRED_ETAG |
|
45 response = self.client.get('/condition/') |
|
46 self.assertFullResponse(response) |
|
47 |
|
48 # Several etags in If-None-Match is a bit exotic but why not? |
|
49 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s", "%s"' % (ETAG, EXPIRED_ETAG) |
|
50 response = self.client.get('/condition/') |
|
51 self.assertNotModified(response) |
|
52 |
|
53 def testIfMatch(self): |
|
54 self.client.defaults['HTTP_IF_MATCH'] = '"%s"' % ETAG |
|
55 response = self.client.put('/condition/etag/', {'data': ''}) |
|
56 self.assertEquals(response.status_code, 200) |
|
57 self.client.defaults['HTTP_IF_MATCH'] = '"%s"' % EXPIRED_ETAG |
|
58 response = self.client.put('/condition/etag/', {'data': ''}) |
|
59 self.assertEquals(response.status_code, 412) |
|
60 |
|
61 def testBothHeaders(self): |
|
62 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR |
|
63 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % ETAG |
|
64 response = self.client.get('/condition/') |
|
65 self.assertNotModified(response) |
|
66 |
|
67 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = EXPIRED_LAST_MODIFIED_STR |
|
68 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % ETAG |
|
69 response = self.client.get('/condition/') |
|
70 self.assertFullResponse(response) |
|
71 |
|
72 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR |
|
73 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % EXPIRED_ETAG |
|
74 response = self.client.get('/condition/') |
|
75 self.assertFullResponse(response) |
|
76 |
|
77 def testSingleCondition1(self): |
|
78 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR |
|
79 response = self.client.get('/condition/last_modified/') |
|
80 self.assertNotModified(response) |
|
81 response = self.client.get('/condition/etag/') |
|
82 self.assertFullResponse(response, check_last_modified=False) |
|
83 |
|
84 def testSingleCondition2(self): |
|
85 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % ETAG |
|
86 response = self.client.get('/condition/etag/') |
|
87 self.assertNotModified(response) |
|
88 response = self.client.get('/condition/last_modified/') |
|
89 self.assertFullResponse(response, check_etag=False) |
|
90 |
|
91 def testSingleCondition3(self): |
|
92 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = EXPIRED_LAST_MODIFIED_STR |
|
93 response = self.client.get('/condition/last_modified/') |
|
94 self.assertFullResponse(response, check_etag=False) |
|
95 |
|
96 def testSingleCondition4(self): |
|
97 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % EXPIRED_ETAG |
|
98 response = self.client.get('/condition/etag/') |
|
99 self.assertFullResponse(response, check_last_modified=False) |
|
100 |
|
101 def testSingleCondition5(self): |
|
102 self.client.defaults['HTTP_IF_MODIFIED_SINCE'] = LAST_MODIFIED_STR |
|
103 response = self.client.get('/condition/last_modified2/') |
|
104 self.assertNotModified(response) |
|
105 response = self.client.get('/condition/etag2/') |
|
106 self.assertFullResponse(response, check_last_modified=False) |
|
107 |
|
108 def testSingleCondition6(self): |
|
109 self.client.defaults['HTTP_IF_NONE_MATCH'] = '"%s"' % ETAG |
|
110 response = self.client.get('/condition/etag2/') |
|
111 self.assertNotModified(response) |
|
112 response = self.client.get('/condition/last_modified2/') |
|
113 self.assertFullResponse(response, check_etag=False) |
|
114 |
|
115 def testInvalidETag(self): |
|
116 self.client.defaults['HTTP_IF_NONE_MATCH'] = r'"\"' |
|
117 response = self.client.get('/condition/etag/') |
|
118 self.assertFullResponse(response, check_last_modified=False) |
|
119 |
|
120 |
|
121 class ETagProcesing(TestCase): |
|
122 def testParsing(self): |
|
123 etags = parse_etags(r'"", "etag", "e\"t\"ag", "e\\tag", W/"weak"') |
|
124 self.assertEquals(etags, ['', 'etag', 'e"t"ag', r'e\tag', 'weak']) |
|
125 |
|
126 def testQuoting(self): |
|
127 quoted_etag = quote_etag(r'e\t"ag') |
|
128 self.assertEquals(quoted_etag, r'"e\\t\"ag"') |