blob: e505150e99bbc02a6928dc9de215aeb26c2df084 [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
Joe Gregoriob6c90c42011-02-11 01:03:22 -050018import StringIO
19import base64
jcgregoriode8238d2007-03-07 19:08:26 +000020import httplib
21import httplib2
22import os
Joe Gregoriob6c90c42011-02-11 01:03:22 -050023import socket
24import sys
jcgregoriode8238d2007-03-07 19:08:26 +000025import time
Joe Gregoriob6c90c42011-02-11 01:03:22 -050026import unittest
27import urlparse
jcgregorio8421f272006-02-14 18:19:51 +000028
Joe Gregorio694a8122011-02-13 21:40:09 -050029
jcgregorio8421f272006-02-14 18:19:51 +000030# Python 2.3 support
31if not hasattr(unittest.TestCase, 'assertTrue'):
32 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
33 unittest.TestCase.assertFalse = unittest.TestCase.failIf
34
jcgregorio2d66d4f2006-02-07 05:34:14 +000035# The test resources base uri
36base = 'http://bitworking.org/projects/httplib2/test/'
37#base = 'http://localhost/projects/httplib2/test/'
jcgregorio90fb4a42006-11-17 16:19:47 +000038cacheDirName = ".cache"
jcgregorio2d66d4f2006-02-07 05:34:14 +000039
jcgregoriode8238d2007-03-07 19:08:26 +000040
41class CredentialsTest(unittest.TestCase):
42 def test(self):
43 c = httplib2.Credentials()
44 c.add("joe", "password")
45 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
46 self.assertEqual(("joe", "password"), list(c.iter(""))[0])
47 c.add("fred", "password2", "wellformedweb.org")
48 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
49 self.assertEqual(1, len(list(c.iter("bitworking.org"))))
50 self.assertEqual(2, len(list(c.iter("wellformedweb.org"))))
51 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
52 c.clear()
53 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
54 c.add("fred", "password2", "wellformedweb.org")
55 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
56 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
57 self.assertEqual(0, len(list(c.iter(""))))
58
59
jcgregorio2d66d4f2006-02-07 05:34:14 +000060class ParserTest(unittest.TestCase):
61 def testFromStd66(self):
62 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
63 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
64 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
65 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
66 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
67 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
68 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
69 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
70
jcgregorio2d66d4f2006-02-07 05:34:14 +000071
jcgregorioa46fe4e2006-11-16 04:13:45 +000072class UrlNormTest(unittest.TestCase):
73 def test(self):
74 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1])
75 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1])
76 self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1])
77 self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1])
78 self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1])
jcgregoriob4e9ab02006-11-17 15:53:15 +000079 self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80"))
jcgregorio132d28e2007-01-23 16:22:53 +000080 try:
81 httplib2.urlnorm("/")
82 self.fail("Non-absolute URIs should raise an exception")
83 except httplib2.RelativeURIError:
84 pass
jcgregorioa46fe4e2006-11-16 04:13:45 +000085
86class UrlSafenameTest(unittest.TestCase):
87 def test(self):
88 # Test that different URIs end up generating different safe names
89 self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b"))
90 self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b"))
91 self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b"))
92 self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1]))
93 self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b"))
94 self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www"))
95 # Test the max length limits
96 uri = "http://" + ("w" * 200) + ".org"
97 uri2 = "http://" + ("w" * 201) + ".org"
98 self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri))
99 # Max length should be 200 + 1 (",") + 32
100 self.assertEqual(233, len(httplib2.safename(uri2)))
101 self.assertEqual(233, len(httplib2.safename(uri)))
102 # Unicode
jcgregoriodebceec2006-12-12 20:26:02 +0000103 if sys.version_info >= (2,3):
104 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 +0000105
jcgregorio14644372007-07-30 14:13:37 +0000106class _MyResponse(StringIO.StringIO):
107 def __init__(self, body, **kwargs):
108 StringIO.StringIO.__init__(self, body)
109 self.headers = kwargs
110
111 def iteritems(self):
112 return self.headers.iteritems()
113
114
115class _MyHTTPConnection(object):
116 "This class is just a mock of httplib.HTTPConnection used for testing"
117
118 def __init__(self, host, port=None, key_file=None, cert_file=None,
joe.gregoriof28536d2007-10-23 14:10:11 +0000119 strict=None, timeout=None, proxy_info=None):
jcgregorio14644372007-07-30 14:13:37 +0000120 self.host = host
121 self.port = port
122 self.timeout = timeout
123 self.log = ""
124
125 def set_debuglevel(self, level):
126 pass
127
128 def connect(self):
129 "Connect to a host on a given port."
130 pass
131
132 def close(self):
133 pass
134
135 def request(self, method, request_uri, body, headers):
136 pass
137
138 def getresponse(self):
139 return _MyResponse("the body", status="200")
jcgregorioa46fe4e2006-11-16 04:13:45 +0000140
jcgregorio90fb4a42006-11-17 16:19:47 +0000141
jcgregorio2d66d4f2006-02-07 05:34:14 +0000142class HttpTest(unittest.TestCase):
143 def setUp(self):
jcgregorio7e3608f2006-06-15 13:01:53 +0000144 if os.path.exists(cacheDirName):
145 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
146 self.http = httplib2.Http(cacheDirName)
jcgregorio36140b52006-06-13 02:17:52 +0000147 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +0000148
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500149 def testIPv6NoSSL(self):
150 try:
151 self.http.request("http://[::1]/")
152 except socket.gaierror:
153 self.fail("should get the address family right for IPv6")
154 except socket.error:
155 # Even if IPv6 isn't installed on a machine it should just raise socket.error
156 pass
157
158 def testIPv6SSL(self):
159 try:
160 self.http.request("https://[::1]/")
161 except socket.gaierror:
162 self.fail("should get the address family right for IPv6")
163 except socket.error:
164 # Even if IPv6 isn't installed on a machine it should just raise socket.error
165 pass
166
jcgregorio14644372007-07-30 14:13:37 +0000167 def testConnectionType(self):
joe.gregoriof28536d2007-10-23 14:10:11 +0000168 self.http.force_exception_to_status_code = False
jcgregorio14644372007-07-30 14:13:37 +0000169 response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection)
170 self.assertEqual(response['content-location'], "http://bitworking.org")
171 self.assertEqual(content, "the body")
172
jcgregorio6a638172007-01-23 16:40:23 +0000173 def testGetUnknownServer(self):
jcgregorio07a9a4a2007-03-08 21:18:39 +0000174 self.http.force_exception_to_status_code = False
jcgregorio6a638172007-01-23 16:40:23 +0000175 try:
176 self.http.request("http://fred.bitworking.org/")
177 self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.")
178 except httplib2.ServerNotFoundError:
179 pass
180
jcgregorio07a9a4a2007-03-08 21:18:39 +0000181 # Now test with exceptions turned off
182 self.http.force_exception_to_status_code = True
183
184 (response, content) = self.http.request("http://fred.bitworking.org/")
185 self.assertEqual(response['content-type'], 'text/plain')
186 self.assertTrue(content.startswith("Unable to find"))
187 self.assertEqual(response.status, 400)
188
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500189 def testGetConnectionRefused(self):
190 self.http.force_exception_to_status_code = False
191 try:
192 self.http.request("http://localhost:7777/")
193 self.fail("An socket.error exception must be thrown on Connection Refused.")
194 except socket.error:
195 pass
196
197 # Now test with exceptions turned off
198 self.http.force_exception_to_status_code = True
199
200 (response, content) = self.http.request("http://localhost:7777/")
201 self.assertEqual(response['content-type'], 'text/plain')
202 self.assertTrue("Connection refused" in content)
203 self.assertEqual(response.status, 400)
204
jcgregorioa898f8f2006-12-12 17:16:55 +0000205 def testGetIRI(self):
jcgregoriodebceec2006-12-12 20:26:02 +0000206 if sys.version_info >= (2,3):
207 uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}")
208 (response, content) = self.http.request(uri, "GET")
209 d = self.reflector(content)
210 self.assertTrue(d.has_key('QUERY_STRING'))
211 self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0)
jcgregorioa898f8f2006-12-12 17:16:55 +0000212
jcgregorio2d66d4f2006-02-07 05:34:14 +0000213 def testGetIsDefaultMethod(self):
214 # Test that GET is the default method
215 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000216 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000217 self.assertEqual(response['x-method'], "GET")
218
219 def testDifferentMethods(self):
220 # Test that all methods can be used
221 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
222 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +0000223 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000224 self.assertEqual(response['x-method'], method)
225
Joe Gregoriob628c0b2009-07-16 12:28:04 -0400226 def testHeadRead(self):
227 # Test that we don't try to read the response of a HEAD request
228 # since httplib blocks response.read() for HEAD requests.
229 # Oddly enough this doesn't appear as a problem when doing HEAD requests
230 # against Apache servers.
231 uri = "http://www.google.com/"
232 (response, content) = self.http.request(uri, "HEAD")
233 self.assertEqual(response.status, 200)
234 self.assertEqual(content, "")
235
jcgregorio2d66d4f2006-02-07 05:34:14 +0000236 def testGetNoCache(self):
237 # Test that can do a GET w/o the cache turned on.
238 http = httplib2.Http()
239 uri = urlparse.urljoin(base, "304/test_etag.txt")
240 (response, content) = http.request(uri, "GET")
241 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +0000242 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000243
Joe Gregorioe202d212009-07-16 14:57:52 -0400244 def testGetOnlyIfCachedCacheHit(self):
245 # Test that can do a GET with cache and 'only-if-cached'
246 uri = urlparse.urljoin(base, "304/test_etag.txt")
247 (response, content) = self.http.request(uri, "GET")
248 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
249 self.assertEqual(response.fromcache, True)
250 self.assertEqual(response.status, 200)
251
jcgregorioe4ce13e2006-04-02 03:05:08 +0000252 def testGetOnlyIfCachedCacheMiss(self):
253 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorioe4ce13e2006-04-02 03:05:08 +0000254 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorioe202d212009-07-16 14:57:52 -0400255 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
jcgregorioe4ce13e2006-04-02 03:05:08 +0000256 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400257 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000258
259 def testGetOnlyIfCachedNoCacheAtAll(self):
260 # Test that can do a GET with no cache with 'only-if-cached'
261 # Of course, there might be an intermediary beyond us
262 # that responds to the 'only-if-cached', so this
263 # test can't really be guaranteed to pass.
264 http = httplib2.Http()
265 uri = urlparse.urljoin(base, "304/test_etag.txt")
266 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
267 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400268 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000269
jcgregorio2d66d4f2006-02-07 05:34:14 +0000270 def testUserAgent(self):
271 # Test that we provide a default user-agent
272 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000273 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000274 self.assertEqual(response.status, 200)
275 self.assertTrue(content.startswith("Python-httplib2/"))
276
277 def testUserAgentNonDefault(self):
278 # Test that the default user-agent can be over-ridden
joe.gregoriof28536d2007-10-23 14:10:11 +0000279
jcgregorio2d66d4f2006-02-07 05:34:14 +0000280 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000281 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000282 self.assertEqual(response.status, 200)
283 self.assertTrue(content.startswith("fred/1.0"))
284
285 def testGet300WithLocation(self):
286 # Test the we automatically follow 300 redirects if a Location: header is provided
287 uri = urlparse.urljoin(base, "300/with-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000288 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000289 self.assertEqual(response.status, 200)
290 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000291 self.assertEqual(response.previous.status, 300)
292 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000293
294 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000295 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000296 self.assertEqual(response.status, 200)
297 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000298 self.assertEqual(response.previous.status, 300)
299 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000300
jcgregorio2f1e1422007-05-03 13:17:33 +0000301 def testGet300WithLocationNoRedirect(self):
302 # Test the we automatically follow 300 redirects if a Location: header is provided
303 self.http.follow_redirects = False
304 uri = urlparse.urljoin(base, "300/with-location-header.asis")
305 (response, content) = self.http.request(uri, "GET")
306 self.assertEqual(response.status, 300)
307
jcgregorio2d66d4f2006-02-07 05:34:14 +0000308 def testGet300WithoutLocation(self):
309 # Not giving a Location: header in a 300 response is acceptable
310 # In which case we just return the 300 response
311 uri = urlparse.urljoin(base, "300/without-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000312 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000313 self.assertEqual(response.status, 300)
314 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000315 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000316
317 def testGet301(self):
318 # Test that we automatically follow 301 redirects
319 # and that we cache the 301 response
320 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000321 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000322 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000323 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000324 self.assertTrue(response.has_key('content-location'))
325 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000326 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000327 self.assertEqual(response.previous.status, 301)
328 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000329
jcgregorio36140b52006-06-13 02:17:52 +0000330 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000331 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000332 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000333 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000334 self.assertEqual(response.previous.status, 301)
335 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000336
Joe Gregorio694a8122011-02-13 21:40:09 -0500337 def testHead301(self):
338 # Test that we automatically follow 301 redirects
339 uri = urlparse.urljoin(base, "301/onestep.asis")
340 destination = urlparse.urljoin(base, "302/final-destination.txt")
341 (response, content) = self.http.request(uri, "HEAD")
342 self.assertEqual(response.status, 200)
343 self.assertEqual(response.previous.status, 301)
344 self.assertEqual(response.previous.fromcache, False)
jcgregorio2f1e1422007-05-03 13:17:33 +0000345
346 def testGet301NoRedirect(self):
347 # Test that we automatically follow 301 redirects
348 # and that we cache the 301 response
349 self.http.follow_redirects = False
350 uri = urlparse.urljoin(base, "301/onestep.asis")
351 destination = urlparse.urljoin(base, "302/final-destination.txt")
352 (response, content) = self.http.request(uri, "GET")
353 self.assertEqual(response.status, 301)
354
355
jcgregorio2d66d4f2006-02-07 05:34:14 +0000356 def testGet302(self):
357 # Test that we automatically follow 302 redirects
358 # and that we DO NOT cache the 302 response
359 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000360 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000361 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000362 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000363 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000364 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000365 self.assertEqual(response.previous.status, 302)
366 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000367
368 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000369 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000370 self.assertEqual(response.status, 200)
371 self.assertEqual(response.fromcache, True)
jcgregorio772adc82006-11-17 21:52:34 +0000372 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000373 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000374 self.assertEqual(response.previous.status, 302)
375 self.assertEqual(response.previous.fromcache, False)
jcgregorio772adc82006-11-17 21:52:34 +0000376 self.assertEqual(response.previous['content-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000377
378 uri = urlparse.urljoin(base, "302/twostep.asis")
379
jcgregorio36140b52006-06-13 02:17:52 +0000380 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000381 self.assertEqual(response.status, 200)
382 self.assertEqual(response.fromcache, True)
383 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000384 self.assertEqual(response.previous.status, 302)
385 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000386
387 def testGet302RedirectionLimit(self):
388 # Test that we can set a lower redirection limit
389 # and that we raise an exception when we exceed
390 # that limit.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000391 self.http.force_exception_to_status_code = False
392
jcgregorio2d66d4f2006-02-07 05:34:14 +0000393 uri = urlparse.urljoin(base, "302/twostep.asis")
394 try:
jcgregorio36140b52006-06-13 02:17:52 +0000395 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000396 self.fail("This should not happen")
397 except httplib2.RedirectLimit:
398 pass
399 except Exception, e:
400 self.fail("Threw wrong kind of exception ")
401
jcgregorio07a9a4a2007-03-08 21:18:39 +0000402 # Re-run the test with out the exceptions
403 self.http.force_exception_to_status_code = True
404
405 (response, content) = self.http.request(uri, "GET", redirections = 1)
406 self.assertEqual(response.status, 500)
407 self.assertTrue(response.reason.startswith("Redirected more"))
408 self.assertEqual("302", response['status'])
409 self.assertTrue(content.startswith("<html>"))
410 self.assertTrue(response.previous != None)
411
jcgregorio2d66d4f2006-02-07 05:34:14 +0000412 def testGet302NoLocation(self):
413 # Test that we throw an exception when we get
414 # a 302 with no Location: header.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000415 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000416 uri = urlparse.urljoin(base, "302/no-location.asis")
417 try:
jcgregorio36140b52006-06-13 02:17:52 +0000418 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000419 self.fail("Should never reach here")
420 except httplib2.RedirectMissingLocation:
421 pass
422 except Exception, e:
423 self.fail("Threw wrong kind of exception ")
424
jcgregorio07a9a4a2007-03-08 21:18:39 +0000425 # Re-run the test with out the exceptions
426 self.http.force_exception_to_status_code = True
427
428 (response, content) = self.http.request(uri, "GET")
429 self.assertEqual(response.status, 500)
430 self.assertTrue(response.reason.startswith("Redirected but"))
431 self.assertEqual("302", response['status'])
432 self.assertTrue(content.startswith("This is content"))
Joe Gregorio84e33252011-05-03 09:09:13 -0400433
jcgregorio2d66d4f2006-02-07 05:34:14 +0000434 def testGet302ViaHttps(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000435 # Google always redirects to http://google.com
Joe Gregorio84e33252011-05-03 09:09:13 -0400436 (response, content) = self.http.request("https://www.google.com", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000437 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000438 self.assertEqual(302, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000439
440 def testGetViaHttps(self):
441 # Test that we can handle HTTPS
jcgregorio36140b52006-06-13 02:17:52 +0000442 (response, content) = self.http.request("https://google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000443 self.assertEqual(200, response.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000444
445 def testGetViaHttpsSpecViolationOnLocation(self):
446 # Test that we follow redirects through HTTPS
447 # even if they violate the spec by including
448 # a relative Location: header instead of an
449 # absolute one.
jcgregorio36140b52006-06-13 02:17:52 +0000450 (response, content) = self.http.request("https://google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000451 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000452 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000453
jcgregoriode8238d2007-03-07 19:08:26 +0000454
455 def testGetViaHttpsKeyCert(self):
jcgregorio2f1e1422007-05-03 13:17:33 +0000456 # At this point I can only test
457 # that the key and cert files are passed in
458 # correctly to httplib. It would be nice to have
459 # a real https endpoint to test against.
460 http = httplib2.Http(timeout=2)
jcgregoriode8238d2007-03-07 19:08:26 +0000461
462 http.add_certificate("akeyfile", "acertfile", "bitworking.org")
463 try:
464 (response, content) = http.request("https://bitworking.org", "GET")
465 except:
466 pass
467 self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
468 self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
469
jcgregorio2f1e1422007-05-03 13:17:33 +0000470 try:
471 (response, content) = http.request("https://notthere.bitworking.org", "GET")
472 except:
473 pass
474 self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
475 self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
476
477
478
jcgregoriode8238d2007-03-07 19:08:26 +0000479
jcgregorio2d66d4f2006-02-07 05:34:14 +0000480 def testGet303(self):
481 # Do a follow-up GET on a Location: header
482 # returned from a POST that gave a 303.
483 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000484 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000485 self.assertEqual(response.status, 200)
486 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000487 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000488
jcgregorio2f1e1422007-05-03 13:17:33 +0000489 def testGet303NoRedirect(self):
490 # Do a follow-up GET on a Location: header
491 # returned from a POST that gave a 303.
492 self.http.follow_redirects = False
493 uri = urlparse.urljoin(base, "303/303.cgi")
494 (response, content) = self.http.request(uri, "POST", " ")
495 self.assertEqual(response.status, 303)
496
jcgregorio2d66d4f2006-02-07 05:34:14 +0000497 def test303ForDifferentMethods(self):
498 # Test that all methods can be used
499 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000500 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000501 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000502 self.assertEqual(response['x-method'], method_on_303)
503
504 def testGet304(self):
505 # Test that we use ETags properly to validate our cache
506 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000507 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000508 self.assertNotEqual(response['etag'], "")
509
jcgregorio36140b52006-06-13 02:17:52 +0000510 (response, content) = self.http.request(uri, "GET")
511 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000512 self.assertEqual(response.status, 200)
513 self.assertEqual(response.fromcache, True)
514
jcgregorio90fb4a42006-11-17 16:19:47 +0000515 cache_file_name = os.path.join(cacheDirName, httplib2.safename(httplib2.urlnorm(uri)[-1]))
516 f = open(cache_file_name, "r")
517 status_line = f.readline()
518 f.close()
519
520 self.assertTrue(status_line.startswith("status:"))
521
jcgregorio36140b52006-06-13 02:17:52 +0000522 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000523 self.assertEqual(response.status, 200)
524 self.assertEqual(response.fromcache, True)
525
jcgregorio36140b52006-06-13 02:17:52 +0000526 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000527 self.assertEqual(response.status, 206)
528 self.assertEqual(response.fromcache, False)
529
jcgregorio25185622006-10-28 05:12:34 +0000530 def testGetIgnoreEtag(self):
531 # Test that we can forcibly ignore ETags
532 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
533 (response, content) = self.http.request(uri, "GET")
534 self.assertNotEqual(response['etag'], "")
535
536 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
537 d = self.reflector(content)
538 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
539
540 self.http.ignore_etag = True
541 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
542 d = self.reflector(content)
543 self.assertEqual(response.fromcache, False)
544 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
545
jcgregorio4b145e82007-01-18 19:46:34 +0000546 def testOverrideEtag(self):
547 # Test that we can forcibly ignore ETags
548 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
549 (response, content) = self.http.request(uri, "GET")
550 self.assertNotEqual(response['etag'], "")
551
552 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
553 d = self.reflector(content)
554 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
555 self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred")
556
pilgrim00a352e2009-05-29 04:04:44 +0000557 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000558 d = self.reflector(content)
559 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
560 self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio25185622006-10-28 05:12:34 +0000561
pilgrim00a352e2009-05-29 04:04:44 +0000562#MAP-commented this out because it consistently fails
563# def testGet304EndToEnd(self):
564# # Test that end to end headers get overwritten in the cache
565# uri = urlparse.urljoin(base, "304/end2end.cgi")
566# (response, content) = self.http.request(uri, "GET")
567# self.assertNotEqual(response['etag'], "")
568# old_date = response['date']
569# time.sleep(2)
570#
571# (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
572# # The response should be from the cache, but the Date: header should be updated.
573# new_date = response['date']
574# self.assertNotEqual(new_date, old_date)
575# self.assertEqual(response.status, 200)
576# self.assertEqual(response.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000577
578 def testGet304LastModified(self):
579 # Test that we can still handle a 304
580 # by only using the last-modified cache validator.
581 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000582 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000583
584 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000585 (response, content) = self.http.request(uri, "GET")
586 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000587 self.assertEqual(response.status, 200)
588 self.assertEqual(response.fromcache, True)
589
590 def testGet307(self):
591 # Test that we do follow 307 redirects but
592 # do not cache the 307
593 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000594 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000595 self.assertEqual(response.status, 200)
596 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000597 self.assertEqual(response.previous.status, 307)
598 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000599
jcgregorio36140b52006-06-13 02:17:52 +0000600 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000601 self.assertEqual(response.status, 200)
602 self.assertEqual(response.fromcache, True)
603 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000604 self.assertEqual(response.previous.status, 307)
605 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000606
607 def testGet410(self):
608 # Test that we pass 410's through
609 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000610 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000611 self.assertEqual(response.status, 410)
612
chris dent89f15142009-12-24 14:02:57 -0600613 def testVaryHeaderSimple(self):
614 """
615 RFC 2616 13.6
616 When the cache receives a subsequent request whose Request-URI
617 specifies one or more cache entries including a Vary header field,
618 the cache MUST NOT use such a cache entry to construct a response
619 to the new request unless all of the selecting request-headers
620 present in the new request match the corresponding stored
621 request-headers in the original request.
622 """
623 # test that the vary header is sent
624 uri = urlparse.urljoin(base, "vary/accept.asis")
625 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
626 self.assertEqual(response.status, 200)
627 self.assertTrue(response.has_key('vary'))
628
629 # get the resource again, from the cache since accept header in this
630 # request is the same as the request
631 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
632 self.assertEqual(response.status, 200)
633 self.assertEqual(response.fromcache, True, msg="Should be from cache")
634
635 # get the resource again, not from cache since Accept headers does not match
636 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
637 self.assertEqual(response.status, 200)
638 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
639
640 # get the resource again, without any Accept header, so again no match
641 (response, content) = self.http.request(uri, "GET")
642 self.assertEqual(response.status, 200)
643 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
644
645 def testNoVary(self):
646 # when there is no vary, a different Accept header (e.g.) should not
647 # impact if the cache is used
648 # test that the vary header is not sent
649 uri = urlparse.urljoin(base, "vary/no-vary.asis")
650 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
651 self.assertEqual(response.status, 200)
652 self.assertFalse(response.has_key('vary'))
653
654 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
655 self.assertEqual(response.status, 200)
656 self.assertEqual(response.fromcache, True, msg="Should be from cache")
657
658 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
659 self.assertEqual(response.status, 200)
660 self.assertEqual(response.fromcache, True, msg="Should be from cache")
661
662 def testVaryHeaderDouble(self):
663 uri = urlparse.urljoin(base, "vary/accept-double.asis")
664 (response, content) = self.http.request(uri, "GET", headers={
665 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
666 self.assertEqual(response.status, 200)
667 self.assertTrue(response.has_key('vary'))
668
669 # we are from cache
670 (response, content) = self.http.request(uri, "GET", headers={
671 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
672 self.assertEqual(response.fromcache, True, msg="Should be from cache")
673
674 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
675 self.assertEqual(response.status, 200)
676 self.assertEqual(response.fromcache, False)
677
678 # get the resource again, not from cache, varied headers don't match exact
679 (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
680 self.assertEqual(response.status, 200)
681 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
682
jcgregorio88ef89b2010-05-13 23:42:11 -0400683 def testVaryUnusedHeader(self):
684 # A header's value is not considered to vary if it's not used at all.
685 uri = urlparse.urljoin(base, "vary/unused-header.asis")
686 (response, content) = self.http.request(uri, "GET", headers={
687 'Accept': 'text/plain'})
688 self.assertEqual(response.status, 200)
689 self.assertTrue(response.has_key('vary'))
690
691 # we are from cache
692 (response, content) = self.http.request(uri, "GET", headers={
693 'Accept': 'text/plain',})
694 self.assertEqual(response.fromcache, True, msg="Should be from cache")
695
chris dent89f15142009-12-24 14:02:57 -0600696
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000697 def testHeadGZip(self):
698 # Test that we don't try to decompress a HEAD response
699 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
700 (response, content) = self.http.request(uri, "HEAD")
701 self.assertEqual(response.status, 200)
702 self.assertNotEqual(int(response['content-length']), 0)
703 self.assertEqual(content, "")
704
jcgregorio2d66d4f2006-02-07 05:34:14 +0000705 def testGetGZip(self):
706 # Test that we support gzip compression
707 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000708 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000709 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000710 self.assertFalse(response.has_key('content-encoding'))
joe.gregorio8b6d2312007-12-16 05:42:07 +0000711 self.assertTrue(response.has_key('-content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000712 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000713 self.assertEqual(content, "This is the final destination.\n")
714
Joe Gregoriod1137c52011-02-13 19:27:35 -0500715 def testPostAndGZipResponse(self):
716 uri = urlparse.urljoin(base, "gzip/post.cgi")
717 (response, content) = self.http.request(uri, "POST", body=" ")
718 self.assertEqual(response.status, 200)
719 self.assertFalse(response.has_key('content-encoding'))
720 self.assertTrue(response.has_key('-content-encoding'))
721
jcgregorio2d66d4f2006-02-07 05:34:14 +0000722 def testGetGZipFailure(self):
723 # Test that we raise a good exception when the gzip fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000724 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000725 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
726 try:
jcgregorio36140b52006-06-13 02:17:52 +0000727 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000728 self.fail("Should never reach here")
729 except httplib2.FailedToDecompressContent:
730 pass
731 except Exception:
732 self.fail("Threw wrong kind of exception")
733
jcgregorio07a9a4a2007-03-08 21:18:39 +0000734 # Re-run the test with out the exceptions
735 self.http.force_exception_to_status_code = True
736
737 (response, content) = self.http.request(uri, "GET")
738 self.assertEqual(response.status, 500)
739 self.assertTrue(response.reason.startswith("Content purported"))
740
741 def testTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000742 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000743 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
744 try:
745 import socket
746 socket.setdefaulttimeout(1)
747 except:
748 # Don't run the test if we can't set the timeout
749 return
750 (response, content) = self.http.request(uri)
751 self.assertEqual(response.status, 408)
752 self.assertTrue(response.reason.startswith("Request Timeout"))
753 self.assertTrue(content.startswith("Request Timeout"))
754
jcgregoriob2697912007-03-09 02:23:47 +0000755 def testIndividualTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000756 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
757 http = httplib2.Http(timeout=1)
joe.gregoriof28536d2007-10-23 14:10:11 +0000758 http.force_exception_to_status_code = True
jcgregoriob2697912007-03-09 02:23:47 +0000759
760 (response, content) = http.request(uri)
761 self.assertEqual(response.status, 408)
762 self.assertTrue(response.reason.startswith("Request Timeout"))
763 self.assertTrue(content.startswith("Request Timeout"))
764
jcgregorio07a9a4a2007-03-08 21:18:39 +0000765
Joe Gregorio1a7609f2009-07-16 10:59:44 -0400766 def testHTTPSInitTimeout(self):
767 c = httplib2.HTTPSConnectionWithTimeout('localhost', 80, timeout=47)
768 self.assertEqual(47, c.timeout)
769
jcgregorio2d66d4f2006-02-07 05:34:14 +0000770 def testGetDeflate(self):
771 # Test that we support deflate compression
772 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000773 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000774 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000775 self.assertFalse(response.has_key('content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000776 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000777 self.assertEqual(content, "This is the final destination.")
778
779 def testGetDeflateFailure(self):
780 # Test that we raise a good exception when the deflate fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000781 self.http.force_exception_to_status_code = False
782
jcgregorio2d66d4f2006-02-07 05:34:14 +0000783 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
784 try:
jcgregorio36140b52006-06-13 02:17:52 +0000785 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000786 self.fail("Should never reach here")
787 except httplib2.FailedToDecompressContent:
788 pass
789 except Exception:
790 self.fail("Threw wrong kind of exception")
791
jcgregorio07a9a4a2007-03-08 21:18:39 +0000792 # Re-run the test with out the exceptions
793 self.http.force_exception_to_status_code = True
794
795 (response, content) = self.http.request(uri, "GET")
796 self.assertEqual(response.status, 500)
797 self.assertTrue(response.reason.startswith("Content purported"))
798
jcgregorio2d66d4f2006-02-07 05:34:14 +0000799 def testGetDuplicateHeaders(self):
800 # Test that duplicate headers get concatenated via ','
801 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
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(content, "This is content\n")
805 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
806
807 def testGetCacheControlNoCache(self):
808 # Test Cache-Control: no-cache on requests
809 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000810 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000811 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000812 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000813 self.assertEqual(response.status, 200)
814 self.assertEqual(response.fromcache, True)
815
jcgregorio36140b52006-06-13 02:17:52 +0000816 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000817 self.assertEqual(response.status, 200)
818 self.assertEqual(response.fromcache, False)
819
820 def testGetCacheControlPragmaNoCache(self):
821 # Test Pragma: no-cache on requests
822 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000823 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000824 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000825 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000826 self.assertEqual(response.status, 200)
827 self.assertEqual(response.fromcache, True)
828
jcgregorio36140b52006-06-13 02:17:52 +0000829 (response, content) = self.http.request(uri, "GET", headers={'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000830 self.assertEqual(response.status, 200)
831 self.assertEqual(response.fromcache, False)
832
833 def testGetCacheControlNoStoreRequest(self):
834 # A no-store request means that the response should not be stored.
835 uri = urlparse.urljoin(base, "304/test_etag.txt")
836
jcgregorio36140b52006-06-13 02:17:52 +0000837 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000838 self.assertEqual(response.status, 200)
839 self.assertEqual(response.fromcache, False)
840
jcgregorio36140b52006-06-13 02:17:52 +0000841 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000842 self.assertEqual(response.status, 200)
843 self.assertEqual(response.fromcache, False)
844
845 def testGetCacheControlNoStoreResponse(self):
846 # A no-store response means that the response should not be stored.
847 uri = urlparse.urljoin(base, "no-store/no-store.asis")
848
jcgregorio36140b52006-06-13 02:17:52 +0000849 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000850 self.assertEqual(response.status, 200)
851 self.assertEqual(response.fromcache, False)
852
jcgregorio36140b52006-06-13 02:17:52 +0000853 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000854 self.assertEqual(response.status, 200)
855 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000856
857 def testGetCacheControlNoCacheNoStoreRequest(self):
858 # Test that a no-store, no-cache clears the entry from the cache
859 # even if it was cached previously.
860 uri = urlparse.urljoin(base, "304/test_etag.txt")
861
jcgregorio36140b52006-06-13 02:17:52 +0000862 (response, content) = self.http.request(uri, "GET")
863 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000864 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000865 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
866 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000867 self.assertEqual(response.status, 200)
868 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000869
870 def testUpdateInvalidatesCache(self):
871 # Test that calling PUT or DELETE on a
872 # URI that is cache invalidates that cache.
873 uri = urlparse.urljoin(base, "304/test_etag.txt")
874
jcgregorio36140b52006-06-13 02:17:52 +0000875 (response, content) = self.http.request(uri, "GET")
876 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000877 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000878 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000879 self.assertEqual(response.status, 405)
880
jcgregorio36140b52006-06-13 02:17:52 +0000881 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000882 self.assertEqual(response.fromcache, False)
883
884 def testUpdateUsesCachedETag(self):
Joe Gregoriobd682082011-05-24 14:06:09 -0400885 # Test that we natively support http://www.w3.org/1999/04/Editing/
jcgregorio2d66d4f2006-02-07 05:34:14 +0000886 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
887
jcgregorio36140b52006-06-13 02:17:52 +0000888 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000889 self.assertEqual(response.status, 200)
890 self.assertEqual(response.fromcache, False)
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 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400894 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000895 self.assertEqual(response.status, 200)
Joe Gregoriocd868102009-09-29 17:09:16 -0400896 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000897 self.assertEqual(response.status, 412)
898
Joe Gregoriobd682082011-05-24 14:06:09 -0400899 def testUpdatePatchUsesCachedETag(self):
900 # Test that we natively support http://www.w3.org/1999/04/Editing/
901 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
902
903 (response, content) = self.http.request(uri, "GET")
904 self.assertEqual(response.status, 200)
905 self.assertEqual(response.fromcache, False)
906 (response, content) = self.http.request(uri, "GET")
907 self.assertEqual(response.status, 200)
908 self.assertEqual(response.fromcache, True)
909 (response, content) = self.http.request(uri, "PATCH", body="foo")
910 self.assertEqual(response.status, 200)
911 (response, content) = self.http.request(uri, "PATCH", body="foo")
912 self.assertEqual(response.status, 412)
913
914
joe.gregorio700f04d2008-09-06 04:46:32 +0000915 def testUpdateUsesCachedETagAndOCMethod(self):
916 # Test that we natively support http://www.w3.org/1999/04/Editing/
917 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
918
919 (response, content) = self.http.request(uri, "GET")
920 self.assertEqual(response.status, 200)
921 self.assertEqual(response.fromcache, False)
922 (response, content) = self.http.request(uri, "GET")
923 self.assertEqual(response.status, 200)
924 self.assertEqual(response.fromcache, True)
925 self.http.optimistic_concurrency_methods.append("DELETE")
926 (response, content) = self.http.request(uri, "DELETE")
927 self.assertEqual(response.status, 200)
928
929
jcgregorio4b145e82007-01-18 19:46:34 +0000930 def testUpdateUsesCachedETagOverridden(self):
931 # Test that we natively support http://www.w3.org/1999/04/Editing/
932 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
933
934 (response, content) = self.http.request(uri, "GET")
935 self.assertEqual(response.status, 200)
936 self.assertEqual(response.fromcache, False)
937 (response, content) = self.http.request(uri, "GET")
938 self.assertEqual(response.status, 200)
939 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400940 (response, content) = self.http.request(uri, "PUT", body="foo", headers={'if-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000941 self.assertEqual(response.status, 412)
942
jcgregorio2d66d4f2006-02-07 05:34:14 +0000943 def testBasicAuth(self):
944 # Test Basic Authentication
945 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000946 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000947 self.assertEqual(response.status, 401)
948
949 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +0000950 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000951 self.assertEqual(response.status, 401)
952
jcgregorio36140b52006-06-13 02:17:52 +0000953 self.http.add_credentials('joe', 'password')
954 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000955 self.assertEqual(response.status, 200)
956
957 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000958 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000959 self.assertEqual(response.status, 200)
960
jcgregoriode8238d2007-03-07 19:08:26 +0000961 def testBasicAuthWithDomain(self):
962 # Test Basic Authentication
963 uri = urlparse.urljoin(base, "basic/file.txt")
964 (response, content) = self.http.request(uri, "GET")
965 self.assertEqual(response.status, 401)
966
967 uri = urlparse.urljoin(base, "basic/")
968 (response, content) = self.http.request(uri, "GET")
969 self.assertEqual(response.status, 401)
970
971 self.http.add_credentials('joe', 'password', "example.org")
972 (response, content) = self.http.request(uri, "GET")
973 self.assertEqual(response.status, 401)
974
975 uri = urlparse.urljoin(base, "basic/file.txt")
976 (response, content) = self.http.request(uri, "GET")
977 self.assertEqual(response.status, 401)
978
979 domain = urlparse.urlparse(base)[1]
980 self.http.add_credentials('joe', 'password', domain)
981 (response, content) = self.http.request(uri, "GET")
982 self.assertEqual(response.status, 200)
983
984 uri = urlparse.urljoin(base, "basic/file.txt")
985 (response, content) = self.http.request(uri, "GET")
986 self.assertEqual(response.status, 200)
987
988
989
990
991
992
jcgregorio2d66d4f2006-02-07 05:34:14 +0000993 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000994 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +0000995 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000996 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000997 self.assertEqual(response.status, 401)
998
999 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +00001000 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001001 self.assertEqual(response.status, 401)
1002
jcgregorio36140b52006-06-13 02:17:52 +00001003 self.http.add_credentials('fred', 'barney')
1004 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001005 self.assertEqual(response.status, 200)
1006
1007 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001008 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001009 self.assertEqual(response.status, 200)
1010
1011 def testBasicAuthNested(self):
1012 # Test Basic Authentication with resources
1013 # that are nested
1014 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001015 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001016 self.assertEqual(response.status, 401)
1017
1018 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001019 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001020 self.assertEqual(response.status, 401)
1021
jcgregorioadbb4f82006-05-19 15:17:42 +00001022 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +00001023 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001024
1025 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001026 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001027 self.assertEqual(response.status, 200)
1028
1029 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001030 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001031 self.assertEqual(response.status, 401)
1032
jcgregorio36140b52006-06-13 02:17:52 +00001033 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001034
1035 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001036 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001037 self.assertEqual(response.status, 200)
1038
1039 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001040 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001041 self.assertEqual(response.status, 200)
1042
1043 def testDigestAuth(self):
1044 # Test that we support Digest Authentication
1045 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +00001046 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001047 self.assertEqual(response.status, 401)
1048
jcgregorio36140b52006-06-13 02:17:52 +00001049 self.http.add_credentials('joe', 'password')
1050 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001051 self.assertEqual(response.status, 200)
1052
1053 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001054 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001055
1056 def testDigestAuthNextNonceAndNC(self):
1057 # Test that if the server sets nextnonce that we reset
1058 # the nonce count back to 1
1059 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001060 self.http.add_credentials('joe', 'password')
1061 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001062 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1063 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +00001064 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001065 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
1066 self.assertEqual(response.status, 200)
1067
1068 if info.has_key('nextnonce'):
1069 self.assertEqual(info2['nc'], 1)
1070
1071 def testDigestAuthStale(self):
1072 # Test that we can handle a nonce becoming stale
1073 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001074 self.http.add_credentials('joe', 'password')
1075 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001076 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1077 self.assertEqual(response.status, 200)
1078
1079 time.sleep(3)
1080 # Sleep long enough that the nonce becomes stale
1081
jcgregorio36140b52006-06-13 02:17:52 +00001082 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001083 self.assertFalse(response.fromcache)
1084 self.assertTrue(response._stale_digest)
1085 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
1086 self.assertEqual(response.status, 200)
1087
1088 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +00001089 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +00001090
1091 def testReflector(self):
1092 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +00001093 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001094 d = self.reflector(content)
1095 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
1096
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001097 def testConnectionClose(self):
1098 uri = "http://www.google.com/"
1099 (response, content) = self.http.request(uri, "GET")
1100 for c in self.http.connections.values():
1101 self.assertNotEqual(None, c.sock)
1102 (response, content) = self.http.request(uri, "GET", headers={"connection": "close"})
1103 for c in self.http.connections.values():
1104 self.assertEqual(None, c.sock)
1105
1106
jcgregorio36140b52006-06-13 02:17:52 +00001107try:
1108 import memcache
1109 class HttpTestMemCached(HttpTest):
1110 def setUp(self):
1111 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +00001112 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +00001113 self.http = httplib2.Http(self.cache)
1114 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +00001115 # Not exactly sure why the sleep is needed here, but
1116 # if not present then some unit tests that rely on caching
1117 # fail. Memcached seems to lose some sets immediately
1118 # after a flush_all if the set is to a value that
1119 # was previously cached. (Maybe the flush is handled async?)
1120 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +00001121 self.http.clear_credentials()
1122except:
1123 pass
1124
1125
1126
chris dent89f15142009-12-24 14:02:57 -06001127
jcgregoriodb8dfc82006-03-31 14:59:46 +00001128# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +00001129
1130class HttpPrivateTest(unittest.TestCase):
1131
1132 def testParseCacheControl(self):
1133 # Test that we can parse the Cache-Control header
1134 self.assertEqual({}, httplib2._parse_cache_control({}))
1135 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
1136 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
1137 self.assertEqual(cc['no-cache'], 1)
1138 self.assertEqual(cc['max-age'], '7200')
1139 cc = httplib2._parse_cache_control({'cache-control': ' , '})
1140 self.assertEqual(cc[''], 1)
1141
Joe Gregorioe314e8b2009-07-16 20:11:28 -04001142 try:
1143 cc = httplib2._parse_cache_control({'cache-control': 'Max-age=3600;post-check=1800,pre-check=3600'})
1144 self.assertTrue("max-age" in cc)
1145 except:
1146 self.fail("Should not throw exception")
1147
jcgregorio2d66d4f2006-02-07 05:34:14 +00001148 def testNormalizeHeaders(self):
1149 # Test that we normalize headers to lowercase
1150 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
1151 self.assertTrue(h.has_key('cache-control'))
1152 self.assertTrue(h.has_key('other'))
1153 self.assertEqual('Stuff', h['other'])
1154
1155 def testExpirationModelTransparent(self):
1156 # Test that no-cache makes our request TRANSPARENT
1157 response_headers = {
1158 'cache-control': 'max-age=7200'
1159 }
1160 request_headers = {
1161 'cache-control': 'no-cache'
1162 }
1163 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
1164
jcgregorio45865012007-01-18 16:38:22 +00001165 def testMaxAgeNonNumeric(self):
1166 # Test that no-cache makes our request TRANSPARENT
1167 response_headers = {
1168 'cache-control': 'max-age=fred, min-fresh=barney'
1169 }
1170 request_headers = {
1171 }
1172 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1173
1174
jcgregorio2d66d4f2006-02-07 05:34:14 +00001175 def testExpirationModelNoCacheResponse(self):
1176 # The date and expires point to an entry that should be
1177 # FRESH, but the no-cache over-rides that.
1178 now = time.time()
1179 response_headers = {
1180 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1181 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1182 'cache-control': 'no-cache'
1183 }
1184 request_headers = {
1185 }
1186 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1187
1188 def testExpirationModelStaleRequestMustReval(self):
1189 # must-revalidate forces STALE
1190 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
1191
1192 def testExpirationModelStaleResponseMustReval(self):
1193 # must-revalidate forces STALE
1194 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
1195
1196 def testExpirationModelFresh(self):
1197 response_headers = {
1198 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1199 'cache-control': 'max-age=2'
1200 }
1201 request_headers = {
1202 }
1203 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1204 time.sleep(3)
1205 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1206
1207 def testExpirationMaxAge0(self):
1208 response_headers = {
1209 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1210 'cache-control': 'max-age=0'
1211 }
1212 request_headers = {
1213 }
1214 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1215
1216 def testExpirationModelDateAndExpires(self):
1217 now = time.time()
1218 response_headers = {
1219 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1220 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1221 }
1222 request_headers = {
1223 }
1224 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1225 time.sleep(3)
1226 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1227
jcgregoriof9511052007-06-01 14:56:34 +00001228 def testExpiresZero(self):
1229 now = time.time()
1230 response_headers = {
1231 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1232 'expires': "0",
1233 }
1234 request_headers = {
1235 }
1236 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1237
jcgregorio2d66d4f2006-02-07 05:34:14 +00001238 def testExpirationModelDateOnly(self):
1239 now = time.time()
1240 response_headers = {
1241 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
1242 }
1243 request_headers = {
1244 }
1245 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1246
1247 def testExpirationModelOnlyIfCached(self):
1248 response_headers = {
1249 }
1250 request_headers = {
1251 'cache-control': 'only-if-cached',
1252 }
1253 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1254
1255 def testExpirationModelMaxAgeBoth(self):
1256 now = time.time()
1257 response_headers = {
1258 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1259 'cache-control': 'max-age=2'
1260 }
1261 request_headers = {
1262 'cache-control': 'max-age=0'
1263 }
1264 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1265
1266 def testExpirationModelDateAndExpiresMinFresh1(self):
1267 now = time.time()
1268 response_headers = {
1269 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1270 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1271 }
1272 request_headers = {
1273 'cache-control': 'min-fresh=2'
1274 }
1275 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1276
1277 def testExpirationModelDateAndExpiresMinFresh2(self):
1278 now = time.time()
1279 response_headers = {
1280 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1281 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1282 }
1283 request_headers = {
1284 'cache-control': 'min-fresh=2'
1285 }
1286 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1287
1288 def testParseWWWAuthenticateEmpty(self):
1289 res = httplib2._parse_www_authenticate({})
1290 self.assertEqual(len(res.keys()), 0)
1291
jcgregoriofd22e432006-04-27 02:00:08 +00001292 def testParseWWWAuthenticate(self):
1293 # different uses of spaces around commas
1294 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
1295 self.assertEqual(len(res.keys()), 1)
1296 self.assertEqual(len(res['test'].keys()), 5)
1297
1298 # tokens with non-alphanum
1299 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
1300 self.assertEqual(len(res.keys()), 1)
1301 self.assertEqual(len(res['t*!%#st'].keys()), 2)
1302
1303 # quoted string with quoted pairs
1304 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
1305 self.assertEqual(len(res.keys()), 1)
1306 self.assertEqual(res['test']['realm'], 'a "test" realm')
1307
1308 def testParseWWWAuthenticateStrict(self):
1309 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
1310 self.testParseWWWAuthenticate();
1311 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
1312
jcgregorio2d66d4f2006-02-07 05:34:14 +00001313 def testParseWWWAuthenticateBasic(self):
1314 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
1315 basic = res['basic']
1316 self.assertEqual('me', basic['realm'])
1317
1318 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
1319 basic = res['basic']
1320 self.assertEqual('me', basic['realm'])
1321 self.assertEqual('MD5', basic['algorithm'])
1322
1323 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
1324 basic = res['basic']
1325 self.assertEqual('me', basic['realm'])
1326 self.assertEqual('MD5', basic['algorithm'])
1327
1328 def testParseWWWAuthenticateBasic2(self):
1329 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
1330 basic = res['basic']
1331 self.assertEqual('me', basic['realm'])
1332 self.assertEqual('fred', basic['other'])
1333
1334 def testParseWWWAuthenticateBasic3(self):
1335 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
1336 basic = res['basic']
1337 self.assertEqual('me', basic['realm'])
1338
1339
1340 def testParseWWWAuthenticateDigest(self):
1341 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1342 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
1343 digest = res['digest']
1344 self.assertEqual('testrealm@host.com', digest['realm'])
1345 self.assertEqual('auth,auth-int', digest['qop'])
1346
1347
1348 def testParseWWWAuthenticateMultiple(self):
1349 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1350 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
1351 digest = res['digest']
1352 self.assertEqual('testrealm@host.com', digest['realm'])
1353 self.assertEqual('auth,auth-int', digest['qop'])
1354 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1355 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1356 basic = res['basic']
1357 self.assertEqual('me', basic['realm'])
1358
1359 def testParseWWWAuthenticateMultiple2(self):
1360 # Handle an added comma between challenges, which might get thrown in if the challenges were
1361 # originally sent in separate www-authenticate headers.
1362 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1363 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
1364 digest = res['digest']
1365 self.assertEqual('testrealm@host.com', digest['realm'])
1366 self.assertEqual('auth,auth-int', digest['qop'])
1367 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1368 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1369 basic = res['basic']
1370 self.assertEqual('me', basic['realm'])
1371
1372 def testParseWWWAuthenticateMultiple3(self):
1373 # Handle an added comma between challenges, which might get thrown in if the challenges were
1374 # originally sent in separate www-authenticate headers.
1375 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1376 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1377 digest = res['digest']
1378 self.assertEqual('testrealm@host.com', digest['realm'])
1379 self.assertEqual('auth,auth-int', digest['qop'])
1380 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1381 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1382 basic = res['basic']
1383 self.assertEqual('me', basic['realm'])
1384 wsse = res['wsse']
1385 self.assertEqual('foo', wsse['realm'])
1386 self.assertEqual('UsernameToken', wsse['profile'])
1387
1388 def testParseWWWAuthenticateMultiple4(self):
1389 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1390 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1391 digest = res['digest']
1392 self.assertEqual('test-real.m@host.com', digest['realm'])
1393 self.assertEqual('\tauth,auth-int', digest['qop'])
1394 self.assertEqual('(*)&^&$%#', digest['nonce'])
1395
1396 def testParseWWWAuthenticateMoreQuoteCombos(self):
1397 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
1398 digest = res['digest']
1399 self.assertEqual('myrealm', digest['realm'])
1400
Joe Gregorio6fa3cf22011-02-13 22:45:06 -05001401 def testParseWWWAuthenticateMalformed(self):
1402 try:
1403 res = httplib2._parse_www_authenticate({'www-authenticate':'OAuth "Facebook Platform" "invalid_token" "Invalid OAuth access token."'})
1404 self.fail("should raise an exception")
1405 except httplib2.MalformedHeader:
1406 pass
1407
jcgregorio2d66d4f2006-02-07 05:34:14 +00001408 def testDigestObject(self):
1409 credentials = ('joe', 'password')
1410 host = None
1411 request_uri = '/projects/httplib2/test/digest/'
1412 headers = {}
1413 response = {
1414 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
1415 }
1416 content = ""
1417
jcgregorio6cbab7e2006-04-21 20:35:43 +00001418 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001419 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
1420 our_request = "Authorization: %s" % headers['Authorization']
1421 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"'
1422 self.assertEqual(our_request, working_request)
1423
1424
1425 def testDigestObjectStale(self):
1426 credentials = ('joe', 'password')
1427 host = None
1428 request_uri = '/projects/httplib2/test/digest/'
1429 headers = {}
1430 response = httplib2.Response({ })
1431 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1432 response.status = 401
1433 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001434 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001435 # Returns true to force a retry
1436 self.assertTrue( d.response(response, content) )
1437
1438 def testDigestObjectAuthInfo(self):
1439 credentials = ('joe', 'password')
1440 host = None
1441 request_uri = '/projects/httplib2/test/digest/'
1442 headers = {}
1443 response = httplib2.Response({ })
1444 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1445 response['authentication-info'] = 'nextnonce="fred"'
1446 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001447 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001448 # Returns true to force a retry
1449 self.assertFalse( d.response(response, content) )
1450 self.assertEqual('fred', d.challenge['nonce'])
1451 self.assertEqual(1, d.challenge['nc'])
1452
1453 def testWsseAlgorithm(self):
1454 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
1455 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
1456 self.assertEqual(expected, digest)
1457
jcgregoriodb8dfc82006-03-31 14:59:46 +00001458 def testEnd2End(self):
1459 # one end to end header
1460 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
1461 end2end = httplib2._get_end2end_headers(response)
1462 self.assertTrue('content-type' in end2end)
1463 self.assertTrue('te' not in end2end)
1464 self.assertTrue('connection' not in end2end)
1465
1466 # one end to end header that gets eliminated
1467 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
1468 end2end = httplib2._get_end2end_headers(response)
1469 self.assertTrue('content-type' not in end2end)
1470 self.assertTrue('te' not in end2end)
1471 self.assertTrue('connection' not in end2end)
1472
1473 # Degenerate case of no headers
1474 response = {}
1475 end2end = httplib2._get_end2end_headers(response)
1476 self.assertEquals(0, len(end2end))
1477
1478 # Degenerate case of connection referrring to a header not passed in
1479 response = {'connection': 'content-type'}
1480 end2end = httplib2._get_end2end_headers(response)
1481 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001482
chris dent89f15142009-12-24 14:02:57 -06001483if __name__ == '__main__':
1484 unittest.main()