blob: 344f9ba492e2d1ed9c1751395a1d7c1ef01c5829 [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 Gregoriob53de9b2011-06-07 15:44:51 -040029try:
30 import ssl
31except ImportError:
32 pass
Joe Gregorio694a8122011-02-13 21:40:09 -050033
jcgregorio8421f272006-02-14 18:19:51 +000034# Python 2.3 support
35if not hasattr(unittest.TestCase, 'assertTrue'):
36 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
37 unittest.TestCase.assertFalse = unittest.TestCase.failIf
38
jcgregorio2d66d4f2006-02-07 05:34:14 +000039# The test resources base uri
40base = 'http://bitworking.org/projects/httplib2/test/'
41#base = 'http://localhost/projects/httplib2/test/'
jcgregorio90fb4a42006-11-17 16:19:47 +000042cacheDirName = ".cache"
jcgregorio2d66d4f2006-02-07 05:34:14 +000043
jcgregoriode8238d2007-03-07 19:08:26 +000044
45class CredentialsTest(unittest.TestCase):
46 def test(self):
47 c = httplib2.Credentials()
48 c.add("joe", "password")
49 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
50 self.assertEqual(("joe", "password"), list(c.iter(""))[0])
51 c.add("fred", "password2", "wellformedweb.org")
52 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
53 self.assertEqual(1, len(list(c.iter("bitworking.org"))))
54 self.assertEqual(2, len(list(c.iter("wellformedweb.org"))))
55 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
56 c.clear()
57 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
58 c.add("fred", "password2", "wellformedweb.org")
59 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
60 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
61 self.assertEqual(0, len(list(c.iter(""))))
62
63
jcgregorio2d66d4f2006-02-07 05:34:14 +000064class ParserTest(unittest.TestCase):
65 def testFromStd66(self):
66 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
67 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
68 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
69 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
70 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
71 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
72 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
73 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
74
jcgregorio2d66d4f2006-02-07 05:34:14 +000075
jcgregorioa46fe4e2006-11-16 04:13:45 +000076class UrlNormTest(unittest.TestCase):
77 def test(self):
78 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1])
79 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1])
80 self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1])
81 self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1])
82 self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1])
jcgregoriob4e9ab02006-11-17 15:53:15 +000083 self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80"))
jcgregorio132d28e2007-01-23 16:22:53 +000084 try:
85 httplib2.urlnorm("/")
86 self.fail("Non-absolute URIs should raise an exception")
87 except httplib2.RelativeURIError:
88 pass
jcgregorioa46fe4e2006-11-16 04:13:45 +000089
90class UrlSafenameTest(unittest.TestCase):
91 def test(self):
92 # Test that different URIs end up generating different safe names
93 self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b"))
94 self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b"))
95 self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b"))
96 self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1]))
97 self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b"))
98 self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www"))
99 # Test the max length limits
100 uri = "http://" + ("w" * 200) + ".org"
101 uri2 = "http://" + ("w" * 201) + ".org"
102 self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri))
103 # Max length should be 200 + 1 (",") + 32
104 self.assertEqual(233, len(httplib2.safename(uri2)))
105 self.assertEqual(233, len(httplib2.safename(uri)))
106 # Unicode
jcgregoriodebceec2006-12-12 20:26:02 +0000107 if sys.version_info >= (2,3):
108 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 +0000109
jcgregorio14644372007-07-30 14:13:37 +0000110class _MyResponse(StringIO.StringIO):
111 def __init__(self, body, **kwargs):
112 StringIO.StringIO.__init__(self, body)
113 self.headers = kwargs
114
115 def iteritems(self):
116 return self.headers.iteritems()
117
118
119class _MyHTTPConnection(object):
120 "This class is just a mock of httplib.HTTPConnection used for testing"
121
122 def __init__(self, host, port=None, key_file=None, cert_file=None,
joe.gregoriof28536d2007-10-23 14:10:11 +0000123 strict=None, timeout=None, proxy_info=None):
jcgregorio14644372007-07-30 14:13:37 +0000124 self.host = host
125 self.port = port
126 self.timeout = timeout
127 self.log = ""
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400128 self.sock = None
jcgregorio14644372007-07-30 14:13:37 +0000129
130 def set_debuglevel(self, level):
131 pass
132
133 def connect(self):
134 "Connect to a host on a given port."
135 pass
136
137 def close(self):
138 pass
139
140 def request(self, method, request_uri, body, headers):
141 pass
142
143 def getresponse(self):
144 return _MyResponse("the body", status="200")
jcgregorioa46fe4e2006-11-16 04:13:45 +0000145
jcgregorio90fb4a42006-11-17 16:19:47 +0000146
jcgregorio2d66d4f2006-02-07 05:34:14 +0000147class HttpTest(unittest.TestCase):
148 def setUp(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400149 if os.path.exists(cacheDirName):
jcgregorio7e3608f2006-06-15 13:01:53 +0000150 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400151
152 if sys.version_info < (2, 6):
153 disable_cert_validation = True
154 else:
155 disable_cert_validation = False
156 self.http = httplib2.Http(
157 cacheDirName,
158 disable_ssl_certificate_validation=disable_cert_validation)
jcgregorio36140b52006-06-13 02:17:52 +0000159 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +0000160
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500161 def testIPv6NoSSL(self):
162 try:
163 self.http.request("http://[::1]/")
164 except socket.gaierror:
165 self.fail("should get the address family right for IPv6")
166 except socket.error:
167 # Even if IPv6 isn't installed on a machine it should just raise socket.error
168 pass
169
170 def testIPv6SSL(self):
171 try:
172 self.http.request("https://[::1]/")
173 except socket.gaierror:
174 self.fail("should get the address family right for IPv6")
Jason R. Coombscee15da2011-08-09 09:13:34 -0400175 except httplib2.CertificateHostnameMismatch:
176 # We connected and verified that the certificate doesn't match
177 # the name. Good enough.
178 pass
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500179 except socket.error:
180 # Even if IPv6 isn't installed on a machine it should just raise socket.error
181 pass
182
jcgregorio14644372007-07-30 14:13:37 +0000183 def testConnectionType(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400184 self.http.force_exception_to_status_code = False
jcgregorio14644372007-07-30 14:13:37 +0000185 response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection)
186 self.assertEqual(response['content-location'], "http://bitworking.org")
187 self.assertEqual(content, "the body")
188
jcgregorio6a638172007-01-23 16:40:23 +0000189 def testGetUnknownServer(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400190 self.http.force_exception_to_status_code = False
jcgregorio6a638172007-01-23 16:40:23 +0000191 try:
192 self.http.request("http://fred.bitworking.org/")
193 self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.")
194 except httplib2.ServerNotFoundError:
195 pass
196
jcgregorio07a9a4a2007-03-08 21:18:39 +0000197 # Now test with exceptions turned off
198 self.http.force_exception_to_status_code = True
199
200 (response, content) = self.http.request("http://fred.bitworking.org/")
201 self.assertEqual(response['content-type'], 'text/plain')
202 self.assertTrue(content.startswith("Unable to find"))
203 self.assertEqual(response.status, 400)
204
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500205 def testGetConnectionRefused(self):
206 self.http.force_exception_to_status_code = False
207 try:
208 self.http.request("http://localhost:7777/")
209 self.fail("An socket.error exception must be thrown on Connection Refused.")
210 except socket.error:
211 pass
212
213 # Now test with exceptions turned off
214 self.http.force_exception_to_status_code = True
215
216 (response, content) = self.http.request("http://localhost:7777/")
217 self.assertEqual(response['content-type'], 'text/plain')
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400218 self.assertTrue("Connection refused" in content
219 or "actively refused" in content,
220 "Unexpected status %(content)s" % vars())
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500221 self.assertEqual(response.status, 400)
222
jcgregorioa898f8f2006-12-12 17:16:55 +0000223 def testGetIRI(self):
jcgregoriodebceec2006-12-12 20:26:02 +0000224 if sys.version_info >= (2,3):
225 uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}")
226 (response, content) = self.http.request(uri, "GET")
227 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400228 self.assertTrue(d.has_key('QUERY_STRING'))
229 self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0)
230
jcgregorio2d66d4f2006-02-07 05:34:14 +0000231 def testGetIsDefaultMethod(self):
232 # Test that GET is the default method
233 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000234 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000235 self.assertEqual(response['x-method'], "GET")
236
237 def testDifferentMethods(self):
238 # Test that all methods can be used
239 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
240 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +0000241 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000242 self.assertEqual(response['x-method'], method)
243
Joe Gregoriob628c0b2009-07-16 12:28:04 -0400244 def testHeadRead(self):
245 # Test that we don't try to read the response of a HEAD request
246 # since httplib blocks response.read() for HEAD requests.
247 # Oddly enough this doesn't appear as a problem when doing HEAD requests
248 # against Apache servers.
249 uri = "http://www.google.com/"
250 (response, content) = self.http.request(uri, "HEAD")
251 self.assertEqual(response.status, 200)
252 self.assertEqual(content, "")
253
jcgregorio2d66d4f2006-02-07 05:34:14 +0000254 def testGetNoCache(self):
255 # Test that can do a GET w/o the cache turned on.
256 http = httplib2.Http()
257 uri = urlparse.urljoin(base, "304/test_etag.txt")
258 (response, content) = http.request(uri, "GET")
259 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +0000260 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000261
Joe Gregorioe202d212009-07-16 14:57:52 -0400262 def testGetOnlyIfCachedCacheHit(self):
263 # Test that can do a GET with cache and 'only-if-cached'
264 uri = urlparse.urljoin(base, "304/test_etag.txt")
265 (response, content) = self.http.request(uri, "GET")
266 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
267 self.assertEqual(response.fromcache, True)
268 self.assertEqual(response.status, 200)
269
jcgregorioe4ce13e2006-04-02 03:05:08 +0000270 def testGetOnlyIfCachedCacheMiss(self):
271 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorioe4ce13e2006-04-02 03:05:08 +0000272 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorioe202d212009-07-16 14:57:52 -0400273 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
jcgregorioe4ce13e2006-04-02 03:05:08 +0000274 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400275 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000276
277 def testGetOnlyIfCachedNoCacheAtAll(self):
278 # Test that can do a GET with no cache with 'only-if-cached'
279 # Of course, there might be an intermediary beyond us
280 # that responds to the 'only-if-cached', so this
281 # test can't really be guaranteed to pass.
282 http = httplib2.Http()
283 uri = urlparse.urljoin(base, "304/test_etag.txt")
284 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
285 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400286 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000287
jcgregorio2d66d4f2006-02-07 05:34:14 +0000288 def testUserAgent(self):
289 # Test that we provide a default user-agent
290 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000291 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000292 self.assertEqual(response.status, 200)
293 self.assertTrue(content.startswith("Python-httplib2/"))
294
295 def testUserAgentNonDefault(self):
296 # Test that the default user-agent can be over-ridden
joe.gregoriof28536d2007-10-23 14:10:11 +0000297
jcgregorio2d66d4f2006-02-07 05:34:14 +0000298 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000299 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000300 self.assertEqual(response.status, 200)
301 self.assertTrue(content.startswith("fred/1.0"))
302
303 def testGet300WithLocation(self):
304 # Test the we automatically follow 300 redirects if a Location: header is provided
305 uri = urlparse.urljoin(base, "300/with-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000306 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000307 self.assertEqual(response.status, 200)
308 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000309 self.assertEqual(response.previous.status, 300)
310 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000311
312 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000313 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000314 self.assertEqual(response.status, 200)
315 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000316 self.assertEqual(response.previous.status, 300)
317 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000318
jcgregorio2f1e1422007-05-03 13:17:33 +0000319 def testGet300WithLocationNoRedirect(self):
320 # Test the we automatically follow 300 redirects if a Location: header is provided
321 self.http.follow_redirects = False
322 uri = urlparse.urljoin(base, "300/with-location-header.asis")
323 (response, content) = self.http.request(uri, "GET")
324 self.assertEqual(response.status, 300)
325
jcgregorio2d66d4f2006-02-07 05:34:14 +0000326 def testGet300WithoutLocation(self):
327 # Not giving a Location: header in a 300 response is acceptable
328 # In which case we just return the 300 response
329 uri = urlparse.urljoin(base, "300/without-location-header.asis")
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, 300)
332 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000333 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000334
335 def testGet301(self):
336 # Test that we automatically follow 301 redirects
337 # and that we cache the 301 response
338 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000339 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000340 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000341 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000342 self.assertTrue(response.has_key('content-location'))
343 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000344 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000345 self.assertEqual(response.previous.status, 301)
346 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000347
jcgregorio36140b52006-06-13 02:17:52 +0000348 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000349 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000350 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000351 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000352 self.assertEqual(response.previous.status, 301)
353 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000354
Joe Gregorio694a8122011-02-13 21:40:09 -0500355 def testHead301(self):
356 # Test that we automatically follow 301 redirects
357 uri = urlparse.urljoin(base, "301/onestep.asis")
358 destination = urlparse.urljoin(base, "302/final-destination.txt")
359 (response, content) = self.http.request(uri, "HEAD")
360 self.assertEqual(response.status, 200)
361 self.assertEqual(response.previous.status, 301)
362 self.assertEqual(response.previous.fromcache, False)
jcgregorio2f1e1422007-05-03 13:17:33 +0000363
364 def testGet301NoRedirect(self):
365 # Test that we automatically follow 301 redirects
366 # and that we cache the 301 response
367 self.http.follow_redirects = False
368 uri = urlparse.urljoin(base, "301/onestep.asis")
369 destination = urlparse.urljoin(base, "302/final-destination.txt")
370 (response, content) = self.http.request(uri, "GET")
371 self.assertEqual(response.status, 301)
372
373
jcgregorio2d66d4f2006-02-07 05:34:14 +0000374 def testGet302(self):
375 # Test that we automatically follow 302 redirects
376 # and that we DO NOT cache the 302 response
377 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000378 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000379 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000380 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000381 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000382 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000383 self.assertEqual(response.previous.status, 302)
384 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000385
386 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000387 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000388 self.assertEqual(response.status, 200)
389 self.assertEqual(response.fromcache, True)
jcgregorio772adc82006-11-17 21:52:34 +0000390 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000391 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000392 self.assertEqual(response.previous.status, 302)
393 self.assertEqual(response.previous.fromcache, False)
jcgregorio772adc82006-11-17 21:52:34 +0000394 self.assertEqual(response.previous['content-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000395
396 uri = urlparse.urljoin(base, "302/twostep.asis")
397
jcgregorio36140b52006-06-13 02:17:52 +0000398 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000399 self.assertEqual(response.status, 200)
400 self.assertEqual(response.fromcache, True)
401 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000402 self.assertEqual(response.previous.status, 302)
403 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000404
405 def testGet302RedirectionLimit(self):
406 # Test that we can set a lower redirection limit
407 # and that we raise an exception when we exceed
408 # that limit.
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400409 self.http.force_exception_to_status_code = False
jcgregorio07a9a4a2007-03-08 21:18:39 +0000410
jcgregorio2d66d4f2006-02-07 05:34:14 +0000411 uri = urlparse.urljoin(base, "302/twostep.asis")
412 try:
jcgregorio36140b52006-06-13 02:17:52 +0000413 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000414 self.fail("This should not happen")
415 except httplib2.RedirectLimit:
416 pass
417 except Exception, e:
418 self.fail("Threw wrong kind of exception ")
419
jcgregorio07a9a4a2007-03-08 21:18:39 +0000420 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400421 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000422
423 (response, content) = self.http.request(uri, "GET", redirections = 1)
424 self.assertEqual(response.status, 500)
425 self.assertTrue(response.reason.startswith("Redirected more"))
426 self.assertEqual("302", response['status'])
427 self.assertTrue(content.startswith("<html>"))
428 self.assertTrue(response.previous != None)
429
jcgregorio2d66d4f2006-02-07 05:34:14 +0000430 def testGet302NoLocation(self):
431 # Test that we throw an exception when we get
432 # a 302 with no Location: header.
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400433 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000434 uri = urlparse.urljoin(base, "302/no-location.asis")
435 try:
jcgregorio36140b52006-06-13 02:17:52 +0000436 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000437 self.fail("Should never reach here")
438 except httplib2.RedirectMissingLocation:
439 pass
440 except Exception, e:
441 self.fail("Threw wrong kind of exception ")
442
jcgregorio07a9a4a2007-03-08 21:18:39 +0000443 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400444 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000445
446 (response, content) = self.http.request(uri, "GET")
447 self.assertEqual(response.status, 500)
448 self.assertTrue(response.reason.startswith("Redirected but"))
449 self.assertEqual("302", response['status'])
450 self.assertTrue(content.startswith("This is content"))
Joe Gregorio84e33252011-05-03 09:09:13 -0400451
Joe Gregorioac335ff2011-11-14 12:29:03 -0500452 def testGet301ViaHttps(self):
453 # Google always redirects to https://www.google.com
454 (response, content) = self.http.request("https://code.google.com/apis/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000455 self.assertEqual(200, response.status)
Joe Gregorioac335ff2011-11-14 12:29:03 -0500456 self.assertEqual(301, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000457
458 def testGetViaHttps(self):
459 # Test that we can handle HTTPS
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400460 (response, content) = self.http.request("https://www.google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000461 self.assertEqual(200, response.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000462
463 def testGetViaHttpsSpecViolationOnLocation(self):
464 # Test that we follow redirects through HTTPS
465 # even if they violate the spec by including
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400466 # a relative Location: header instead of an
jcgregorio2d66d4f2006-02-07 05:34:14 +0000467 # absolute one.
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400468 (response, content) = self.http.request("https://www.google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000469 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000470 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000471
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400472 def testSslCertValidation(self):
473 if sys.version_info >= (2, 6):
474 # Test that we get an ssl.SSLError when specifying a non-existent CA
475 # certs file.
476 http = httplib2.Http(ca_certs='/nosuchfile')
477 self.assertRaises(ssl.SSLError,
478 http.request, "https://www.google.com/", "GET")
479
480 # Test that we get a SSLHandshakeError if we try to access
481 # https;//www.google.com, using a CA cert file that doesn't contain
482 # the CA Gogole uses (i.e., simulating a cert that's not signed by a
483 # trusted CA).
484 other_ca_certs = os.path.join(
485 os.path.dirname(os.path.abspath(httplib2.__file__ )),
486 "test", "other_cacerts.txt")
487 http = httplib2.Http(ca_certs=other_ca_certs)
488 self.assertRaises(httplib2.SSLHandshakeError,
489 http.request, "https://www.google.com/", "GET")
490
Joe Gregorioc69dc782011-06-23 08:56:59 -0400491 def testSslCertValidationDoubleDots(self):
492 if sys.version_info >= (2, 6):
493 # Test that we get match a double dot cert
494 try:
495 self.http.request("https://1.www.appspot.com/", "GET")
496 except httplib2.CertificateHostnameMismatch:
497 self.fail('cert with *.*.appspot.com should not raise an exception.')
498
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400499 def testSslHostnameValidation(self):
Joe Gregorio3e563132012-03-02 10:52:45 -0500500 pass
501 # No longer a valid test.
502 #if sys.version_info >= (2, 6):
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400503 # The SSL server at google.com:443 returns a certificate for
504 # 'www.google.com', which results in a host name mismatch.
505 # Note that this test only works because the ssl module and httplib2
506 # do not support SNI; for requests specifying a server name of
507 # 'google.com' via SNI, a matching cert would be returned.
Joe Gregorio3e563132012-03-02 10:52:45 -0500508 # self.assertRaises(httplib2.CertificateHostnameMismatch,
509 # self.http.request, "https://google.com/", "GET")
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400510
511 def testSslCertValidationWithoutSslModuleFails(self):
512 if sys.version_info < (2, 6):
513 http = httplib2.Http(disable_ssl_certificate_validation=False)
514 self.assertRaises(httplib2.CertificateValidationUnsupported,
515 http.request, "https://www.google.com/", "GET")
jcgregoriode8238d2007-03-07 19:08:26 +0000516
517 def testGetViaHttpsKeyCert(self):
jcgregorio2f1e1422007-05-03 13:17:33 +0000518 # At this point I can only test
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400519 # that the key and cert files are passed in
520 # correctly to httplib. It would be nice to have
jcgregorio2f1e1422007-05-03 13:17:33 +0000521 # a real https endpoint to test against.
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400522
523 # bitworking.org presents an certificate for a non-matching host
524 # (*.webfaction.com), so we need to disable cert checking for this test.
525 http = httplib2.Http(timeout=2, disable_ssl_certificate_validation=True)
jcgregoriode8238d2007-03-07 19:08:26 +0000526
527 http.add_certificate("akeyfile", "acertfile", "bitworking.org")
528 try:
529 (response, content) = http.request("https://bitworking.org", "GET")
530 except:
531 pass
532 self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
533 self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
534
jcgregorio2f1e1422007-05-03 13:17:33 +0000535 try:
536 (response, content) = http.request("https://notthere.bitworking.org", "GET")
537 except:
538 pass
539 self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
540 self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
541
542
543
jcgregoriode8238d2007-03-07 19:08:26 +0000544
jcgregorio2d66d4f2006-02-07 05:34:14 +0000545 def testGet303(self):
546 # Do a follow-up GET on a Location: header
547 # returned from a POST that gave a 303.
548 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000549 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000550 self.assertEqual(response.status, 200)
551 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000552 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000553
jcgregorio2f1e1422007-05-03 13:17:33 +0000554 def testGet303NoRedirect(self):
555 # Do a follow-up GET on a Location: header
556 # returned from a POST that gave a 303.
557 self.http.follow_redirects = False
558 uri = urlparse.urljoin(base, "303/303.cgi")
559 (response, content) = self.http.request(uri, "POST", " ")
560 self.assertEqual(response.status, 303)
561
jcgregorio2d66d4f2006-02-07 05:34:14 +0000562 def test303ForDifferentMethods(self):
563 # Test that all methods can be used
564 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400565 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000566 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000567 self.assertEqual(response['x-method'], method_on_303)
568
Joe Gregoriob30ed372012-07-23 14:45:17 -0400569 def test303AndForwardAuthorizationHeader(self):
570 # Test that all methods can be used
571 uri = urlparse.urljoin(base, "303/redirect-to-header-reflector.cgi")
572 headers = {'authorization': 'Bearer foo'}
573 response, content = self.http.request(uri, 'GET', body=" ",
574 headers=headers)
575 # self.assertTrue('authorization' not in content)
576 self.http.follow_all_redirects = True
577 self.http.forward_authorization_headers = True
578 response, content = self.http.request(uri, 'GET', body=" ",
579 headers=headers)
580 # Oh, how I wish Apache didn't eat the Authorization header.
581 # self.assertTrue('authorization' in content)
582
jcgregorio2d66d4f2006-02-07 05:34:14 +0000583 def testGet304(self):
584 # Test that we use ETags properly to validate our cache
585 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000586 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000587 self.assertNotEqual(response['etag'], "")
588
jcgregorio36140b52006-06-13 02:17:52 +0000589 (response, content) = self.http.request(uri, "GET")
590 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000591 self.assertEqual(response.status, 200)
592 self.assertEqual(response.fromcache, True)
593
jcgregorio90fb4a42006-11-17 16:19:47 +0000594 cache_file_name = os.path.join(cacheDirName, httplib2.safename(httplib2.urlnorm(uri)[-1]))
595 f = open(cache_file_name, "r")
596 status_line = f.readline()
597 f.close()
598
599 self.assertTrue(status_line.startswith("status:"))
600
jcgregorio36140b52006-06-13 02:17:52 +0000601 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000602 self.assertEqual(response.status, 200)
603 self.assertEqual(response.fromcache, True)
604
jcgregorio36140b52006-06-13 02:17:52 +0000605 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000606 self.assertEqual(response.status, 206)
607 self.assertEqual(response.fromcache, False)
608
jcgregorio25185622006-10-28 05:12:34 +0000609 def testGetIgnoreEtag(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400610 # Test that we can forcibly ignore ETags
jcgregorio25185622006-10-28 05:12:34 +0000611 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
612 (response, content) = self.http.request(uri, "GET")
613 self.assertNotEqual(response['etag'], "")
614
615 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
616 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400617 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
jcgregorio25185622006-10-28 05:12:34 +0000618
619 self.http.ignore_etag = True
620 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
621 d = self.reflector(content)
622 self.assertEqual(response.fromcache, False)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400623 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
jcgregorio25185622006-10-28 05:12:34 +0000624
jcgregorio4b145e82007-01-18 19:46:34 +0000625 def testOverrideEtag(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400626 # Test that we can forcibly ignore ETags
jcgregorio4b145e82007-01-18 19:46:34 +0000627 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
628 (response, content) = self.http.request(uri, "GET")
629 self.assertNotEqual(response['etag'], "")
630
631 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'})
632 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400633 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
634 self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio4b145e82007-01-18 19:46:34 +0000635
pilgrim00a352e2009-05-29 04:04:44 +0000636 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000637 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400638 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
639 self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio25185622006-10-28 05:12:34 +0000640
pilgrim00a352e2009-05-29 04:04:44 +0000641#MAP-commented this out because it consistently fails
642# def testGet304EndToEnd(self):
643# # Test that end to end headers get overwritten in the cache
644# uri = urlparse.urljoin(base, "304/end2end.cgi")
645# (response, content) = self.http.request(uri, "GET")
646# self.assertNotEqual(response['etag'], "")
647# old_date = response['date']
648# time.sleep(2)
649#
650# (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
651# # The response should be from the cache, but the Date: header should be updated.
652# new_date = response['date']
653# self.assertNotEqual(new_date, old_date)
654# self.assertEqual(response.status, 200)
655# self.assertEqual(response.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000656
657 def testGet304LastModified(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400658 # Test that we can still handle a 304
jcgregorio2d66d4f2006-02-07 05:34:14 +0000659 # by only using the last-modified cache validator.
660 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000661 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000662
663 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000664 (response, content) = self.http.request(uri, "GET")
665 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000666 self.assertEqual(response.status, 200)
667 self.assertEqual(response.fromcache, True)
668
669 def testGet307(self):
670 # Test that we do follow 307 redirects but
671 # do not cache the 307
672 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000673 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000674 self.assertEqual(response.status, 200)
675 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000676 self.assertEqual(response.previous.status, 307)
677 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000678
jcgregorio36140b52006-06-13 02:17:52 +0000679 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000680 self.assertEqual(response.status, 200)
681 self.assertEqual(response.fromcache, True)
682 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000683 self.assertEqual(response.previous.status, 307)
684 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000685
686 def testGet410(self):
687 # Test that we pass 410's through
688 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000689 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000690 self.assertEqual(response.status, 410)
691
chris dent89f15142009-12-24 14:02:57 -0600692 def testVaryHeaderSimple(self):
693 """
694 RFC 2616 13.6
695 When the cache receives a subsequent request whose Request-URI
696 specifies one or more cache entries including a Vary header field,
697 the cache MUST NOT use such a cache entry to construct a response
698 to the new request unless all of the selecting request-headers
699 present in the new request match the corresponding stored
700 request-headers in the original request.
701 """
702 # test that the vary header is sent
703 uri = urlparse.urljoin(base, "vary/accept.asis")
704 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
705 self.assertEqual(response.status, 200)
706 self.assertTrue(response.has_key('vary'))
707
708 # get the resource again, from the cache since accept header in this
709 # request is the same as the request
710 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
711 self.assertEqual(response.status, 200)
712 self.assertEqual(response.fromcache, True, msg="Should be from cache")
713
714 # get the resource again, not from cache since Accept headers does not match
715 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
716 self.assertEqual(response.status, 200)
717 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
718
719 # get the resource again, without any Accept header, so again no match
720 (response, content) = self.http.request(uri, "GET")
721 self.assertEqual(response.status, 200)
722 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
723
724 def testNoVary(self):
725 # when there is no vary, a different Accept header (e.g.) should not
726 # impact if the cache is used
727 # test that the vary header is not sent
728 uri = urlparse.urljoin(base, "vary/no-vary.asis")
729 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
730 self.assertEqual(response.status, 200)
731 self.assertFalse(response.has_key('vary'))
732
733 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
734 self.assertEqual(response.status, 200)
735 self.assertEqual(response.fromcache, True, msg="Should be from cache")
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400736
chris dent89f15142009-12-24 14:02:57 -0600737 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
738 self.assertEqual(response.status, 200)
739 self.assertEqual(response.fromcache, True, msg="Should be from cache")
740
741 def testVaryHeaderDouble(self):
742 uri = urlparse.urljoin(base, "vary/accept-double.asis")
743 (response, content) = self.http.request(uri, "GET", headers={
744 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
745 self.assertEqual(response.status, 200)
746 self.assertTrue(response.has_key('vary'))
747
748 # we are from cache
749 (response, content) = self.http.request(uri, "GET", headers={
750 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
751 self.assertEqual(response.fromcache, True, msg="Should be from cache")
752
753 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
754 self.assertEqual(response.status, 200)
755 self.assertEqual(response.fromcache, False)
756
757 # get the resource again, not from cache, varied headers don't match exact
758 (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
759 self.assertEqual(response.status, 200)
760 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
761
jcgregorio88ef89b2010-05-13 23:42:11 -0400762 def testVaryUnusedHeader(self):
763 # A header's value is not considered to vary if it's not used at all.
764 uri = urlparse.urljoin(base, "vary/unused-header.asis")
765 (response, content) = self.http.request(uri, "GET", headers={
766 'Accept': 'text/plain'})
767 self.assertEqual(response.status, 200)
768 self.assertTrue(response.has_key('vary'))
769
770 # we are from cache
771 (response, content) = self.http.request(uri, "GET", headers={
772 'Accept': 'text/plain',})
773 self.assertEqual(response.fromcache, True, msg="Should be from cache")
774
chris dent89f15142009-12-24 14:02:57 -0600775
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000776 def testHeadGZip(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400777 # Test that we don't try to decompress a HEAD response
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000778 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
779 (response, content) = self.http.request(uri, "HEAD")
780 self.assertEqual(response.status, 200)
781 self.assertNotEqual(int(response['content-length']), 0)
782 self.assertEqual(content, "")
783
jcgregorio2d66d4f2006-02-07 05:34:14 +0000784 def testGetGZip(self):
785 # Test that we support gzip compression
786 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000787 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000788 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000789 self.assertFalse(response.has_key('content-encoding'))
joe.gregorio8b6d2312007-12-16 05:42:07 +0000790 self.assertTrue(response.has_key('-content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000791 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000792 self.assertEqual(content, "This is the final destination.\n")
793
Joe Gregoriod1137c52011-02-13 19:27:35 -0500794 def testPostAndGZipResponse(self):
795 uri = urlparse.urljoin(base, "gzip/post.cgi")
796 (response, content) = self.http.request(uri, "POST", body=" ")
797 self.assertEqual(response.status, 200)
798 self.assertFalse(response.has_key('content-encoding'))
799 self.assertTrue(response.has_key('-content-encoding'))
800
jcgregorio2d66d4f2006-02-07 05:34:14 +0000801 def testGetGZipFailure(self):
802 # Test that we raise a good exception when the gzip fails
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400803 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000804 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
805 try:
jcgregorio36140b52006-06-13 02:17:52 +0000806 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000807 self.fail("Should never reach here")
808 except httplib2.FailedToDecompressContent:
809 pass
810 except Exception:
811 self.fail("Threw wrong kind of exception")
812
jcgregorio07a9a4a2007-03-08 21:18:39 +0000813 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400814 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000815
816 (response, content) = self.http.request(uri, "GET")
817 self.assertEqual(response.status, 500)
818 self.assertTrue(response.reason.startswith("Content purported"))
819
820 def testTimeout(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400821 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000822 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
823 try:
824 import socket
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400825 socket.setdefaulttimeout(1)
jcgregorio07a9a4a2007-03-08 21:18:39 +0000826 except:
827 # Don't run the test if we can't set the timeout
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400828 return
jcgregorio07a9a4a2007-03-08 21:18:39 +0000829 (response, content) = self.http.request(uri)
830 self.assertEqual(response.status, 408)
831 self.assertTrue(response.reason.startswith("Request Timeout"))
832 self.assertTrue(content.startswith("Request Timeout"))
833
jcgregoriob2697912007-03-09 02:23:47 +0000834 def testIndividualTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000835 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
836 http = httplib2.Http(timeout=1)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400837 http.force_exception_to_status_code = True
jcgregoriob2697912007-03-09 02:23:47 +0000838
839 (response, content) = http.request(uri)
840 self.assertEqual(response.status, 408)
841 self.assertTrue(response.reason.startswith("Request Timeout"))
842 self.assertTrue(content.startswith("Request Timeout"))
843
jcgregorio07a9a4a2007-03-08 21:18:39 +0000844
Joe Gregorio1a7609f2009-07-16 10:59:44 -0400845 def testHTTPSInitTimeout(self):
846 c = httplib2.HTTPSConnectionWithTimeout('localhost', 80, timeout=47)
847 self.assertEqual(47, c.timeout)
848
jcgregorio2d66d4f2006-02-07 05:34:14 +0000849 def testGetDeflate(self):
850 # Test that we support deflate compression
851 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000852 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000853 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000854 self.assertFalse(response.has_key('content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000855 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000856 self.assertEqual(content, "This is the final destination.")
857
858 def testGetDeflateFailure(self):
859 # Test that we raise a good exception when the deflate fails
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400860 self.http.force_exception_to_status_code = False
jcgregorio07a9a4a2007-03-08 21:18:39 +0000861
jcgregorio2d66d4f2006-02-07 05:34:14 +0000862 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
863 try:
jcgregorio36140b52006-06-13 02:17:52 +0000864 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000865 self.fail("Should never reach here")
866 except httplib2.FailedToDecompressContent:
867 pass
868 except Exception:
869 self.fail("Threw wrong kind of exception")
870
jcgregorio07a9a4a2007-03-08 21:18:39 +0000871 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400872 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000873
874 (response, content) = self.http.request(uri, "GET")
875 self.assertEqual(response.status, 500)
876 self.assertTrue(response.reason.startswith("Content purported"))
877
jcgregorio2d66d4f2006-02-07 05:34:14 +0000878 def testGetDuplicateHeaders(self):
879 # Test that duplicate headers get concatenated via ','
880 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000881 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000882 self.assertEqual(response.status, 200)
883 self.assertEqual(content, "This is content\n")
884 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
885
886 def testGetCacheControlNoCache(self):
887 # Test Cache-Control: no-cache on requests
888 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000889 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000890 self.assertNotEqual(response['etag'], "")
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)
894
jcgregorio36140b52006-06-13 02:17:52 +0000895 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000896 self.assertEqual(response.status, 200)
897 self.assertEqual(response.fromcache, False)
898
899 def testGetCacheControlPragmaNoCache(self):
900 # Test Pragma: no-cache on requests
901 uri = urlparse.urljoin(base, "304/test_etag.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000902 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000903 self.assertNotEqual(response['etag'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000904 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000905 self.assertEqual(response.status, 200)
906 self.assertEqual(response.fromcache, True)
907
jcgregorio36140b52006-06-13 02:17:52 +0000908 (response, content) = self.http.request(uri, "GET", headers={'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000909 self.assertEqual(response.status, 200)
910 self.assertEqual(response.fromcache, False)
911
912 def testGetCacheControlNoStoreRequest(self):
913 # A no-store request means that the response should not be stored.
914 uri = urlparse.urljoin(base, "304/test_etag.txt")
915
jcgregorio36140b52006-06-13 02:17:52 +0000916 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000917 self.assertEqual(response.status, 200)
918 self.assertEqual(response.fromcache, False)
919
jcgregorio36140b52006-06-13 02:17:52 +0000920 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000921 self.assertEqual(response.status, 200)
922 self.assertEqual(response.fromcache, False)
923
924 def testGetCacheControlNoStoreResponse(self):
925 # A no-store response means that the response should not be stored.
926 uri = urlparse.urljoin(base, "no-store/no-store.asis")
927
jcgregorio36140b52006-06-13 02:17:52 +0000928 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000929 self.assertEqual(response.status, 200)
930 self.assertEqual(response.fromcache, False)
931
jcgregorio36140b52006-06-13 02:17:52 +0000932 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000933 self.assertEqual(response.status, 200)
934 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000935
936 def testGetCacheControlNoCacheNoStoreRequest(self):
937 # Test that a no-store, no-cache clears the entry from the cache
938 # even if it was cached previously.
939 uri = urlparse.urljoin(base, "304/test_etag.txt")
940
jcgregorio36140b52006-06-13 02:17:52 +0000941 (response, content) = self.http.request(uri, "GET")
942 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000943 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000944 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
945 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000946 self.assertEqual(response.status, 200)
947 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000948
949 def testUpdateInvalidatesCache(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400950 # Test that calling PUT or DELETE on a
jcgregorio2d66d4f2006-02-07 05:34:14 +0000951 # URI that is cache invalidates that cache.
952 uri = urlparse.urljoin(base, "304/test_etag.txt")
953
jcgregorio36140b52006-06-13 02:17:52 +0000954 (response, content) = self.http.request(uri, "GET")
955 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000956 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000957 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000958 self.assertEqual(response.status, 405)
959
jcgregorio36140b52006-06-13 02:17:52 +0000960 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000961 self.assertEqual(response.fromcache, False)
962
963 def testUpdateUsesCachedETag(self):
Joe Gregoriobd682082011-05-24 14:06:09 -0400964 # Test that we natively support http://www.w3.org/1999/04/Editing/
jcgregorio2d66d4f2006-02-07 05:34:14 +0000965 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
966
jcgregorio36140b52006-06-13 02:17:52 +0000967 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000968 self.assertEqual(response.status, 200)
969 self.assertEqual(response.fromcache, False)
jcgregorio36140b52006-06-13 02:17:52 +0000970 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000971 self.assertEqual(response.status, 200)
972 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -0400973 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000974 self.assertEqual(response.status, 200)
Joe Gregoriocd868102009-09-29 17:09:16 -0400975 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000976 self.assertEqual(response.status, 412)
977
Joe Gregoriobd682082011-05-24 14:06:09 -0400978 def testUpdatePatchUsesCachedETag(self):
979 # Test that we natively support http://www.w3.org/1999/04/Editing/
980 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
981
982 (response, content) = self.http.request(uri, "GET")
983 self.assertEqual(response.status, 200)
984 self.assertEqual(response.fromcache, False)
985 (response, content) = self.http.request(uri, "GET")
986 self.assertEqual(response.status, 200)
987 self.assertEqual(response.fromcache, True)
988 (response, content) = self.http.request(uri, "PATCH", body="foo")
989 self.assertEqual(response.status, 200)
990 (response, content) = self.http.request(uri, "PATCH", body="foo")
991 self.assertEqual(response.status, 412)
992
993
joe.gregorio700f04d2008-09-06 04:46:32 +0000994 def testUpdateUsesCachedETagAndOCMethod(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400995 # Test that we natively support http://www.w3.org/1999/04/Editing/
joe.gregorio700f04d2008-09-06 04:46:32 +0000996 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
997
998 (response, content) = self.http.request(uri, "GET")
999 self.assertEqual(response.status, 200)
1000 self.assertEqual(response.fromcache, False)
1001 (response, content) = self.http.request(uri, "GET")
1002 self.assertEqual(response.status, 200)
1003 self.assertEqual(response.fromcache, True)
1004 self.http.optimistic_concurrency_methods.append("DELETE")
1005 (response, content) = self.http.request(uri, "DELETE")
1006 self.assertEqual(response.status, 200)
1007
1008
jcgregorio4b145e82007-01-18 19:46:34 +00001009 def testUpdateUsesCachedETagOverridden(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001010 # Test that we natively support http://www.w3.org/1999/04/Editing/
jcgregorio4b145e82007-01-18 19:46:34 +00001011 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
1012
1013 (response, content) = self.http.request(uri, "GET")
1014 self.assertEqual(response.status, 200)
1015 self.assertEqual(response.fromcache, False)
1016 (response, content) = self.http.request(uri, "GET")
1017 self.assertEqual(response.status, 200)
1018 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -04001019 (response, content) = self.http.request(uri, "PUT", body="foo", headers={'if-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +00001020 self.assertEqual(response.status, 412)
1021
jcgregorio2d66d4f2006-02-07 05:34:14 +00001022 def testBasicAuth(self):
1023 # Test Basic Authentication
1024 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001025 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001026 self.assertEqual(response.status, 401)
1027
1028 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +00001029 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001030 self.assertEqual(response.status, 401)
1031
jcgregorio36140b52006-06-13 02:17:52 +00001032 self.http.add_credentials('joe', 'password')
1033 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001034 self.assertEqual(response.status, 200)
1035
1036 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001037 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001038 self.assertEqual(response.status, 200)
1039
jcgregoriode8238d2007-03-07 19:08:26 +00001040 def testBasicAuthWithDomain(self):
1041 # Test Basic Authentication
1042 uri = urlparse.urljoin(base, "basic/file.txt")
1043 (response, content) = self.http.request(uri, "GET")
1044 self.assertEqual(response.status, 401)
1045
1046 uri = urlparse.urljoin(base, "basic/")
1047 (response, content) = self.http.request(uri, "GET")
1048 self.assertEqual(response.status, 401)
1049
1050 self.http.add_credentials('joe', 'password', "example.org")
1051 (response, content) = self.http.request(uri, "GET")
1052 self.assertEqual(response.status, 401)
1053
1054 uri = urlparse.urljoin(base, "basic/file.txt")
1055 (response, content) = self.http.request(uri, "GET")
1056 self.assertEqual(response.status, 401)
1057
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001058 domain = urlparse.urlparse(base)[1]
jcgregoriode8238d2007-03-07 19:08:26 +00001059 self.http.add_credentials('joe', 'password', domain)
1060 (response, content) = self.http.request(uri, "GET")
1061 self.assertEqual(response.status, 200)
1062
1063 uri = urlparse.urljoin(base, "basic/file.txt")
1064 (response, content) = self.http.request(uri, "GET")
1065 self.assertEqual(response.status, 200)
1066
1067
1068
1069
1070
1071
jcgregorio2d66d4f2006-02-07 05:34:14 +00001072 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +00001073 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +00001074 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001075 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001076 self.assertEqual(response.status, 401)
1077
1078 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +00001079 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001080 self.assertEqual(response.status, 401)
1081
jcgregorio36140b52006-06-13 02:17:52 +00001082 self.http.add_credentials('fred', 'barney')
1083 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001084 self.assertEqual(response.status, 200)
1085
1086 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001087 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001088 self.assertEqual(response.status, 200)
1089
1090 def testBasicAuthNested(self):
1091 # Test Basic Authentication with resources
1092 # that are nested
1093 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001094 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001095 self.assertEqual(response.status, 401)
1096
1097 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001098 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001099 self.assertEqual(response.status, 401)
1100
jcgregorioadbb4f82006-05-19 15:17:42 +00001101 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +00001102 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001103
1104 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001105 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001106 self.assertEqual(response.status, 200)
1107
1108 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001109 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001110 self.assertEqual(response.status, 401)
1111
jcgregorio36140b52006-06-13 02:17:52 +00001112 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001113
1114 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001115 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001116 self.assertEqual(response.status, 200)
1117
1118 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001119 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001120 self.assertEqual(response.status, 200)
1121
1122 def testDigestAuth(self):
1123 # Test that we support Digest Authentication
1124 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +00001125 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001126 self.assertEqual(response.status, 401)
1127
jcgregorio36140b52006-06-13 02:17:52 +00001128 self.http.add_credentials('joe', 'password')
1129 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001130 self.assertEqual(response.status, 200)
1131
1132 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001133 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001134
1135 def testDigestAuthNextNonceAndNC(self):
1136 # Test that if the server sets nextnonce that we reset
1137 # the nonce count back to 1
1138 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001139 self.http.add_credentials('joe', 'password')
1140 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001141 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1142 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +00001143 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001144 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
1145 self.assertEqual(response.status, 200)
1146
1147 if info.has_key('nextnonce'):
1148 self.assertEqual(info2['nc'], 1)
1149
1150 def testDigestAuthStale(self):
1151 # Test that we can handle a nonce becoming stale
1152 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001153 self.http.add_credentials('joe', 'password')
1154 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001155 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1156 self.assertEqual(response.status, 200)
1157
1158 time.sleep(3)
1159 # Sleep long enough that the nonce becomes stale
1160
jcgregorio36140b52006-06-13 02:17:52 +00001161 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001162 self.assertFalse(response.fromcache)
1163 self.assertTrue(response._stale_digest)
1164 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
1165 self.assertEqual(response.status, 200)
1166
1167 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +00001168 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +00001169
1170 def testReflector(self):
1171 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +00001172 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001173 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001174 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001175
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001176 def testConnectionClose(self):
1177 uri = "http://www.google.com/"
1178 (response, content) = self.http.request(uri, "GET")
1179 for c in self.http.connections.values():
1180 self.assertNotEqual(None, c.sock)
1181 (response, content) = self.http.request(uri, "GET", headers={"connection": "close"})
1182 for c in self.http.connections.values():
1183 self.assertEqual(None, c.sock)
1184
1185
jcgregorio36140b52006-06-13 02:17:52 +00001186try:
1187 import memcache
1188 class HttpTestMemCached(HttpTest):
1189 def setUp(self):
1190 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +00001191 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +00001192 self.http = httplib2.Http(self.cache)
1193 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +00001194 # Not exactly sure why the sleep is needed here, but
1195 # if not present then some unit tests that rely on caching
1196 # fail. Memcached seems to lose some sets immediately
1197 # after a flush_all if the set is to a value that
1198 # was previously cached. (Maybe the flush is handled async?)
1199 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +00001200 self.http.clear_credentials()
1201except:
1202 pass
1203
1204
1205
chris dent89f15142009-12-24 14:02:57 -06001206
jcgregoriodb8dfc82006-03-31 14:59:46 +00001207# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +00001208
1209class HttpPrivateTest(unittest.TestCase):
1210
1211 def testParseCacheControl(self):
1212 # Test that we can parse the Cache-Control header
1213 self.assertEqual({}, httplib2._parse_cache_control({}))
1214 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
1215 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
1216 self.assertEqual(cc['no-cache'], 1)
1217 self.assertEqual(cc['max-age'], '7200')
1218 cc = httplib2._parse_cache_control({'cache-control': ' , '})
1219 self.assertEqual(cc[''], 1)
1220
Joe Gregorioe314e8b2009-07-16 20:11:28 -04001221 try:
1222 cc = httplib2._parse_cache_control({'cache-control': 'Max-age=3600;post-check=1800,pre-check=3600'})
1223 self.assertTrue("max-age" in cc)
1224 except:
1225 self.fail("Should not throw exception")
1226
jcgregorio2d66d4f2006-02-07 05:34:14 +00001227 def testNormalizeHeaders(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001228 # Test that we normalize headers to lowercase
jcgregorio2d66d4f2006-02-07 05:34:14 +00001229 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
1230 self.assertTrue(h.has_key('cache-control'))
1231 self.assertTrue(h.has_key('other'))
1232 self.assertEqual('Stuff', h['other'])
1233
1234 def testExpirationModelTransparent(self):
1235 # Test that no-cache makes our request TRANSPARENT
1236 response_headers = {
1237 'cache-control': 'max-age=7200'
1238 }
1239 request_headers = {
1240 'cache-control': 'no-cache'
1241 }
1242 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
1243
jcgregorio45865012007-01-18 16:38:22 +00001244 def testMaxAgeNonNumeric(self):
1245 # Test that no-cache makes our request TRANSPARENT
1246 response_headers = {
1247 'cache-control': 'max-age=fred, min-fresh=barney'
1248 }
1249 request_headers = {
1250 }
1251 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1252
1253
jcgregorio2d66d4f2006-02-07 05:34:14 +00001254 def testExpirationModelNoCacheResponse(self):
1255 # The date and expires point to an entry that should be
1256 # FRESH, but the no-cache over-rides that.
1257 now = time.time()
1258 response_headers = {
1259 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1260 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1261 'cache-control': 'no-cache'
1262 }
1263 request_headers = {
1264 }
1265 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1266
1267 def testExpirationModelStaleRequestMustReval(self):
1268 # must-revalidate forces STALE
1269 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
1270
1271 def testExpirationModelStaleResponseMustReval(self):
1272 # must-revalidate forces STALE
1273 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
1274
1275 def testExpirationModelFresh(self):
1276 response_headers = {
1277 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1278 'cache-control': 'max-age=2'
1279 }
1280 request_headers = {
1281 }
1282 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1283 time.sleep(3)
1284 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1285
1286 def testExpirationMaxAge0(self):
1287 response_headers = {
1288 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1289 'cache-control': 'max-age=0'
1290 }
1291 request_headers = {
1292 }
1293 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1294
1295 def testExpirationModelDateAndExpires(self):
1296 now = time.time()
1297 response_headers = {
1298 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1299 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1300 }
1301 request_headers = {
1302 }
1303 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1304 time.sleep(3)
1305 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1306
jcgregoriof9511052007-06-01 14:56:34 +00001307 def testExpiresZero(self):
1308 now = time.time()
1309 response_headers = {
1310 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1311 'expires': "0",
1312 }
1313 request_headers = {
1314 }
1315 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1316
jcgregorio2d66d4f2006-02-07 05:34:14 +00001317 def testExpirationModelDateOnly(self):
1318 now = time.time()
1319 response_headers = {
1320 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
1321 }
1322 request_headers = {
1323 }
1324 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1325
1326 def testExpirationModelOnlyIfCached(self):
1327 response_headers = {
1328 }
1329 request_headers = {
1330 'cache-control': 'only-if-cached',
1331 }
1332 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1333
1334 def testExpirationModelMaxAgeBoth(self):
1335 now = time.time()
1336 response_headers = {
1337 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1338 'cache-control': 'max-age=2'
1339 }
1340 request_headers = {
1341 'cache-control': 'max-age=0'
1342 }
1343 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1344
1345 def testExpirationModelDateAndExpiresMinFresh1(self):
1346 now = time.time()
1347 response_headers = {
1348 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1349 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1350 }
1351 request_headers = {
1352 'cache-control': 'min-fresh=2'
1353 }
1354 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1355
1356 def testExpirationModelDateAndExpiresMinFresh2(self):
1357 now = time.time()
1358 response_headers = {
1359 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1360 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1361 }
1362 request_headers = {
1363 'cache-control': 'min-fresh=2'
1364 }
1365 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1366
1367 def testParseWWWAuthenticateEmpty(self):
1368 res = httplib2._parse_www_authenticate({})
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001369 self.assertEqual(len(res.keys()), 0)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001370
jcgregoriofd22e432006-04-27 02:00:08 +00001371 def testParseWWWAuthenticate(self):
1372 # different uses of spaces around commas
1373 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
1374 self.assertEqual(len(res.keys()), 1)
1375 self.assertEqual(len(res['test'].keys()), 5)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001376
jcgregoriofd22e432006-04-27 02:00:08 +00001377 # tokens with non-alphanum
1378 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
1379 self.assertEqual(len(res.keys()), 1)
1380 self.assertEqual(len(res['t*!%#st'].keys()), 2)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001381
jcgregoriofd22e432006-04-27 02:00:08 +00001382 # quoted string with quoted pairs
1383 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
1384 self.assertEqual(len(res.keys()), 1)
1385 self.assertEqual(res['test']['realm'], 'a "test" realm')
1386
1387 def testParseWWWAuthenticateStrict(self):
1388 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
1389 self.testParseWWWAuthenticate();
1390 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
1391
jcgregorio2d66d4f2006-02-07 05:34:14 +00001392 def testParseWWWAuthenticateBasic(self):
1393 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
1394 basic = res['basic']
1395 self.assertEqual('me', basic['realm'])
1396
1397 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
1398 basic = res['basic']
1399 self.assertEqual('me', basic['realm'])
1400 self.assertEqual('MD5', basic['algorithm'])
1401
1402 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
1403 basic = res['basic']
1404 self.assertEqual('me', basic['realm'])
1405 self.assertEqual('MD5', basic['algorithm'])
1406
1407 def testParseWWWAuthenticateBasic2(self):
1408 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
1409 basic = res['basic']
1410 self.assertEqual('me', basic['realm'])
1411 self.assertEqual('fred', basic['other'])
1412
1413 def testParseWWWAuthenticateBasic3(self):
1414 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
1415 basic = res['basic']
1416 self.assertEqual('me', basic['realm'])
1417
1418
1419 def testParseWWWAuthenticateDigest(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001420 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001421 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
1422 digest = res['digest']
1423 self.assertEqual('testrealm@host.com', digest['realm'])
1424 self.assertEqual('auth,auth-int', digest['qop'])
1425
1426
1427 def testParseWWWAuthenticateMultiple(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001428 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001429 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
1430 digest = res['digest']
1431 self.assertEqual('testrealm@host.com', digest['realm'])
1432 self.assertEqual('auth,auth-int', digest['qop'])
1433 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1434 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1435 basic = res['basic']
1436 self.assertEqual('me', basic['realm'])
1437
1438 def testParseWWWAuthenticateMultiple2(self):
1439 # Handle an added comma between challenges, which might get thrown in if the challenges were
1440 # originally sent in separate www-authenticate headers.
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001441 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001442 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
1443 digest = res['digest']
1444 self.assertEqual('testrealm@host.com', digest['realm'])
1445 self.assertEqual('auth,auth-int', digest['qop'])
1446 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1447 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1448 basic = res['basic']
1449 self.assertEqual('me', basic['realm'])
1450
1451 def testParseWWWAuthenticateMultiple3(self):
1452 # Handle an added comma between challenges, which might get thrown in if the challenges were
1453 # originally sent in separate www-authenticate headers.
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001454 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001455 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1456 digest = res['digest']
1457 self.assertEqual('testrealm@host.com', digest['realm'])
1458 self.assertEqual('auth,auth-int', digest['qop'])
1459 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1460 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1461 basic = res['basic']
1462 self.assertEqual('me', basic['realm'])
1463 wsse = res['wsse']
1464 self.assertEqual('foo', wsse['realm'])
1465 self.assertEqual('UsernameToken', wsse['profile'])
1466
1467 def testParseWWWAuthenticateMultiple4(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001468 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1469 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001470 digest = res['digest']
1471 self.assertEqual('test-real.m@host.com', digest['realm'])
1472 self.assertEqual('\tauth,auth-int', digest['qop'])
1473 self.assertEqual('(*)&^&$%#', digest['nonce'])
1474
1475 def testParseWWWAuthenticateMoreQuoteCombos(self):
1476 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
1477 digest = res['digest']
1478 self.assertEqual('myrealm', digest['realm'])
1479
Joe Gregorio6fa3cf22011-02-13 22:45:06 -05001480 def testParseWWWAuthenticateMalformed(self):
1481 try:
1482 res = httplib2._parse_www_authenticate({'www-authenticate':'OAuth "Facebook Platform" "invalid_token" "Invalid OAuth access token."'})
1483 self.fail("should raise an exception")
1484 except httplib2.MalformedHeader:
1485 pass
1486
jcgregorio2d66d4f2006-02-07 05:34:14 +00001487 def testDigestObject(self):
1488 credentials = ('joe', 'password')
1489 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001490 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001491 headers = {}
1492 response = {
1493 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
1494 }
1495 content = ""
Joe Gregorio875a8b52011-06-13 14:06:23 -04001496
jcgregorio6cbab7e2006-04-21 20:35:43 +00001497 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001498 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
Joe Gregorio875a8b52011-06-13 14:06:23 -04001499 our_request = "authorization: %s" % headers['authorization']
1500 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"'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001501 self.assertEqual(our_request, working_request)
1502
Joe Gregorio03d99102011-06-22 16:55:52 -04001503 def testDigestObjectWithOpaque(self):
1504 credentials = ('joe', 'password')
1505 host = None
1506 request_uri = '/projects/httplib2/test/digest/'
1507 headers = {}
1508 response = {
1509 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", opaque="atestopaque"'
1510 }
1511 content = ""
1512
1513 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
1514 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
1515 our_request = "authorization: %s" % headers['authorization']
1516 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", opaque="atestopaque"'
1517 self.assertEqual(our_request, working_request)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001518
1519 def testDigestObjectStale(self):
1520 credentials = ('joe', 'password')
1521 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001522 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001523 headers = {}
1524 response = httplib2.Response({ })
1525 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1526 response.status = 401
1527 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001528 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001529 # Returns true to force a retry
1530 self.assertTrue( d.response(response, content) )
1531
1532 def testDigestObjectAuthInfo(self):
1533 credentials = ('joe', 'password')
1534 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001535 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001536 headers = {}
1537 response = httplib2.Response({ })
1538 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1539 response['authentication-info'] = 'nextnonce="fred"'
1540 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001541 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001542 # Returns true to force a retry
1543 self.assertFalse( d.response(response, content) )
1544 self.assertEqual('fred', d.challenge['nonce'])
1545 self.assertEqual(1, d.challenge['nc'])
1546
1547 def testWsseAlgorithm(self):
1548 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
1549 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
1550 self.assertEqual(expected, digest)
1551
jcgregoriodb8dfc82006-03-31 14:59:46 +00001552 def testEnd2End(self):
1553 # one end to end header
1554 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
1555 end2end = httplib2._get_end2end_headers(response)
1556 self.assertTrue('content-type' in end2end)
1557 self.assertTrue('te' not in end2end)
1558 self.assertTrue('connection' not in end2end)
1559
1560 # one end to end header that gets eliminated
1561 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
1562 end2end = httplib2._get_end2end_headers(response)
1563 self.assertTrue('content-type' not in end2end)
1564 self.assertTrue('te' not in end2end)
1565 self.assertTrue('connection' not in end2end)
1566
1567 # Degenerate case of no headers
1568 response = {}
1569 end2end = httplib2._get_end2end_headers(response)
1570 self.assertEquals(0, len(end2end))
1571
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001572 # Degenerate case of connection referrring to a header not passed in
jcgregoriodb8dfc82006-03-31 14:59:46 +00001573 response = {'connection': 'content-type'}
1574 end2end = httplib2._get_end2end_headers(response)
1575 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001576
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001577
1578class TestProxyInfo(unittest.TestCase):
1579 def setUp(self):
1580 self.orig_env = dict(os.environ)
1581
1582 def tearDown(self):
1583 os.environ.clear()
1584 os.environ.update(self.orig_env)
1585
1586 def test_from_url(self):
1587 pi = httplib2.ProxyInfo.from_url('http://myproxy.example.com')
1588 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1589 self.assertEquals(pi.proxy_port, 80)
1590 self.assertEquals(pi.proxy_user, None)
1591
1592 def test_from_url_ident(self):
1593 pi = httplib2.ProxyInfo.from_url('http://zoidberg:fish@someproxy:99')
1594 self.assertEquals(pi.proxy_host, 'someproxy')
1595 self.assertEquals(pi.proxy_port, 99)
1596 self.assertEquals(pi.proxy_user, 'zoidberg')
1597 self.assertEquals(pi.proxy_pass, 'fish')
1598
1599 def test_from_env(self):
1600 os.environ['http_proxy'] = 'http://myproxy.example.com:8080'
1601 pi = httplib2.ProxyInfo.from_environment()
1602 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1603 self.assertEquals(pi.proxy_port, 8080)
1604 self.assertEquals(pi.bypass_hosts, [])
1605
1606 def test_from_env_no_proxy(self):
1607 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1608 os.environ['https_proxy'] = 'http://myproxy.example.com:81'
1609 os.environ['no_proxy'] = 'localhost,otherhost.domain.local'
1610 pi = httplib2.ProxyInfo.from_environment('https')
1611 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1612 self.assertEquals(pi.proxy_port, 81)
1613 self.assertEquals(pi.bypass_hosts, ['localhost',
1614 'otherhost.domain.local'])
1615
1616 def test_from_env_none(self):
1617 os.environ.clear()
1618 pi = httplib2.ProxyInfo.from_environment()
1619 self.assertEquals(pi, None)
1620
Jason R. Coombs43840892011-08-09 10:30:46 -04001621 def test_applies_to(self):
1622 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1623 os.environ['https_proxy'] = 'http://myproxy.example.com:81'
Jason R. Coombs96279c52011-08-16 12:53:27 -04001624 os.environ['no_proxy'] = 'localhost,otherhost.domain.local,example.com'
Jason R. Coombs43840892011-08-09 10:30:46 -04001625 pi = httplib2.ProxyInfo.from_environment()
1626 self.assertFalse(pi.applies_to('localhost'))
1627 self.assertTrue(pi.applies_to('www.google.com'))
Jason R. Coombs96279c52011-08-16 12:53:27 -04001628 self.assertFalse(pi.applies_to('www.example.com'))
1629
1630 def test_no_proxy_star(self):
1631 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1632 os.environ['NO_PROXY'] = '*'
1633 pi = httplib2.ProxyInfo.from_environment()
1634 for host in ('localhost', '169.254.38.192', 'www.google.com'):
1635 self.assertFalse(pi.applies_to(host))
Jason R. Coombs43840892011-08-09 10:30:46 -04001636
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001637
chris dent89f15142009-12-24 14:02:57 -06001638if __name__ == '__main__':
1639 unittest.main()