blob: 3dcfe03b5b58269336d800eee9d013f23d4648b2 [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
29# Python 2.3 support
30if not hasattr(unittest.TestCase, 'assertTrue'):
31 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
32 unittest.TestCase.assertFalse = unittest.TestCase.failIf
33
jcgregorio2d66d4f2006-02-07 05:34:14 +000034# The test resources base uri
35base = 'http://bitworking.org/projects/httplib2/test/'
36#base = 'http://localhost/projects/httplib2/test/'
jcgregorio90fb4a42006-11-17 16:19:47 +000037cacheDirName = ".cache"
jcgregorio2d66d4f2006-02-07 05:34:14 +000038
jcgregoriode8238d2007-03-07 19:08:26 +000039
40class CredentialsTest(unittest.TestCase):
41 def test(self):
42 c = httplib2.Credentials()
43 c.add("joe", "password")
44 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
45 self.assertEqual(("joe", "password"), list(c.iter(""))[0])
46 c.add("fred", "password2", "wellformedweb.org")
47 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
48 self.assertEqual(1, len(list(c.iter("bitworking.org"))))
49 self.assertEqual(2, len(list(c.iter("wellformedweb.org"))))
50 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
51 c.clear()
52 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
53 c.add("fred", "password2", "wellformedweb.org")
54 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
55 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
56 self.assertEqual(0, len(list(c.iter(""))))
57
58
jcgregorio2d66d4f2006-02-07 05:34:14 +000059class ParserTest(unittest.TestCase):
60 def testFromStd66(self):
61 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
62 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
63 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
64 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
65 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
66 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
67 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
68 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
69
jcgregorio2d66d4f2006-02-07 05:34:14 +000070
jcgregorioa46fe4e2006-11-16 04:13:45 +000071class UrlNormTest(unittest.TestCase):
72 def test(self):
73 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1])
74 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1])
75 self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1])
76 self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1])
77 self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1])
jcgregoriob4e9ab02006-11-17 15:53:15 +000078 self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80"))
jcgregorio132d28e2007-01-23 16:22:53 +000079 try:
80 httplib2.urlnorm("/")
81 self.fail("Non-absolute URIs should raise an exception")
82 except httplib2.RelativeURIError:
83 pass
jcgregorioa46fe4e2006-11-16 04:13:45 +000084
85class UrlSafenameTest(unittest.TestCase):
86 def test(self):
87 # Test that different URIs end up generating different safe names
88 self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b"))
89 self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b"))
90 self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b"))
91 self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1]))
92 self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b"))
93 self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www"))
94 # Test the max length limits
95 uri = "http://" + ("w" * 200) + ".org"
96 uri2 = "http://" + ("w" * 201) + ".org"
97 self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri))
98 # Max length should be 200 + 1 (",") + 32
99 self.assertEqual(233, len(httplib2.safename(uri2)))
100 self.assertEqual(233, len(httplib2.safename(uri)))
101 # Unicode
jcgregoriodebceec2006-12-12 20:26:02 +0000102 if sys.version_info >= (2,3):
103 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 +0000104
jcgregorio14644372007-07-30 14:13:37 +0000105class _MyResponse(StringIO.StringIO):
106 def __init__(self, body, **kwargs):
107 StringIO.StringIO.__init__(self, body)
108 self.headers = kwargs
109
110 def iteritems(self):
111 return self.headers.iteritems()
112
113
114class _MyHTTPConnection(object):
115 "This class is just a mock of httplib.HTTPConnection used for testing"
116
117 def __init__(self, host, port=None, key_file=None, cert_file=None,
joe.gregoriof28536d2007-10-23 14:10:11 +0000118 strict=None, timeout=None, proxy_info=None):
jcgregorio14644372007-07-30 14:13:37 +0000119 self.host = host
120 self.port = port
121 self.timeout = timeout
122 self.log = ""
123
124 def set_debuglevel(self, level):
125 pass
126
127 def connect(self):
128 "Connect to a host on a given port."
129 pass
130
131 def close(self):
132 pass
133
134 def request(self, method, request_uri, body, headers):
135 pass
136
137 def getresponse(self):
138 return _MyResponse("the body", status="200")
jcgregorioa46fe4e2006-11-16 04:13:45 +0000139
jcgregorio90fb4a42006-11-17 16:19:47 +0000140
jcgregorio2d66d4f2006-02-07 05:34:14 +0000141class HttpTest(unittest.TestCase):
142 def setUp(self):
jcgregorio7e3608f2006-06-15 13:01:53 +0000143 if os.path.exists(cacheDirName):
144 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
145 self.http = httplib2.Http(cacheDirName)
jcgregorio36140b52006-06-13 02:17:52 +0000146 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +0000147
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500148 def testIPv6NoSSL(self):
149 try:
150 self.http.request("http://[::1]/")
151 except socket.gaierror:
152 self.fail("should get the address family right for IPv6")
153 except socket.error:
154 # Even if IPv6 isn't installed on a machine it should just raise socket.error
155 pass
156
157 def testIPv6SSL(self):
158 try:
159 self.http.request("https://[::1]/")
160 except socket.gaierror:
161 self.fail("should get the address family right for IPv6")
162 except socket.error:
163 # Even if IPv6 isn't installed on a machine it should just raise socket.error
164 pass
165
jcgregorio14644372007-07-30 14:13:37 +0000166 def testConnectionType(self):
joe.gregoriof28536d2007-10-23 14:10:11 +0000167 self.http.force_exception_to_status_code = False
jcgregorio14644372007-07-30 14:13:37 +0000168 response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection)
169 self.assertEqual(response['content-location'], "http://bitworking.org")
170 self.assertEqual(content, "the body")
171
jcgregorio6a638172007-01-23 16:40:23 +0000172 def testGetUnknownServer(self):
jcgregorio07a9a4a2007-03-08 21:18:39 +0000173 self.http.force_exception_to_status_code = False
jcgregorio6a638172007-01-23 16:40:23 +0000174 try:
175 self.http.request("http://fred.bitworking.org/")
176 self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.")
177 except httplib2.ServerNotFoundError:
178 pass
179
jcgregorio07a9a4a2007-03-08 21:18:39 +0000180 # Now test with exceptions turned off
181 self.http.force_exception_to_status_code = True
182
183 (response, content) = self.http.request("http://fred.bitworking.org/")
184 self.assertEqual(response['content-type'], 'text/plain')
185 self.assertTrue(content.startswith("Unable to find"))
186 self.assertEqual(response.status, 400)
187
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500188 def testGetConnectionRefused(self):
189 self.http.force_exception_to_status_code = False
190 try:
191 self.http.request("http://localhost:7777/")
192 self.fail("An socket.error exception must be thrown on Connection Refused.")
193 except socket.error:
194 pass
195
196 # Now test with exceptions turned off
197 self.http.force_exception_to_status_code = True
198
199 (response, content) = self.http.request("http://localhost:7777/")
200 self.assertEqual(response['content-type'], 'text/plain')
201 self.assertTrue("Connection refused" in content)
202 self.assertEqual(response.status, 400)
203
jcgregorioa898f8f2006-12-12 17:16:55 +0000204 def testGetIRI(self):
jcgregoriodebceec2006-12-12 20:26:02 +0000205 if sys.version_info >= (2,3):
206 uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}")
207 (response, content) = self.http.request(uri, "GET")
208 d = self.reflector(content)
209 self.assertTrue(d.has_key('QUERY_STRING'))
210 self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0)
jcgregorioa898f8f2006-12-12 17:16:55 +0000211
jcgregorio2d66d4f2006-02-07 05:34:14 +0000212 def testGetIsDefaultMethod(self):
213 # Test that GET is the default method
214 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000215 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000216 self.assertEqual(response['x-method'], "GET")
217
218 def testDifferentMethods(self):
219 # Test that all methods can be used
220 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
221 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +0000222 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000223 self.assertEqual(response['x-method'], method)
224
Joe Gregoriob628c0b2009-07-16 12:28:04 -0400225 def testHeadRead(self):
226 # Test that we don't try to read the response of a HEAD request
227 # since httplib blocks response.read() for HEAD requests.
228 # Oddly enough this doesn't appear as a problem when doing HEAD requests
229 # against Apache servers.
230 uri = "http://www.google.com/"
231 (response, content) = self.http.request(uri, "HEAD")
232 self.assertEqual(response.status, 200)
233 self.assertEqual(content, "")
234
jcgregorio2d66d4f2006-02-07 05:34:14 +0000235 def testGetNoCache(self):
236 # Test that can do a GET w/o the cache turned on.
237 http = httplib2.Http()
238 uri = urlparse.urljoin(base, "304/test_etag.txt")
239 (response, content) = http.request(uri, "GET")
240 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +0000241 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000242
Joe Gregorioe202d212009-07-16 14:57:52 -0400243 def testGetOnlyIfCachedCacheHit(self):
244 # Test that can do a GET with cache and 'only-if-cached'
245 uri = urlparse.urljoin(base, "304/test_etag.txt")
246 (response, content) = self.http.request(uri, "GET")
247 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
248 self.assertEqual(response.fromcache, True)
249 self.assertEqual(response.status, 200)
250
jcgregorioe4ce13e2006-04-02 03:05:08 +0000251 def testGetOnlyIfCachedCacheMiss(self):
252 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorioe4ce13e2006-04-02 03:05:08 +0000253 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorioe202d212009-07-16 14:57:52 -0400254 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
jcgregorioe4ce13e2006-04-02 03:05:08 +0000255 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400256 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000257
258 def testGetOnlyIfCachedNoCacheAtAll(self):
259 # Test that can do a GET with no cache with 'only-if-cached'
260 # Of course, there might be an intermediary beyond us
261 # that responds to the 'only-if-cached', so this
262 # test can't really be guaranteed to pass.
263 http = httplib2.Http()
264 uri = urlparse.urljoin(base, "304/test_etag.txt")
265 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
266 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400267 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000268
jcgregorio2d66d4f2006-02-07 05:34:14 +0000269 def testUserAgent(self):
270 # Test that we provide a default user-agent
271 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000272 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000273 self.assertEqual(response.status, 200)
274 self.assertTrue(content.startswith("Python-httplib2/"))
275
276 def testUserAgentNonDefault(self):
277 # Test that the default user-agent can be over-ridden
joe.gregoriof28536d2007-10-23 14:10:11 +0000278
jcgregorio2d66d4f2006-02-07 05:34:14 +0000279 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000280 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000281 self.assertEqual(response.status, 200)
282 self.assertTrue(content.startswith("fred/1.0"))
283
284 def testGet300WithLocation(self):
285 # Test the we automatically follow 300 redirects if a Location: header is provided
286 uri = urlparse.urljoin(base, "300/with-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000287 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000288 self.assertEqual(response.status, 200)
289 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000290 self.assertEqual(response.previous.status, 300)
291 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000292
293 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000294 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000295 self.assertEqual(response.status, 200)
296 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000297 self.assertEqual(response.previous.status, 300)
298 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000299
jcgregorio2f1e1422007-05-03 13:17:33 +0000300 def testGet300WithLocationNoRedirect(self):
301 # Test the we automatically follow 300 redirects if a Location: header is provided
302 self.http.follow_redirects = False
303 uri = urlparse.urljoin(base, "300/with-location-header.asis")
304 (response, content) = self.http.request(uri, "GET")
305 self.assertEqual(response.status, 300)
306
jcgregorio2d66d4f2006-02-07 05:34:14 +0000307 def testGet300WithoutLocation(self):
308 # Not giving a Location: header in a 300 response is acceptable
309 # In which case we just return the 300 response
310 uri = urlparse.urljoin(base, "300/without-location-header.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, 300)
313 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000314 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000315
316 def testGet301(self):
317 # Test that we automatically follow 301 redirects
318 # and that we cache the 301 response
319 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000320 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000321 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000322 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000323 self.assertTrue(response.has_key('content-location'))
324 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000325 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000326 self.assertEqual(response.previous.status, 301)
327 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000328
jcgregorio36140b52006-06-13 02:17:52 +0000329 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000330 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000331 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000332 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000333 self.assertEqual(response.previous.status, 301)
334 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000335
jcgregorio2f1e1422007-05-03 13:17:33 +0000336
337 def testGet301NoRedirect(self):
338 # Test that we automatically follow 301 redirects
339 # and that we cache the 301 response
340 self.http.follow_redirects = False
341 uri = urlparse.urljoin(base, "301/onestep.asis")
342 destination = urlparse.urljoin(base, "302/final-destination.txt")
343 (response, content) = self.http.request(uri, "GET")
344 self.assertEqual(response.status, 301)
345
346
jcgregorio2d66d4f2006-02-07 05:34:14 +0000347 def testGet302(self):
348 # Test that we automatically follow 302 redirects
349 # and that we DO NOT cache the 302 response
350 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000351 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000352 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000353 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000354 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000355 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000356 self.assertEqual(response.previous.status, 302)
357 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000358
359 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000360 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000361 self.assertEqual(response.status, 200)
362 self.assertEqual(response.fromcache, True)
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)
jcgregorio772adc82006-11-17 21:52:34 +0000367 self.assertEqual(response.previous['content-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000368
369 uri = urlparse.urljoin(base, "302/twostep.asis")
370
jcgregorio36140b52006-06-13 02:17:52 +0000371 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000372 self.assertEqual(response.status, 200)
373 self.assertEqual(response.fromcache, True)
374 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000375 self.assertEqual(response.previous.status, 302)
376 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000377
378 def testGet302RedirectionLimit(self):
379 # Test that we can set a lower redirection limit
380 # and that we raise an exception when we exceed
381 # that limit.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000382 self.http.force_exception_to_status_code = False
383
jcgregorio2d66d4f2006-02-07 05:34:14 +0000384 uri = urlparse.urljoin(base, "302/twostep.asis")
385 try:
jcgregorio36140b52006-06-13 02:17:52 +0000386 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000387 self.fail("This should not happen")
388 except httplib2.RedirectLimit:
389 pass
390 except Exception, e:
391 self.fail("Threw wrong kind of exception ")
392
jcgregorio07a9a4a2007-03-08 21:18:39 +0000393 # Re-run the test with out the exceptions
394 self.http.force_exception_to_status_code = True
395
396 (response, content) = self.http.request(uri, "GET", redirections = 1)
397 self.assertEqual(response.status, 500)
398 self.assertTrue(response.reason.startswith("Redirected more"))
399 self.assertEqual("302", response['status'])
400 self.assertTrue(content.startswith("<html>"))
401 self.assertTrue(response.previous != None)
402
jcgregorio2d66d4f2006-02-07 05:34:14 +0000403 def testGet302NoLocation(self):
404 # Test that we throw an exception when we get
405 # a 302 with no Location: header.
jcgregorio07a9a4a2007-03-08 21:18:39 +0000406 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000407 uri = urlparse.urljoin(base, "302/no-location.asis")
408 try:
jcgregorio36140b52006-06-13 02:17:52 +0000409 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000410 self.fail("Should never reach here")
411 except httplib2.RedirectMissingLocation:
412 pass
413 except Exception, e:
414 self.fail("Threw wrong kind of exception ")
415
jcgregorio07a9a4a2007-03-08 21:18:39 +0000416 # Re-run the test with out the exceptions
417 self.http.force_exception_to_status_code = True
418
419 (response, content) = self.http.request(uri, "GET")
420 self.assertEqual(response.status, 500)
421 self.assertTrue(response.reason.startswith("Redirected but"))
422 self.assertEqual("302", response['status'])
423 self.assertTrue(content.startswith("This is content"))
424
jcgregorio2d66d4f2006-02-07 05:34:14 +0000425 def testGet302ViaHttps(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000426 # Google always redirects to http://google.com
jcgregorio36140b52006-06-13 02:17:52 +0000427 (response, content) = self.http.request("https://google.com", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000428 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000429 self.assertEqual(302, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000430
431 def testGetViaHttps(self):
432 # Test that we can handle HTTPS
jcgregorio36140b52006-06-13 02:17:52 +0000433 (response, content) = self.http.request("https://google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000434 self.assertEqual(200, response.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000435
436 def testGetViaHttpsSpecViolationOnLocation(self):
437 # Test that we follow redirects through HTTPS
438 # even if they violate the spec by including
439 # a relative Location: header instead of an
440 # absolute one.
jcgregorio36140b52006-06-13 02:17:52 +0000441 (response, content) = self.http.request("https://google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000442 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000443 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000444
jcgregoriode8238d2007-03-07 19:08:26 +0000445
446 def testGetViaHttpsKeyCert(self):
jcgregorio2f1e1422007-05-03 13:17:33 +0000447 # At this point I can only test
448 # that the key and cert files are passed in
449 # correctly to httplib. It would be nice to have
450 # a real https endpoint to test against.
451 http = httplib2.Http(timeout=2)
jcgregoriode8238d2007-03-07 19:08:26 +0000452
453 http.add_certificate("akeyfile", "acertfile", "bitworking.org")
454 try:
455 (response, content) = http.request("https://bitworking.org", "GET")
456 except:
457 pass
458 self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
459 self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
460
jcgregorio2f1e1422007-05-03 13:17:33 +0000461 try:
462 (response, content) = http.request("https://notthere.bitworking.org", "GET")
463 except:
464 pass
465 self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
466 self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
467
468
469
jcgregoriode8238d2007-03-07 19:08:26 +0000470
jcgregorio2d66d4f2006-02-07 05:34:14 +0000471 def testGet303(self):
472 # Do a follow-up GET on a Location: header
473 # returned from a POST that gave a 303.
474 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000475 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000476 self.assertEqual(response.status, 200)
477 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000478 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000479
jcgregorio2f1e1422007-05-03 13:17:33 +0000480 def testGet303NoRedirect(self):
481 # Do a follow-up GET on a Location: header
482 # returned from a POST that gave a 303.
483 self.http.follow_redirects = False
484 uri = urlparse.urljoin(base, "303/303.cgi")
485 (response, content) = self.http.request(uri, "POST", " ")
486 self.assertEqual(response.status, 303)
487
jcgregorio2d66d4f2006-02-07 05:34:14 +0000488 def test303ForDifferentMethods(self):
489 # Test that all methods can be used
490 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000491 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000492 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000493 self.assertEqual(response['x-method'], method_on_303)
494
495 def testGet304(self):
496 # Test that we use ETags properly to validate our cache
497 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000498 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000499 self.assertNotEqual(response['etag'], "")
500
jcgregorio36140b52006-06-13 02:17:52 +0000501 (response, content) = self.http.request(uri, "GET")
502 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000503 self.assertEqual(response.status, 200)
504 self.assertEqual(response.fromcache, True)
505
jcgregorio90fb4a42006-11-17 16:19:47 +0000506 cache_file_name = os.path.join(cacheDirName, httplib2.safename(httplib2.urlnorm(uri)[-1]))
507 f = open(cache_file_name, "r")
508 status_line = f.readline()
509 f.close()
510
511 self.assertTrue(status_line.startswith("status:"))
512
jcgregorio36140b52006-06-13 02:17:52 +0000513 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000514 self.assertEqual(response.status, 200)
515 self.assertEqual(response.fromcache, True)
516
jcgregorio36140b52006-06-13 02:17:52 +0000517 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000518 self.assertEqual(response.status, 206)
519 self.assertEqual(response.fromcache, False)
520
jcgregorio25185622006-10-28 05:12:34 +0000521 def testGetIgnoreEtag(self):
522 # Test that we can forcibly ignore ETags
523 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
524 (response, content) = self.http.request(uri, "GET")
525 self.assertNotEqual(response['etag'], "")
526
527 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
528 d = self.reflector(content)
529 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
530
531 self.http.ignore_etag = True
532 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
533 d = self.reflector(content)
534 self.assertEqual(response.fromcache, False)
535 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
536
jcgregorio4b145e82007-01-18 19:46:34 +0000537 def testOverrideEtag(self):
538 # Test that we can forcibly ignore ETags
539 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
540 (response, content) = self.http.request(uri, "GET")
541 self.assertNotEqual(response['etag'], "")
542
543 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
544 d = self.reflector(content)
545 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
546 self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred")
547
pilgrim00a352e2009-05-29 04:04:44 +0000548 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000549 d = self.reflector(content)
550 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
551 self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio25185622006-10-28 05:12:34 +0000552
pilgrim00a352e2009-05-29 04:04:44 +0000553#MAP-commented this out because it consistently fails
554# def testGet304EndToEnd(self):
555# # Test that end to end headers get overwritten in the cache
556# uri = urlparse.urljoin(base, "304/end2end.cgi")
557# (response, content) = self.http.request(uri, "GET")
558# self.assertNotEqual(response['etag'], "")
559# old_date = response['date']
560# time.sleep(2)
561#
562# (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
563# # The response should be from the cache, but the Date: header should be updated.
564# new_date = response['date']
565# self.assertNotEqual(new_date, old_date)
566# self.assertEqual(response.status, 200)
567# self.assertEqual(response.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000568
569 def testGet304LastModified(self):
570 # Test that we can still handle a 304
571 # by only using the last-modified cache validator.
572 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000573 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000574
575 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000576 (response, content) = self.http.request(uri, "GET")
577 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000578 self.assertEqual(response.status, 200)
579 self.assertEqual(response.fromcache, True)
580
581 def testGet307(self):
582 # Test that we do follow 307 redirects but
583 # do not cache the 307
584 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000585 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000586 self.assertEqual(response.status, 200)
587 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000588 self.assertEqual(response.previous.status, 307)
589 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000590
jcgregorio36140b52006-06-13 02:17:52 +0000591 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000592 self.assertEqual(response.status, 200)
593 self.assertEqual(response.fromcache, True)
594 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000595 self.assertEqual(response.previous.status, 307)
596 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000597
598 def testGet410(self):
599 # Test that we pass 410's through
600 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000601 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000602 self.assertEqual(response.status, 410)
603
chris dent89f15142009-12-24 14:02:57 -0600604 def testVaryHeaderSimple(self):
605 """
606 RFC 2616 13.6
607 When the cache receives a subsequent request whose Request-URI
608 specifies one or more cache entries including a Vary header field,
609 the cache MUST NOT use such a cache entry to construct a response
610 to the new request unless all of the selecting request-headers
611 present in the new request match the corresponding stored
612 request-headers in the original request.
613 """
614 # test that the vary header is sent
615 uri = urlparse.urljoin(base, "vary/accept.asis")
616 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
617 self.assertEqual(response.status, 200)
618 self.assertTrue(response.has_key('vary'))
619
620 # get the resource again, from the cache since accept header in this
621 # request is the same as the request
622 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
623 self.assertEqual(response.status, 200)
624 self.assertEqual(response.fromcache, True, msg="Should be from cache")
625
626 # get the resource again, not from cache since Accept headers does not match
627 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
628 self.assertEqual(response.status, 200)
629 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
630
631 # get the resource again, without any Accept header, so again no match
632 (response, content) = self.http.request(uri, "GET")
633 self.assertEqual(response.status, 200)
634 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
635
636 def testNoVary(self):
637 # when there is no vary, a different Accept header (e.g.) should not
638 # impact if the cache is used
639 # test that the vary header is not sent
640 uri = urlparse.urljoin(base, "vary/no-vary.asis")
641 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
642 self.assertEqual(response.status, 200)
643 self.assertFalse(response.has_key('vary'))
644
645 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
646 self.assertEqual(response.status, 200)
647 self.assertEqual(response.fromcache, True, msg="Should be from cache")
648
649 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
650 self.assertEqual(response.status, 200)
651 self.assertEqual(response.fromcache, True, msg="Should be from cache")
652
653 def testVaryHeaderDouble(self):
654 uri = urlparse.urljoin(base, "vary/accept-double.asis")
655 (response, content) = self.http.request(uri, "GET", headers={
656 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
657 self.assertEqual(response.status, 200)
658 self.assertTrue(response.has_key('vary'))
659
660 # we are from cache
661 (response, content) = self.http.request(uri, "GET", headers={
662 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
663 self.assertEqual(response.fromcache, True, msg="Should be from cache")
664
665 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
666 self.assertEqual(response.status, 200)
667 self.assertEqual(response.fromcache, False)
668
669 # get the resource again, not from cache, varied headers don't match exact
670 (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
671 self.assertEqual(response.status, 200)
672 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
673
jcgregorio88ef89b2010-05-13 23:42:11 -0400674 def testVaryUnusedHeader(self):
675 # A header's value is not considered to vary if it's not used at all.
676 uri = urlparse.urljoin(base, "vary/unused-header.asis")
677 (response, content) = self.http.request(uri, "GET", headers={
678 'Accept': 'text/plain'})
679 self.assertEqual(response.status, 200)
680 self.assertTrue(response.has_key('vary'))
681
682 # we are from cache
683 (response, content) = self.http.request(uri, "GET", headers={
684 'Accept': 'text/plain',})
685 self.assertEqual(response.fromcache, True, msg="Should be from cache")
686
chris dent89f15142009-12-24 14:02:57 -0600687
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000688 def testHeadGZip(self):
689 # Test that we don't try to decompress a HEAD response
690 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
691 (response, content) = self.http.request(uri, "HEAD")
692 self.assertEqual(response.status, 200)
693 self.assertNotEqual(int(response['content-length']), 0)
694 self.assertEqual(content, "")
695
jcgregorio2d66d4f2006-02-07 05:34:14 +0000696 def testGetGZip(self):
697 # Test that we support gzip compression
698 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000699 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000700 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000701 self.assertFalse(response.has_key('content-encoding'))
joe.gregorio8b6d2312007-12-16 05:42:07 +0000702 self.assertTrue(response.has_key('-content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000703 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000704 self.assertEqual(content, "This is the final destination.\n")
705
Joe Gregoriod1137c52011-02-13 19:27:35 -0500706 def testPostAndGZipResponse(self):
707 uri = urlparse.urljoin(base, "gzip/post.cgi")
708 (response, content) = self.http.request(uri, "POST", body=" ")
709 self.assertEqual(response.status, 200)
710 self.assertFalse(response.has_key('content-encoding'))
711 self.assertTrue(response.has_key('-content-encoding'))
712
jcgregorio2d66d4f2006-02-07 05:34:14 +0000713 def testGetGZipFailure(self):
714 # Test that we raise a good exception when the gzip fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000715 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000716 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
717 try:
jcgregorio36140b52006-06-13 02:17:52 +0000718 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000719 self.fail("Should never reach here")
720 except httplib2.FailedToDecompressContent:
721 pass
722 except Exception:
723 self.fail("Threw wrong kind of exception")
724
jcgregorio07a9a4a2007-03-08 21:18:39 +0000725 # Re-run the test with out the exceptions
726 self.http.force_exception_to_status_code = True
727
728 (response, content) = self.http.request(uri, "GET")
729 self.assertEqual(response.status, 500)
730 self.assertTrue(response.reason.startswith("Content purported"))
731
732 def testTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000733 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000734 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
735 try:
736 import socket
737 socket.setdefaulttimeout(1)
738 except:
739 # Don't run the test if we can't set the timeout
740 return
741 (response, content) = self.http.request(uri)
742 self.assertEqual(response.status, 408)
743 self.assertTrue(response.reason.startswith("Request Timeout"))
744 self.assertTrue(content.startswith("Request Timeout"))
745
jcgregoriob2697912007-03-09 02:23:47 +0000746 def testIndividualTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000747 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
748 http = httplib2.Http(timeout=1)
joe.gregoriof28536d2007-10-23 14:10:11 +0000749 http.force_exception_to_status_code = True
jcgregoriob2697912007-03-09 02:23:47 +0000750
751 (response, content) = http.request(uri)
752 self.assertEqual(response.status, 408)
753 self.assertTrue(response.reason.startswith("Request Timeout"))
754 self.assertTrue(content.startswith("Request Timeout"))
755
jcgregorio07a9a4a2007-03-08 21:18:39 +0000756
Joe Gregorio1a7609f2009-07-16 10:59:44 -0400757 def testHTTPSInitTimeout(self):
758 c = httplib2.HTTPSConnectionWithTimeout('localhost', 80, timeout=47)
759 self.assertEqual(47, c.timeout)
760
jcgregorio2d66d4f2006-02-07 05:34:14 +0000761 def testGetDeflate(self):
762 # Test that we support deflate compression
763 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000764 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000765 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000766 self.assertFalse(response.has_key('content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000767 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000768 self.assertEqual(content, "This is the final destination.")
769
770 def testGetDeflateFailure(self):
771 # Test that we raise a good exception when the deflate fails
jcgregorio07a9a4a2007-03-08 21:18:39 +0000772 self.http.force_exception_to_status_code = False
773
jcgregorio2d66d4f2006-02-07 05:34:14 +0000774 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
775 try:
jcgregorio36140b52006-06-13 02:17:52 +0000776 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000777 self.fail("Should never reach here")
778 except httplib2.FailedToDecompressContent:
779 pass
780 except Exception:
781 self.fail("Threw wrong kind of exception")
782
jcgregorio07a9a4a2007-03-08 21:18:39 +0000783 # Re-run the test with out the exceptions
784 self.http.force_exception_to_status_code = True
785
786 (response, content) = self.http.request(uri, "GET")
787 self.assertEqual(response.status, 500)
788 self.assertTrue(response.reason.startswith("Content purported"))
789
jcgregorio2d66d4f2006-02-07 05:34:14 +0000790 def testGetDuplicateHeaders(self):
791 # Test that duplicate headers get concatenated via ','
792 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000793 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000794 self.assertEqual(response.status, 200)
795 self.assertEqual(content, "This is content\n")
796 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
797
798 def testGetCacheControlNoCache(self):
799 # Test Cache-Control: no-cache on requests
800 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000801 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000802 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000803 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000804 self.assertEqual(response.status, 200)
805 self.assertEqual(response.fromcache, True)
806
jcgregorio36140b52006-06-13 02:17:52 +0000807 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000808 self.assertEqual(response.status, 200)
809 self.assertEqual(response.fromcache, False)
810
811 def testGetCacheControlPragmaNoCache(self):
812 # Test Pragma: no-cache on requests
813 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000814 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000815 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000816 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000817 self.assertEqual(response.status, 200)
818 self.assertEqual(response.fromcache, True)
819
jcgregorio36140b52006-06-13 02:17:52 +0000820 (response, content) = self.http.request(uri, "GET", headers={'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000821 self.assertEqual(response.status, 200)
822 self.assertEqual(response.fromcache, False)
823
824 def testGetCacheControlNoStoreRequest(self):
825 # A no-store request means that the response should not be stored.
826 uri = urlparse.urljoin(base, "304/test_etag.txt")
827
jcgregorio36140b52006-06-13 02:17:52 +0000828 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000829 self.assertEqual(response.status, 200)
830 self.assertEqual(response.fromcache, False)
831
jcgregorio36140b52006-06-13 02:17:52 +0000832 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000833 self.assertEqual(response.status, 200)
834 self.assertEqual(response.fromcache, False)
835
836 def testGetCacheControlNoStoreResponse(self):
837 # A no-store response means that the response should not be stored.
838 uri = urlparse.urljoin(base, "no-store/no-store.asis")
839
jcgregorio36140b52006-06-13 02:17:52 +0000840 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000841 self.assertEqual(response.status, 200)
842 self.assertEqual(response.fromcache, False)
843
jcgregorio36140b52006-06-13 02:17:52 +0000844 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000845 self.assertEqual(response.status, 200)
846 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000847
848 def testGetCacheControlNoCacheNoStoreRequest(self):
849 # Test that a no-store, no-cache clears the entry from the cache
850 # even if it was cached previously.
851 uri = urlparse.urljoin(base, "304/test_etag.txt")
852
jcgregorio36140b52006-06-13 02:17:52 +0000853 (response, content) = self.http.request(uri, "GET")
854 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000855 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000856 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
857 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000858 self.assertEqual(response.status, 200)
859 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000860
861 def testUpdateInvalidatesCache(self):
862 # Test that calling PUT or DELETE on a
863 # URI that is cache invalidates that cache.
864 uri = urlparse.urljoin(base, "304/test_etag.txt")
865
jcgregorio36140b52006-06-13 02:17:52 +0000866 (response, content) = self.http.request(uri, "GET")
867 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000868 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000869 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000870 self.assertEqual(response.status, 405)
871
jcgregorio36140b52006-06-13 02:17:52 +0000872 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000873 self.assertEqual(response.fromcache, False)
874
875 def testUpdateUsesCachedETag(self):
876 # Test that we natively support http://www.w3.org/1999/04/Editing/
877 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
878
jcgregorio36140b52006-06-13 02:17:52 +0000879 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000880 self.assertEqual(response.status, 200)
881 self.assertEqual(response.fromcache, False)
jcgregorio36140b52006-06-13 02:17:52 +0000882 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000883 self.assertEqual(response.status, 200)
884 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400885 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000886 self.assertEqual(response.status, 200)
Joe Gregoriocd868102009-09-29 17:09:16 -0400887 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000888 self.assertEqual(response.status, 412)
889
joe.gregorio700f04d2008-09-06 04:46:32 +0000890 def testUpdateUsesCachedETagAndOCMethod(self):
891 # Test that we natively support http://www.w3.org/1999/04/Editing/
892 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
893
894 (response, content) = self.http.request(uri, "GET")
895 self.assertEqual(response.status, 200)
896 self.assertEqual(response.fromcache, False)
897 (response, content) = self.http.request(uri, "GET")
898 self.assertEqual(response.status, 200)
899 self.assertEqual(response.fromcache, True)
900 self.http.optimistic_concurrency_methods.append("DELETE")
901 (response, content) = self.http.request(uri, "DELETE")
902 self.assertEqual(response.status, 200)
903
904
jcgregorio4b145e82007-01-18 19:46:34 +0000905 def testUpdateUsesCachedETagOverridden(self):
906 # Test that we natively support http://www.w3.org/1999/04/Editing/
907 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
908
909 (response, content) = self.http.request(uri, "GET")
910 self.assertEqual(response.status, 200)
911 self.assertEqual(response.fromcache, False)
912 (response, content) = self.http.request(uri, "GET")
913 self.assertEqual(response.status, 200)
914 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400915 (response, content) = self.http.request(uri, "PUT", body="foo", headers={'if-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000916 self.assertEqual(response.status, 412)
917
jcgregorio2d66d4f2006-02-07 05:34:14 +0000918 def testBasicAuth(self):
919 # Test Basic Authentication
920 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000921 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000922 self.assertEqual(response.status, 401)
923
924 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +0000925 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000926 self.assertEqual(response.status, 401)
927
jcgregorio36140b52006-06-13 02:17:52 +0000928 self.http.add_credentials('joe', 'password')
929 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000930 self.assertEqual(response.status, 200)
931
932 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000933 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000934 self.assertEqual(response.status, 200)
935
jcgregoriode8238d2007-03-07 19:08:26 +0000936 def testBasicAuthWithDomain(self):
937 # Test Basic Authentication
938 uri = urlparse.urljoin(base, "basic/file.txt")
939 (response, content) = self.http.request(uri, "GET")
940 self.assertEqual(response.status, 401)
941
942 uri = urlparse.urljoin(base, "basic/")
943 (response, content) = self.http.request(uri, "GET")
944 self.assertEqual(response.status, 401)
945
946 self.http.add_credentials('joe', 'password', "example.org")
947 (response, content) = self.http.request(uri, "GET")
948 self.assertEqual(response.status, 401)
949
950 uri = urlparse.urljoin(base, "basic/file.txt")
951 (response, content) = self.http.request(uri, "GET")
952 self.assertEqual(response.status, 401)
953
954 domain = urlparse.urlparse(base)[1]
955 self.http.add_credentials('joe', 'password', domain)
956 (response, content) = self.http.request(uri, "GET")
957 self.assertEqual(response.status, 200)
958
959 uri = urlparse.urljoin(base, "basic/file.txt")
960 (response, content) = self.http.request(uri, "GET")
961 self.assertEqual(response.status, 200)
962
963
964
965
966
967
jcgregorio2d66d4f2006-02-07 05:34:14 +0000968 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +0000969 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +0000970 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000971 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000972 self.assertEqual(response.status, 401)
973
974 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +0000975 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000976 self.assertEqual(response.status, 401)
977
jcgregorio36140b52006-06-13 02:17:52 +0000978 self.http.add_credentials('fred', 'barney')
979 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000980 self.assertEqual(response.status, 200)
981
982 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000983 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000984 self.assertEqual(response.status, 200)
985
986 def testBasicAuthNested(self):
987 # Test Basic Authentication with resources
988 # that are nested
989 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +0000990 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000991 self.assertEqual(response.status, 401)
992
993 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +0000994 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000995 self.assertEqual(response.status, 401)
996
jcgregorioadbb4f82006-05-19 15:17:42 +0000997 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +0000998 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +0000999
1000 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001001 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001002 self.assertEqual(response.status, 200)
1003
1004 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001005 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001006 self.assertEqual(response.status, 401)
1007
jcgregorio36140b52006-06-13 02:17:52 +00001008 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001009
1010 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001011 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001012 self.assertEqual(response.status, 200)
1013
1014 uri = urlparse.urljoin(base, "basic-nested/subdir")
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, 200)
1017
1018 def testDigestAuth(self):
1019 # Test that we support Digest Authentication
1020 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +00001021 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001022 self.assertEqual(response.status, 401)
1023
jcgregorio36140b52006-06-13 02:17:52 +00001024 self.http.add_credentials('joe', 'password')
1025 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001026 self.assertEqual(response.status, 200)
1027
1028 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001029 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001030
1031 def testDigestAuthNextNonceAndNC(self):
1032 # Test that if the server sets nextnonce that we reset
1033 # the nonce count back to 1
1034 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001035 self.http.add_credentials('joe', 'password')
1036 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001037 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1038 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +00001039 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001040 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
1041 self.assertEqual(response.status, 200)
1042
1043 if info.has_key('nextnonce'):
1044 self.assertEqual(info2['nc'], 1)
1045
1046 def testDigestAuthStale(self):
1047 # Test that we can handle a nonce becoming stale
1048 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001049 self.http.add_credentials('joe', 'password')
1050 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001051 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1052 self.assertEqual(response.status, 200)
1053
1054 time.sleep(3)
1055 # Sleep long enough that the nonce becomes stale
1056
jcgregorio36140b52006-06-13 02:17:52 +00001057 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001058 self.assertFalse(response.fromcache)
1059 self.assertTrue(response._stale_digest)
1060 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
1061 self.assertEqual(response.status, 200)
1062
1063 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +00001064 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +00001065
1066 def testReflector(self):
1067 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +00001068 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001069 d = self.reflector(content)
1070 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
1071
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001072 def testConnectionClose(self):
1073 uri = "http://www.google.com/"
1074 (response, content) = self.http.request(uri, "GET")
1075 for c in self.http.connections.values():
1076 self.assertNotEqual(None, c.sock)
1077 (response, content) = self.http.request(uri, "GET", headers={"connection": "close"})
1078 for c in self.http.connections.values():
1079 self.assertEqual(None, c.sock)
1080
1081
jcgregorio36140b52006-06-13 02:17:52 +00001082try:
1083 import memcache
1084 class HttpTestMemCached(HttpTest):
1085 def setUp(self):
1086 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +00001087 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +00001088 self.http = httplib2.Http(self.cache)
1089 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +00001090 # Not exactly sure why the sleep is needed here, but
1091 # if not present then some unit tests that rely on caching
1092 # fail. Memcached seems to lose some sets immediately
1093 # after a flush_all if the set is to a value that
1094 # was previously cached. (Maybe the flush is handled async?)
1095 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +00001096 self.http.clear_credentials()
1097except:
1098 pass
1099
1100
1101
chris dent89f15142009-12-24 14:02:57 -06001102
jcgregoriodb8dfc82006-03-31 14:59:46 +00001103# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +00001104
1105class HttpPrivateTest(unittest.TestCase):
1106
1107 def testParseCacheControl(self):
1108 # Test that we can parse the Cache-Control header
1109 self.assertEqual({}, httplib2._parse_cache_control({}))
1110 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
1111 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
1112 self.assertEqual(cc['no-cache'], 1)
1113 self.assertEqual(cc['max-age'], '7200')
1114 cc = httplib2._parse_cache_control({'cache-control': ' , '})
1115 self.assertEqual(cc[''], 1)
1116
Joe Gregorioe314e8b2009-07-16 20:11:28 -04001117 try:
1118 cc = httplib2._parse_cache_control({'cache-control': 'Max-age=3600;post-check=1800,pre-check=3600'})
1119 self.assertTrue("max-age" in cc)
1120 except:
1121 self.fail("Should not throw exception")
1122
jcgregorio2d66d4f2006-02-07 05:34:14 +00001123 def testNormalizeHeaders(self):
1124 # Test that we normalize headers to lowercase
1125 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
1126 self.assertTrue(h.has_key('cache-control'))
1127 self.assertTrue(h.has_key('other'))
1128 self.assertEqual('Stuff', h['other'])
1129
1130 def testExpirationModelTransparent(self):
1131 # Test that no-cache makes our request TRANSPARENT
1132 response_headers = {
1133 'cache-control': 'max-age=7200'
1134 }
1135 request_headers = {
1136 'cache-control': 'no-cache'
1137 }
1138 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
1139
jcgregorio45865012007-01-18 16:38:22 +00001140 def testMaxAgeNonNumeric(self):
1141 # Test that no-cache makes our request TRANSPARENT
1142 response_headers = {
1143 'cache-control': 'max-age=fred, min-fresh=barney'
1144 }
1145 request_headers = {
1146 }
1147 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1148
1149
jcgregorio2d66d4f2006-02-07 05:34:14 +00001150 def testExpirationModelNoCacheResponse(self):
1151 # The date and expires point to an entry that should be
1152 # FRESH, but the no-cache over-rides that.
1153 now = time.time()
1154 response_headers = {
1155 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1156 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1157 'cache-control': 'no-cache'
1158 }
1159 request_headers = {
1160 }
1161 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1162
1163 def testExpirationModelStaleRequestMustReval(self):
1164 # must-revalidate forces STALE
1165 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
1166
1167 def testExpirationModelStaleResponseMustReval(self):
1168 # must-revalidate forces STALE
1169 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
1170
1171 def testExpirationModelFresh(self):
1172 response_headers = {
1173 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1174 'cache-control': 'max-age=2'
1175 }
1176 request_headers = {
1177 }
1178 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1179 time.sleep(3)
1180 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1181
1182 def testExpirationMaxAge0(self):
1183 response_headers = {
1184 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1185 'cache-control': 'max-age=0'
1186 }
1187 request_headers = {
1188 }
1189 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1190
1191 def testExpirationModelDateAndExpires(self):
1192 now = time.time()
1193 response_headers = {
1194 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1195 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1196 }
1197 request_headers = {
1198 }
1199 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1200 time.sleep(3)
1201 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1202
jcgregoriof9511052007-06-01 14:56:34 +00001203 def testExpiresZero(self):
1204 now = time.time()
1205 response_headers = {
1206 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1207 'expires': "0",
1208 }
1209 request_headers = {
1210 }
1211 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1212
jcgregorio2d66d4f2006-02-07 05:34:14 +00001213 def testExpirationModelDateOnly(self):
1214 now = time.time()
1215 response_headers = {
1216 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
1217 }
1218 request_headers = {
1219 }
1220 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1221
1222 def testExpirationModelOnlyIfCached(self):
1223 response_headers = {
1224 }
1225 request_headers = {
1226 'cache-control': 'only-if-cached',
1227 }
1228 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1229
1230 def testExpirationModelMaxAgeBoth(self):
1231 now = time.time()
1232 response_headers = {
1233 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1234 'cache-control': 'max-age=2'
1235 }
1236 request_headers = {
1237 'cache-control': 'max-age=0'
1238 }
1239 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1240
1241 def testExpirationModelDateAndExpiresMinFresh1(self):
1242 now = time.time()
1243 response_headers = {
1244 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1245 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1246 }
1247 request_headers = {
1248 'cache-control': 'min-fresh=2'
1249 }
1250 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1251
1252 def testExpirationModelDateAndExpiresMinFresh2(self):
1253 now = time.time()
1254 response_headers = {
1255 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1256 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1257 }
1258 request_headers = {
1259 'cache-control': 'min-fresh=2'
1260 }
1261 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1262
1263 def testParseWWWAuthenticateEmpty(self):
1264 res = httplib2._parse_www_authenticate({})
1265 self.assertEqual(len(res.keys()), 0)
1266
jcgregoriofd22e432006-04-27 02:00:08 +00001267 def testParseWWWAuthenticate(self):
1268 # different uses of spaces around commas
1269 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
1270 self.assertEqual(len(res.keys()), 1)
1271 self.assertEqual(len(res['test'].keys()), 5)
1272
1273 # tokens with non-alphanum
1274 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
1275 self.assertEqual(len(res.keys()), 1)
1276 self.assertEqual(len(res['t*!%#st'].keys()), 2)
1277
1278 # quoted string with quoted pairs
1279 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
1280 self.assertEqual(len(res.keys()), 1)
1281 self.assertEqual(res['test']['realm'], 'a "test" realm')
1282
1283 def testParseWWWAuthenticateStrict(self):
1284 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
1285 self.testParseWWWAuthenticate();
1286 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
1287
jcgregorio2d66d4f2006-02-07 05:34:14 +00001288 def testParseWWWAuthenticateBasic(self):
1289 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
1290 basic = res['basic']
1291 self.assertEqual('me', basic['realm'])
1292
1293 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
1294 basic = res['basic']
1295 self.assertEqual('me', basic['realm'])
1296 self.assertEqual('MD5', basic['algorithm'])
1297
1298 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
1299 basic = res['basic']
1300 self.assertEqual('me', basic['realm'])
1301 self.assertEqual('MD5', basic['algorithm'])
1302
1303 def testParseWWWAuthenticateBasic2(self):
1304 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
1305 basic = res['basic']
1306 self.assertEqual('me', basic['realm'])
1307 self.assertEqual('fred', basic['other'])
1308
1309 def testParseWWWAuthenticateBasic3(self):
1310 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
1311 basic = res['basic']
1312 self.assertEqual('me', basic['realm'])
1313
1314
1315 def testParseWWWAuthenticateDigest(self):
1316 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1317 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
1318 digest = res['digest']
1319 self.assertEqual('testrealm@host.com', digest['realm'])
1320 self.assertEqual('auth,auth-int', digest['qop'])
1321
1322
1323 def testParseWWWAuthenticateMultiple(self):
1324 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1325 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
1326 digest = res['digest']
1327 self.assertEqual('testrealm@host.com', digest['realm'])
1328 self.assertEqual('auth,auth-int', digest['qop'])
1329 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1330 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1331 basic = res['basic']
1332 self.assertEqual('me', basic['realm'])
1333
1334 def testParseWWWAuthenticateMultiple2(self):
1335 # Handle an added comma between challenges, which might get thrown in if the challenges were
1336 # originally sent in separate www-authenticate headers.
1337 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1338 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
1339 digest = res['digest']
1340 self.assertEqual('testrealm@host.com', digest['realm'])
1341 self.assertEqual('auth,auth-int', digest['qop'])
1342 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1343 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1344 basic = res['basic']
1345 self.assertEqual('me', basic['realm'])
1346
1347 def testParseWWWAuthenticateMultiple3(self):
1348 # Handle an added comma between challenges, which might get thrown in if the challenges were
1349 # originally sent in separate www-authenticate headers.
1350 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1351 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1352 digest = res['digest']
1353 self.assertEqual('testrealm@host.com', digest['realm'])
1354 self.assertEqual('auth,auth-int', digest['qop'])
1355 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1356 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1357 basic = res['basic']
1358 self.assertEqual('me', basic['realm'])
1359 wsse = res['wsse']
1360 self.assertEqual('foo', wsse['realm'])
1361 self.assertEqual('UsernameToken', wsse['profile'])
1362
1363 def testParseWWWAuthenticateMultiple4(self):
1364 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1365 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1366 digest = res['digest']
1367 self.assertEqual('test-real.m@host.com', digest['realm'])
1368 self.assertEqual('\tauth,auth-int', digest['qop'])
1369 self.assertEqual('(*)&^&$%#', digest['nonce'])
1370
1371 def testParseWWWAuthenticateMoreQuoteCombos(self):
1372 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
1373 digest = res['digest']
1374 self.assertEqual('myrealm', digest['realm'])
1375
1376 def testDigestObject(self):
1377 credentials = ('joe', 'password')
1378 host = None
1379 request_uri = '/projects/httplib2/test/digest/'
1380 headers = {}
1381 response = {
1382 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
1383 }
1384 content = ""
1385
jcgregorio6cbab7e2006-04-21 20:35:43 +00001386 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001387 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
1388 our_request = "Authorization: %s" % headers['Authorization']
1389 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"'
1390 self.assertEqual(our_request, working_request)
1391
1392
1393 def testDigestObjectStale(self):
1394 credentials = ('joe', 'password')
1395 host = None
1396 request_uri = '/projects/httplib2/test/digest/'
1397 headers = {}
1398 response = httplib2.Response({ })
1399 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1400 response.status = 401
1401 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001402 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001403 # Returns true to force a retry
1404 self.assertTrue( d.response(response, content) )
1405
1406 def testDigestObjectAuthInfo(self):
1407 credentials = ('joe', 'password')
1408 host = None
1409 request_uri = '/projects/httplib2/test/digest/'
1410 headers = {}
1411 response = httplib2.Response({ })
1412 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1413 response['authentication-info'] = 'nextnonce="fred"'
1414 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001415 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001416 # Returns true to force a retry
1417 self.assertFalse( d.response(response, content) )
1418 self.assertEqual('fred', d.challenge['nonce'])
1419 self.assertEqual(1, d.challenge['nc'])
1420
1421 def testWsseAlgorithm(self):
1422 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
1423 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
1424 self.assertEqual(expected, digest)
1425
jcgregoriodb8dfc82006-03-31 14:59:46 +00001426 def testEnd2End(self):
1427 # one end to end header
1428 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
1429 end2end = httplib2._get_end2end_headers(response)
1430 self.assertTrue('content-type' in end2end)
1431 self.assertTrue('te' not in end2end)
1432 self.assertTrue('connection' not in end2end)
1433
1434 # one end to end header that gets eliminated
1435 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
1436 end2end = httplib2._get_end2end_headers(response)
1437 self.assertTrue('content-type' not in end2end)
1438 self.assertTrue('te' not in end2end)
1439 self.assertTrue('connection' not in end2end)
1440
1441 # Degenerate case of no headers
1442 response = {}
1443 end2end = httplib2._get_end2end_headers(response)
1444 self.assertEquals(0, len(end2end))
1445
1446 # Degenerate case of connection referrring to a header not passed in
1447 response = {'connection': 'content-type'}
1448 end2end = httplib2._get_end2end_headers(response)
1449 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001450
chris dent89f15142009-12-24 14:02:57 -06001451if __name__ == '__main__':
1452 unittest.main()