blob: d9bfdb6b976b33204dfc6d9bae26223a0c158329 [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 Gregorio46546a62012-10-03 14:31:10 -040023import pickle
Joe Gregoriob6c90c42011-02-11 01:03:22 -050024import socket
25import sys
jcgregoriode8238d2007-03-07 19:08:26 +000026import time
Joe Gregoriob6c90c42011-02-11 01:03:22 -050027import unittest
28import urlparse
jcgregorio8421f272006-02-14 18:19:51 +000029
Joe Gregoriob53de9b2011-06-07 15:44:51 -040030try:
31 import ssl
32except ImportError:
33 pass
Joe Gregorio694a8122011-02-13 21:40:09 -050034
jcgregorio8421f272006-02-14 18:19:51 +000035# Python 2.3 support
36if not hasattr(unittest.TestCase, 'assertTrue'):
37 unittest.TestCase.assertTrue = unittest.TestCase.failUnless
38 unittest.TestCase.assertFalse = unittest.TestCase.failIf
39
jcgregorio2d66d4f2006-02-07 05:34:14 +000040# The test resources base uri
41base = 'http://bitworking.org/projects/httplib2/test/'
42#base = 'http://localhost/projects/httplib2/test/'
jcgregorio90fb4a42006-11-17 16:19:47 +000043cacheDirName = ".cache"
jcgregorio2d66d4f2006-02-07 05:34:14 +000044
jcgregoriode8238d2007-03-07 19:08:26 +000045
46class CredentialsTest(unittest.TestCase):
47 def test(self):
48 c = httplib2.Credentials()
49 c.add("joe", "password")
50 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
51 self.assertEqual(("joe", "password"), list(c.iter(""))[0])
52 c.add("fred", "password2", "wellformedweb.org")
53 self.assertEqual(("joe", "password"), list(c.iter("bitworking.org"))[0])
54 self.assertEqual(1, len(list(c.iter("bitworking.org"))))
55 self.assertEqual(2, len(list(c.iter("wellformedweb.org"))))
56 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
57 c.clear()
58 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
59 c.add("fred", "password2", "wellformedweb.org")
60 self.assertTrue(("fred", "password2") in list(c.iter("wellformedweb.org")))
61 self.assertEqual(0, len(list(c.iter("bitworking.org"))))
62 self.assertEqual(0, len(list(c.iter(""))))
63
64
jcgregorio2d66d4f2006-02-07 05:34:14 +000065class ParserTest(unittest.TestCase):
66 def testFromStd66(self):
67 self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com"))
68 self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com"))
69 self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080"))
70 self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/"))
71 self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path"))
72 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2"))
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 self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred"))
75
jcgregorio2d66d4f2006-02-07 05:34:14 +000076
jcgregorioa46fe4e2006-11-16 04:13:45 +000077class UrlNormTest(unittest.TestCase):
78 def test(self):
79 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1])
80 self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1])
81 self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1])
82 self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1])
83 self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1])
jcgregoriob4e9ab02006-11-17 15:53:15 +000084 self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80"))
jcgregorio132d28e2007-01-23 16:22:53 +000085 try:
86 httplib2.urlnorm("/")
87 self.fail("Non-absolute URIs should raise an exception")
88 except httplib2.RelativeURIError:
89 pass
jcgregorioa46fe4e2006-11-16 04:13:45 +000090
91class UrlSafenameTest(unittest.TestCase):
92 def test(self):
93 # Test that different URIs end up generating different safe names
94 self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b"))
95 self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b"))
96 self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b"))
97 self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1]))
98 self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b"))
99 self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www"))
100 # Test the max length limits
101 uri = "http://" + ("w" * 200) + ".org"
102 uri2 = "http://" + ("w" * 201) + ".org"
103 self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri))
104 # Max length should be 200 + 1 (",") + 32
105 self.assertEqual(233, len(httplib2.safename(uri2)))
106 self.assertEqual(233, len(httplib2.safename(uri)))
107 # Unicode
jcgregoriodebceec2006-12-12 20:26:02 +0000108 if sys.version_info >= (2,3):
109 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 +0000110
jcgregorio14644372007-07-30 14:13:37 +0000111class _MyResponse(StringIO.StringIO):
112 def __init__(self, body, **kwargs):
113 StringIO.StringIO.__init__(self, body)
114 self.headers = kwargs
115
116 def iteritems(self):
117 return self.headers.iteritems()
118
119
120class _MyHTTPConnection(object):
121 "This class is just a mock of httplib.HTTPConnection used for testing"
122
123 def __init__(self, host, port=None, key_file=None, cert_file=None,
joe.gregoriof28536d2007-10-23 14:10:11 +0000124 strict=None, timeout=None, proxy_info=None):
jcgregorio14644372007-07-30 14:13:37 +0000125 self.host = host
126 self.port = port
127 self.timeout = timeout
128 self.log = ""
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400129 self.sock = None
jcgregorio14644372007-07-30 14:13:37 +0000130
131 def set_debuglevel(self, level):
132 pass
133
134 def connect(self):
135 "Connect to a host on a given port."
136 pass
137
138 def close(self):
139 pass
140
141 def request(self, method, request_uri, body, headers):
142 pass
143
144 def getresponse(self):
145 return _MyResponse("the body", status="200")
jcgregorioa46fe4e2006-11-16 04:13:45 +0000146
Joe Gregorio1d3a7092013-03-08 14:14:56 -0500147class _MyHTTPBadStatusConnection(object):
148 "Mock of httplib.HTTPConnection that raises BadStatusLine."
149
150 num_calls = 0
151
152 def __init__(self, host, port=None, key_file=None, cert_file=None,
153 strict=None, timeout=None, proxy_info=None):
154 self.host = host
155 self.port = port
156 self.timeout = timeout
157 self.log = ""
158 self.sock = None
159 _MyHTTPBadStatusConnection.num_calls = 0
160
161 def set_debuglevel(self, level):
162 pass
163
164 def connect(self):
165 pass
166
167 def close(self):
168 pass
169
170 def request(self, method, request_uri, body, headers):
171 pass
172
173 def getresponse(self):
174 _MyHTTPBadStatusConnection.num_calls += 1
175 raise httplib.BadStatusLine("")
176
jcgregorio90fb4a42006-11-17 16:19:47 +0000177
jcgregorio2d66d4f2006-02-07 05:34:14 +0000178class HttpTest(unittest.TestCase):
179 def setUp(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400180 if os.path.exists(cacheDirName):
jcgregorio7e3608f2006-06-15 13:01:53 +0000181 [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400182
183 if sys.version_info < (2, 6):
184 disable_cert_validation = True
185 else:
186 disable_cert_validation = False
187 self.http = httplib2.Http(
188 cacheDirName,
189 disable_ssl_certificate_validation=disable_cert_validation)
jcgregorio36140b52006-06-13 02:17:52 +0000190 self.http.clear_credentials()
jcgregorio2d66d4f2006-02-07 05:34:14 +0000191
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500192 def testIPv6NoSSL(self):
193 try:
194 self.http.request("http://[::1]/")
195 except socket.gaierror:
196 self.fail("should get the address family right for IPv6")
197 except socket.error:
198 # Even if IPv6 isn't installed on a machine it should just raise socket.error
199 pass
200
201 def testIPv6SSL(self):
202 try:
203 self.http.request("https://[::1]/")
204 except socket.gaierror:
205 self.fail("should get the address family right for IPv6")
Jason R. Coombscee15da2011-08-09 09:13:34 -0400206 except httplib2.CertificateHostnameMismatch:
207 # We connected and verified that the certificate doesn't match
208 # the name. Good enough.
209 pass
Joe Gregoriof3ee17b2011-02-13 11:59:51 -0500210 except socket.error:
211 # Even if IPv6 isn't installed on a machine it should just raise socket.error
212 pass
213
jcgregorio14644372007-07-30 14:13:37 +0000214 def testConnectionType(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400215 self.http.force_exception_to_status_code = False
jcgregorio14644372007-07-30 14:13:37 +0000216 response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection)
217 self.assertEqual(response['content-location'], "http://bitworking.org")
218 self.assertEqual(content, "the body")
219
Joe Gregorio1d3a7092013-03-08 14:14:56 -0500220 def testBadStatusLineRetry(self):
221 old_retries = httplib2.RETRIES
222 httplib2.RETRIES = 1
223 self.http.force_exception_to_status_code = False
224 try:
225 response, content = self.http.request("http://bitworking.org",
226 connection_type=_MyHTTPBadStatusConnection)
227 except httplib.BadStatusLine:
228 self.assertEqual(2, _MyHTTPBadStatusConnection.num_calls)
229 httplib2.RETRIES = old_retries
230
jcgregorio6a638172007-01-23 16:40:23 +0000231 def testGetUnknownServer(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400232 self.http.force_exception_to_status_code = False
jcgregorio6a638172007-01-23 16:40:23 +0000233 try:
234 self.http.request("http://fred.bitworking.org/")
235 self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.")
236 except httplib2.ServerNotFoundError:
237 pass
238
jcgregorio07a9a4a2007-03-08 21:18:39 +0000239 # Now test with exceptions turned off
240 self.http.force_exception_to_status_code = True
241
242 (response, content) = self.http.request("http://fred.bitworking.org/")
243 self.assertEqual(response['content-type'], 'text/plain')
244 self.assertTrue(content.startswith("Unable to find"))
245 self.assertEqual(response.status, 400)
246
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500247 def testGetConnectionRefused(self):
248 self.http.force_exception_to_status_code = False
249 try:
250 self.http.request("http://localhost:7777/")
251 self.fail("An socket.error exception must be thrown on Connection Refused.")
252 except socket.error:
253 pass
254
255 # Now test with exceptions turned off
256 self.http.force_exception_to_status_code = True
257
258 (response, content) = self.http.request("http://localhost:7777/")
259 self.assertEqual(response['content-type'], 'text/plain')
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400260 self.assertTrue("Connection refused" in content
261 or "actively refused" in content,
262 "Unexpected status %(content)s" % vars())
Joe Gregoriob6c90c42011-02-11 01:03:22 -0500263 self.assertEqual(response.status, 400)
264
jcgregorioa898f8f2006-12-12 17:16:55 +0000265 def testGetIRI(self):
jcgregoriodebceec2006-12-12 20:26:02 +0000266 if sys.version_info >= (2,3):
267 uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}")
268 (response, content) = self.http.request(uri, "GET")
269 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400270 self.assertTrue(d.has_key('QUERY_STRING'))
271 self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0)
272
jcgregorio2d66d4f2006-02-07 05:34:14 +0000273 def testGetIsDefaultMethod(self):
274 # Test that GET is the default method
275 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000276 (response, content) = self.http.request(uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000277 self.assertEqual(response['x-method'], "GET")
278
279 def testDifferentMethods(self):
280 # Test that all methods can be used
281 uri = urlparse.urljoin(base, "methods/method_reflector.cgi")
282 for method in ["GET", "PUT", "DELETE", "POST"]:
jcgregorio36140b52006-06-13 02:17:52 +0000283 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000284 self.assertEqual(response['x-method'], method)
285
Joe Gregoriob628c0b2009-07-16 12:28:04 -0400286 def testHeadRead(self):
287 # Test that we don't try to read the response of a HEAD request
288 # since httplib blocks response.read() for HEAD requests.
289 # Oddly enough this doesn't appear as a problem when doing HEAD requests
290 # against Apache servers.
291 uri = "http://www.google.com/"
292 (response, content) = self.http.request(uri, "HEAD")
293 self.assertEqual(response.status, 200)
294 self.assertEqual(content, "")
295
jcgregorio2d66d4f2006-02-07 05:34:14 +0000296 def testGetNoCache(self):
297 # Test that can do a GET w/o the cache turned on.
298 http = httplib2.Http()
299 uri = urlparse.urljoin(base, "304/test_etag.txt")
300 (response, content) = http.request(uri, "GET")
301 self.assertEqual(response.status, 200)
jcgregorioa0713ab2006-07-01 05:21:34 +0000302 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000303
Joe Gregorioe202d212009-07-16 14:57:52 -0400304 def testGetOnlyIfCachedCacheHit(self):
305 # Test that can do a GET with cache and 'only-if-cached'
306 uri = urlparse.urljoin(base, "304/test_etag.txt")
307 (response, content) = self.http.request(uri, "GET")
308 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
309 self.assertEqual(response.fromcache, True)
310 self.assertEqual(response.status, 200)
311
jcgregorioe4ce13e2006-04-02 03:05:08 +0000312 def testGetOnlyIfCachedCacheMiss(self):
313 # Test that can do a GET with no cache with 'only-if-cached'
jcgregorioe4ce13e2006-04-02 03:05:08 +0000314 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorioe202d212009-07-16 14:57:52 -0400315 (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
jcgregorioe4ce13e2006-04-02 03:05:08 +0000316 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400317 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000318
319 def testGetOnlyIfCachedNoCacheAtAll(self):
320 # Test that can do a GET with no cache with 'only-if-cached'
321 # Of course, there might be an intermediary beyond us
322 # that responds to the 'only-if-cached', so this
323 # test can't really be guaranteed to pass.
324 http = httplib2.Http()
325 uri = urlparse.urljoin(base, "304/test_etag.txt")
326 (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'})
327 self.assertEqual(response.fromcache, False)
Joe Gregorioe202d212009-07-16 14:57:52 -0400328 self.assertEqual(response.status, 504)
jcgregorioe4ce13e2006-04-02 03:05:08 +0000329
jcgregorio2d66d4f2006-02-07 05:34:14 +0000330 def testUserAgent(self):
331 # Test that we provide a default user-agent
332 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000333 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000334 self.assertEqual(response.status, 200)
335 self.assertTrue(content.startswith("Python-httplib2/"))
336
337 def testUserAgentNonDefault(self):
338 # Test that the default user-agent can be over-ridden
joe.gregoriof28536d2007-10-23 14:10:11 +0000339
jcgregorio2d66d4f2006-02-07 05:34:14 +0000340 uri = urlparse.urljoin(base, "user-agent/test.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000341 (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000342 self.assertEqual(response.status, 200)
343 self.assertTrue(content.startswith("fred/1.0"))
344
345 def testGet300WithLocation(self):
346 # Test the we automatically follow 300 redirects if a Location: header is provided
347 uri = urlparse.urljoin(base, "300/with-location-header.asis")
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)
350 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000351 self.assertEqual(response.previous.status, 300)
352 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000353
354 # Confirm that the intermediate 300 is not cached
jcgregorio36140b52006-06-13 02:17:52 +0000355 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000356 self.assertEqual(response.status, 200)
357 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000358 self.assertEqual(response.previous.status, 300)
359 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000360
jcgregorio2f1e1422007-05-03 13:17:33 +0000361 def testGet300WithLocationNoRedirect(self):
362 # Test the we automatically follow 300 redirects if a Location: header is provided
363 self.http.follow_redirects = False
364 uri = urlparse.urljoin(base, "300/with-location-header.asis")
365 (response, content) = self.http.request(uri, "GET")
366 self.assertEqual(response.status, 300)
367
jcgregorio2d66d4f2006-02-07 05:34:14 +0000368 def testGet300WithoutLocation(self):
369 # Not giving a Location: header in a 300 response is acceptable
370 # In which case we just return the 300 response
371 uri = urlparse.urljoin(base, "300/without-location-header.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000372 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000373 self.assertEqual(response.status, 300)
374 self.assertTrue(response['content-type'].startswith("text/html"))
jcgregorioa0713ab2006-07-01 05:21:34 +0000375 self.assertEqual(response.previous, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000376
377 def testGet301(self):
378 # Test that we automatically follow 301 redirects
379 # and that we cache the 301 response
380 uri = urlparse.urljoin(base, "301/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000381 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000382 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000383 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000384 self.assertTrue(response.has_key('content-location'))
385 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000386 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000387 self.assertEqual(response.previous.status, 301)
388 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000389
jcgregorio36140b52006-06-13 02:17:52 +0000390 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000391 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000392 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000393 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000394 self.assertEqual(response.previous.status, 301)
395 self.assertEqual(response.previous.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000396
Joe Gregorio694a8122011-02-13 21:40:09 -0500397 def testHead301(self):
398 # Test that we automatically follow 301 redirects
399 uri = urlparse.urljoin(base, "301/onestep.asis")
400 destination = urlparse.urljoin(base, "302/final-destination.txt")
401 (response, content) = self.http.request(uri, "HEAD")
402 self.assertEqual(response.status, 200)
403 self.assertEqual(response.previous.status, 301)
404 self.assertEqual(response.previous.fromcache, False)
jcgregorio2f1e1422007-05-03 13:17:33 +0000405
406 def testGet301NoRedirect(self):
407 # Test that we automatically follow 301 redirects
408 # and that we cache the 301 response
409 self.http.follow_redirects = False
410 uri = urlparse.urljoin(base, "301/onestep.asis")
411 destination = urlparse.urljoin(base, "302/final-destination.txt")
412 (response, content) = self.http.request(uri, "GET")
413 self.assertEqual(response.status, 301)
414
415
jcgregorio2d66d4f2006-02-07 05:34:14 +0000416 def testGet302(self):
417 # Test that we automatically follow 302 redirects
418 # and that we DO NOT cache the 302 response
419 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio8e300b92006-11-07 16:44:35 +0000420 destination = urlparse.urljoin(base, "302/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000421 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000422 self.assertEqual(response.status, 200)
jcgregorio772adc82006-11-17 21:52:34 +0000423 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000424 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000425 self.assertEqual(response.previous.status, 302)
426 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000427
428 uri = urlparse.urljoin(base, "302/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000429 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000430 self.assertEqual(response.status, 200)
431 self.assertEqual(response.fromcache, True)
jcgregorio772adc82006-11-17 21:52:34 +0000432 self.assertEqual(response['content-location'], destination)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000433 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000434 self.assertEqual(response.previous.status, 302)
435 self.assertEqual(response.previous.fromcache, False)
jcgregorio772adc82006-11-17 21:52:34 +0000436 self.assertEqual(response.previous['content-location'], uri)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000437
438 uri = urlparse.urljoin(base, "302/twostep.asis")
439
jcgregorio36140b52006-06-13 02:17:52 +0000440 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000441 self.assertEqual(response.status, 200)
442 self.assertEqual(response.fromcache, True)
443 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000444 self.assertEqual(response.previous.status, 302)
445 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000446
447 def testGet302RedirectionLimit(self):
448 # Test that we can set a lower redirection limit
449 # and that we raise an exception when we exceed
450 # that limit.
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400451 self.http.force_exception_to_status_code = False
jcgregorio07a9a4a2007-03-08 21:18:39 +0000452
jcgregorio2d66d4f2006-02-07 05:34:14 +0000453 uri = urlparse.urljoin(base, "302/twostep.asis")
454 try:
jcgregorio36140b52006-06-13 02:17:52 +0000455 (response, content) = self.http.request(uri, "GET", redirections = 1)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000456 self.fail("This should not happen")
457 except httplib2.RedirectLimit:
458 pass
459 except Exception, e:
460 self.fail("Threw wrong kind of exception ")
461
jcgregorio07a9a4a2007-03-08 21:18:39 +0000462 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400463 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000464
465 (response, content) = self.http.request(uri, "GET", redirections = 1)
466 self.assertEqual(response.status, 500)
467 self.assertTrue(response.reason.startswith("Redirected more"))
468 self.assertEqual("302", response['status'])
469 self.assertTrue(content.startswith("<html>"))
470 self.assertTrue(response.previous != None)
471
jcgregorio2d66d4f2006-02-07 05:34:14 +0000472 def testGet302NoLocation(self):
473 # Test that we throw an exception when we get
474 # a 302 with no Location: header.
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400475 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000476 uri = urlparse.urljoin(base, "302/no-location.asis")
477 try:
jcgregorio36140b52006-06-13 02:17:52 +0000478 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000479 self.fail("Should never reach here")
480 except httplib2.RedirectMissingLocation:
481 pass
482 except Exception, e:
483 self.fail("Threw wrong kind of exception ")
484
jcgregorio07a9a4a2007-03-08 21:18:39 +0000485 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400486 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000487
488 (response, content) = self.http.request(uri, "GET")
489 self.assertEqual(response.status, 500)
490 self.assertTrue(response.reason.startswith("Redirected but"))
491 self.assertEqual("302", response['status'])
492 self.assertTrue(content.startswith("This is content"))
Joe Gregorio84e33252011-05-03 09:09:13 -0400493
Joe Gregorioac335ff2011-11-14 12:29:03 -0500494 def testGet301ViaHttps(self):
495 # Google always redirects to https://www.google.com
496 (response, content) = self.http.request("https://code.google.com/apis/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000497 self.assertEqual(200, response.status)
Joe Gregorioac335ff2011-11-14 12:29:03 -0500498 self.assertEqual(301, response.previous.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000499
500 def testGetViaHttps(self):
501 # Test that we can handle HTTPS
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400502 (response, content) = self.http.request("https://www.google.com/adsense/", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000503 self.assertEqual(200, response.status)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000504
505 def testGetViaHttpsSpecViolationOnLocation(self):
506 # Test that we follow redirects through HTTPS
507 # even if they violate the spec by including
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400508 # a relative Location: header instead of an
jcgregorio2d66d4f2006-02-07 05:34:14 +0000509 # absolute one.
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400510 (response, content) = self.http.request("https://www.google.com/adsense", "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000511 self.assertEqual(200, response.status)
jcgregorioa0713ab2006-07-01 05:21:34 +0000512 self.assertNotEqual(None, response.previous)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000513
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400514 def testSslCertValidation(self):
515 if sys.version_info >= (2, 6):
516 # Test that we get an ssl.SSLError when specifying a non-existent CA
517 # certs file.
518 http = httplib2.Http(ca_certs='/nosuchfile')
519 self.assertRaises(ssl.SSLError,
520 http.request, "https://www.google.com/", "GET")
521
522 # Test that we get a SSLHandshakeError if we try to access
523 # https;//www.google.com, using a CA cert file that doesn't contain
524 # the CA Gogole uses (i.e., simulating a cert that's not signed by a
525 # trusted CA).
526 other_ca_certs = os.path.join(
527 os.path.dirname(os.path.abspath(httplib2.__file__ )),
528 "test", "other_cacerts.txt")
529 http = httplib2.Http(ca_certs=other_ca_certs)
530 self.assertRaises(httplib2.SSLHandshakeError,
531 http.request, "https://www.google.com/", "GET")
532
Joe Gregorioc69dc782011-06-23 08:56:59 -0400533 def testSslCertValidationDoubleDots(self):
Joe Gregorio0197ec82014-03-06 14:56:29 -0500534 pass
535 # No longer a valid test.
536 #if sys.version_info >= (2, 6):
537 # Test that we get match a double dot cert
538 #try:
539 # self.http.request("https://www.appspot.com/", "GET")
540 #except httplib2.CertificateHostnameMismatch:
541 # self.fail('cert with *.*.appspot.com should not raise an exception.')
Joe Gregorioc69dc782011-06-23 08:56:59 -0400542
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400543 def testSslHostnameValidation(self):
Joe Gregorio3e563132012-03-02 10:52:45 -0500544 pass
545 # No longer a valid test.
546 #if sys.version_info >= (2, 6):
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400547 # The SSL server at google.com:443 returns a certificate for
548 # 'www.google.com', which results in a host name mismatch.
549 # Note that this test only works because the ssl module and httplib2
550 # do not support SNI; for requests specifying a server name of
551 # 'google.com' via SNI, a matching cert would be returned.
Joe Gregorio3e563132012-03-02 10:52:45 -0500552 # self.assertRaises(httplib2.CertificateHostnameMismatch,
553 # self.http.request, "https://google.com/", "GET")
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400554
555 def testSslCertValidationWithoutSslModuleFails(self):
556 if sys.version_info < (2, 6):
557 http = httplib2.Http(disable_ssl_certificate_validation=False)
558 self.assertRaises(httplib2.CertificateValidationUnsupported,
559 http.request, "https://www.google.com/", "GET")
jcgregoriode8238d2007-03-07 19:08:26 +0000560
561 def testGetViaHttpsKeyCert(self):
jcgregorio2f1e1422007-05-03 13:17:33 +0000562 # At this point I can only test
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400563 # that the key and cert files are passed in
564 # correctly to httplib. It would be nice to have
jcgregorio2f1e1422007-05-03 13:17:33 +0000565 # a real https endpoint to test against.
Joe Gregoriob53de9b2011-06-07 15:44:51 -0400566
567 # bitworking.org presents an certificate for a non-matching host
568 # (*.webfaction.com), so we need to disable cert checking for this test.
569 http = httplib2.Http(timeout=2, disable_ssl_certificate_validation=True)
jcgregoriode8238d2007-03-07 19:08:26 +0000570
571 http.add_certificate("akeyfile", "acertfile", "bitworking.org")
572 try:
573 (response, content) = http.request("https://bitworking.org", "GET")
574 except:
575 pass
576 self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
577 self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
578
jcgregorio2f1e1422007-05-03 13:17:33 +0000579 try:
580 (response, content) = http.request("https://notthere.bitworking.org", "GET")
581 except:
582 pass
583 self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
584 self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
585
586
587
jcgregoriode8238d2007-03-07 19:08:26 +0000588
jcgregorio2d66d4f2006-02-07 05:34:14 +0000589 def testGet303(self):
590 # Do a follow-up GET on a Location: header
591 # returned from a POST that gave a 303.
592 uri = urlparse.urljoin(base, "303/303.cgi")
jcgregorio36140b52006-06-13 02:17:52 +0000593 (response, content) = self.http.request(uri, "POST", " ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000594 self.assertEqual(response.status, 200)
595 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000596 self.assertEqual(response.previous.status, 303)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000597
jcgregorio2f1e1422007-05-03 13:17:33 +0000598 def testGet303NoRedirect(self):
599 # Do a follow-up GET on a Location: header
600 # returned from a POST that gave a 303.
601 self.http.follow_redirects = False
602 uri = urlparse.urljoin(base, "303/303.cgi")
603 (response, content) = self.http.request(uri, "POST", " ")
604 self.assertEqual(response.status, 303)
605
jcgregorio2d66d4f2006-02-07 05:34:14 +0000606 def test303ForDifferentMethods(self):
607 # Test that all methods can be used
608 uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi")
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400609 for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]:
jcgregorio36140b52006-06-13 02:17:52 +0000610 (response, content) = self.http.request(uri, method, body=" ")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000611 self.assertEqual(response['x-method'], method_on_303)
612
Joe Gregoriob30ed372012-07-23 14:45:17 -0400613 def test303AndForwardAuthorizationHeader(self):
614 # Test that all methods can be used
615 uri = urlparse.urljoin(base, "303/redirect-to-header-reflector.cgi")
616 headers = {'authorization': 'Bearer foo'}
617 response, content = self.http.request(uri, 'GET', body=" ",
618 headers=headers)
619 # self.assertTrue('authorization' not in content)
620 self.http.follow_all_redirects = True
621 self.http.forward_authorization_headers = True
622 response, content = self.http.request(uri, 'GET', body=" ",
623 headers=headers)
624 # Oh, how I wish Apache didn't eat the Authorization header.
625 # self.assertTrue('authorization' in content)
626
jcgregorio2d66d4f2006-02-07 05:34:14 +0000627 def testGet304(self):
628 # Test that we use ETags properly to validate our cache
629 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500630 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000631 self.assertNotEqual(response['etag'], "")
632
jcgregorio36140b52006-06-13 02:17:52 +0000633 (response, content) = self.http.request(uri, "GET")
634 (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'must-revalidate'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000635 self.assertEqual(response.status, 200)
636 self.assertEqual(response.fromcache, True)
637
jcgregorio90fb4a42006-11-17 16:19:47 +0000638 cache_file_name = os.path.join(cacheDirName, httplib2.safename(httplib2.urlnorm(uri)[-1]))
639 f = open(cache_file_name, "r")
640 status_line = f.readline()
641 f.close()
642
643 self.assertTrue(status_line.startswith("status:"))
644
jcgregorio36140b52006-06-13 02:17:52 +0000645 (response, content) = self.http.request(uri, "HEAD")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000646 self.assertEqual(response.status, 200)
647 self.assertEqual(response.fromcache, True)
648
jcgregorio36140b52006-06-13 02:17:52 +0000649 (response, content) = self.http.request(uri, "GET", headers = {'range': 'bytes=0-0'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000650 self.assertEqual(response.status, 206)
651 self.assertEqual(response.fromcache, False)
652
jcgregorio25185622006-10-28 05:12:34 +0000653 def testGetIgnoreEtag(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400654 # Test that we can forcibly ignore ETags
jcgregorio25185622006-10-28 05:12:34 +0000655 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500656 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio25185622006-10-28 05:12:34 +0000657 self.assertNotEqual(response['etag'], "")
658
Joe Gregorio0197ec82014-03-06 14:56:29 -0500659 (response, content) = self.http.request(uri, "GET", headers = {'accept-encoding': 'identity', 'cache-control': 'max-age=0'})
jcgregorio25185622006-10-28 05:12:34 +0000660 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400661 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
jcgregorio25185622006-10-28 05:12:34 +0000662
663 self.http.ignore_etag = True
Joe Gregorio0197ec82014-03-06 14:56:29 -0500664 (response, content) = self.http.request(uri, "GET", headers = {'accept-encoding': 'identity', 'cache-control': 'max-age=0'})
jcgregorio25185622006-10-28 05:12:34 +0000665 d = self.reflector(content)
666 self.assertEqual(response.fromcache, False)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400667 self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH'))
jcgregorio25185622006-10-28 05:12:34 +0000668
jcgregorio4b145e82007-01-18 19:46:34 +0000669 def testOverrideEtag(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400670 # Test that we can forcibly ignore ETags
jcgregorio4b145e82007-01-18 19:46:34 +0000671 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500672 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio4b145e82007-01-18 19:46:34 +0000673 self.assertNotEqual(response['etag'], "")
674
Joe Gregorio0197ec82014-03-06 14:56:29 -0500675 (response, content) = self.http.request(uri, "GET", headers = {'accept-encoding': 'identity', 'cache-control': 'max-age=0'})
jcgregorio4b145e82007-01-18 19:46:34 +0000676 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400677 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
678 self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio4b145e82007-01-18 19:46:34 +0000679
Joe Gregorio0197ec82014-03-06 14:56:29 -0500680 (response, content) = self.http.request(uri, "GET", headers = {'accept-encoding': 'identity', 'cache-control': 'max-age=0', 'if-none-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +0000681 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400682 self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH'))
683 self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred")
jcgregorio25185622006-10-28 05:12:34 +0000684
pilgrim00a352e2009-05-29 04:04:44 +0000685#MAP-commented this out because it consistently fails
686# def testGet304EndToEnd(self):
687# # Test that end to end headers get overwritten in the cache
688# uri = urlparse.urljoin(base, "304/end2end.cgi")
689# (response, content) = self.http.request(uri, "GET")
690# self.assertNotEqual(response['etag'], "")
691# old_date = response['date']
692# time.sleep(2)
693#
694# (response, content) = self.http.request(uri, "GET", headers = {'Cache-Control': 'max-age=0'})
695# # The response should be from the cache, but the Date: header should be updated.
696# new_date = response['date']
697# self.assertNotEqual(new_date, old_date)
698# self.assertEqual(response.status, 200)
699# self.assertEqual(response.fromcache, True)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000700
701 def testGet304LastModified(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400702 # Test that we can still handle a 304
jcgregorio2d66d4f2006-02-07 05:34:14 +0000703 # by only using the last-modified cache validator.
704 uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000705 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000706
707 self.assertNotEqual(response['last-modified'], "")
jcgregorio36140b52006-06-13 02:17:52 +0000708 (response, content) = self.http.request(uri, "GET")
709 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000710 self.assertEqual(response.status, 200)
711 self.assertEqual(response.fromcache, True)
712
713 def testGet307(self):
714 # Test that we do follow 307 redirects but
715 # do not cache the 307
716 uri = urlparse.urljoin(base, "307/onestep.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000717 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000718 self.assertEqual(response.status, 200)
719 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000720 self.assertEqual(response.previous.status, 307)
721 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000722
jcgregorio36140b52006-06-13 02:17:52 +0000723 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000724 self.assertEqual(response.status, 200)
725 self.assertEqual(response.fromcache, True)
726 self.assertEqual(content, "This is the final destination.\n")
jcgregorioa0713ab2006-07-01 05:21:34 +0000727 self.assertEqual(response.previous.status, 307)
728 self.assertEqual(response.previous.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000729
730 def testGet410(self):
731 # Test that we pass 410's through
732 uri = urlparse.urljoin(base, "410/410.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000733 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000734 self.assertEqual(response.status, 410)
735
chris dent89f15142009-12-24 14:02:57 -0600736 def testVaryHeaderSimple(self):
737 """
738 RFC 2616 13.6
739 When the cache receives a subsequent request whose Request-URI
740 specifies one or more cache entries including a Vary header field,
741 the cache MUST NOT use such a cache entry to construct a response
742 to the new request unless all of the selecting request-headers
743 present in the new request match the corresponding stored
744 request-headers in the original request.
745 """
746 # test that the vary header is sent
747 uri = urlparse.urljoin(base, "vary/accept.asis")
748 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
749 self.assertEqual(response.status, 200)
750 self.assertTrue(response.has_key('vary'))
751
752 # get the resource again, from the cache since accept header in this
753 # request is the same as the request
754 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
755 self.assertEqual(response.status, 200)
756 self.assertEqual(response.fromcache, True, msg="Should be from cache")
757
758 # get the resource again, not from cache since Accept headers does not match
759 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
760 self.assertEqual(response.status, 200)
761 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
762
763 # get the resource again, without any Accept header, so again no match
764 (response, content) = self.http.request(uri, "GET")
765 self.assertEqual(response.status, 200)
766 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
767
768 def testNoVary(self):
Joe Gregorio46546a62012-10-03 14:31:10 -0400769 pass
chris dent89f15142009-12-24 14:02:57 -0600770 # when there is no vary, a different Accept header (e.g.) should not
771 # impact if the cache is used
772 # test that the vary header is not sent
Joe Gregorio46546a62012-10-03 14:31:10 -0400773 # uri = urlparse.urljoin(base, "vary/no-vary.asis")
774 # (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
775 # self.assertEqual(response.status, 200)
776 # self.assertFalse(response.has_key('vary'))
chris dent89f15142009-12-24 14:02:57 -0600777
Joe Gregorio46546a62012-10-03 14:31:10 -0400778 # (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
779 # self.assertEqual(response.status, 200)
780 # self.assertEqual(response.fromcache, True, msg="Should be from cache")
781 #
782 # (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/html'})
783 # self.assertEqual(response.status, 200)
784 # self.assertEqual(response.fromcache, True, msg="Should be from cache")
chris dent89f15142009-12-24 14:02:57 -0600785
786 def testVaryHeaderDouble(self):
787 uri = urlparse.urljoin(base, "vary/accept-double.asis")
788 (response, content) = self.http.request(uri, "GET", headers={
789 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
790 self.assertEqual(response.status, 200)
791 self.assertTrue(response.has_key('vary'))
792
793 # we are from cache
794 (response, content) = self.http.request(uri, "GET", headers={
795 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'})
796 self.assertEqual(response.fromcache, True, msg="Should be from cache")
797
798 (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'})
799 self.assertEqual(response.status, 200)
800 self.assertEqual(response.fromcache, False)
801
802 # get the resource again, not from cache, varied headers don't match exact
803 (response, content) = self.http.request(uri, "GET", headers={'Accept-Language': 'da'})
804 self.assertEqual(response.status, 200)
805 self.assertEqual(response.fromcache, False, msg="Should not be from cache")
806
jcgregorio88ef89b2010-05-13 23:42:11 -0400807 def testVaryUnusedHeader(self):
808 # A header's value is not considered to vary if it's not used at all.
809 uri = urlparse.urljoin(base, "vary/unused-header.asis")
810 (response, content) = self.http.request(uri, "GET", headers={
811 'Accept': 'text/plain'})
812 self.assertEqual(response.status, 200)
813 self.assertTrue(response.has_key('vary'))
814
815 # we are from cache
816 (response, content) = self.http.request(uri, "GET", headers={
817 'Accept': 'text/plain',})
818 self.assertEqual(response.fromcache, True, msg="Should be from cache")
819
chris dent89f15142009-12-24 14:02:57 -0600820
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000821 def testHeadGZip(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400822 # Test that we don't try to decompress a HEAD response
joe.gregorio0d4a2b82007-10-23 14:28:35 +0000823 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
824 (response, content) = self.http.request(uri, "HEAD")
825 self.assertEqual(response.status, 200)
826 self.assertNotEqual(int(response['content-length']), 0)
827 self.assertEqual(content, "")
828
jcgregorio2d66d4f2006-02-07 05:34:14 +0000829 def testGetGZip(self):
830 # Test that we support gzip compression
831 uri = urlparse.urljoin(base, "gzip/final-destination.txt")
jcgregorio36140b52006-06-13 02:17:52 +0000832 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000833 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000834 self.assertFalse(response.has_key('content-encoding'))
joe.gregorio8b6d2312007-12-16 05:42:07 +0000835 self.assertTrue(response.has_key('-content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000836 self.assertEqual(int(response['content-length']), len("This is the final destination.\n"))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000837 self.assertEqual(content, "This is the final destination.\n")
838
Joe Gregoriod1137c52011-02-13 19:27:35 -0500839 def testPostAndGZipResponse(self):
840 uri = urlparse.urljoin(base, "gzip/post.cgi")
841 (response, content) = self.http.request(uri, "POST", body=" ")
842 self.assertEqual(response.status, 200)
843 self.assertFalse(response.has_key('content-encoding'))
844 self.assertTrue(response.has_key('-content-encoding'))
845
jcgregorio2d66d4f2006-02-07 05:34:14 +0000846 def testGetGZipFailure(self):
847 # Test that we raise a good exception when the gzip fails
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400848 self.http.force_exception_to_status_code = False
jcgregorio2d66d4f2006-02-07 05:34:14 +0000849 uri = urlparse.urljoin(base, "gzip/failed-compression.asis")
850 try:
jcgregorio36140b52006-06-13 02:17:52 +0000851 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000852 self.fail("Should never reach here")
853 except httplib2.FailedToDecompressContent:
854 pass
855 except Exception:
856 self.fail("Threw wrong kind of exception")
857
jcgregorio07a9a4a2007-03-08 21:18:39 +0000858 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400859 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000860
861 (response, content) = self.http.request(uri, "GET")
862 self.assertEqual(response.status, 500)
863 self.assertTrue(response.reason.startswith("Content purported"))
864
865 def testTimeout(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400866 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000867 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
868 try:
869 import socket
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400870 socket.setdefaulttimeout(1)
jcgregorio07a9a4a2007-03-08 21:18:39 +0000871 except:
872 # Don't run the test if we can't set the timeout
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400873 return
jcgregorio07a9a4a2007-03-08 21:18:39 +0000874 (response, content) = self.http.request(uri)
875 self.assertEqual(response.status, 408)
876 self.assertTrue(response.reason.startswith("Request Timeout"))
877 self.assertTrue(content.startswith("Request Timeout"))
878
jcgregoriob2697912007-03-09 02:23:47 +0000879 def testIndividualTimeout(self):
jcgregoriob2697912007-03-09 02:23:47 +0000880 uri = urlparse.urljoin(base, "timeout/timeout.cgi")
881 http = httplib2.Http(timeout=1)
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400882 http.force_exception_to_status_code = True
jcgregoriob2697912007-03-09 02:23:47 +0000883
884 (response, content) = http.request(uri)
885 self.assertEqual(response.status, 408)
886 self.assertTrue(response.reason.startswith("Request Timeout"))
887 self.assertTrue(content.startswith("Request Timeout"))
888
jcgregorio07a9a4a2007-03-08 21:18:39 +0000889
Joe Gregorio1a7609f2009-07-16 10:59:44 -0400890 def testHTTPSInitTimeout(self):
891 c = httplib2.HTTPSConnectionWithTimeout('localhost', 80, timeout=47)
892 self.assertEqual(47, c.timeout)
893
jcgregorio2d66d4f2006-02-07 05:34:14 +0000894 def testGetDeflate(self):
895 # Test that we support deflate compression
896 uri = urlparse.urljoin(base, "deflate/deflated.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000897 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000898 self.assertEqual(response.status, 200)
jcgregorio90fb4a42006-11-17 16:19:47 +0000899 self.assertFalse(response.has_key('content-encoding'))
jcgregorio153f5882006-11-06 03:33:24 +0000900 self.assertEqual(int(response['content-length']), len("This is the final destination."))
jcgregorio2d66d4f2006-02-07 05:34:14 +0000901 self.assertEqual(content, "This is the final destination.")
902
903 def testGetDeflateFailure(self):
904 # Test that we raise a good exception when the deflate fails
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400905 self.http.force_exception_to_status_code = False
jcgregorio07a9a4a2007-03-08 21:18:39 +0000906
jcgregorio2d66d4f2006-02-07 05:34:14 +0000907 uri = urlparse.urljoin(base, "deflate/failed-compression.asis")
908 try:
jcgregorio36140b52006-06-13 02:17:52 +0000909 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000910 self.fail("Should never reach here")
911 except httplib2.FailedToDecompressContent:
912 pass
913 except Exception:
914 self.fail("Threw wrong kind of exception")
915
jcgregorio07a9a4a2007-03-08 21:18:39 +0000916 # Re-run the test with out the exceptions
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400917 self.http.force_exception_to_status_code = True
jcgregorio07a9a4a2007-03-08 21:18:39 +0000918
919 (response, content) = self.http.request(uri, "GET")
920 self.assertEqual(response.status, 500)
921 self.assertTrue(response.reason.startswith("Content purported"))
922
jcgregorio2d66d4f2006-02-07 05:34:14 +0000923 def testGetDuplicateHeaders(self):
924 # Test that duplicate headers get concatenated via ','
925 uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis")
jcgregorio36140b52006-06-13 02:17:52 +0000926 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000927 self.assertEqual(response.status, 200)
928 self.assertEqual(content, "This is content\n")
929 self.assertEqual(response['link'].split(",")[0], '<http://bitworking.org>; rel="home"; title="BitWorking"')
930
931 def testGetCacheControlNoCache(self):
932 # Test Cache-Control: no-cache on requests
933 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500934 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000935 self.assertNotEqual(response['etag'], "")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500936 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000937 self.assertEqual(response.status, 200)
938 self.assertEqual(response.fromcache, True)
939
Joe Gregorio0197ec82014-03-06 14:56:29 -0500940 (response, content) = self.http.request(uri, "GET", headers={'accept-encoding': 'identity', 'Cache-Control': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000941 self.assertEqual(response.status, 200)
942 self.assertEqual(response.fromcache, False)
943
944 def testGetCacheControlPragmaNoCache(self):
945 # Test Pragma: no-cache on requests
946 uri = urlparse.urljoin(base, "304/test_etag.txt")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500947 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000948 self.assertNotEqual(response['etag'], "")
Joe Gregorio0197ec82014-03-06 14:56:29 -0500949 (response, content) = self.http.request(uri, "GET", headers= {'accept-encoding': 'identity'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000950 self.assertEqual(response.status, 200)
951 self.assertEqual(response.fromcache, True)
952
Joe Gregorio0197ec82014-03-06 14:56:29 -0500953 (response, content) = self.http.request(uri, "GET", headers={'accept-encoding': 'identity', 'Pragma': 'no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000954 self.assertEqual(response.status, 200)
955 self.assertEqual(response.fromcache, False)
956
957 def testGetCacheControlNoStoreRequest(self):
958 # A no-store request means that the response should not be stored.
959 uri = urlparse.urljoin(base, "304/test_etag.txt")
960
jcgregorio36140b52006-06-13 02:17:52 +0000961 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000962 self.assertEqual(response.status, 200)
963 self.assertEqual(response.fromcache, False)
964
jcgregorio36140b52006-06-13 02:17:52 +0000965 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000966 self.assertEqual(response.status, 200)
967 self.assertEqual(response.fromcache, False)
968
969 def testGetCacheControlNoStoreResponse(self):
970 # A no-store response means that the response should not be stored.
971 uri = urlparse.urljoin(base, "no-store/no-store.asis")
972
jcgregorio36140b52006-06-13 02:17:52 +0000973 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000974 self.assertEqual(response.status, 200)
975 self.assertEqual(response.fromcache, False)
976
jcgregorio36140b52006-06-13 02:17:52 +0000977 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000978 self.assertEqual(response.status, 200)
979 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000980
981 def testGetCacheControlNoCacheNoStoreRequest(self):
982 # Test that a no-store, no-cache clears the entry from the cache
983 # even if it was cached previously.
984 uri = urlparse.urljoin(base, "304/test_etag.txt")
985
jcgregorio36140b52006-06-13 02:17:52 +0000986 (response, content) = self.http.request(uri, "GET")
987 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +0000988 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +0000989 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
990 (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store, no-cache'})
jcgregorio2d66d4f2006-02-07 05:34:14 +0000991 self.assertEqual(response.status, 200)
992 self.assertEqual(response.fromcache, False)
jcgregorio2d66d4f2006-02-07 05:34:14 +0000993
994 def testUpdateInvalidatesCache(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -0400995 # Test that calling PUT or DELETE on a
jcgregorio2d66d4f2006-02-07 05:34:14 +0000996 # URI that is cache invalidates that cache.
997 uri = urlparse.urljoin(base, "304/test_etag.txt")
998
jcgregorio36140b52006-06-13 02:17:52 +0000999 (response, content) = self.http.request(uri, "GET")
1000 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001001 self.assertEqual(response.fromcache, True)
jcgregorio36140b52006-06-13 02:17:52 +00001002 (response, content) = self.http.request(uri, "DELETE")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001003 self.assertEqual(response.status, 405)
1004
jcgregorio36140b52006-06-13 02:17:52 +00001005 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001006 self.assertEqual(response.fromcache, False)
1007
1008 def testUpdateUsesCachedETag(self):
Joe Gregoriobd682082011-05-24 14:06:09 -04001009 # Test that we natively support http://www.w3.org/1999/04/Editing/
jcgregorio2d66d4f2006-02-07 05:34:14 +00001010 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
1011
jcgregorio36140b52006-06-13 02:17:52 +00001012 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001013 self.assertEqual(response.status, 200)
1014 self.assertEqual(response.fromcache, False)
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 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -04001018 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001019 self.assertEqual(response.status, 200)
Joe Gregoriocd868102009-09-29 17:09:16 -04001020 (response, content) = self.http.request(uri, "PUT", body="foo")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001021 self.assertEqual(response.status, 412)
1022
Joe Gregoriobd682082011-05-24 14:06:09 -04001023 def testUpdatePatchUsesCachedETag(self):
1024 # Test that we natively support http://www.w3.org/1999/04/Editing/
1025 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
1026
1027 (response, content) = self.http.request(uri, "GET")
1028 self.assertEqual(response.status, 200)
1029 self.assertEqual(response.fromcache, False)
1030 (response, content) = self.http.request(uri, "GET")
1031 self.assertEqual(response.status, 200)
1032 self.assertEqual(response.fromcache, True)
1033 (response, content) = self.http.request(uri, "PATCH", body="foo")
1034 self.assertEqual(response.status, 200)
1035 (response, content) = self.http.request(uri, "PATCH", body="foo")
1036 self.assertEqual(response.status, 412)
1037
1038
joe.gregorio700f04d2008-09-06 04:46:32 +00001039 def testUpdateUsesCachedETagAndOCMethod(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001040 # Test that we natively support http://www.w3.org/1999/04/Editing/
joe.gregorio700f04d2008-09-06 04:46:32 +00001041 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
1042
1043 (response, content) = self.http.request(uri, "GET")
1044 self.assertEqual(response.status, 200)
1045 self.assertEqual(response.fromcache, False)
1046 (response, content) = self.http.request(uri, "GET")
1047 self.assertEqual(response.status, 200)
1048 self.assertEqual(response.fromcache, True)
1049 self.http.optimistic_concurrency_methods.append("DELETE")
1050 (response, content) = self.http.request(uri, "DELETE")
1051 self.assertEqual(response.status, 200)
1052
1053
jcgregorio4b145e82007-01-18 19:46:34 +00001054 def testUpdateUsesCachedETagOverridden(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001055 # Test that we natively support http://www.w3.org/1999/04/Editing/
jcgregorio4b145e82007-01-18 19:46:34 +00001056 uri = urlparse.urljoin(base, "conditional-updates/test.cgi")
1057
1058 (response, content) = self.http.request(uri, "GET")
1059 self.assertEqual(response.status, 200)
1060 self.assertEqual(response.fromcache, False)
1061 (response, content) = self.http.request(uri, "GET")
1062 self.assertEqual(response.status, 200)
1063 self.assertEqual(response.fromcache, True)
Joe Gregoriocd868102009-09-29 17:09:16 -04001064 (response, content) = self.http.request(uri, "PUT", body="foo", headers={'if-match': 'fred'})
jcgregorio4b145e82007-01-18 19:46:34 +00001065 self.assertEqual(response.status, 412)
1066
jcgregorio2d66d4f2006-02-07 05:34:14 +00001067 def testBasicAuth(self):
1068 # Test Basic Authentication
1069 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001070 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001071 self.assertEqual(response.status, 401)
1072
1073 uri = urlparse.urljoin(base, "basic/")
jcgregorio36140b52006-06-13 02:17:52 +00001074 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001075 self.assertEqual(response.status, 401)
1076
jcgregorio36140b52006-06-13 02:17:52 +00001077 self.http.add_credentials('joe', 'password')
1078 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001079 self.assertEqual(response.status, 200)
1080
1081 uri = urlparse.urljoin(base, "basic/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001082 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001083 self.assertEqual(response.status, 200)
1084
jcgregoriode8238d2007-03-07 19:08:26 +00001085 def testBasicAuthWithDomain(self):
1086 # Test Basic Authentication
1087 uri = urlparse.urljoin(base, "basic/file.txt")
1088 (response, content) = self.http.request(uri, "GET")
1089 self.assertEqual(response.status, 401)
1090
1091 uri = urlparse.urljoin(base, "basic/")
1092 (response, content) = self.http.request(uri, "GET")
1093 self.assertEqual(response.status, 401)
1094
1095 self.http.add_credentials('joe', 'password', "example.org")
1096 (response, content) = self.http.request(uri, "GET")
1097 self.assertEqual(response.status, 401)
1098
1099 uri = urlparse.urljoin(base, "basic/file.txt")
1100 (response, content) = self.http.request(uri, "GET")
1101 self.assertEqual(response.status, 401)
1102
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001103 domain = urlparse.urlparse(base)[1]
jcgregoriode8238d2007-03-07 19:08:26 +00001104 self.http.add_credentials('joe', 'password', domain)
1105 (response, content) = self.http.request(uri, "GET")
1106 self.assertEqual(response.status, 200)
1107
1108 uri = urlparse.urljoin(base, "basic/file.txt")
1109 (response, content) = self.http.request(uri, "GET")
1110 self.assertEqual(response.status, 200)
1111
1112
1113
1114
1115
1116
jcgregorio2d66d4f2006-02-07 05:34:14 +00001117 def testBasicAuthTwoDifferentCredentials(self):
jcgregorioadbb4f82006-05-19 15:17:42 +00001118 # Test Basic Authentication with multiple sets of credentials
jcgregorio2d66d4f2006-02-07 05:34:14 +00001119 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001120 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001121 self.assertEqual(response.status, 401)
1122
1123 uri = urlparse.urljoin(base, "basic2/")
jcgregorio36140b52006-06-13 02:17:52 +00001124 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001125 self.assertEqual(response.status, 401)
1126
jcgregorio36140b52006-06-13 02:17:52 +00001127 self.http.add_credentials('fred', 'barney')
1128 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001129 self.assertEqual(response.status, 200)
1130
1131 uri = urlparse.urljoin(base, "basic2/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001132 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001133 self.assertEqual(response.status, 200)
1134
1135 def testBasicAuthNested(self):
1136 # Test Basic Authentication with resources
1137 # that are nested
1138 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001139 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001140 self.assertEqual(response.status, 401)
1141
1142 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001143 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001144 self.assertEqual(response.status, 401)
1145
jcgregorioadbb4f82006-05-19 15:17:42 +00001146 # Now add in credentials one at a time and test.
jcgregorio36140b52006-06-13 02:17:52 +00001147 self.http.add_credentials('joe', 'password')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001148
1149 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001150 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001151 self.assertEqual(response.status, 200)
1152
1153 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001154 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001155 self.assertEqual(response.status, 401)
1156
jcgregorio36140b52006-06-13 02:17:52 +00001157 self.http.add_credentials('fred', 'barney')
jcgregorio2d66d4f2006-02-07 05:34:14 +00001158
1159 uri = urlparse.urljoin(base, "basic-nested/")
jcgregorio36140b52006-06-13 02:17:52 +00001160 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001161 self.assertEqual(response.status, 200)
1162
1163 uri = urlparse.urljoin(base, "basic-nested/subdir")
jcgregorio36140b52006-06-13 02:17:52 +00001164 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001165 self.assertEqual(response.status, 200)
1166
1167 def testDigestAuth(self):
1168 # Test that we support Digest Authentication
1169 uri = urlparse.urljoin(base, "digest/")
jcgregorio36140b52006-06-13 02:17:52 +00001170 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001171 self.assertEqual(response.status, 401)
1172
jcgregorio36140b52006-06-13 02:17:52 +00001173 self.http.add_credentials('joe', 'password')
1174 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001175 self.assertEqual(response.status, 200)
1176
1177 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001178 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001179
1180 def testDigestAuthNextNonceAndNC(self):
1181 # Test that if the server sets nextnonce that we reset
1182 # the nonce count back to 1
1183 uri = urlparse.urljoin(base, "digest/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001184 self.http.add_credentials('joe', 'password')
1185 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001186 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1187 self.assertEqual(response.status, 200)
jcgregorio36140b52006-06-13 02:17:52 +00001188 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001189 info2 = httplib2._parse_www_authenticate(response, 'authentication-info')
1190 self.assertEqual(response.status, 200)
1191
1192 if info.has_key('nextnonce'):
1193 self.assertEqual(info2['nc'], 1)
1194
1195 def testDigestAuthStale(self):
1196 # Test that we can handle a nonce becoming stale
1197 uri = urlparse.urljoin(base, "digest-expire/file.txt")
jcgregorio36140b52006-06-13 02:17:52 +00001198 self.http.add_credentials('joe', 'password')
1199 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001200 info = httplib2._parse_www_authenticate(response, 'authentication-info')
1201 self.assertEqual(response.status, 200)
1202
1203 time.sleep(3)
1204 # Sleep long enough that the nonce becomes stale
1205
jcgregorio36140b52006-06-13 02:17:52 +00001206 (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"})
jcgregorio2d66d4f2006-02-07 05:34:14 +00001207 self.assertFalse(response.fromcache)
1208 self.assertTrue(response._stale_digest)
1209 info3 = httplib2._parse_www_authenticate(response, 'authentication-info')
1210 self.assertEqual(response.status, 200)
1211
1212 def reflector(self, content):
jcgregorio25185622006-10-28 05:12:34 +00001213 return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] )
jcgregorio2d66d4f2006-02-07 05:34:14 +00001214
1215 def testReflector(self):
1216 uri = urlparse.urljoin(base, "reflector/reflector.cgi")
jcgregorio36140b52006-06-13 02:17:52 +00001217 (response, content) = self.http.request(uri, "GET")
jcgregorio2d66d4f2006-02-07 05:34:14 +00001218 d = self.reflector(content)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001219 self.assertTrue(d.has_key('HTTP_USER_AGENT'))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001220
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001221 def testConnectionClose(self):
1222 uri = "http://www.google.com/"
1223 (response, content) = self.http.request(uri, "GET")
1224 for c in self.http.connections.values():
1225 self.assertNotEqual(None, c.sock)
1226 (response, content) = self.http.request(uri, "GET", headers={"connection": "close"})
1227 for c in self.http.connections.values():
1228 self.assertEqual(None, c.sock)
1229
Joe Gregorio46546a62012-10-03 14:31:10 -04001230 def testPickleHttp(self):
1231 pickled_http = pickle.dumps(self.http)
1232 new_http = pickle.loads(pickled_http)
1233
1234 self.assertEqual(sorted(new_http.__dict__.keys()),
1235 sorted(self.http.__dict__.keys()))
1236 for key in new_http.__dict__:
1237 if key in ('certificates', 'credentials'):
1238 self.assertEqual(new_http.__dict__[key].credentials,
1239 self.http.__dict__[key].credentials)
1240 elif key == 'cache':
1241 self.assertEqual(new_http.__dict__[key].cache,
1242 self.http.__dict__[key].cache)
1243 else:
1244 self.assertEqual(new_http.__dict__[key],
1245 self.http.__dict__[key])
1246
1247 def testPickleHttpWithConnection(self):
1248 self.http.request('http://bitworking.org',
1249 connection_type=_MyHTTPConnection)
1250 pickled_http = pickle.dumps(self.http)
1251 new_http = pickle.loads(pickled_http)
1252
1253 self.assertEqual(self.http.connections.keys(), ['http:bitworking.org'])
1254 self.assertEqual(new_http.connections, {})
1255
1256 def testPickleCustomRequestHttp(self):
1257 def dummy_request(*args, **kwargs):
1258 return new_request(*args, **kwargs)
1259 dummy_request.dummy_attr = 'dummy_value'
1260
1261 self.http.request = dummy_request
1262 pickled_http = pickle.dumps(self.http)
1263 self.assertFalse("S'request'" in pickled_http)
Joe Gregorio84cc10a2009-09-01 13:02:49 -04001264
jcgregorio36140b52006-06-13 02:17:52 +00001265try:
1266 import memcache
1267 class HttpTestMemCached(HttpTest):
1268 def setUp(self):
1269 self.cache = memcache.Client(['127.0.0.1:11211'], debug=0)
jcgregorio47d24672006-06-29 05:18:59 +00001270 #self.cache = memcache.Client(['10.0.0.4:11211'], debug=1)
jcgregorio36140b52006-06-13 02:17:52 +00001271 self.http = httplib2.Http(self.cache)
1272 self.cache.flush_all()
jcgregorio47d24672006-06-29 05:18:59 +00001273 # Not exactly sure why the sleep is needed here, but
1274 # if not present then some unit tests that rely on caching
1275 # fail. Memcached seems to lose some sets immediately
1276 # after a flush_all if the set is to a value that
1277 # was previously cached. (Maybe the flush is handled async?)
1278 time.sleep(1)
jcgregorio36140b52006-06-13 02:17:52 +00001279 self.http.clear_credentials()
1280except:
1281 pass
1282
1283
1284
chris dent89f15142009-12-24 14:02:57 -06001285
jcgregoriodb8dfc82006-03-31 14:59:46 +00001286# ------------------------------------------------------------------------
jcgregorio2d66d4f2006-02-07 05:34:14 +00001287
1288class HttpPrivateTest(unittest.TestCase):
1289
1290 def testParseCacheControl(self):
1291 # Test that we can parse the Cache-Control header
1292 self.assertEqual({}, httplib2._parse_cache_control({}))
1293 self.assertEqual({'no-cache': 1}, httplib2._parse_cache_control({'cache-control': ' no-cache'}))
1294 cc = httplib2._parse_cache_control({'cache-control': ' no-cache, max-age = 7200'})
1295 self.assertEqual(cc['no-cache'], 1)
1296 self.assertEqual(cc['max-age'], '7200')
1297 cc = httplib2._parse_cache_control({'cache-control': ' , '})
1298 self.assertEqual(cc[''], 1)
1299
Joe Gregorioe314e8b2009-07-16 20:11:28 -04001300 try:
1301 cc = httplib2._parse_cache_control({'cache-control': 'Max-age=3600;post-check=1800,pre-check=3600'})
1302 self.assertTrue("max-age" in cc)
1303 except:
1304 self.fail("Should not throw exception")
1305
jcgregorio2d66d4f2006-02-07 05:34:14 +00001306 def testNormalizeHeaders(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001307 # Test that we normalize headers to lowercase
jcgregorio2d66d4f2006-02-07 05:34:14 +00001308 h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'})
1309 self.assertTrue(h.has_key('cache-control'))
1310 self.assertTrue(h.has_key('other'))
1311 self.assertEqual('Stuff', h['other'])
1312
1313 def testExpirationModelTransparent(self):
1314 # Test that no-cache makes our request TRANSPARENT
1315 response_headers = {
1316 'cache-control': 'max-age=7200'
1317 }
1318 request_headers = {
1319 'cache-control': 'no-cache'
1320 }
1321 self.assertEqual("TRANSPARENT", httplib2._entry_disposition(response_headers, request_headers))
1322
jcgregorio45865012007-01-18 16:38:22 +00001323 def testMaxAgeNonNumeric(self):
1324 # Test that no-cache makes our request TRANSPARENT
1325 response_headers = {
1326 'cache-control': 'max-age=fred, min-fresh=barney'
1327 }
1328 request_headers = {
1329 }
1330 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1331
1332
jcgregorio2d66d4f2006-02-07 05:34:14 +00001333 def testExpirationModelNoCacheResponse(self):
1334 # The date and expires point to an entry that should be
1335 # FRESH, but the no-cache over-rides that.
1336 now = time.time()
1337 response_headers = {
1338 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1339 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1340 'cache-control': 'no-cache'
1341 }
1342 request_headers = {
1343 }
1344 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1345
1346 def testExpirationModelStaleRequestMustReval(self):
1347 # must-revalidate forces STALE
1348 self.assertEqual("STALE", httplib2._entry_disposition({}, {'cache-control': 'must-revalidate'}))
1349
1350 def testExpirationModelStaleResponseMustReval(self):
1351 # must-revalidate forces STALE
1352 self.assertEqual("STALE", httplib2._entry_disposition({'cache-control': 'must-revalidate'}, {}))
1353
1354 def testExpirationModelFresh(self):
1355 response_headers = {
1356 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1357 'cache-control': 'max-age=2'
1358 }
1359 request_headers = {
1360 }
1361 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1362 time.sleep(3)
1363 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1364
1365 def testExpirationMaxAge0(self):
1366 response_headers = {
1367 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()),
1368 'cache-control': 'max-age=0'
1369 }
1370 request_headers = {
1371 }
1372 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1373
1374 def testExpirationModelDateAndExpires(self):
1375 now = time.time()
1376 response_headers = {
1377 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1378 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1379 }
1380 request_headers = {
1381 }
1382 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1383 time.sleep(3)
1384 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1385
jcgregoriof9511052007-06-01 14:56:34 +00001386 def testExpiresZero(self):
1387 now = time.time()
1388 response_headers = {
1389 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1390 'expires': "0",
1391 }
1392 request_headers = {
1393 }
1394 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1395
jcgregorio2d66d4f2006-02-07 05:34:14 +00001396 def testExpirationModelDateOnly(self):
1397 now = time.time()
1398 response_headers = {
1399 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+3)),
1400 }
1401 request_headers = {
1402 }
1403 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1404
1405 def testExpirationModelOnlyIfCached(self):
1406 response_headers = {
1407 }
1408 request_headers = {
1409 'cache-control': 'only-if-cached',
1410 }
1411 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1412
1413 def testExpirationModelMaxAgeBoth(self):
1414 now = time.time()
1415 response_headers = {
1416 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1417 'cache-control': 'max-age=2'
1418 }
1419 request_headers = {
1420 'cache-control': 'max-age=0'
1421 }
1422 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1423
1424 def testExpirationModelDateAndExpiresMinFresh1(self):
1425 now = time.time()
1426 response_headers = {
1427 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1428 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+2)),
1429 }
1430 request_headers = {
1431 'cache-control': 'min-fresh=2'
1432 }
1433 self.assertEqual("STALE", httplib2._entry_disposition(response_headers, request_headers))
1434
1435 def testExpirationModelDateAndExpiresMinFresh2(self):
1436 now = time.time()
1437 response_headers = {
1438 'date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)),
1439 'expires': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now+4)),
1440 }
1441 request_headers = {
1442 'cache-control': 'min-fresh=2'
1443 }
1444 self.assertEqual("FRESH", httplib2._entry_disposition(response_headers, request_headers))
1445
1446 def testParseWWWAuthenticateEmpty(self):
1447 res = httplib2._parse_www_authenticate({})
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001448 self.assertEqual(len(res.keys()), 0)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001449
jcgregoriofd22e432006-04-27 02:00:08 +00001450 def testParseWWWAuthenticate(self):
1451 # different uses of spaces around commas
1452 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'})
1453 self.assertEqual(len(res.keys()), 1)
1454 self.assertEqual(len(res['test'].keys()), 5)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001455
jcgregoriofd22e432006-04-27 02:00:08 +00001456 # tokens with non-alphanum
1457 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'})
1458 self.assertEqual(len(res.keys()), 1)
1459 self.assertEqual(len(res['t*!%#st'].keys()), 2)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001460
jcgregoriofd22e432006-04-27 02:00:08 +00001461 # quoted string with quoted pairs
1462 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'})
1463 self.assertEqual(len(res.keys()), 1)
1464 self.assertEqual(res['test']['realm'], 'a "test" realm')
1465
1466 def testParseWWWAuthenticateStrict(self):
1467 httplib2.USE_WWW_AUTH_STRICT_PARSING = 1;
1468 self.testParseWWWAuthenticate();
1469 httplib2.USE_WWW_AUTH_STRICT_PARSING = 0;
1470
jcgregorio2d66d4f2006-02-07 05:34:14 +00001471 def testParseWWWAuthenticateBasic(self):
1472 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'})
1473 basic = res['basic']
1474 self.assertEqual('me', basic['realm'])
1475
1476 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'})
1477 basic = res['basic']
1478 self.assertEqual('me', basic['realm'])
1479 self.assertEqual('MD5', basic['algorithm'])
1480
1481 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'})
1482 basic = res['basic']
1483 self.assertEqual('me', basic['realm'])
1484 self.assertEqual('MD5', basic['algorithm'])
1485
1486 def testParseWWWAuthenticateBasic2(self):
1487 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '})
1488 basic = res['basic']
1489 self.assertEqual('me', basic['realm'])
1490 self.assertEqual('fred', basic['other'])
1491
1492 def testParseWWWAuthenticateBasic3(self):
1493 res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '})
1494 basic = res['basic']
1495 self.assertEqual('me', basic['realm'])
1496
1497
1498 def testParseWWWAuthenticateDigest(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001499 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001500 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'})
1501 digest = res['digest']
1502 self.assertEqual('testrealm@host.com', digest['realm'])
1503 self.assertEqual('auth,auth-int', digest['qop'])
1504
1505
1506 def testParseWWWAuthenticateMultiple(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001507 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001508 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '})
1509 digest = res['digest']
1510 self.assertEqual('testrealm@host.com', digest['realm'])
1511 self.assertEqual('auth,auth-int', digest['qop'])
1512 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1513 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1514 basic = res['basic']
1515 self.assertEqual('me', basic['realm'])
1516
1517 def testParseWWWAuthenticateMultiple2(self):
1518 # Handle an added comma between challenges, which might get thrown in if the challenges were
1519 # originally sent in separate www-authenticate headers.
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001520 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001521 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '})
1522 digest = res['digest']
1523 self.assertEqual('testrealm@host.com', digest['realm'])
1524 self.assertEqual('auth,auth-int', digest['qop'])
1525 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1526 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1527 basic = res['basic']
1528 self.assertEqual('me', basic['realm'])
1529
1530 def testParseWWWAuthenticateMultiple3(self):
1531 # Handle an added comma between challenges, which might get thrown in if the challenges were
1532 # originally sent in separate www-authenticate headers.
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001533 res = httplib2._parse_www_authenticate({ 'www-authenticate':
jcgregorio2d66d4f2006-02-07 05:34:14 +00001534 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'})
1535 digest = res['digest']
1536 self.assertEqual('testrealm@host.com', digest['realm'])
1537 self.assertEqual('auth,auth-int', digest['qop'])
1538 self.assertEqual('dcd98b7102dd2f0e8b11d0f600bfb0c093', digest['nonce'])
1539 self.assertEqual('5ccc069c403ebaf9f0171e9517f40e41', digest['opaque'])
1540 basic = res['basic']
1541 self.assertEqual('me', basic['realm'])
1542 wsse = res['wsse']
1543 self.assertEqual('foo', wsse['realm'])
1544 self.assertEqual('UsernameToken', wsse['profile'])
1545
1546 def testParseWWWAuthenticateMultiple4(self):
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001547 res = httplib2._parse_www_authenticate({ 'www-authenticate':
1548 '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 +00001549 digest = res['digest']
1550 self.assertEqual('test-real.m@host.com', digest['realm'])
1551 self.assertEqual('\tauth,auth-int', digest['qop'])
1552 self.assertEqual('(*)&^&$%#', digest['nonce'])
1553
1554 def testParseWWWAuthenticateMoreQuoteCombos(self):
1555 res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'})
1556 digest = res['digest']
1557 self.assertEqual('myrealm', digest['realm'])
1558
Joe Gregorio6fa3cf22011-02-13 22:45:06 -05001559 def testParseWWWAuthenticateMalformed(self):
1560 try:
1561 res = httplib2._parse_www_authenticate({'www-authenticate':'OAuth "Facebook Platform" "invalid_token" "Invalid OAuth access token."'})
1562 self.fail("should raise an exception")
1563 except httplib2.MalformedHeader:
1564 pass
1565
jcgregorio2d66d4f2006-02-07 05:34:14 +00001566 def testDigestObject(self):
1567 credentials = ('joe', 'password')
1568 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001569 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001570 headers = {}
1571 response = {
1572 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"'
1573 }
1574 content = ""
Joe Gregorio875a8b52011-06-13 14:06:23 -04001575
jcgregorio6cbab7e2006-04-21 20:35:43 +00001576 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001577 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
Joe Gregorio875a8b52011-06-13 14:06:23 -04001578 our_request = "authorization: %s" % headers['authorization']
1579 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 +00001580 self.assertEqual(our_request, working_request)
1581
Joe Gregorio03d99102011-06-22 16:55:52 -04001582 def testDigestObjectWithOpaque(self):
1583 credentials = ('joe', 'password')
1584 host = None
1585 request_uri = '/projects/httplib2/test/digest/'
1586 headers = {}
1587 response = {
1588 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", opaque="atestopaque"'
1589 }
1590 content = ""
1591
1592 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
1593 d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46")
1594 our_request = "authorization: %s" % headers['authorization']
1595 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"'
1596 self.assertEqual(our_request, working_request)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001597
1598 def testDigestObjectStale(self):
1599 credentials = ('joe', 'password')
1600 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001601 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001602 headers = {}
1603 response = httplib2.Response({ })
1604 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1605 response.status = 401
1606 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001607 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001608 # Returns true to force a retry
1609 self.assertTrue( d.response(response, content) )
1610
1611 def testDigestObjectAuthInfo(self):
1612 credentials = ('joe', 'password')
1613 host = None
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001614 request_uri = '/projects/httplib2/test/digest/'
jcgregorio2d66d4f2006-02-07 05:34:14 +00001615 headers = {}
1616 response = httplib2.Response({ })
1617 response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'
1618 response['authentication-info'] = 'nextnonce="fred"'
1619 content = ""
jcgregorio6cbab7e2006-04-21 20:35:43 +00001620 d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None)
jcgregorio2d66d4f2006-02-07 05:34:14 +00001621 # Returns true to force a retry
1622 self.assertFalse( d.response(response, content) )
1623 self.assertEqual('fred', d.challenge['nonce'])
1624 self.assertEqual(1, d.challenge['nc'])
1625
1626 def testWsseAlgorithm(self):
1627 digest = httplib2._wsse_username_token("d36e316282959a9ed4c89851497a717f", "2003-12-15T14:43:07Z", "taadtaadpstcsm")
1628 expected = "quR/EWLAV4xLf9Zqyw4pDmfV9OY="
1629 self.assertEqual(expected, digest)
1630
jcgregoriodb8dfc82006-03-31 14:59:46 +00001631 def testEnd2End(self):
1632 # one end to end header
1633 response = {'content-type': 'application/atom+xml', 'te': 'deflate'}
1634 end2end = httplib2._get_end2end_headers(response)
1635 self.assertTrue('content-type' in end2end)
1636 self.assertTrue('te' not in end2end)
1637 self.assertTrue('connection' not in end2end)
1638
1639 # one end to end header that gets eliminated
1640 response = {'connection': 'content-type', 'content-type': 'application/atom+xml', 'te': 'deflate'}
1641 end2end = httplib2._get_end2end_headers(response)
1642 self.assertTrue('content-type' not in end2end)
1643 self.assertTrue('te' not in end2end)
1644 self.assertTrue('connection' not in end2end)
1645
1646 # Degenerate case of no headers
1647 response = {}
1648 end2end = httplib2._get_end2end_headers(response)
1649 self.assertEquals(0, len(end2end))
1650
Jason R. Coombs88c1f282011-08-09 08:53:31 -04001651 # Degenerate case of connection referrring to a header not passed in
jcgregoriodb8dfc82006-03-31 14:59:46 +00001652 response = {'connection': 'content-type'}
1653 end2end = httplib2._get_end2end_headers(response)
1654 self.assertEquals(0, len(end2end))
jcgregorio2d66d4f2006-02-07 05:34:14 +00001655
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001656
1657class TestProxyInfo(unittest.TestCase):
1658 def setUp(self):
1659 self.orig_env = dict(os.environ)
1660
1661 def tearDown(self):
1662 os.environ.clear()
1663 os.environ.update(self.orig_env)
1664
1665 def test_from_url(self):
Joe Gregorio46546a62012-10-03 14:31:10 -04001666 pi = httplib2.proxy_info_from_url('http://myproxy.example.com')
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001667 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1668 self.assertEquals(pi.proxy_port, 80)
1669 self.assertEquals(pi.proxy_user, None)
1670
1671 def test_from_url_ident(self):
Joe Gregorio46546a62012-10-03 14:31:10 -04001672 pi = httplib2.proxy_info_from_url('http://zoidberg:fish@someproxy:99')
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001673 self.assertEquals(pi.proxy_host, 'someproxy')
1674 self.assertEquals(pi.proxy_port, 99)
1675 self.assertEquals(pi.proxy_user, 'zoidberg')
1676 self.assertEquals(pi.proxy_pass, 'fish')
1677
1678 def test_from_env(self):
1679 os.environ['http_proxy'] = 'http://myproxy.example.com:8080'
Joe Gregorio46546a62012-10-03 14:31:10 -04001680 pi = httplib2.proxy_info_from_environment()
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001681 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1682 self.assertEquals(pi.proxy_port, 8080)
1683 self.assertEquals(pi.bypass_hosts, [])
1684
1685 def test_from_env_no_proxy(self):
1686 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1687 os.environ['https_proxy'] = 'http://myproxy.example.com:81'
1688 os.environ['no_proxy'] = 'localhost,otherhost.domain.local'
Joe Gregorio46546a62012-10-03 14:31:10 -04001689 pi = httplib2.proxy_info_from_environment('https')
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001690 self.assertEquals(pi.proxy_host, 'myproxy.example.com')
1691 self.assertEquals(pi.proxy_port, 81)
1692 self.assertEquals(pi.bypass_hosts, ['localhost',
1693 'otherhost.domain.local'])
1694
1695 def test_from_env_none(self):
1696 os.environ.clear()
Joe Gregorio46546a62012-10-03 14:31:10 -04001697 pi = httplib2.proxy_info_from_environment()
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001698 self.assertEquals(pi, None)
1699
Jason R. Coombs43840892011-08-09 10:30:46 -04001700 def test_applies_to(self):
1701 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1702 os.environ['https_proxy'] = 'http://myproxy.example.com:81'
Jason R. Coombs96279c52011-08-16 12:53:27 -04001703 os.environ['no_proxy'] = 'localhost,otherhost.domain.local,example.com'
Joe Gregorio46546a62012-10-03 14:31:10 -04001704 pi = httplib2.proxy_info_from_environment()
Jason R. Coombs43840892011-08-09 10:30:46 -04001705 self.assertFalse(pi.applies_to('localhost'))
1706 self.assertTrue(pi.applies_to('www.google.com'))
Jason R. Coombs96279c52011-08-16 12:53:27 -04001707 self.assertFalse(pi.applies_to('www.example.com'))
1708
1709 def test_no_proxy_star(self):
1710 os.environ['http_proxy'] = 'http://myproxy.example.com:80'
1711 os.environ['NO_PROXY'] = '*'
Joe Gregorio46546a62012-10-03 14:31:10 -04001712 pi = httplib2.proxy_info_from_environment()
Jason R. Coombs96279c52011-08-16 12:53:27 -04001713 for host in ('localhost', '169.254.38.192', 'www.google.com'):
1714 self.assertFalse(pi.applies_to(host))
Jason R. Coombs43840892011-08-09 10:30:46 -04001715
Jason R. Coombs8a487d02011-08-09 09:35:58 -04001716
chris dent89f15142009-12-24 14:02:57 -06001717if __name__ == '__main__':
1718 unittest.main()