blob: 84f83a2b79259f952c6ffae0f7b558f030ebdc6b [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
18import unittest, httplib2, os, urlparse, time, base64
19
jcgregorio8421f272006-02-14 18:19:51 +000020
21# Python 2.3 support
22if not hasattr(unittest.TestCase, 'assertTrue'):
23 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
24 unittest.TestCase.assertFalse = unittest.TestCase.failIf
25
jcgregorio2d66d4f2006-02-07 05:34:14 +000026# The test resources base uri
27base = 'http://bitworking.org/projects/httplib2/test/'
28#base = 'http://localhost/projects/httplib2/test/'
29
30class ParserTest(unittest.TestCase):
31 def testFromStd66(self):
32 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
33 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
34 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
35 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
36 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
37 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
38 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
39 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
40
jcgregorio2d66d4f2006-02-07 05:34:14 +000041
42class HttpTest(unittest.TestCase):
43 def setUp(self):
jcgregorio7e3608f2006-06-15 13:01:53 +000044 cacheDirName = ".cache"
45 if os.path.exists(cacheDirName):
46 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
47 self.http = httplib2.Http(cacheDirName)
jcgregorio36140b52006-06-13 02:17:52 +000048 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +000049
50 def testGetIsDefaultMethod(self):
51 # Test that GET is the default method
52 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +000053 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +000054 self.assertEqual(response['x-method'], "GET")
55
56 def testDifferentMethods(self):
57 # Test that all methods can be used
58 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
59 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +000060 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +000061 self.assertEqual(response['x-method'], method)
62
63 def testGetNoCache(self):
64 # Test that can do a GET w/o the cache turned on.
65 http = httplib2.Http()
66 uri = urlparse.urljoin(base, "304/test_etag.txt")
67 (response, content) = http.request(uri, "GET")
68 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +000069 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +000070
jcgregorioe4ce13e2006-04-02 03:05:08 +000071 def testGetOnlyIfCachedCacheMiss(self):
72 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorio36140b52006-06-13 02:17:52 +000073 http = httplib2.Http()
jcgregorioe4ce13e2006-04-02 03:05:08 +000074 uri = urlparse.urljoin(base, "304/test_etag.txt")
75 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
76 self.assertEqual(response.fromcache, False)
jcgregorio36140b52006-06-13 02:17:52 +000077 self.assertEqual(response.status, 200)
jcgregorioe4ce13e2006-04-02 03:05:08 +000078
79 def testGetOnlyIfCachedNoCacheAtAll(self):
80 # Test that can do a GET with no cache with 'only-if-cached'
81 # Of course, there might be an intermediary beyond us
82 # that responds to the 'only-if-cached', so this
83 # test can't really be guaranteed to pass.
84 http = httplib2.Http()
85 uri = urlparse.urljoin(base, "304/test_etag.txt")
86 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
87 self.assertEqual(response.fromcache, False)
88 self.assertEqual(response.status, 200)
89
jcgregorio2d66d4f2006-02-07 05:34:14 +000090 def testUserAgent(self):
91 # Test that we provide a default user-agent
92 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +000093 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +000094 self.assertEqual(response.status, 200)
95 self.assertTrue(content.startswith("Python-httplib2/"))
96
97 def testUserAgentNonDefault(self):
98 # Test that the default user-agent can be over-ridden
99 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000100 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000101 self.assertEqual(response.status, 200)
102 self.assertTrue(content.startswith("fred/1.0"))
103
104 def testGet300WithLocation(self):
105 # Test the we automatically follow 300 redirects if a Location: header is provided
106 uri = urlparse.urljoin(base, "300/with-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000107 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000108 self.assertEqual(response.status, 200)
109 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000110 self.assertEqual(response.previous.status, 300)
111 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000112
113 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000114 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000115 self.assertEqual(response.status, 200)
116 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000117 self.assertEqual(response.previous.status, 300)
118 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000119
120 def testGet300WithoutLocation(self):
121 # Not giving a Location: header in a 300 response is acceptable
122 # In which case we just return the 300 response
123 uri = urlparse.urljoin(base, "300/without-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000124 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000125 self.assertEqual(response.status, 300)
126 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000127 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000128
129 def testGet301(self):
130 # Test that we automatically follow 301 redirects
131 # and that we cache the 301 response
132 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000133 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000134 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000135 self.assertEqual(response.status, 200)
jcgregorio8e300b92006-11-07 16:44:35 +0000136 self.assertTrue(response.has_key('-location'))
137 self.assertEqual(response['-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000138 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000139 self.assertEqual(response.previous.status, 301)
140 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000141
jcgregorio36140b52006-06-13 02:17:52 +0000142 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000143 self.assertEqual(response.status, 200)
jcgregorio8e300b92006-11-07 16:44:35 +0000144 self.assertEqual(response['-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000145 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000146 self.assertEqual(response.previous.status, 301)
147 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000148
149 def testGet302(self):
150 # Test that we automatically follow 302 redirects
151 # and that we DO NOT cache the 302 response
152 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000153 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000154 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000155 self.assertEqual(response.status, 200)
jcgregorio8e300b92006-11-07 16:44:35 +0000156 self.assertEqual(response['-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000157 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000158 self.assertEqual(response.previous.status, 302)
159 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000160
161 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000162 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000163 self.assertEqual(response.status, 200)
164 self.assertEqual(response.fromcache, True)
jcgregorio8e300b92006-11-07 16:44:35 +0000165 self.assertEqual(response['-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000166 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000167 self.assertEqual(response.previous.status, 302)
168 self.assertEqual(response.previous.fromcache, False)
jcgregorio8e300b92006-11-07 16:44:35 +0000169 self.assertEqual(response.previous['-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000170
171 uri = urlparse.urljoin(base, "302/twostep.asis")
172
jcgregorio36140b52006-06-13 02:17:52 +0000173 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000174 self.assertEqual(response.status, 200)
175 self.assertEqual(response.fromcache, True)
176 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000177 self.assertEqual(response.previous.status, 302)
178 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000179
180 def testGet302RedirectionLimit(self):
181 # Test that we can set a lower redirection limit
182 # and that we raise an exception when we exceed
183 # that limit.
184 uri = urlparse.urljoin(base, "302/twostep.asis")
185 try:
jcgregorio36140b52006-06-13 02:17:52 +0000186 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000187 self.fail("This should not happen")
188 except httplib2.RedirectLimit:
189 pass
190 except Exception, e:
191 self.fail("Threw wrong kind of exception ")
192
193 def testGet302NoLocation(self):
194 # Test that we throw an exception when we get
195 # a 302 with no Location: header.
196 uri = urlparse.urljoin(base, "302/no-location.asis")
197 try:
jcgregorio36140b52006-06-13 02:17:52 +0000198 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000199 self.fail("Should never reach here")
200 except httplib2.RedirectMissingLocation:
201 pass
202 except Exception, e:
203 self.fail("Threw wrong kind of exception ")
204
205 def testGet302ViaHttps(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000206 # Google always redirects to http://google.com
jcgregorio36140b52006-06-13 02:17:52 +0000207 (response, content) = self.http.request("https://google.com", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000208 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000209 self.assertEqual(302, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000210
211 def testGetViaHttps(self):
212 # Test that we can handle HTTPS
jcgregorio36140b52006-06-13 02:17:52 +0000213 (response, content) = self.http.request("https://google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000214 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000215 self.assertEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000216
217 def testGetViaHttpsSpecViolationOnLocation(self):
218 # Test that we follow redirects through HTTPS
219 # even if they violate the spec by including
220 # a relative Location: header instead of an
221 # absolute one.
jcgregorio36140b52006-06-13 02:17:52 +0000222 (response, content) = self.http.request("https://google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000223 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000224 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000225
226 def testGet303(self):
227 # Do a follow-up GET on a Location: header
228 # returned from a POST that gave a 303.
229 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000230 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000231 self.assertEqual(response.status, 200)
232 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000233 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000234
235 def test303ForDifferentMethods(self):
236 # Test that all methods can be used
237 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
238 # HEAD really does send a HEAD, but apparently Apache changes
239 # every HEAD into a GET, so our script returns x-method: GET.
240 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000241 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000242 self.assertEqual(response['x-method'], method_on_303)
243
244 def testGet304(self):
245 # Test that we use ETags properly to validate our cache
246 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000247 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000248 self.assertNotEqual(response['etag'], "")
249
jcgregorio36140b52006-06-13 02:17:52 +0000250 (response, content) = self.http.request(uri, "GET")
251 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000252 self.assertEqual(response.status, 200)
253 self.assertEqual(response.fromcache, True)
254
jcgregorio36140b52006-06-13 02:17:52 +0000255 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000256 self.assertEqual(response.status, 200)
257 self.assertEqual(response.fromcache, True)
258
jcgregorio36140b52006-06-13 02:17:52 +0000259 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000260 self.assertEqual(response.status, 206)
261 self.assertEqual(response.fromcache, False)
262
jcgregorio25185622006-10-28 05:12:34 +0000263 def testGetIgnoreEtag(self):
264 # Test that we can forcibly ignore ETags
265 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
266 (response, content) = self.http.request(uri, "GET")
267 self.assertNotEqual(response['etag'], "")
268
269 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
270 d = self.reflector(content)
271 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
272
273 self.http.ignore_etag = True
274 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
275 d = self.reflector(content)
276 self.assertEqual(response.fromcache, False)
277 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
278
279
jcgregorio2d66d4f2006-02-07 05:34:14 +0000280 def testGet304EndToEnd(self):
281 # Test that end to end headers get overwritten in the cache
282 uri = urlparse.urljoin(base, "304/end2end.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000283 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000284 self.assertNotEqual(response['etag'], "")
285 old_date = response['date']
286 time.sleep(2)
287
jcgregorio36140b52006-06-13 02:17:52 +0000288 (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000289 # The response should be from the cache, but the Date: header should be updated.
290 new_date = response['date']
291 self.assertNotEqual(new_date, old_date)
292 self.assertEqual(response.status, 200)
293 self.assertEqual(response.fromcache, True)
294
295 def testGet304LastModified(self):
296 # Test that we can still handle a 304
297 # by only using the last-modified cache validator.
298 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000299 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000300
301 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000302 (response, content) = self.http.request(uri, "GET")
303 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000304 self.assertEqual(response.status, 200)
305 self.assertEqual(response.fromcache, True)
306
307 def testGet307(self):
308 # Test that we do follow 307 redirects but
309 # do not cache the 307
310 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000311 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000312 self.assertEqual(response.status, 200)
313 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000314 self.assertEqual(response.previous.status, 307)
315 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000316
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)
319 self.assertEqual(response.fromcache, True)
320 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000321 self.assertEqual(response.previous.status, 307)
322 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000323
324 def testGet410(self):
325 # Test that we pass 410's through
326 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000327 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000328 self.assertEqual(response.status, 410)
329
330 def testGetGZip(self):
331 # Test that we support gzip compression
332 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000333 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000334 self.assertEqual(response.status, 200)
335 self.assertEqual(response['content-encoding'], "gzip")
jcgregorio153f5882006-11-06 03:33:24 +0000336 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000337 self.assertEqual(content, "This is the final destination.\n")
338
339 def testGetGZipFailure(self):
340 # Test that we raise a good exception when the gzip fails
341 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
342 try:
jcgregorio36140b52006-06-13 02:17:52 +0000343 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000344 self.fail("Should never reach here")
345 except httplib2.FailedToDecompressContent:
346 pass
347 except Exception:
348 self.fail("Threw wrong kind of exception")
349
350 def testGetDeflate(self):
351 # Test that we support deflate compression
352 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000353 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000354 self.assertEqual(response.status, 200)
355 self.assertEqual(response['content-encoding'], "deflate")
jcgregorio153f5882006-11-06 03:33:24 +0000356 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000357 self.assertEqual(content, "This is the final destination.")
358
359 def testGetDeflateFailure(self):
360 # Test that we raise a good exception when the deflate fails
361 uri = urlparse.urljoin(base, "deflate/deflated.asis")
362 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
363 try:
jcgregorio36140b52006-06-13 02:17:52 +0000364 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000365 self.fail("Should never reach here")
366 except httplib2.FailedToDecompressContent:
367 pass
368 except Exception:
369 self.fail("Threw wrong kind of exception")
370
371 def testGetDuplicateHeaders(self):
372 # Test that duplicate headers get concatenated via ','
373 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000374 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000375 self.assertEqual(response.status, 200)
376 self.assertEqual(content, "This is content\n")
377 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
378
379 def testGetCacheControlNoCache(self):
380 # Test Cache-Control: no-cache on requests
381 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000382 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000383 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000384 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000385 self.assertEqual(response.status, 200)
386 self.assertEqual(response.fromcache, True)
387
jcgregorio36140b52006-06-13 02:17:52 +0000388 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000389 self.assertEqual(response.status, 200)
390 self.assertEqual(response.fromcache, False)
391
392 def testGetCacheControlPragmaNoCache(self):
393 # Test Pragma: no-cache on requests
394 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000395 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000396 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000397 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000398 self.assertEqual(response.status, 200)
399 self.assertEqual(response.fromcache, True)
400
jcgregorio36140b52006-06-13 02:17:52 +0000401 (response, content) = self.http.request(uri, "GET", headers={'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000402 self.assertEqual(response.status, 200)
403 self.assertEqual(response.fromcache, False)
404
405 def testGetCacheControlNoStoreRequest(self):
406 # A no-store request means that the response should not be stored.
407 uri = urlparse.urljoin(base, "304/test_etag.txt")
408
jcgregorio36140b52006-06-13 02:17:52 +0000409 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000410 self.assertEqual(response.status, 200)
411 self.assertEqual(response.fromcache, False)
412
jcgregorio36140b52006-06-13 02:17:52 +0000413 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000414 self.assertEqual(response.status, 200)
415 self.assertEqual(response.fromcache, False)
416
417 def testGetCacheControlNoStoreResponse(self):
418 # A no-store response means that the response should not be stored.
419 uri = urlparse.urljoin(base, "no-store/no-store.asis")
420
jcgregorio36140b52006-06-13 02:17:52 +0000421 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000422 self.assertEqual(response.status, 200)
423 self.assertEqual(response.fromcache, False)
424
jcgregorio36140b52006-06-13 02:17:52 +0000425 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000426 self.assertEqual(response.status, 200)
427 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000428
429 def testGetCacheControlNoCacheNoStoreRequest(self):
430 # Test that a no-store, no-cache clears the entry from the cache
431 # even if it was cached previously.
432 uri = urlparse.urljoin(base, "304/test_etag.txt")
433
jcgregorio36140b52006-06-13 02:17:52 +0000434 (response, content) = self.http.request(uri, "GET")
435 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000436 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000437 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
438 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000439 self.assertEqual(response.status, 200)
440 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000441
442 def testUpdateInvalidatesCache(self):
443 # Test that calling PUT or DELETE on a
444 # URI that is cache invalidates that cache.
445 uri = urlparse.urljoin(base, "304/test_etag.txt")
446
jcgregorio36140b52006-06-13 02:17:52 +0000447 (response, content) = self.http.request(uri, "GET")
448 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000449 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000450 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000451 self.assertEqual(response.status, 405)
452
jcgregorio36140b52006-06-13 02:17:52 +0000453 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000454 self.assertEqual(response.fromcache, False)
455
456 def testUpdateUsesCachedETag(self):
457 # Test that we natively support http://www.w3.org/1999/04/Editing/
458 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
459
jcgregorio36140b52006-06-13 02:17:52 +0000460 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000461 self.assertEqual(response.status, 200)
462 self.assertEqual(response.fromcache, False)
jcgregorio36140b52006-06-13 02:17:52 +0000463 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000464 self.assertEqual(response.status, 200)
465 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000466 (response, content) = self.http.request(uri, "PUT")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000467 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +0000468 (response, content) = self.http.request(uri, "PUT")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000469 self.assertEqual(response.status, 412)
470
471 def testBasicAuth(self):
472 # Test Basic Authentication
473 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000474 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000475 self.assertEqual(response.status, 401)
476
477 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +0000478 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000479 self.assertEqual(response.status, 401)
480
jcgregorio36140b52006-06-13 02:17:52 +0000481 self.http.add_credentials('joe', 'password')
482 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000483 self.assertEqual(response.status, 200)
484
485 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000486 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000487 self.assertEqual(response.status, 200)
488
489 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000490 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +0000491 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000492 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000493 self.assertEqual(response.status, 401)
494
495 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +0000496 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000497 self.assertEqual(response.status, 401)
498
jcgregorio36140b52006-06-13 02:17:52 +0000499 self.http.add_credentials('fred', 'barney')
500 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000501 self.assertEqual(response.status, 200)
502
503 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000504 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000505 self.assertEqual(response.status, 200)
506
507 def testBasicAuthNested(self):
508 # Test Basic Authentication with resources
509 # that are nested
510 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000511 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000512 self.assertEqual(response.status, 401)
513
514 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000515 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000516 self.assertEqual(response.status, 401)
517
jcgregorioadbb4f82006-05-19 15:17:42 +0000518 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +0000519 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +0000520
521 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000522 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000523 self.assertEqual(response.status, 200)
524
525 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000526 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000527 self.assertEqual(response.status, 401)
528
jcgregorio36140b52006-06-13 02:17:52 +0000529 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +0000530
531 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000532 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000533 self.assertEqual(response.status, 200)
534
535 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000536 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000537 self.assertEqual(response.status, 200)
538
539 def testDigestAuth(self):
540 # Test that we support Digest Authentication
541 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +0000542 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000543 self.assertEqual(response.status, 401)
544
jcgregorio36140b52006-06-13 02:17:52 +0000545 self.http.add_credentials('joe', 'password')
546 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000547 self.assertEqual(response.status, 200)
548
549 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000550 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000551
552 def testDigestAuthNextNonceAndNC(self):
553 # Test that if the server sets nextnonce that we reset
554 # the nonce count back to 1
555 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000556 self.http.add_credentials('joe', 'password')
557 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000558 info = httplib2._parse_www_authenticate(response, 'authentication-info')
559 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +0000560 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000561 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
562 self.assertEqual(response.status, 200)
563
564 if info.has_key('nextnonce'):
565 self.assertEqual(info2['nc'], 1)
566
567 def testDigestAuthStale(self):
568 # Test that we can handle a nonce becoming stale
569 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000570 self.http.add_credentials('joe', 'password')
571 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000572 info = httplib2._parse_www_authenticate(response, 'authentication-info')
573 self.assertEqual(response.status, 200)
574
575 time.sleep(3)
576 # Sleep long enough that the nonce becomes stale
577
jcgregorio36140b52006-06-13 02:17:52 +0000578 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000579 self.assertFalse(response.fromcache)
580 self.assertTrue(response._stale_digest)
581 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
582 self.assertEqual(response.status, 200)
583
584 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +0000585 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +0000586
587 def testReflector(self):
588 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000589 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000590 d = self.reflector(content)
591 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
592
jcgregorio36140b52006-06-13 02:17:52 +0000593try:
594 import memcache
595 class HttpTestMemCached(HttpTest):
596 def setUp(self):
597 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +0000598 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +0000599 self.http = httplib2.Http(self.cache)
600 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +0000601 # Not exactly sure why the sleep is needed here, but
602 # if not present then some unit tests that rely on caching
603 # fail. Memcached seems to lose some sets immediately
604 # after a flush_all if the set is to a value that
605 # was previously cached. (Maybe the flush is handled async?)
606 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +0000607 self.http.clear_credentials()
608except:
609 pass
610
611
612
jcgregoriodb8dfc82006-03-31 14:59:46 +0000613# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +0000614
615class HttpPrivateTest(unittest.TestCase):
616
617 def testParseCacheControl(self):
618 # Test that we can parse the Cache-Control header
619 self.assertEqual({}, httplib2._parse_cache_control({}))
620 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
621 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
622 self.assertEqual(cc['no-cache'], 1)
623 self.assertEqual(cc['max-age'], '7200')
624 cc = httplib2._parse_cache_control({'cache-control': ' , '})
625 self.assertEqual(cc[''], 1)
626
627 def testNormalizeHeaders(self):
628 # Test that we normalize headers to lowercase
629 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
630 self.assertTrue(h.has_key('cache-control'))
631 self.assertTrue(h.has_key('other'))
632 self.assertEqual('Stuff', h['other'])
633
634 def testExpirationModelTransparent(self):
635 # Test that no-cache makes our request TRANSPARENT
636 response_headers = {
637 'cache-control': 'max-age=7200'
638 }
639 request_headers = {
640 'cache-control': 'no-cache'
641 }
642 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
643
644 def testExpirationModelNoCacheResponse(self):
645 # The date and expires point to an entry that should be
646 # FRESH, but the no-cache over-rides that.
647 now = time.time()
648 response_headers = {
649 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
650 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
651 'cache-control': 'no-cache'
652 }
653 request_headers = {
654 }
655 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
656
657 def testExpirationModelStaleRequestMustReval(self):
658 # must-revalidate forces STALE
659 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
660
661 def testExpirationModelStaleResponseMustReval(self):
662 # must-revalidate forces STALE
663 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
664
665 def testExpirationModelFresh(self):
666 response_headers = {
667 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
668 'cache-control': 'max-age=2'
669 }
670 request_headers = {
671 }
672 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
673 time.sleep(3)
674 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
675
676 def testExpirationMaxAge0(self):
677 response_headers = {
678 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
679 'cache-control': 'max-age=0'
680 }
681 request_headers = {
682 }
683 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
684
685 def testExpirationModelDateAndExpires(self):
686 now = time.time()
687 response_headers = {
688 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
689 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
690 }
691 request_headers = {
692 }
693 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
694 time.sleep(3)
695 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
696
697 def testExpirationModelDateOnly(self):
698 now = time.time()
699 response_headers = {
700 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
701 }
702 request_headers = {
703 }
704 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
705
706 def testExpirationModelOnlyIfCached(self):
707 response_headers = {
708 }
709 request_headers = {
710 'cache-control': 'only-if-cached',
711 }
712 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
713
714 def testExpirationModelMaxAgeBoth(self):
715 now = time.time()
716 response_headers = {
717 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
718 'cache-control': 'max-age=2'
719 }
720 request_headers = {
721 'cache-control': 'max-age=0'
722 }
723 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
724
725 def testExpirationModelDateAndExpiresMinFresh1(self):
726 now = time.time()
727 response_headers = {
728 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
729 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
730 }
731 request_headers = {
732 'cache-control': 'min-fresh=2'
733 }
734 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
735
736 def testExpirationModelDateAndExpiresMinFresh2(self):
737 now = time.time()
738 response_headers = {
739 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
740 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
741 }
742 request_headers = {
743 'cache-control': 'min-fresh=2'
744 }
745 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
746
747 def testParseWWWAuthenticateEmpty(self):
748 res = httplib2._parse_www_authenticate({})
749 self.assertEqual(len(res.keys()), 0)
750
jcgregoriofd22e432006-04-27 02:00:08 +0000751 def testParseWWWAuthenticate(self):
752 # different uses of spaces around commas
753 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
754 self.assertEqual(len(res.keys()), 1)
755 self.assertEqual(len(res['test'].keys()), 5)
756
757 # tokens with non-alphanum
758 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
759 self.assertEqual(len(res.keys()), 1)
760 self.assertEqual(len(res['t*!%#st'].keys()), 2)
761
762 # quoted string with quoted pairs
763 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
764 self.assertEqual(len(res.keys()), 1)
765 self.assertEqual(res['test']['realm'], 'a "test" realm')
766
767 def testParseWWWAuthenticateStrict(self):
768 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
769 self.testParseWWWAuthenticate();
770 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
771
jcgregorio2d66d4f2006-02-07 05:34:14 +0000772 def testParseWWWAuthenticateBasic(self):
773 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
774 basic = res['basic']
775 self.assertEqual('me', basic['realm'])
776
777 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
778 basic = res['basic']
779 self.assertEqual('me', basic['realm'])
780 self.assertEqual('MD5', basic['algorithm'])
781
782 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
783 basic = res['basic']
784 self.assertEqual('me', basic['realm'])
785 self.assertEqual('MD5', basic['algorithm'])
786
787 def testParseWWWAuthenticateBasic2(self):
788 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
789 basic = res['basic']
790 self.assertEqual('me', basic['realm'])
791 self.assertEqual('fred', basic['other'])
792
793 def testParseWWWAuthenticateBasic3(self):
794 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
795 basic = res['basic']
796 self.assertEqual('me', basic['realm'])
797
798
799 def testParseWWWAuthenticateDigest(self):
800 res = httplib2._parse_www_authenticate({ 'www-authenticate':
801 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
802 digest = res['digest']
803 self.assertEqual('testrealm@host.com', digest['realm'])
804 self.assertEqual('auth,auth-int', digest['qop'])
805
806
807 def testParseWWWAuthenticateMultiple(self):
808 res = httplib2._parse_www_authenticate({ 'www-authenticate':
809 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
810 digest = res['digest']
811 self.assertEqual('testrealm@host.com', digest['realm'])
812 self.assertEqual('auth,auth-int', digest['qop'])
813 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
814 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
815 basic = res['basic']
816 self.assertEqual('me', basic['realm'])
817
818 def testParseWWWAuthenticateMultiple2(self):
819 # Handle an added comma between challenges, which might get thrown in if the challenges were
820 # originally sent in separate www-authenticate headers.
821 res = httplib2._parse_www_authenticate({ 'www-authenticate':
822 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
823 digest = res['digest']
824 self.assertEqual('testrealm@host.com', digest['realm'])
825 self.assertEqual('auth,auth-int', digest['qop'])
826 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
827 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
828 basic = res['basic']
829 self.assertEqual('me', basic['realm'])
830
831 def testParseWWWAuthenticateMultiple3(self):
832 # Handle an added comma between challenges, which might get thrown in if the challenges were
833 # originally sent in separate www-authenticate headers.
834 res = httplib2._parse_www_authenticate({ 'www-authenticate':
835 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
836 digest = res['digest']
837 self.assertEqual('testrealm@host.com', digest['realm'])
838 self.assertEqual('auth,auth-int', digest['qop'])
839 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
840 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
841 basic = res['basic']
842 self.assertEqual('me', basic['realm'])
843 wsse = res['wsse']
844 self.assertEqual('foo', wsse['realm'])
845 self.assertEqual('UsernameToken', wsse['profile'])
846
847 def testParseWWWAuthenticateMultiple4(self):
848 res = httplib2._parse_www_authenticate({ 'www-authenticate':
849 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
850 digest = res['digest']
851 self.assertEqual('test-real.m@host.com', digest['realm'])
852 self.assertEqual('\tauth,auth-int', digest['qop'])
853 self.assertEqual('(*)&^&$%#', digest['nonce'])
854
855 def testParseWWWAuthenticateMoreQuoteCombos(self):
856 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
857 digest = res['digest']
858 self.assertEqual('myrealm', digest['realm'])
859
860 def testDigestObject(self):
861 credentials = ('joe', 'password')
862 host = None
863 request_uri = '/projects/httplib2/test/digest/'
864 headers = {}
865 response = {
866 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
867 }
868 content = ""
869
jcgregorio6cbab7e2006-04-21 20:35:43 +0000870 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000871 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
872 our_request = "Authorization: %s" % headers['Authorization']
873 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"'
874 self.assertEqual(our_request, working_request)
875
876
877 def testDigestObjectStale(self):
878 credentials = ('joe', 'password')
879 host = None
880 request_uri = '/projects/httplib2/test/digest/'
881 headers = {}
882 response = httplib2.Response({ })
883 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
884 response.status = 401
885 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +0000886 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000887 # Returns true to force a retry
888 self.assertTrue( d.response(response, content) )
889
890 def testDigestObjectAuthInfo(self):
891 credentials = ('joe', 'password')
892 host = None
893 request_uri = '/projects/httplib2/test/digest/'
894 headers = {}
895 response = httplib2.Response({ })
896 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
897 response['authentication-info'] = 'nextnonce="fred"'
898 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +0000899 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000900 # Returns true to force a retry
901 self.assertFalse( d.response(response, content) )
902 self.assertEqual('fred', d.challenge['nonce'])
903 self.assertEqual(1, d.challenge['nc'])
904
905 def testWsseAlgorithm(self):
906 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
907 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
908 self.assertEqual(expected, digest)
909
jcgregoriodb8dfc82006-03-31 14:59:46 +0000910 def testEnd2End(self):
911 # one end to end header
912 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
913 end2end = httplib2._get_end2end_headers(response)
914 self.assertTrue('content-type' in end2end)
915 self.assertTrue('te' not in end2end)
916 self.assertTrue('connection' not in end2end)
917
918 # one end to end header that gets eliminated
919 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
920 end2end = httplib2._get_end2end_headers(response)
921 self.assertTrue('content-type' not in end2end)
922 self.assertTrue('te' not in end2end)
923 self.assertTrue('connection' not in end2end)
924
925 # Degenerate case of no headers
926 response = {}
927 end2end = httplib2._get_end2end_headers(response)
928 self.assertEquals(0, len(end2end))
929
930 # Degenerate case of connection referrring to a header not passed in
931 response = {'connection': 'content-type'}
932 end2end = httplib2._get_end2end_headers(response)
933 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000934
935unittest.main()
936