diff options
author | chris dent <chris.dent@gmail.com> | 2009-12-24 14:02:57 -0600 |
---|---|---|
committer | chris dent <chris.dent@gmail.com> | 2009-12-24 14:02:57 -0600 |
commit | 89f1514bff479bf72adc5f82f3287167447d0423 (patch) | |
tree | 8fa11473de3a6d4ddeb705681be15b23514d6d84 | |
parent | 25dffefd19323728d8dea13e798d56ae4347884b (diff) | |
download | httplib2-89f1514bff479bf72adc5f82f3287167447d0423.tar.gz |
Add support for Vary header
-rw-r--r-- | python2/httplib2/__init__.py | 24 | ||||
-rwxr-xr-x | python2/httplib2test.py | 75 | ||||
-rw-r--r--[-rwxr-xr-x] | python3/httplib2/__init__.py | 24 | ||||
-rwxr-xr-x | python3/httplib2test.py | 70 | ||||
-rwxr-xr-x | test/vary/accept-double.asis | 7 | ||||
-rwxr-xr-x | test/vary/accept.asis | 6 | ||||
-rwxr-xr-x | test/vary/no-vary.asis | 5 |
7 files changed, 210 insertions, 1 deletions
diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py index 7980ca7..d2abdb4 100644 --- a/python2/httplib2/__init__.py +++ b/python2/httplib2/__init__.py @@ -370,6 +370,18 @@ def _updateCache(request_headers, response_headers, content, cache, cachekey): if key not in ['status','content-encoding','transfer-encoding']: info[key] = value + # Add annotations to the cache to indicate what headers + # are variant for this request. + vary = response_headers.get('vary', None) + if vary: + vary_headers = vary.lower().replace(' ', '').split(',') + for header in vary_headers: + key = '-varied-%s' % header + try: + info[key] = request_headers[header] + except KeyError: + pass + status = response_headers.status if status == 304: status = 200 @@ -1036,6 +1048,18 @@ a string that contains the response entity body. # RFC 2616 Section 13.10 self.cache.delete(cachekey) + # Check the vary header in the cache to see if this request + # matches what varies in the cache. + if method in ['GET', 'HEAD'] and 'vary' in info: + vary = info['vary'] + vary_headers = vary.lower().replace(' ', '').split(',') + for header in vary_headers: + key = '-varied-%s' % header + value = info[key] + if headers.get(header, '') != value: + cached_value = None + break + if cached_value and method in ["GET", "HEAD"] and self.cache and 'range' not in headers: if info.has_key('-x-permanent-redirect-url'): # Should cached permanent redirects be counted in our redirection count? For now, yes. diff --git a/python2/httplib2test.py b/python2/httplib2test.py index 5bab45e..3bfde31 100755 --- a/python2/httplib2test.py +++ b/python2/httplib2test.py @@ -573,6 +573,77 @@ class HttpTest(unittest.TestCase): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 410) + def testVaryHeaderSimple(self): + """ + RFC 2616 13.6 + When the cache receives a subsequent request whose Request-URI + specifies one or more cache entries including a Vary header field, + the cache MUST NOT use such a cache entry to construct a response + to the new request unless all of the selecting request-headers + present in the new request match the corresponding stored + request-headers in the original request. + """ + # test that the vary header is sent + uri = urlparse.urljoin(base, "vary/accept.asis") + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) + self.assertEqual(response.status, 200) + self.assertTrue(response.has_key('vary')) + + # get the resource again, from the cache since accept header in this + # request is the same as the request + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, True, msg="Should be from cache") + + # get the resource again, not from cache since Accept headers does not match + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, False, msg="Should not be from cache") + + # get the resource again, without any Accept header, so again no match + (response, content) = self.http.request(uri, "GET") + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, False, msg="Should not be from cache") + + def testNoVary(self): + # when there is no vary, a different Accept header (e.g.) should not + # impact if the cache is used + # test that the vary header is not sent + uri = urlparse.urljoin(base, "vary/no-vary.asis") + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) + self.assertEqual(response.status, 200) + self.assertFalse(response.has_key('vary')) + + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, True, msg="Should be from cache") + + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, True, msg="Should be from cache") + + def testVaryHeaderDouble(self): + uri = urlparse.urljoin(base, "vary/accept-double.asis") + (response, content) = self.http.request(uri, "GET", headers={ + 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'}) + self.assertEqual(response.status, 200) + self.assertTrue(response.has_key('vary')) + + # we are from cache + (response, content) = self.http.request(uri, "GET", headers={ + 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'}) + self.assertEqual(response.fromcache, True, msg="Should be from cache") + + (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, False) + + # get the resource again, not from cache, varied headers don't match exact + (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'}) + self.assertEqual(response.status, 200) + self.assertEqual(response.fromcache, False, msg="Should not be from cache") + + def testHeadGZip(self): # Test that we don't try to decompress a HEAD response uri = urlparse.urljoin(base, "gzip/final-destination.txt") @@ -980,6 +1051,7 @@ except: + # ------------------------------------------------------------------------ class HttpPrivateTest(unittest.TestCase): @@ -1328,4 +1400,5 @@ class HttpPrivateTest(unittest.TestCase): end2end = httplib2._get_end2end_headers(response) self.assertEquals(0, len(end2end)) -unittest.main() +if __name__ == '__main__': + unittest.main() diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index d90a4a3..1cbcd4c 100755..100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -337,6 +337,18 @@ def _updateCache(request_headers, response_headers, content, cache, cachekey): if key not in ['status','content-encoding','transfer-encoding']: info[key] = value + # Add annotations to the cache to indicate what headers + # are variant for this request. + vary = response_headers.get('vary', None) + if vary: + vary_headers = vary.lower().replace(' ', '').split(',') + for header in vary_headers: + key = '-varied-%s' % header + try: + info[key] = request_headers[header] + except KeyError: + pass + status = response_headers.status if status == 304: status = 200 @@ -1023,6 +1035,18 @@ a string that contains the response entity body. # RFC 2616 Section 13.10 self.cache.delete(cachekey) + # Check the vary header in the cache to see if this request + # matches what varies in the cache. + if method in ['GET', 'HEAD'] and 'vary' in info: + vary = info['vary'] + vary_headers = vary.lower().replace(' ', '').split(',') + for header in vary_headers: + key = '-varied-%s' % header + value = info[key] + if headers.get(header, '') != value: + cached_value = None + break + if cached_value and method in ["GET", "HEAD"] and self.cache and 'range' not in headers: if '-x-permanent-redirect-url' in info: # Should cached permanent redirects be counted in our redirection count? For now, yes. diff --git a/python3/httplib2test.py b/python3/httplib2test.py index 78ec082..810e88a 100755 --- a/python3/httplib2test.py +++ b/python3/httplib2test.py @@ -571,6 +571,76 @@ class HttpTest(unittest.TestCase): (response, content) = self.http.request(uri, "GET")
self.assertEqual(response.status, 410)
+ def testVaryHeaderSimple(self):
+ """
+ RFC 2616 13.6
+ When the cache receives a subsequent request whose Request-URI
+ specifies one or more cache entries including a Vary header field,
+ the cache MUST NOT use such a cache entry to construct a response
+ to the new request unless all of the selecting request-headers
+ present in the new request match the corresponding stored
+ request-headers in the original request.
+ """
+ # test that the vary header is sent
+ uri = urllib.parse.urljoin(base, "vary/accept.asis")
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
+ self.assertEqual(response.status, 200)
+ self.assertTrue('vary' in response)
+
+ # get the resource again, from the cache since accept header in this
+ # request is the same as the request
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, True, msg="Should be from cache")
+
+ # get the resource again, not from cache since Accept headers does not match
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, False, msg="Should not be from cache")
+
+ # get the resource again, without any Accept header, so again no match
+ (response, content) = self.http.request(uri, "GET")
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, False, msg="Should not be from cache")
+
+ def testNoVary(self):
+ # when there is no vary, a different Accept header (e.g.) should not
+ # impact if the cache is used
+ # test that the vary header is not sent
+ uri = urllib.parse.urljoin(base, "vary/no-vary.asis")
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
+ self.assertEqual(response.status, 200)
+ self.assertFalse('vary' in response)
+
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, True, msg="Should be from cache")
+
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, True, msg="Should be from cache")
+
+ def testVaryHeaderDouble(self):
+ uri = urllib.parse.urljoin(base, "vary/accept-double.asis")
+ (response, content) = self.http.request(uri, "GET", headers={
+ 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
+ self.assertEqual(response.status, 200)
+ self.assertTrue('vary' in response)
+
+ # we are from cache
+ (response, content) = self.http.request(uri, "GET", headers={
+ 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
+ self.assertEqual(response.fromcache, True, msg="Should be from cache")
+
+ (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, False)
+
+ # get the resource again, not from cache, varied headers don't match exact
+ (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.fromcache, False, msg="Should not be from cache")
+
def testHeadGZip(self):
# Test that we don't try to decompress a HEAD response
uri = urllib.parse.urljoin(base, "gzip/final-destination.txt")
diff --git a/test/vary/accept-double.asis b/test/vary/accept-double.asis new file mode 100755 index 0000000..a2fc2fb --- /dev/null +++ b/test/vary/accept-double.asis @@ -0,0 +1,7 @@ +#!/usr/bin/tail --lines=+2 +Content-Type: text/plain +Vary: Accept, Accept-Language +Status: 200 OK + +We could've been some HTML. +And Danish! diff --git a/test/vary/accept.asis b/test/vary/accept.asis new file mode 100755 index 0000000..559945f --- /dev/null +++ b/test/vary/accept.asis @@ -0,0 +1,6 @@ +#!/usr/bin/tail --lines=+2 +Content-Type: text/plain +Vary: Accept +Status: 200 OK + +We could've been some HTML. diff --git a/test/vary/no-vary.asis b/test/vary/no-vary.asis new file mode 100755 index 0000000..46dc9a1 --- /dev/null +++ b/test/vary/no-vary.asis @@ -0,0 +1,5 @@ +#!/usr/bin/tail --lines=+2 +Content-Type: text/plain +Status: 200 OK + +We could've been some HTML. |