blob: 5a81a70da7212febf597a993cf97a56e758ddb22 [file] [log] [blame]
jcgregorio2d66d4f2006-02-07 05:34:14 +00001#!/usr/bin/env python2.4
2"""
3httplib2test
4
5A set of unit tests for httplib2.py.
6
7Requires Python 2.4 or later
8"""
9
10__author__ = "Joe Gregorio (joe@bitworking.org)"
11__copyright__ = "Copyright 2006, Joe Gregorio"
12__contributors__ = []
13__license__ = "MIT"
14__history__ = """ """
15__version__ = "0.1 ($Rev: 118 $)"
16
17
jcgregoriode8238d2007-03-07 19:08:26 +000018import sys
19import unittest
20import httplib
21import httplib2
22import os
23import urlparse
24import time
25import base64
jcgregorio14644372007-07-30 14:13:37 +000026import StringIO
jcgregorio8421f272006-02-14 18:19:51 +000027
28# Python 2.3 support
29if not hasattr(unittest.TestCase, 'assertTrue'):
30 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
31 unittest.TestCase.assertFalse = unittest.TestCase.failIf
32
jcgregorio2d66d4f2006-02-07 05:34:14 +000033# The test resources base uri
34base = 'http://bitworking.org/projects/httplib2/test/'
35#base = 'http://localhost/projects/httplib2/test/'
jcgregorio90fb4a42006-11-17 16:19:47 +000036cacheDirName = ".cache"
jcgregorio2d66d4f2006-02-07 05:34:14 +000037
jcgregoriode8238d2007-03-07 19:08:26 +000038
39class CredentialsTest(unittest.TestCase):
40 def test(self):
41 c = httplib2.Credentials()
42 c.add("joe", "password")
43 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
44 self.assertEqual(("joe", "password"), list(c.iter(""))[0])
45 c.add("fred", "password2", "wellformedweb.org")
46 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
47 self.assertEqual(1, len(list(c.iter("bitworking.org"))))
48 self.assertEqual(2, len(list(c.iter("wellformedweb.org"))))
49 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
50 c.clear()
51 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
52 c.add("fred", "password2", "wellformedweb.org")
53 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
54 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
55 self.assertEqual(0, len(list(c.iter(""))))
56
57
jcgregorio2d66d4f2006-02-07 05:34:14 +000058class ParserTest(unittest.TestCase):
59 def testFromStd66(self):
60 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
61 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
62 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
63 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
64 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
65 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
66 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
67 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
68
jcgregorio2d66d4f2006-02-07 05:34:14 +000069
jcgregorioa46fe4e2006-11-16 04:13:45 +000070class UrlNormTest(unittest.TestCase):
71 def test(self):
72 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1])
73 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1])
74 self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1])
75 self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1])
76 self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1])
jcgregoriob4e9ab02006-11-17 15:53:15 +000077 self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80"))
jcgregorio132d28e2007-01-23 16:22:53 +000078 try:
79 httplib2.urlnorm("/")
80 self.fail("Non-absolute URIs should raise an exception")
81 except httplib2.RelativeURIError:
82 pass
jcgregorioa46fe4e2006-11-16 04:13:45 +000083
84class UrlSafenameTest(unittest.TestCase):
85 def test(self):
86 # Test that different URIs end up generating different safe names
87 self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b"))
88 self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b"))
89 self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b"))
90 self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1]))
91 self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b"))
92 self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www"))
93 # Test the max length limits
94 uri = "http://" + ("w" * 200) + ".org"
95 uri2 = "http://" + ("w" * 201) + ".org"
96 self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri))
97 # Max length should be 200 + 1 (",") + 32
98 self.assertEqual(233, len(httplib2.safename(uri2)))
99 self.assertEqual(233, len(httplib2.safename(uri)))
100 # Unicode
jcgregoriodebceec2006-12-12 20:26:02 +0000101 if sys.version_info >= (2,3):
102 self.assertEqual( "xn--http,-4y1d.org,fred,a=b,579924c35db315e5a32e3d9963388193", httplib2.safename(u"http://\u2304.org/fred/?a=b"))
jcgregorioa46fe4e2006-11-16 04:13:45 +0000103
jcgregorio14644372007-07-30 14:13:37 +0000104class _MyResponse(StringIO.StringIO):
105 def __init__(self, body, **kwargs):
106 StringIO.StringIO.__init__(self, body)
107 self.headers = kwargs
108
109 def iteritems(self):
110 return self.headers.iteritems()
111
112
113class _MyHTTPConnection(object):
114 "This class is just a mock of httplib.HTTPConnection used for testing"
115
116 def __init__(self, host, port=None, key_file=None, cert_file=None,
joe.gregoriof28536d2007-10-23 14:10:11 +0000117 strict=None, timeout=None, proxy_info=None):
jcgregorio14644372007-07-30 14:13:37 +0000118 self.host = host
119 self.port = port
120 self.timeout = timeout
121 self.log = ""
122
123 def set_debuglevel(self, level):
124 pass
125
126 def connect(self):
127 "Connect to a host on a given port."
128 pass
129
130 def close(self):
131 pass
132
133 def request(self, method, request_uri, body, headers):
134 pass
135
136 def getresponse(self):
137 return _MyResponse("the body", status="200")
jcgregorioa46fe4e2006-11-16 04:13:45 +0000138
jcgregorio90fb4a42006-11-17 16:19:47 +0000139
jcgregorio2d66d4f2006-02-07 05:34:14 +0000140class HttpTest(unittest.TestCase):
141 def setUp(self):
jcgregorio7e3608f2006-06-15 13:01:53 +0000142 if os.path.exists(cacheDirName):
143 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
144 self.http = httplib2.Http(cacheDirName)
jcgregorio36140b52006-06-13 02:17:52 +0000145 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +0000146
jcgregorio14644372007-07-30 14:13:37 +0000147 def testConnectionType(self):
joe.gregoriof28536d2007-10-23 14:10:11 +0000148 self.http.force_exception_to_status_code = False
jcgregorio14644372007-07-30 14:13:37 +0000149 response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection)
150 self.assertEqual(response['content-location'], "http://bitworking.org")
151 self.assertEqual(content, "the body")
152
jcgregorio6a638172007-01-23 16:40:23 +0000153 def testGetUnknownServer(self):
jcgregorio07a9a4a2007-03-08 21:18:39 +0000154 self.http.force_exception_to_status_code = False
jcgregorio6a638172007-01-23 16:40:23 +0000155 try:
156 self.http.request("http://fred.bitworking.org/")
157 self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.")
158 except httplib2.ServerNotFoundError:
159 pass
160
jcgregorio07a9a4a2007-03-08 21:18:39 +0000161 # Now test with exceptions turned off
162 self.http.force_exception_to_status_code = True
163
164 (response, content) = self.http.request("http://fred.bitworking.org/")
165 self.assertEqual(response['content-type'], 'text/plain')
166 self.assertTrue(content.startswith("Unable to find"))
167 self.assertEqual(response.status, 400)
168
jcgregorioa898f8f2006-12-12 17:16:55 +0000169 def testGetIRI(self):
jcgregoriodebceec2006-12-12 20:26:02 +0000170 if sys.version_info >= (2,3):
171 uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}")
172 (response, content) = self.http.request(uri, "GET")
173 d = self.reflector(content)
174 self.assertTrue(d.has_key('QUERY_STRING'))
175 self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0)
jcgregorioa898f8f2006-12-12 17:16:55 +0000176
jcgregorio2d66d4f2006-02-07 05:34:14 +0000177 def testGetIsDefaultMethod(self):
178 # Test that GET is the default method
179 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000180 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000181 self.assertEqual(response['x-method'], "GET")
182
183 def testDifferentMethods(self):
184 # Test that all methods can be used
185 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
186 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +0000187 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000188 self.assertEqual(response['x-method'], method)
189
Joe Gregoriob628c0b2009-07-16 12:28:04 -0400190 def testHeadRead(self):
191 # Test that we don't try to read the response of a HEAD request
192 # since httplib blocks response.read() for HEAD requests.
193 # Oddly enough this doesn't appear as a problem when doing HEAD requests
194 # against Apache servers.
195 uri = "http://www.google.com/"
196 (response, content) = self.http.request(uri, "HEAD")
197 self.assertEqual(response.status, 200)
198 self.assertEqual(content, "")
199
jcgregorio2d66d4f2006-02-07 05:34:14 +0000200 def testGetNoCache(self):
201 # Test that can do a GET w/o the cache turned on.
202 http = httplib2.Http()
203 uri = urlparse.urljoin(base, "304/test_etag.txt")
204 (response, content) = http.request(uri, "GET")
205 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +0000206 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000207
Joe Gregorioe202d212009-07-16 14:57:52 -0400208 def testGetOnlyIfCachedCacheHit(self):
209 # Test that can do a GET with cache and 'only-if-cached'
210 uri = urlparse.urljoin(base, "304/test_etag.txt")
211 (response, content) = self.http.request(uri, "GET")
212 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
213 self.assertEqual(response.fromcache, True)
214 self.assertEqual(response.status, 200)
215
jcgregorioe4ce13e2006-04-02 03:05:08 +0000216 def testGetOnlyIfCachedCacheMiss(self):
217 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorioe4ce13e2006-04-02 03:05:08 +0000218 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorioe202d212009-07-16 14:57:52 -0400219 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
jcgregorioe4ce13e2006-04-02 03:05:08 +0000220 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400221 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000222
223 def testGetOnlyIfCachedNoCacheAtAll(self):
224 # Test that can do a GET with no cache with 'only-if-cached'
225 # Of course, there might be an intermediary beyond us
226 # that responds to the 'only-if-cached', so this
227 # test can't really be guaranteed to pass.
228 http = httplib2.Http()
229 uri = urlparse.urljoin(base, "304/test_etag.txt")
230 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
231 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400232 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000233
jcgregorio2d66d4f2006-02-07 05:34:14 +0000234 def testUserAgent(self):
235 # Test that we provide a default user-agent
236 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000237 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000238 self.assertEqual(response.status, 200)
239 self.assertTrue(content.startswith("Python-httplib2/"))
240
241 def testUserAgentNonDefault(self):
242 # Test that the default user-agent can be over-ridden
joe.gregoriof28536d2007-10-23 14:10:11 +0000243
jcgregorio2d66d4f2006-02-07 05:34:14 +0000244 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000245 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000246 self.assertEqual(response.status, 200)
247 self.assertTrue(content.startswith("fred/1.0"))
248
249 def testGet300WithLocation(self):
250 # Test the we automatically follow 300 redirects if a Location: header is provided
251 uri = urlparse.urljoin(base, "300/with-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000252 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000253 self.assertEqual(response.status, 200)
254 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000255 self.assertEqual(response.previous.status, 300)
256 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000257
258 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000259 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000260 self.assertEqual(response.status, 200)
261 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000262 self.assertEqual(response.previous.status, 300)
263 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000264
jcgregorio2f1e1422007-05-03 13:17:33 +0000265 def testGet300WithLocationNoRedirect(self):
266 # Test the we automatically follow 300 redirects if a Location: header is provided
267 self.http.follow_redirects = False
268 uri = urlparse.urljoin(base, "300/with-location-header.asis")
269 (response, content) = self.http.request(uri, "GET")
270 self.assertEqual(response.status, 300)
271
jcgregorio2d66d4f2006-02-07 05:34:14 +0000272 def testGet300WithoutLocation(self):
273 # Not giving a Location: header in a 300 response is acceptable
274 # In which case we just return the 300 response
275 uri = urlparse.urljoin(base, "300/without-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000276 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000277 self.assertEqual(response.status, 300)
278 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000279 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000280
281 def testGet301(self):
282 # Test that we automatically follow 301 redirects
283 # and that we cache the 301 response
284 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000285 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000286 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000287 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000288 self.assertTrue(response.has_key('content-location'))
289 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000290 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000291 self.assertEqual(response.previous.status, 301)
292 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000293
jcgregorio36140b52006-06-13 02:17:52 +0000294 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000295 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000296 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000297 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000298 self.assertEqual(response.previous.status, 301)
299 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000300
jcgregorio2f1e1422007-05-03 13:17:33 +0000301
302 def testGet301NoRedirect(self):
303 # Test that we automatically follow 301 redirects
304 # and that we cache the 301 response
305 self.http.follow_redirects = False
306 uri = urlparse.urljoin(base, "301/onestep.asis")
307 destination = urlparse.urljoin(base, "302/final-destination.txt")
308 (response, content) = self.http.request(uri, "GET")
309 self.assertEqual(response.status, 301)
310
311
jcgregorio2d66d4f2006-02-07 05:34:14 +0000312 def testGet302(self):
313 # Test that we automatically follow 302 redirects
314 # and that we DO NOT cache the 302 response
315 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000316 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000317 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000318 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000319 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000320 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000321 self.assertEqual(response.previous.status, 302)
322 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000323
324 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000325 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000326 self.assertEqual(response.status, 200)
327 self.assertEqual(response.fromcache, True)
jcgregorio772adc82006-11-17 21:52:34 +0000328 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000329 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000330 self.assertEqual(response.previous.status, 302)
331 self.assertEqual(response.previous.fromcache, False)
jcgregorio772adc82006-11-17 21:52:34 +0000332 self.assertEqual(response.previous['content-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000333
334 uri = urlparse.urljoin(base, "302/twostep.asis")
335
jcgregorio36140b52006-06-13 02:17:52 +0000336 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000337 self.assertEqual(response.status, 200)
338 self.assertEqual(response.fromcache, True)
339 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000340 self.assertEqual(response.previous.status, 302)
341 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000342
343 def testGet302RedirectionLimit(self):
344 # Test that we can set a lower redirection limit
345 # and that we raise an exception when we exceed
346 # that limit.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000347 self.http.force_exception_to_status_code = False
348
jcgregorio2d66d4f2006-02-07 05:34:14 +0000349 uri = urlparse.urljoin(base, "302/twostep.asis")
350 try:
jcgregorio36140b52006-06-13 02:17:52 +0000351 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000352 self.fail("This should not happen")
353 except httplib2.RedirectLimit:
354 pass
355 except Exception, e:
356 self.fail("Threw wrong kind of exception ")
357
jcgregorio07a9a4a2007-03-08 21:18:39 +0000358 # Re-run the test with out the exceptions
359 self.http.force_exception_to_status_code = True
360
361 (response, content) = self.http.request(uri, "GET", redirections = 1)
362 self.assertEqual(response.status, 500)
363 self.assertTrue(response.reason.startswith("Redirected more"))
364 self.assertEqual("302", response['status'])
365 self.assertTrue(content.startswith("<html>"))
366 self.assertTrue(response.previous != None)
367
jcgregorio2d66d4f2006-02-07 05:34:14 +0000368 def testGet302NoLocation(self):
369 # Test that we throw an exception when we get
370 # a 302 with no Location: header.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000371 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000372 uri = urlparse.urljoin(base, "302/no-location.asis")
373 try:
jcgregorio36140b52006-06-13 02:17:52 +0000374 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000375 self.fail("Should never reach here")
376 except httplib2.RedirectMissingLocation:
377 pass
378 except Exception, e:
379 self.fail("Threw wrong kind of exception ")
380
jcgregorio07a9a4a2007-03-08 21:18:39 +0000381 # Re-run the test with out the exceptions
382 self.http.force_exception_to_status_code = True
383
384 (response, content) = self.http.request(uri, "GET")
385 self.assertEqual(response.status, 500)
386 self.assertTrue(response.reason.startswith("Redirected but"))
387 self.assertEqual("302", response['status'])
388 self.assertTrue(content.startswith("This is content"))
389
jcgregorio2d66d4f2006-02-07 05:34:14 +0000390 def testGet302ViaHttps(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000391 # Google always redirects to http://google.com
jcgregorio36140b52006-06-13 02:17:52 +0000392 (response, content) = self.http.request("https://google.com", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000393 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000394 self.assertEqual(302, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000395
396 def testGetViaHttps(self):
397 # Test that we can handle HTTPS
jcgregorio36140b52006-06-13 02:17:52 +0000398 (response, content) = self.http.request("https://google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000399 self.assertEqual(200, response.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000400
401 def testGetViaHttpsSpecViolationOnLocation(self):
402 # Test that we follow redirects through HTTPS
403 # even if they violate the spec by including
404 # a relative Location: header instead of an
405 # absolute one.
jcgregorio36140b52006-06-13 02:17:52 +0000406 (response, content) = self.http.request("https://google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000407 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000408 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000409
jcgregoriode8238d2007-03-07 19:08:26 +0000410
411 def testGetViaHttpsKeyCert(self):
jcgregorio2f1e1422007-05-03 13:17:33 +0000412 # At this point I can only test
413 # that the key and cert files are passed in
414 # correctly to httplib. It would be nice to have
415 # a real https endpoint to test against.
416 http = httplib2.Http(timeout=2)
jcgregoriode8238d2007-03-07 19:08:26 +0000417
418 http.add_certificate("akeyfile", "acertfile", "bitworking.org")
419 try:
420 (response, content) = http.request("https://bitworking.org", "GET")
421 except:
422 pass
423 self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
424 self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
425
jcgregorio2f1e1422007-05-03 13:17:33 +0000426 try:
427 (response, content) = http.request("https://notthere.bitworking.org", "GET")
428 except:
429 pass
430 self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
431 self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
432
433
434
jcgregoriode8238d2007-03-07 19:08:26 +0000435
jcgregorio2d66d4f2006-02-07 05:34:14 +0000436 def testGet303(self):
437 # Do a follow-up GET on a Location: header
438 # returned from a POST that gave a 303.
439 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000440 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000441 self.assertEqual(response.status, 200)
442 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000443 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000444
jcgregorio2f1e1422007-05-03 13:17:33 +0000445 def testGet303NoRedirect(self):
446 # Do a follow-up GET on a Location: header
447 # returned from a POST that gave a 303.
448 self.http.follow_redirects = False
449 uri = urlparse.urljoin(base, "303/303.cgi")
450 (response, content) = self.http.request(uri, "POST", " ")
451 self.assertEqual(response.status, 303)
452
jcgregorio2d66d4f2006-02-07 05:34:14 +0000453 def test303ForDifferentMethods(self):
454 # Test that all methods can be used
455 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000456 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000457 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000458 self.assertEqual(response['x-method'], method_on_303)
459
460 def testGet304(self):
461 # Test that we use ETags properly to validate our cache
462 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000463 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000464 self.assertNotEqual(response['etag'], "")
465
jcgregorio36140b52006-06-13 02:17:52 +0000466 (response, content) = self.http.request(uri, "GET")
467 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000468 self.assertEqual(response.status, 200)
469 self.assertEqual(response.fromcache, True)
470
jcgregorio90fb4a42006-11-17 16:19:47 +0000471 cache_file_name = os.path.join(cacheDirName, httplib2.safename(httplib2.urlnorm(uri)[-1]))
472 f = open(cache_file_name, "r")
473 status_line = f.readline()
474 f.close()
475
476 self.assertTrue(status_line.startswith("status:"))
477
jcgregorio36140b52006-06-13 02:17:52 +0000478 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000479 self.assertEqual(response.status, 200)
480 self.assertEqual(response.fromcache, True)
481
jcgregorio36140b52006-06-13 02:17:52 +0000482 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000483 self.assertEqual(response.status, 206)
484 self.assertEqual(response.fromcache, False)
485
jcgregorio25185622006-10-28 05:12:34 +0000486 def testGetIgnoreEtag(self):
487 # Test that we can forcibly ignore ETags
488 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
489 (response, content) = self.http.request(uri, "GET")
490 self.assertNotEqual(response['etag'], "")
491
492 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
493 d = self.reflector(content)
494 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
495
496 self.http.ignore_etag = True
497 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
498 d = self.reflector(content)
499 self.assertEqual(response.fromcache, False)
500 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
501
jcgregorio4b145e82007-01-18 19:46:34 +0000502 def testOverrideEtag(self):
503 # Test that we can forcibly ignore ETags
504 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
505 (response, content) = self.http.request(uri, "GET")
506 self.assertNotEqual(response['etag'], "")
507
508 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
509 d = self.reflector(content)
510 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
511 self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred")
512
pilgrim00a352e2009-05-29 04:04:44 +0000513 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000514 d = self.reflector(content)
515 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
516 self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio25185622006-10-28 05:12:34 +0000517
pilgrim00a352e2009-05-29 04:04:44 +0000518#MAP-commented this out because it consistently fails
519# def testGet304EndToEnd(self):
520# # Test that end to end headers get overwritten in the cache
521# uri = urlparse.urljoin(base, "304/end2end.cgi")
522# (response, content) = self.http.request(uri, "GET")
523# self.assertNotEqual(response['etag'], "")
524# old_date = response['date']
525# time.sleep(2)
526#
527# (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
528# # The response should be from the cache, but the Date: header should be updated.
529# new_date = response['date']
530# self.assertNotEqual(new_date, old_date)
531# self.assertEqual(response.status, 200)
532# self.assertEqual(response.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000533
534 def testGet304LastModified(self):
535 # Test that we can still handle a 304
536 # by only using the last-modified cache validator.
537 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000538 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000539
540 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000541 (response, content) = self.http.request(uri, "GET")
542 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000543 self.assertEqual(response.status, 200)
544 self.assertEqual(response.fromcache, True)
545
546 def testGet307(self):
547 # Test that we do follow 307 redirects but
548 # do not cache the 307
549 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000550 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000551 self.assertEqual(response.status, 200)
552 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000553 self.assertEqual(response.previous.status, 307)
554 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000555
jcgregorio36140b52006-06-13 02:17:52 +0000556 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000557 self.assertEqual(response.status, 200)
558 self.assertEqual(response.fromcache, True)
559 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000560 self.assertEqual(response.previous.status, 307)
561 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000562
563 def testGet410(self):
564 # Test that we pass 410's through
565 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000566 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000567 self.assertEqual(response.status, 410)
568
chris dent89f15142009-12-24 14:02:57 -0600569 def testVaryHeaderSimple(self):
570 """
571 RFC 2616 13.6
572 When the cache receives a subsequent request whose Request-URI
573 specifies one or more cache entries including a Vary header field,
574 the cache MUST NOT use such a cache entry to construct a response
575 to the new request unless all of the selecting request-headers
576 present in the new request match the corresponding stored
577 request-headers in the original request.
578 """
579 # test that the vary header is sent
580 uri = urlparse.urljoin(base, "vary/accept.asis")
581 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
582 self.assertEqual(response.status, 200)
583 self.assertTrue(response.has_key('vary'))
584
585 # get the resource again, from the cache since accept header in this
586 # request is the same as the request
587 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
588 self.assertEqual(response.status, 200)
589 self.assertEqual(response.fromcache, True, msg="Should be from cache")
590
591 # get the resource again, not from cache since Accept headers does not match
592 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
593 self.assertEqual(response.status, 200)
594 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
595
596 # get the resource again, without any Accept header, so again no match
597 (response, content) = self.http.request(uri, "GET")
598 self.assertEqual(response.status, 200)
599 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
600
601 def testNoVary(self):
602 # when there is no vary, a different Accept header (e.g.) should not
603 # impact if the cache is used
604 # test that the vary header is not sent
605 uri = urlparse.urljoin(base, "vary/no-vary.asis")
606 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
607 self.assertEqual(response.status, 200)
608 self.assertFalse(response.has_key('vary'))
609
610 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
611 self.assertEqual(response.status, 200)
612 self.assertEqual(response.fromcache, True, msg="Should be from cache")
613
614 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
615 self.assertEqual(response.status, 200)
616 self.assertEqual(response.fromcache, True, msg="Should be from cache")
617
618 def testVaryHeaderDouble(self):
619 uri = urlparse.urljoin(base, "vary/accept-double.asis")
620 (response, content) = self.http.request(uri, "GET", headers={
621 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
622 self.assertEqual(response.status, 200)
623 self.assertTrue(response.has_key('vary'))
624
625 # we are from cache
626 (response, content) = self.http.request(uri, "GET", headers={
627 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
628 self.assertEqual(response.fromcache, True, msg="Should be from cache")
629
630 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
631 self.assertEqual(response.status, 200)
632 self.assertEqual(response.fromcache, False)
633
634 # get the resource again, not from cache, varied headers don't match exact
635 (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
636 self.assertEqual(response.status, 200)
637 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
638
jcgregorio88ef89b2010-05-13 23:42:11 -0400639 def testVaryUnusedHeader(self):
640 # A header's value is not considered to vary if it's not used at all.
641 uri = urlparse.urljoin(base, "vary/unused-header.asis")
642 (response, content) = self.http.request(uri, "GET", headers={
643 'Accept': 'text/plain'})
644 self.assertEqual(response.status, 200)
645 self.assertTrue(response.has_key('vary'))
646
647 # we are from cache
648 (response, content) = self.http.request(uri, "GET", headers={
649 'Accept': 'text/plain',})
650 self.assertEqual(response.fromcache, True, msg="Should be from cache")
651
chris dent89f15142009-12-24 14:02:57 -0600652
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000653 def testHeadGZip(self):
654 # Test that we don't try to decompress a HEAD response
655 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
656 (response, content) = self.http.request(uri, "HEAD")
657 self.assertEqual(response.status, 200)
658 self.assertNotEqual(int(response['content-length']), 0)
659 self.assertEqual(content, "")
660
jcgregorio2d66d4f2006-02-07 05:34:14 +0000661 def testGetGZip(self):
662 # Test that we support gzip compression
663 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000664 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000665 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000666 self.assertFalse(response.has_key('content-encoding'))
joe.gregorio8b6d2312007-12-16 05:42:07 +0000667 self.assertTrue(response.has_key('-content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000668 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000669 self.assertEqual(content, "This is the final destination.\n")
670
671 def testGetGZipFailure(self):
672 # Test that we raise a good exception when the gzip fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000673 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000674 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
675 try:
jcgregorio36140b52006-06-13 02:17:52 +0000676 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000677 self.fail("Should never reach here")
678 except httplib2.FailedToDecompressContent:
679 pass
680 except Exception:
681 self.fail("Threw wrong kind of exception")
682
jcgregorio07a9a4a2007-03-08 21:18:39 +0000683 # Re-run the test with out the exceptions
684 self.http.force_exception_to_status_code = True
685
686 (response, content) = self.http.request(uri, "GET")
687 self.assertEqual(response.status, 500)
688 self.assertTrue(response.reason.startswith("Content purported"))
689
690 def testTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000691 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000692 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
693 try:
694 import socket
695 socket.setdefaulttimeout(1)
696 except:
697 # Don't run the test if we can't set the timeout
698 return
699 (response, content) = self.http.request(uri)
700 self.assertEqual(response.status, 408)
701 self.assertTrue(response.reason.startswith("Request Timeout"))
702 self.assertTrue(content.startswith("Request Timeout"))
703
jcgregoriob2697912007-03-09 02:23:47 +0000704 def testIndividualTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000705 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
706 http = httplib2.Http(timeout=1)
joe.gregoriof28536d2007-10-23 14:10:11 +0000707 http.force_exception_to_status_code = True
jcgregoriob2697912007-03-09 02:23:47 +0000708
709 (response, content) = http.request(uri)
710 self.assertEqual(response.status, 408)
711 self.assertTrue(response.reason.startswith("Request Timeout"))
712 self.assertTrue(content.startswith("Request Timeout"))
713
jcgregorio07a9a4a2007-03-08 21:18:39 +0000714
Joe Gregorio1a7609f2009-07-16 10:59:44 -0400715 def testHTTPSInitTimeout(self):
716 c = httplib2.HTTPSConnectionWithTimeout('localhost', 80, timeout=47)
717 self.assertEqual(47, c.timeout)
718
jcgregorio2d66d4f2006-02-07 05:34:14 +0000719 def testGetDeflate(self):
720 # Test that we support deflate compression
721 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000722 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000723 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000724 self.assertFalse(response.has_key('content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000725 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000726 self.assertEqual(content, "This is the final destination.")
727
728 def testGetDeflateFailure(self):
729 # Test that we raise a good exception when the deflate fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000730 self.http.force_exception_to_status_code = False
731
jcgregorio2d66d4f2006-02-07 05:34:14 +0000732 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
733 try:
jcgregorio36140b52006-06-13 02:17:52 +0000734 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000735 self.fail("Should never reach here")
736 except httplib2.FailedToDecompressContent:
737 pass
738 except Exception:
739 self.fail("Threw wrong kind of exception")
740
jcgregorio07a9a4a2007-03-08 21:18:39 +0000741 # Re-run the test with out the exceptions
742 self.http.force_exception_to_status_code = True
743
744 (response, content) = self.http.request(uri, "GET")
745 self.assertEqual(response.status, 500)
746 self.assertTrue(response.reason.startswith("Content purported"))
747
jcgregorio2d66d4f2006-02-07 05:34:14 +0000748 def testGetDuplicateHeaders(self):
749 # Test that duplicate headers get concatenated via ','
750 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000751 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000752 self.assertEqual(response.status, 200)
753 self.assertEqual(content, "This is content\n")
754 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
755
756 def testGetCacheControlNoCache(self):
757 # Test Cache-Control: no-cache on requests
758 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000759 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000760 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000761 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000762 self.assertEqual(response.status, 200)
763 self.assertEqual(response.fromcache, True)
764
jcgregorio36140b52006-06-13 02:17:52 +0000765 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000766 self.assertEqual(response.status, 200)
767 self.assertEqual(response.fromcache, False)
768
769 def testGetCacheControlPragmaNoCache(self):
770 # Test Pragma: no-cache on requests
771 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000772 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000773 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000774 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000775 self.assertEqual(response.status, 200)
776 self.assertEqual(response.fromcache, True)
777
jcgregorio36140b52006-06-13 02:17:52 +0000778 (response, content) = self.http.request(uri, "GET", headers={'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000779 self.assertEqual(response.status, 200)
780 self.assertEqual(response.fromcache, False)
781
782 def testGetCacheControlNoStoreRequest(self):
783 # A no-store request means that the response should not be stored.
784 uri = urlparse.urljoin(base, "304/test_etag.txt")
785
jcgregorio36140b52006-06-13 02:17:52 +0000786 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000787 self.assertEqual(response.status, 200)
788 self.assertEqual(response.fromcache, False)
789
jcgregorio36140b52006-06-13 02:17:52 +0000790 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000791 self.assertEqual(response.status, 200)
792 self.assertEqual(response.fromcache, False)
793
794 def testGetCacheControlNoStoreResponse(self):
795 # A no-store response means that the response should not be stored.
796 uri = urlparse.urljoin(base, "no-store/no-store.asis")
797
jcgregorio36140b52006-06-13 02:17:52 +0000798 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000799 self.assertEqual(response.status, 200)
800 self.assertEqual(response.fromcache, False)
801
jcgregorio36140b52006-06-13 02:17:52 +0000802 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000803 self.assertEqual(response.status, 200)
804 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000805
806 def testGetCacheControlNoCacheNoStoreRequest(self):
807 # Test that a no-store, no-cache clears the entry from the cache
808 # even if it was cached previously.
809 uri = urlparse.urljoin(base, "304/test_etag.txt")
810
jcgregorio36140b52006-06-13 02:17:52 +0000811 (response, content) = self.http.request(uri, "GET")
812 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000813 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000814 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
815 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000816 self.assertEqual(response.status, 200)
817 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000818
819 def testUpdateInvalidatesCache(self):
820 # Test that calling PUT or DELETE on a
821 # URI that is cache invalidates that cache.
822 uri = urlparse.urljoin(base, "304/test_etag.txt")
823
jcgregorio36140b52006-06-13 02:17:52 +0000824 (response, content) = self.http.request(uri, "GET")
825 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000826 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000827 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000828 self.assertEqual(response.status, 405)
829
jcgregorio36140b52006-06-13 02:17:52 +0000830 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000831 self.assertEqual(response.fromcache, False)
832
833 def testUpdateUsesCachedETag(self):
834 # Test that we natively support http://www.w3.org/1999/04/Editing/
835 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
836
jcgregorio36140b52006-06-13 02:17:52 +0000837 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000838 self.assertEqual(response.status, 200)
839 self.assertEqual(response.fromcache, False)
jcgregorio36140b52006-06-13 02:17:52 +0000840 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000841 self.assertEqual(response.status, 200)
842 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400843 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000844 self.assertEqual(response.status, 200)
Joe Gregoriocd868102009-09-29 17:09:16 -0400845 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000846 self.assertEqual(response.status, 412)
847
joe.gregorio700f04d2008-09-06 04:46:32 +0000848 def testUpdateUsesCachedETagAndOCMethod(self):
849 # Test that we natively support http://www.w3.org/1999/04/Editing/
850 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
851
852 (response, content) = self.http.request(uri, "GET")
853 self.assertEqual(response.status, 200)
854 self.assertEqual(response.fromcache, False)
855 (response, content) = self.http.request(uri, "GET")
856 self.assertEqual(response.status, 200)
857 self.assertEqual(response.fromcache, True)
858 self.http.optimistic_concurrency_methods.append("DELETE")
859 (response, content) = self.http.request(uri, "DELETE")
860 self.assertEqual(response.status, 200)
861
862
jcgregorio4b145e82007-01-18 19:46:34 +0000863 def testUpdateUsesCachedETagOverridden(self):
864 # Test that we natively support http://www.w3.org/1999/04/Editing/
865 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
866
867 (response, content) = self.http.request(uri, "GET")
868 self.assertEqual(response.status, 200)
869 self.assertEqual(response.fromcache, False)
870 (response, content) = self.http.request(uri, "GET")
871 self.assertEqual(response.status, 200)
872 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400873 (response, content) = self.http.request(uri, "PUT", body="foo", headers={'if-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000874 self.assertEqual(response.status, 412)
875
jcgregorio2d66d4f2006-02-07 05:34:14 +0000876 def testBasicAuth(self):
877 # Test Basic Authentication
878 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000879 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000880 self.assertEqual(response.status, 401)
881
882 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +0000883 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000884 self.assertEqual(response.status, 401)
885
jcgregorio36140b52006-06-13 02:17:52 +0000886 self.http.add_credentials('joe', 'password')
887 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000888 self.assertEqual(response.status, 200)
889
890 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000891 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000892 self.assertEqual(response.status, 200)
893
jcgregoriode8238d2007-03-07 19:08:26 +0000894 def testBasicAuthWithDomain(self):
895 # Test Basic Authentication
896 uri = urlparse.urljoin(base, "basic/file.txt")
897 (response, content) = self.http.request(uri, "GET")
898 self.assertEqual(response.status, 401)
899
900 uri = urlparse.urljoin(base, "basic/")
901 (response, content) = self.http.request(uri, "GET")
902 self.assertEqual(response.status, 401)
903
904 self.http.add_credentials('joe', 'password', "example.org")
905 (response, content) = self.http.request(uri, "GET")
906 self.assertEqual(response.status, 401)
907
908 uri = urlparse.urljoin(base, "basic/file.txt")
909 (response, content) = self.http.request(uri, "GET")
910 self.assertEqual(response.status, 401)
911
912 domain = urlparse.urlparse(base)[1]
913 self.http.add_credentials('joe', 'password', domain)
914 (response, content) = self.http.request(uri, "GET")
915 self.assertEqual(response.status, 200)
916
917 uri = urlparse.urljoin(base, "basic/file.txt")
918 (response, content) = self.http.request(uri, "GET")
919 self.assertEqual(response.status, 200)
920
921
922
923
924
925
jcgregorio2d66d4f2006-02-07 05:34:14 +0000926 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000927 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +0000928 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000929 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000930 self.assertEqual(response.status, 401)
931
932 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +0000933 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000934 self.assertEqual(response.status, 401)
935
jcgregorio36140b52006-06-13 02:17:52 +0000936 self.http.add_credentials('fred', 'barney')
937 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000938 self.assertEqual(response.status, 200)
939
940 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000941 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000942 self.assertEqual(response.status, 200)
943
944 def testBasicAuthNested(self):
945 # Test Basic Authentication with resources
946 # that are nested
947 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000948 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000949 self.assertEqual(response.status, 401)
950
951 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000952 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000953 self.assertEqual(response.status, 401)
954
jcgregorioadbb4f82006-05-19 15:17:42 +0000955 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +0000956 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +0000957
958 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000959 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000960 self.assertEqual(response.status, 200)
961
962 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000963 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000964 self.assertEqual(response.status, 401)
965
jcgregorio36140b52006-06-13 02:17:52 +0000966 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +0000967
968 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000969 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000970 self.assertEqual(response.status, 200)
971
972 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000973 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000974 self.assertEqual(response.status, 200)
975
976 def testDigestAuth(self):
977 # Test that we support Digest Authentication
978 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +0000979 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000980 self.assertEqual(response.status, 401)
981
jcgregorio36140b52006-06-13 02:17:52 +0000982 self.http.add_credentials('joe', 'password')
983 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000984 self.assertEqual(response.status, 200)
985
986 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000987 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000988
989 def testDigestAuthNextNonceAndNC(self):
990 # Test that if the server sets nextnonce that we reset
991 # the nonce count back to 1
992 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000993 self.http.add_credentials('joe', 'password')
994 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000995 info = httplib2._parse_www_authenticate(response, 'authentication-info')
996 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +0000997 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000998 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
999 self.assertEqual(response.status, 200)
1000
1001 if info.has_key('nextnonce'):
1002 self.assertEqual(info2['nc'], 1)
1003
1004 def testDigestAuthStale(self):
1005 # Test that we can handle a nonce becoming stale
1006 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001007 self.http.add_credentials('joe', 'password')
1008 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001009 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1010 self.assertEqual(response.status, 200)
1011
1012 time.sleep(3)
1013 # Sleep long enough that the nonce becomes stale
1014
jcgregorio36140b52006-06-13 02:17:52 +00001015 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001016 self.assertFalse(response.fromcache)
1017 self.assertTrue(response._stale_digest)
1018 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
1019 self.assertEqual(response.status, 200)
1020
1021 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +00001022 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +00001023
1024 def testReflector(self):
1025 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +00001026 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001027 d = self.reflector(content)
1028 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
1029
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001030 def testConnectionClose(self):
1031 uri = "http://www.google.com/"
1032 (response, content) = self.http.request(uri, "GET")
1033 for c in self.http.connections.values():
1034 self.assertNotEqual(None, c.sock)
1035 (response, content) = self.http.request(uri, "GET", headers={"connection": "close"})
1036 for c in self.http.connections.values():
1037 self.assertEqual(None, c.sock)
1038
1039
jcgregorio36140b52006-06-13 02:17:52 +00001040try:
1041 import memcache
1042 class HttpTestMemCached(HttpTest):
1043 def setUp(self):
1044 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +00001045 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +00001046 self.http = httplib2.Http(self.cache)
1047 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +00001048 # Not exactly sure why the sleep is needed here, but
1049 # if not present then some unit tests that rely on caching
1050 # fail. Memcached seems to lose some sets immediately
1051 # after a flush_all if the set is to a value that
1052 # was previously cached. (Maybe the flush is handled async?)
1053 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +00001054 self.http.clear_credentials()
1055except:
1056 pass
1057
1058
1059
chris dent89f15142009-12-24 14:02:57 -06001060
jcgregoriodb8dfc82006-03-31 14:59:46 +00001061# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +00001062
1063class HttpPrivateTest(unittest.TestCase):
1064
1065 def testParseCacheControl(self):
1066 # Test that we can parse the Cache-Control header
1067 self.assertEqual({}, httplib2._parse_cache_control({}))
1068 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
1069 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
1070 self.assertEqual(cc['no-cache'], 1)
1071 self.assertEqual(cc['max-age'], '7200')
1072 cc = httplib2._parse_cache_control({'cache-control': ' , '})
1073 self.assertEqual(cc[''], 1)
1074
Joe Gregorioe314e8b2009-07-16 20:11:28 -04001075 try:
1076 cc = httplib2._parse_cache_control({'cache-control': 'Max-age=3600;post-check=1800,pre-check=3600'})
1077 self.assertTrue("max-age" in cc)
1078 except:
1079 self.fail("Should not throw exception")
1080
jcgregorio2d66d4f2006-02-07 05:34:14 +00001081 def testNormalizeHeaders(self):
1082 # Test that we normalize headers to lowercase
1083 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
1084 self.assertTrue(h.has_key('cache-control'))
1085 self.assertTrue(h.has_key('other'))
1086 self.assertEqual('Stuff', h['other'])
1087
1088 def testExpirationModelTransparent(self):
1089 # Test that no-cache makes our request TRANSPARENT
1090 response_headers = {
1091 'cache-control': 'max-age=7200'
1092 }
1093 request_headers = {
1094 'cache-control': 'no-cache'
1095 }
1096 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
1097
jcgregorio45865012007-01-18 16:38:22 +00001098 def testMaxAgeNonNumeric(self):
1099 # Test that no-cache makes our request TRANSPARENT
1100 response_headers = {
1101 'cache-control': 'max-age=fred, min-fresh=barney'
1102 }
1103 request_headers = {
1104 }
1105 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1106
1107
jcgregorio2d66d4f2006-02-07 05:34:14 +00001108 def testExpirationModelNoCacheResponse(self):
1109 # The date and expires point to an entry that should be
1110 # FRESH, but the no-cache over-rides that.
1111 now = time.time()
1112 response_headers = {
1113 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1114 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1115 'cache-control': 'no-cache'
1116 }
1117 request_headers = {
1118 }
1119 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1120
1121 def testExpirationModelStaleRequestMustReval(self):
1122 # must-revalidate forces STALE
1123 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
1124
1125 def testExpirationModelStaleResponseMustReval(self):
1126 # must-revalidate forces STALE
1127 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
1128
1129 def testExpirationModelFresh(self):
1130 response_headers = {
1131 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1132 'cache-control': 'max-age=2'
1133 }
1134 request_headers = {
1135 }
1136 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1137 time.sleep(3)
1138 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1139
1140 def testExpirationMaxAge0(self):
1141 response_headers = {
1142 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1143 'cache-control': 'max-age=0'
1144 }
1145 request_headers = {
1146 }
1147 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1148
1149 def testExpirationModelDateAndExpires(self):
1150 now = time.time()
1151 response_headers = {
1152 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1153 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1154 }
1155 request_headers = {
1156 }
1157 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1158 time.sleep(3)
1159 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1160
jcgregoriof9511052007-06-01 14:56:34 +00001161 def testExpiresZero(self):
1162 now = time.time()
1163 response_headers = {
1164 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1165 'expires': "0",
1166 }
1167 request_headers = {
1168 }
1169 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1170
jcgregorio2d66d4f2006-02-07 05:34:14 +00001171 def testExpirationModelDateOnly(self):
1172 now = time.time()
1173 response_headers = {
1174 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
1175 }
1176 request_headers = {
1177 }
1178 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1179
1180 def testExpirationModelOnlyIfCached(self):
1181 response_headers = {
1182 }
1183 request_headers = {
1184 'cache-control': 'only-if-cached',
1185 }
1186 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1187
1188 def testExpirationModelMaxAgeBoth(self):
1189 now = time.time()
1190 response_headers = {
1191 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1192 'cache-control': 'max-age=2'
1193 }
1194 request_headers = {
1195 'cache-control': 'max-age=0'
1196 }
1197 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1198
1199 def testExpirationModelDateAndExpiresMinFresh1(self):
1200 now = time.time()
1201 response_headers = {
1202 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1203 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1204 }
1205 request_headers = {
1206 'cache-control': 'min-fresh=2'
1207 }
1208 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1209
1210 def testExpirationModelDateAndExpiresMinFresh2(self):
1211 now = time.time()
1212 response_headers = {
1213 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1214 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1215 }
1216 request_headers = {
1217 'cache-control': 'min-fresh=2'
1218 }
1219 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1220
1221 def testParseWWWAuthenticateEmpty(self):
1222 res = httplib2._parse_www_authenticate({})
1223 self.assertEqual(len(res.keys()), 0)
1224
jcgregoriofd22e432006-04-27 02:00:08 +00001225 def testParseWWWAuthenticate(self):
1226 # different uses of spaces around commas
1227 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
1228 self.assertEqual(len(res.keys()), 1)
1229 self.assertEqual(len(res['test'].keys()), 5)
1230
1231 # tokens with non-alphanum
1232 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
1233 self.assertEqual(len(res.keys()), 1)
1234 self.assertEqual(len(res['t*!%#st'].keys()), 2)
1235
1236 # quoted string with quoted pairs
1237 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
1238 self.assertEqual(len(res.keys()), 1)
1239 self.assertEqual(res['test']['realm'], 'a "test" realm')
1240
1241 def testParseWWWAuthenticateStrict(self):
1242 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
1243 self.testParseWWWAuthenticate();
1244 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
1245
jcgregorio2d66d4f2006-02-07 05:34:14 +00001246 def testParseWWWAuthenticateBasic(self):
1247 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
1248 basic = res['basic']
1249 self.assertEqual('me', basic['realm'])
1250
1251 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
1252 basic = res['basic']
1253 self.assertEqual('me', basic['realm'])
1254 self.assertEqual('MD5', basic['algorithm'])
1255
1256 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
1257 basic = res['basic']
1258 self.assertEqual('me', basic['realm'])
1259 self.assertEqual('MD5', basic['algorithm'])
1260
1261 def testParseWWWAuthenticateBasic2(self):
1262 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
1263 basic = res['basic']
1264 self.assertEqual('me', basic['realm'])
1265 self.assertEqual('fred', basic['other'])
1266
1267 def testParseWWWAuthenticateBasic3(self):
1268 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
1269 basic = res['basic']
1270 self.assertEqual('me', basic['realm'])
1271
1272
1273 def testParseWWWAuthenticateDigest(self):
1274 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1275 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
1276 digest = res['digest']
1277 self.assertEqual('testrealm@host.com', digest['realm'])
1278 self.assertEqual('auth,auth-int', digest['qop'])
1279
1280
1281 def testParseWWWAuthenticateMultiple(self):
1282 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1283 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
1284 digest = res['digest']
1285 self.assertEqual('testrealm@host.com', digest['realm'])
1286 self.assertEqual('auth,auth-int', digest['qop'])
1287 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1288 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1289 basic = res['basic']
1290 self.assertEqual('me', basic['realm'])
1291
1292 def testParseWWWAuthenticateMultiple2(self):
1293 # Handle an added comma between challenges, which might get thrown in if the challenges were
1294 # originally sent in separate www-authenticate headers.
1295 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1296 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
1297 digest = res['digest']
1298 self.assertEqual('testrealm@host.com', digest['realm'])
1299 self.assertEqual('auth,auth-int', digest['qop'])
1300 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1301 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1302 basic = res['basic']
1303 self.assertEqual('me', basic['realm'])
1304
1305 def testParseWWWAuthenticateMultiple3(self):
1306 # Handle an added comma between challenges, which might get thrown in if the challenges were
1307 # originally sent in separate www-authenticate headers.
1308 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1309 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1310 digest = res['digest']
1311 self.assertEqual('testrealm@host.com', digest['realm'])
1312 self.assertEqual('auth,auth-int', digest['qop'])
1313 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1314 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1315 basic = res['basic']
1316 self.assertEqual('me', basic['realm'])
1317 wsse = res['wsse']
1318 self.assertEqual('foo', wsse['realm'])
1319 self.assertEqual('UsernameToken', wsse['profile'])
1320
1321 def testParseWWWAuthenticateMultiple4(self):
1322 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1323 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1324 digest = res['digest']
1325 self.assertEqual('test-real.m@host.com', digest['realm'])
1326 self.assertEqual('\tauth,auth-int', digest['qop'])
1327 self.assertEqual('(*)&^&$%#', digest['nonce'])
1328
1329 def testParseWWWAuthenticateMoreQuoteCombos(self):
1330 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
1331 digest = res['digest']
1332 self.assertEqual('myrealm', digest['realm'])
1333
1334 def testDigestObject(self):
1335 credentials = ('joe', 'password')
1336 host = None
1337 request_uri = '/projects/httplib2/test/digest/'
1338 headers = {}
1339 response = {
1340 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
1341 }
1342 content = ""
1343
jcgregorio6cbab7e2006-04-21 20:35:43 +00001344 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001345 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
1346 our_request = "Authorization: %s" % headers['Authorization']
1347 working_request = 'Authorization: Digest username="joe", realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", uri="/projects/httplib2/test/digest/", algorithm=MD5, response="97ed129401f7cdc60e5db58a80f3ea8b", qop=auth, nc=00000001, cnonce="33033375ec278a46"'
1348 self.assertEqual(our_request, working_request)
1349
1350
1351 def testDigestObjectStale(self):
1352 credentials = ('joe', 'password')
1353 host = None
1354 request_uri = '/projects/httplib2/test/digest/'
1355 headers = {}
1356 response = httplib2.Response({ })
1357 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1358 response.status = 401
1359 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001360 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001361 # Returns true to force a retry
1362 self.assertTrue( d.response(response, content) )
1363
1364 def testDigestObjectAuthInfo(self):
1365 credentials = ('joe', 'password')
1366 host = None
1367 request_uri = '/projects/httplib2/test/digest/'
1368 headers = {}
1369 response = httplib2.Response({ })
1370 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1371 response['authentication-info'] = 'nextnonce="fred"'
1372 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001373 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001374 # Returns true to force a retry
1375 self.assertFalse( d.response(response, content) )
1376 self.assertEqual('fred', d.challenge['nonce'])
1377 self.assertEqual(1, d.challenge['nc'])
1378
1379 def testWsseAlgorithm(self):
1380 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
1381 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
1382 self.assertEqual(expected, digest)
1383
jcgregoriodb8dfc82006-03-31 14:59:46 +00001384 def testEnd2End(self):
1385 # one end to end header
1386 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
1387 end2end = httplib2._get_end2end_headers(response)
1388 self.assertTrue('content-type' in end2end)
1389 self.assertTrue('te' not in end2end)
1390 self.assertTrue('connection' not in end2end)
1391
1392 # one end to end header that gets eliminated
1393 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
1394 end2end = httplib2._get_end2end_headers(response)
1395 self.assertTrue('content-type' not in end2end)
1396 self.assertTrue('te' not in end2end)
1397 self.assertTrue('connection' not in end2end)
1398
1399 # Degenerate case of no headers
1400 response = {}
1401 end2end = httplib2._get_end2end_headers(response)
1402 self.assertEquals(0, len(end2end))
1403
1404 # Degenerate case of connection referrring to a header not passed in
1405 response = {'connection': 'content-type'}
1406 end2end = httplib2._get_end2end_headers(response)
1407 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001408
chris dent89f15142009-12-24 14:02:57 -06001409if __name__ == '__main__':
1410 unittest.main()