blob: 32ffd0acefb2e2a16227352e06f2ebca4e1ceb89 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Christian Heimesc5f05e42008-02-23 17:40:11 +00004import os
Facundo Batista4f1b1ed2008-05-29 16:39:26 +00005import socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00006import StringIO
Jeremy Hyltone3e61042001-05-09 15:50:25 +00007
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00008import urllib2
9from urllib2 import Request, OpenerDirector
Jeremy Hyltone3e61042001-05-09 15:50:25 +000010
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -060011try:
12 import ssl
13except ImportError:
14 ssl = None
15
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000016# XXX
17# Request
18# CacheFTPHandler (hard to write)
Georg Brandlfa42bd72006-04-30 07:06:11 +000019# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000020
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000021class TrivialTests(unittest.TestCase):
22 def test_trivial(self):
23 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000024
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000025 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000026
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000027 # XXX Name hacking to get this to work on Windows.
Serhiy Storchaka38a33ec2015-01-26 10:26:00 +020028 fname = os.path.abspath(urllib2.__file__).replace(os.sep, '/')
Senthil Kumaran2e3da142010-01-10 17:35:05 +000029
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000030 # And more hacking to get it to work on MacOS. This assumes
31 # urllib.pathname2url works, unfortunately...
Ronald Oussoren9545a232010-05-05 19:09:31 +000032 if os.name == 'riscos':
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000033 import string
34 fname = os.expand(fname)
35 fname = fname.translate(string.maketrans("/.", "./"))
36
Senthil Kumaran2e3da142010-01-10 17:35:05 +000037 if os.name == 'nt':
38 file_url = "file:///%s" % fname
39 else:
40 file_url = "file://%s" % fname
41
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000042 f = urllib2.urlopen(file_url)
43
44 buf = f.read()
45 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000046
Georg Brandle1b13d22005-08-24 22:20:32 +000047 def test_parse_http_list(self):
48 tests = [('a,b,c', ['a', 'b', 'c']),
49 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
50 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
51 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
52 for string, list in tests:
Ezio Melotti2623a372010-11-21 13:34:58 +000053 self.assertEqual(urllib2.parse_http_list(string), list)
Georg Brandle1b13d22005-08-24 22:20:32 +000054
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -060055 @unittest.skipUnless(ssl, "ssl module required")
56 def test_cafile_and_context(self):
57 context = ssl.create_default_context()
58 with self.assertRaises(ValueError):
59 urllib2.urlopen(
60 "https://localhost", cafile="/nonexistent/path", context=context
61 )
62
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000063
Georg Brandl8c036cc2006-08-20 13:15:39 +000064def test_request_headers_dict():
65 """
66 The Request.headers dictionary is not a documented interface. It should
67 stay that way, because the complete set of headers are only accessible
68 through the .get_header(), .has_header(), .header_items() interface.
69 However, .headers pre-dates those methods, and so real code will be using
70 the dictionary.
71
72 The introduction in 2.4 of those methods was a mistake for the same reason:
73 code that previously saw all (urllib2 user)-provided headers in .headers
74 now sees only a subset (and the function interface is ugly and incomplete).
75 A better change would have been to replace .headers dict with a dict
76 subclass (or UserDict.DictMixin instance?) that preserved the .headers
77 interface and also provided access to the "unredirected" headers. It's
78 probably too late to fix that, though.
79
80
81 Check .capitalize() case normalization:
82
83 >>> url = "http://example.com"
84 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
85 'blah'
86 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
87 'blah'
88
89 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
90 but that could be changed in future.
91
92 """
93
94def test_request_headers_methods():
95 """
96 Note the case normalization of header names here, to .capitalize()-case.
97 This should be preserved for backwards-compatibility. (In the HTTP case,
98 normalization to .title()-case is done by urllib2 before sending headers to
99 httplib).
100
101 >>> url = "http://example.com"
102 >>> r = Request(url, headers={"Spam-eggs": "blah"})
103 >>> r.has_header("Spam-eggs")
104 True
105 >>> r.header_items()
106 [('Spam-eggs', 'blah')]
107 >>> r.add_header("Foo-Bar", "baz")
108 >>> items = r.header_items()
109 >>> items.sort()
110 >>> items
111 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
112
113 Note that e.g. r.has_header("spam-EggS") is currently False, and
114 r.get_header("spam-EggS") returns None, but that could be changed in
115 future.
116
117 >>> r.has_header("Not-there")
118 False
119 >>> print r.get_header("Not-there")
120 None
121 >>> r.get_header("Not-there", "default")
122 'default'
123
124 """
125
126
Georg Brandlfa42bd72006-04-30 07:06:11 +0000127def test_password_manager(self):
128 """
129 >>> mgr = urllib2.HTTPPasswordMgr()
130 >>> add = mgr.add_password
131 >>> add("Some Realm", "http://example.com/", "joe", "password")
132 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
133 >>> add("c", "http://example.com/foo", "foo", "ni")
134 >>> add("c", "http://example.com/bar", "bar", "nini")
135 >>> add("b", "http://example.com/", "first", "blah")
136 >>> add("b", "http://example.com/", "second", "spam")
137 >>> add("a", "http://example.com", "1", "a")
138 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
139 >>> add("Some Realm", "d.example.com", "4", "d")
140 >>> add("Some Realm", "e.example.com:3128", "5", "e")
141
142 >>> mgr.find_user_password("Some Realm", "example.com")
143 ('joe', 'password')
144 >>> mgr.find_user_password("Some Realm", "http://example.com")
145 ('joe', 'password')
146 >>> mgr.find_user_password("Some Realm", "http://example.com/")
147 ('joe', 'password')
148 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
149 ('joe', 'password')
150 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
151 ('joe', 'password')
152 >>> mgr.find_user_password("c", "http://example.com/foo")
153 ('foo', 'ni')
154 >>> mgr.find_user_password("c", "http://example.com/bar")
155 ('bar', 'nini')
156
Georg Brandl2b330372006-05-28 20:23:12 +0000157 Actually, this is really undefined ATM
158## Currently, we use the highest-level path where more than one match:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000159
Georg Brandl2b330372006-05-28 20:23:12 +0000160## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
161## ('joe', 'password')
Georg Brandlfa42bd72006-04-30 07:06:11 +0000162
163 Use latest add_password() in case of conflict:
164
165 >>> mgr.find_user_password("b", "http://example.com/")
166 ('second', 'spam')
167
168 No special relationship between a.example.com and example.com:
169
170 >>> mgr.find_user_password("a", "http://example.com/")
171 ('1', 'a')
172 >>> mgr.find_user_password("a", "http://a.example.com/")
173 (None, None)
174
175 Ports:
176
177 >>> mgr.find_user_password("Some Realm", "c.example.com")
178 (None, None)
179 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
180 ('3', 'c')
181 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
182 ('3', 'c')
183 >>> mgr.find_user_password("Some Realm", "d.example.com")
184 ('4', 'd')
185 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
186 ('5', 'e')
187
188 """
189 pass
190
191
Georg Brandl2b330372006-05-28 20:23:12 +0000192def test_password_manager_default_port(self):
193 """
194 >>> mgr = urllib2.HTTPPasswordMgr()
195 >>> add = mgr.add_password
196
197 The point to note here is that we can't guess the default port if there's
198 no scheme. This applies to both add_password and find_user_password.
199
200 >>> add("f", "http://g.example.com:80", "10", "j")
201 >>> add("g", "http://h.example.com", "11", "k")
202 >>> add("h", "i.example.com:80", "12", "l")
203 >>> add("i", "j.example.com", "13", "m")
204 >>> mgr.find_user_password("f", "g.example.com:100")
205 (None, None)
206 >>> mgr.find_user_password("f", "g.example.com:80")
207 ('10', 'j')
208 >>> mgr.find_user_password("f", "g.example.com")
209 (None, None)
210 >>> mgr.find_user_password("f", "http://g.example.com:100")
211 (None, None)
212 >>> mgr.find_user_password("f", "http://g.example.com:80")
213 ('10', 'j')
214 >>> mgr.find_user_password("f", "http://g.example.com")
215 ('10', 'j')
216 >>> mgr.find_user_password("g", "h.example.com")
217 ('11', 'k')
218 >>> mgr.find_user_password("g", "h.example.com:80")
219 ('11', 'k')
220 >>> mgr.find_user_password("g", "http://h.example.com:80")
221 ('11', 'k')
222 >>> mgr.find_user_password("h", "i.example.com")
223 (None, None)
224 >>> mgr.find_user_password("h", "i.example.com:80")
225 ('12', 'l')
226 >>> mgr.find_user_password("h", "http://i.example.com:80")
227 ('12', 'l')
228 >>> mgr.find_user_password("i", "j.example.com")
229 ('13', 'm')
230 >>> mgr.find_user_password("i", "j.example.com:80")
231 (None, None)
232 >>> mgr.find_user_password("i", "http://j.example.com")
233 ('13', 'm')
234 >>> mgr.find_user_password("i", "http://j.example.com:80")
235 (None, None)
236
237 """
238
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000239class MockOpener:
240 addheaders = []
Senthil Kumaran5fee4602009-07-19 02:43:43 +0000241 def open(self, req, data=None,timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
242 self.req, self.data, self.timeout = req, data, timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000243 def error(self, proto, *args):
244 self.proto, self.args = proto, args
245
246class MockFile:
247 def read(self, count=None): pass
248 def readline(self, count=None): pass
249 def close(self): pass
250
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000251class MockHeaders(dict):
252 def getheaders(self, name):
253 return self.values()
254
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000255class MockResponse(StringIO.StringIO):
256 def __init__(self, code, msg, headers, data, url=None):
257 StringIO.StringIO.__init__(self, data)
258 self.code, self.msg, self.headers, self.url = code, msg, headers, url
259 def info(self):
260 return self.headers
261 def geturl(self):
262 return self.url
263
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000264class MockCookieJar:
265 def add_cookie_header(self, request):
266 self.ach_req = request
267 def extract_cookies(self, response, request):
268 self.ec_req, self.ec_r = request, response
269
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000270class FakeMethod:
271 def __init__(self, meth_name, action, handle):
272 self.meth_name = meth_name
273 self.handle = handle
274 self.action = action
275 def __call__(self, *args):
276 return self.handle(self.meth_name, self.action, *args)
277
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000278class MockHTTPResponse:
279 def __init__(self, fp, msg, status, reason):
280 self.fp = fp
281 self.msg = msg
282 self.status = status
283 self.reason = reason
284 def read(self):
285 return ''
286
287class MockHTTPClass:
288 def __init__(self):
289 self.req_headers = []
290 self.data = None
291 self.raise_on_endheaders = False
292 self._tunnel_headers = {}
293
294 def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
295 self.host = host
296 self.timeout = timeout
297 return self
298
299 def set_debuglevel(self, level):
300 self.level = level
301
302 def set_tunnel(self, host, port=None, headers=None):
303 self._tunnel_host = host
304 self._tunnel_port = port
305 if headers:
306 self._tunnel_headers = headers
307 else:
308 self._tunnel_headers.clear()
Victor Stinnerc74a6ba2011-06-17 14:06:27 +0200309
Benjamin Peterson32935932009-12-24 01:09:53 +0000310 def request(self, method, url, body=None, headers=None):
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000311 self.method = method
312 self.selector = url
Benjamin Peterson32935932009-12-24 01:09:53 +0000313 if headers is not None:
314 self.req_headers += headers.items()
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000315 self.req_headers.sort()
316 if body:
317 self.data = body
318 if self.raise_on_endheaders:
319 import socket
320 raise socket.error()
Victor Stinnerc74a6ba2011-06-17 14:06:27 +0200321
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000322 def getresponse(self):
323 return MockHTTPResponse(MockFile(), {}, 200, "OK")
324
Victor Stinnerc74a6ba2011-06-17 14:06:27 +0200325 def close(self):
326 pass
327
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000328class MockHandler:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000329 # useful for testing handler machinery
330 # see add_ordered_mock_handlers() docstring
Georg Brandl720096a2006-04-02 20:45:34 +0000331 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000332 def __init__(self, methods):
333 self._define_methods(methods)
334 def _define_methods(self, methods):
335 for spec in methods:
336 if len(spec) == 2: name, action = spec
337 else: name, action = spec, None
338 meth = FakeMethod(name, action, self.handle)
339 setattr(self.__class__, name, meth)
340 def handle(self, fn_name, action, *args, **kwds):
341 self.parent.calls.append((self, fn_name, args, kwds))
342 if action is None:
343 return None
344 elif action == "return self":
345 return self
346 elif action == "return response":
347 res = MockResponse(200, "OK", {}, "")
348 return res
349 elif action == "return request":
350 return Request("http://blah/")
351 elif action.startswith("error"):
352 code = action[action.rfind(" ")+1:]
353 try:
354 code = int(code)
355 except ValueError:
356 pass
357 res = MockResponse(200, "OK", {}, "")
358 return self.parent.error("http", args[0], res, code, "", {})
359 elif action == "raise":
360 raise urllib2.URLError("blah")
361 assert False
362 def close(self): pass
363 def add_parent(self, parent):
364 self.parent = parent
365 self.parent.calls = []
366 def __lt__(self, other):
367 if not hasattr(other, "handler_order"):
368 # No handler_order, leave in original order. Yuck.
369 return True
370 return self.handler_order < other.handler_order
371
372def add_ordered_mock_handlers(opener, meth_spec):
373 """Create MockHandlers and add them to an OpenerDirector.
374
375 meth_spec: list of lists of tuples and strings defining methods to define
376 on handlers. eg:
377
378 [["http_error", "ftp_open"], ["http_open"]]
379
380 defines methods .http_error() and .ftp_open() on one handler, and
381 .http_open() on another. These methods just record their arguments and
382 return None. Using a tuple instead of a string causes the method to
383 perform some action (see MockHandler.handle()), eg:
384
385 [["http_error"], [("http_open", "return request")]]
386
387 defines .http_error() on one handler (which simply returns None), and
388 .http_open() on another handler, which returns a Request object.
389
390 """
391 handlers = []
392 count = 0
393 for meths in meth_spec:
394 class MockHandlerSubclass(MockHandler): pass
395 h = MockHandlerSubclass(meths)
Georg Brandl720096a2006-04-02 20:45:34 +0000396 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000397 h.add_parent(opener)
398 count = count + 1
399 handlers.append(h)
400 opener.add_handler(h)
401 return handlers
402
Georg Brandlfa42bd72006-04-30 07:06:11 +0000403def build_test_opener(*handler_instances):
404 opener = OpenerDirector()
405 for h in handler_instances:
406 opener.add_handler(h)
407 return opener
408
409class MockHTTPHandler(urllib2.BaseHandler):
410 # useful for testing redirections and auth
411 # sends supplied headers and code as first response
412 # sends 200 OK as second response
413 def __init__(self, code, headers):
414 self.code = code
415 self.headers = headers
416 self.reset()
417 def reset(self):
418 self._count = 0
419 self.requests = []
420 def http_open(self, req):
421 import mimetools, httplib, copy
422 from StringIO import StringIO
423 self.requests.append(copy.deepcopy(req))
424 if self._count == 0:
425 self._count = self._count + 1
426 name = httplib.responses[self.code]
427 msg = mimetools.Message(StringIO(self.headers))
428 return self.parent.error(
429 "http", req, MockFile(), self.code, name, msg)
430 else:
431 self.req = req
432 msg = mimetools.Message(StringIO("\r\n\r\n"))
433 return MockResponse(200, "OK", msg, "", req.get_full_url())
434
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000435class MockHTTPSHandler(urllib2.AbstractHTTPHandler):
436 # Useful for testing the Proxy-Authorization request by verifying the
437 # properties of httpcon
Benjamin Peterson32935932009-12-24 01:09:53 +0000438
439 def __init__(self):
440 urllib2.AbstractHTTPHandler.__init__(self)
441 self.httpconn = MockHTTPClass()
442
Senthil Kumaran7713acf2009-12-20 06:05:13 +0000443 def https_open(self, req):
444 return self.do_open(self.httpconn, req)
445
Georg Brandlfa42bd72006-04-30 07:06:11 +0000446class MockPasswordManager:
447 def add_password(self, realm, uri, user, password):
448 self.realm = realm
449 self.url = uri
450 self.user = user
451 self.password = password
452 def find_user_password(self, realm, authuri):
453 self.target_realm = realm
454 self.target_url = authuri
455 return self.user, self.password
456
457
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000458class OpenerDirectorTests(unittest.TestCase):
459
Georg Brandlf91149e2007-07-12 08:05:45 +0000460 def test_add_non_handler(self):
461 class NonHandler(object):
462 pass
463 self.assertRaises(TypeError,
464 OpenerDirector().add_handler, NonHandler())
465
Georg Brandl261e2512006-05-29 20:52:54 +0000466 def test_badly_named_methods(self):
467 # test work-around for three methods that accidentally follow the
468 # naming conventions for handler methods
469 # (*_open() / *_request() / *_response())
470
471 # These used to call the accidentally-named methods, causing a
472 # TypeError in real code; here, returning self from these mock
473 # methods would either cause no exception, or AttributeError.
474
475 from urllib2 import URLError
476
477 o = OpenerDirector()
478 meth_spec = [
479 [("do_open", "return self"), ("proxy_open", "return self")],
480 [("redirect_request", "return self")],
481 ]
482 handlers = add_ordered_mock_handlers(o, meth_spec)
483 o.add_handler(urllib2.UnknownHandler())
484 for scheme in "do", "proxy", "redirect":
485 self.assertRaises(URLError, o.open, scheme+"://example.com/")
486
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000487 def test_handled(self):
488 # handler returning non-None means no more handlers will be called
489 o = OpenerDirector()
490 meth_spec = [
491 ["http_open", "ftp_open", "http_error_302"],
492 ["ftp_open"],
493 [("http_open", "return self")],
494 [("http_open", "return self")],
495 ]
496 handlers = add_ordered_mock_handlers(o, meth_spec)
497
498 req = Request("http://example.com/")
499 r = o.open(req)
500 # Second .http_open() gets called, third doesn't, since second returned
501 # non-None. Handlers without .http_open() never get any methods called
502 # on them.
503 # In fact, second mock handler defining .http_open() returns self
504 # (instead of response), which becomes the OpenerDirector's return
505 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000506 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000507 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
508 for expected, got in zip(calls, o.calls):
509 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000510 self.assertEqual((handler, name), expected)
511 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000512
513 def test_handler_order(self):
514 o = OpenerDirector()
515 handlers = []
516 for meths, handler_order in [
517 ([("http_open", "return self")], 500),
518 (["http_open"], 0),
519 ]:
520 class MockHandlerSubclass(MockHandler): pass
521 h = MockHandlerSubclass(meths)
522 h.handler_order = handler_order
523 handlers.append(h)
524 o.add_handler(h)
525
526 r = o.open("http://example.com/")
527 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000528 self.assertEqual(o.calls[0][0], handlers[1])
529 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000530
531 def test_raise(self):
532 # raising URLError stops processing of request
533 o = OpenerDirector()
534 meth_spec = [
535 [("http_open", "raise")],
536 [("http_open", "return self")],
537 ]
538 handlers = add_ordered_mock_handlers(o, meth_spec)
539
540 req = Request("http://example.com/")
541 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000542 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000543
544## def test_error(self):
545## # XXX this doesn't actually seem to be used in standard library,
546## # but should really be tested anyway...
547
548 def test_http_error(self):
549 # XXX http_error_default
550 # http errors are a special case
551 o = OpenerDirector()
552 meth_spec = [
553 [("http_open", "error 302")],
554 [("http_error_400", "raise"), "http_open"],
555 [("http_error_302", "return response"), "http_error_303",
556 "http_error"],
557 [("http_error_302")],
558 ]
559 handlers = add_ordered_mock_handlers(o, meth_spec)
560
561 class Unknown:
562 def __eq__(self, other): return True
563
564 req = Request("http://example.com/")
565 r = o.open(req)
566 assert len(o.calls) == 2
567 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000568 (handlers[2], "http_error_302",
569 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000570 for expected, got in zip(calls, o.calls):
571 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000572 self.assertEqual((handler, method_name), got[:2])
573 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000574
575 def test_processors(self):
576 # *_request / *_response methods get called appropriately
577 o = OpenerDirector()
578 meth_spec = [
579 [("http_request", "return request"),
580 ("http_response", "return response")],
581 [("http_request", "return request"),
582 ("http_response", "return response")],
583 ]
584 handlers = add_ordered_mock_handlers(o, meth_spec)
585
586 req = Request("http://example.com/")
587 r = o.open(req)
588 # processor methods are called on *all* handlers that define them,
589 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000590 calls = [
591 (handlers[0], "http_request"), (handlers[1], "http_request"),
592 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000593
594 for i, (handler, name, args, kwds) in enumerate(o.calls):
595 if i < 2:
596 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000597 self.assertEqual((handler, name), calls[i])
598 self.assertEqual(len(args), 1)
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000599 self.assertIsInstance(args[0], Request)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000600 else:
601 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000602 self.assertEqual((handler, name), calls[i])
603 self.assertEqual(len(args), 2)
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000604 self.assertIsInstance(args[0], Request)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000605 # response from opener.open is None, because there's no
606 # handler that defines http_open to handle it
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200607 if args[1] is not None:
608 self.assertIsInstance(args[1], MockResponse)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000609
610
Tim Peters58eb11c2004-01-18 20:29:55 +0000611def sanepathname2url(path):
612 import urllib
613 urlpath = urllib.pathname2url(path)
614 if os.name == "nt" and urlpath.startswith("///"):
615 urlpath = urlpath[2:]
616 # XXX don't ask me about the mac...
617 return urlpath
618
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000619class HandlerTests(unittest.TestCase):
620
621 def test_ftp(self):
622 class MockFTPWrapper:
623 def __init__(self, data): self.data = data
624 def retrfile(self, filename, filetype):
625 self.filename, self.filetype = filename, filetype
626 return StringIO.StringIO(self.data), len(self.data)
Nadeem Vawdab42c53e2011-07-23 15:51:16 +0200627 def close(self): pass
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000628
629 class NullFTPHandler(urllib2.FTPHandler):
630 def __init__(self, data): self.data = data
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000631 def connect_ftp(self, user, passwd, host, port, dirs,
632 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000633 self.user, self.passwd = user, passwd
634 self.host, self.port = host, port
635 self.dirs = dirs
636 self.ftpwrapper = MockFTPWrapper(self.data)
637 return self.ftpwrapper
638
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000639 import ftplib
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000640 data = "rheum rhaponicum"
641 h = NullFTPHandler(data)
642 o = h.parent = MockOpener()
643
Senthil Kumaran9fce5512010-11-20 11:24:08 +0000644 for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000645 ("ftp://localhost/foo/bar/baz.html",
Senthil Kumaran9fce5512010-11-20 11:24:08 +0000646 "localhost", ftplib.FTP_PORT, "", "", "I",
647 ["foo", "bar"], "baz.html", "text/html"),
648 ("ftp://parrot@localhost/foo/bar/baz.html",
649 "localhost", ftplib.FTP_PORT, "parrot", "", "I",
650 ["foo", "bar"], "baz.html", "text/html"),
651 ("ftp://%25parrot@localhost/foo/bar/baz.html",
652 "localhost", ftplib.FTP_PORT, "%parrot", "", "I",
653 ["foo", "bar"], "baz.html", "text/html"),
654 ("ftp://%2542parrot@localhost/foo/bar/baz.html",
655 "localhost", ftplib.FTP_PORT, "%42parrot", "", "I",
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000656 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000657 ("ftp://localhost:80/foo/bar/",
Senthil Kumaran9fce5512010-11-20 11:24:08 +0000658 "localhost", 80, "", "", "D",
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000659 ["foo", "bar"], "", None),
660 ("ftp://localhost/baz.gif;type=a",
Senthil Kumaran9fce5512010-11-20 11:24:08 +0000661 "localhost", ftplib.FTP_PORT, "", "", "A",
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000662 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000663 ]:
Facundo Batista10951d52007-06-06 17:15:23 +0000664 req = Request(url)
665 req.timeout = None
666 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000667 # ftp authentication not yet implemented by FTPHandler
Senthil Kumaran9fce5512010-11-20 11:24:08 +0000668 self.assertEqual(h.user, user)
669 self.assertEqual(h.passwd, passwd)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000670 self.assertEqual(h.host, socket.gethostbyname(host))
671 self.assertEqual(h.port, port)
672 self.assertEqual(h.dirs, dirs)
673 self.assertEqual(h.ftpwrapper.filename, filename)
674 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000675 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000676 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000677 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000678
679 def test_file(self):
Christian Heimesc5f05e42008-02-23 17:40:11 +0000680 import rfc822, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000681 h = urllib2.FileHandler()
682 o = h.parent = MockOpener()
683
Tim Peters58eb11c2004-01-18 20:29:55 +0000684 TESTFN = test_support.TESTFN
685 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000686 towrite = "hello, world\n"
Georg Brandldd2245f2006-03-31 17:18:06 +0000687 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000688 "file://localhost%s" % urlpath,
689 "file://%s" % urlpath,
690 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Georg Brandldd2245f2006-03-31 17:18:06 +0000691 ]
692 try:
Tim Peters480725d2006-04-03 02:46:44 +0000693 localaddr = socket.gethostbyname(socket.gethostname())
Georg Brandldd2245f2006-03-31 17:18:06 +0000694 except socket.gaierror:
695 localaddr = ''
696 if localaddr:
697 urls.append("file://%s%s" % (localaddr, urlpath))
Tim Peters480725d2006-04-03 02:46:44 +0000698
Georg Brandldd2245f2006-03-31 17:18:06 +0000699 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000700 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000701 try:
702 try:
703 f.write(towrite)
704 finally:
705 f.close()
706
707 r = h.file_open(Request(url))
708 try:
709 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000710 headers = r.info()
Senthil Kumaran6057ba12010-05-08 03:11:50 +0000711 respurl = r.geturl()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000712 finally:
713 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000714 stats = os.stat(TESTFN)
715 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000716 finally:
717 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000718 self.assertEqual(data, towrite)
719 self.assertEqual(headers["Content-type"], "text/plain")
720 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000721 self.assertEqual(headers["Last-modified"], modified)
Senthil Kumaran6057ba12010-05-08 03:11:50 +0000722 self.assertEqual(respurl, url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000723
724 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000725 "file://localhost:80%s" % urlpath,
Georg Brandlceede5c2007-03-13 08:14:27 +0000726 "file:///file_does_not_exist.txt",
727 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
728 os.getcwd(), TESTFN),
729 "file://somerandomhost.ontheinternet.com%s/%s" %
730 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000731 ]:
732 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000733 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000734 try:
735 f.write(towrite)
736 finally:
737 f.close()
738
739 self.assertRaises(urllib2.URLError,
740 h.file_open, Request(url))
741 finally:
742 os.remove(TESTFN)
743
744 h = urllib2.FileHandler()
745 o = h.parent = MockOpener()
746 # XXXX why does // mean ftp (and /// mean not ftp!), and where
747 # is file: scheme specified? I think this is really a bug, and
748 # what was intended was to distinguish between URLs like:
749 # file:/blah.txt (a file)
750 # file://localhost/blah.txt (a file)
751 # file:///blah.txt (a file)
752 # file://ftp.example.com/blah.txt (an ftp URL)
753 for url, ftp in [
754 ("file://ftp.example.com//foo.txt", True),
755 ("file://ftp.example.com///foo.txt", False),
756# XXXX bug: fails with OSError, should be URLError
757 ("file://ftp.example.com/foo.txt", False),
Senthil Kumaran87ed31a2010-07-11 03:18:51 +0000758 ("file://somehost//foo/something.txt", True),
759 ("file://localhost//foo/something.txt", False),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000760 ]:
761 req = Request(url)
762 try:
763 h.file_open(req)
764 # XXXX remove OSError when bug fixed
765 except (urllib2.URLError, OSError):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000766 self.assertTrue(not ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000767 else:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000768 self.assertTrue(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000769 self.assertEqual(req.type, "ftp")
Benjamin Peterson98104272011-01-12 19:27:17 +0000770 self.assertEqual(req.type == "ftp", ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000771
772 def test_http(self):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000773
774 h = urllib2.AbstractHTTPHandler()
775 o = h.parent = MockOpener()
776
777 url = "http://example.com/"
778 for method, data in [("GET", None), ("POST", "blah")]:
779 req = Request(url, data, {"Foo": "bar"})
Facundo Batista10951d52007-06-06 17:15:23 +0000780 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000781 req.add_unredirected_header("Spam", "eggs")
782 http = MockHTTPClass()
783 r = h.do_open(http, req)
784
785 # result attributes
786 r.read; r.readline # wrapped MockFile methods
787 r.info; r.geturl # addinfourl methods
788 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
789 hdrs = r.info()
790 hdrs.get; hdrs.has_key # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000791 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000792
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000793 self.assertEqual(http.host, "example.com")
794 self.assertEqual(http.level, 0)
795 self.assertEqual(http.method, method)
796 self.assertEqual(http.selector, "/")
797 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000798 [("Connection", "close"),
799 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000800 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000801
802 # check socket.error converted to URLError
803 http.raise_on_endheaders = True
804 self.assertRaises(urllib2.URLError, h.do_open, http, req)
805
806 # check adding of standard headers
807 o.addheaders = [("Spam", "eggs")]
808 for data in "", None: # POST, GET
809 req = Request("http://example.com/", data)
810 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000811 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000812 if data is None: # GET
Ezio Melottiaa980582010-01-23 23:04:36 +0000813 self.assertNotIn("Content-length", req.unredirected_hdrs)
814 self.assertNotIn("Content-type", req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000815 else: # POST
Georg Brandl8c036cc2006-08-20 13:15:39 +0000816 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
817 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000818 "application/x-www-form-urlencoded")
819 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000820 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
821 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000822
823 # don't clobber existing headers
824 req.add_unredirected_header("Content-length", "foo")
825 req.add_unredirected_header("Content-type", "bar")
826 req.add_unredirected_header("Host", "baz")
827 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000828 newreq = h.do_request_(req)
Georg Brandl8c036cc2006-08-20 13:15:39 +0000829 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
830 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000831 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
832 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000833
Facundo Batistaeb90b782008-08-16 14:44:07 +0000834 def test_http_doubleslash(self):
835 # Checks that the presence of an unnecessary double slash in a url doesn't break anything
836 # Previously, a double slash directly after the host could cause incorrect parsing of the url
837 h = urllib2.AbstractHTTPHandler()
838 o = h.parent = MockOpener()
839
840 data = ""
841 ds_urls = [
842 "http://example.com/foo/bar/baz.html",
843 "http://example.com//foo/bar/baz.html",
844 "http://example.com/foo//bar/baz.html",
845 "http://example.com/foo/bar//baz.html",
846 ]
847
848 for ds_url in ds_urls:
849 ds_req = Request(ds_url, data)
850
851 # Check whether host is determined correctly if there is no proxy
852 np_ds_req = h.do_request_(ds_req)
853 self.assertEqual(np_ds_req.unredirected_hdrs["Host"],"example.com")
854
855 # Check whether host is determined correctly if there is a proxy
856 ds_req.set_proxy("someproxy:3128",None)
857 p_ds_req = h.do_request_(ds_req)
858 self.assertEqual(p_ds_req.unredirected_hdrs["Host"],"example.com")
859
Senthil Kumaran0b7cac12010-11-22 05:04:33 +0000860 def test_fixpath_in_weirdurls(self):
861 # Issue4493: urllib2 to supply '/' when to urls where path does not
862 # start with'/'
863
864 h = urllib2.AbstractHTTPHandler()
865 o = h.parent = MockOpener()
866
867 weird_url = 'http://www.python.org?getspam'
868 req = Request(weird_url)
869 newreq = h.do_request_(req)
870 self.assertEqual(newreq.get_host(),'www.python.org')
871 self.assertEqual(newreq.get_selector(),'/?getspam')
872
873 url_without_path = 'http://www.python.org'
874 req = Request(url_without_path)
875 newreq = h.do_request_(req)
876 self.assertEqual(newreq.get_host(),'www.python.org')
877 self.assertEqual(newreq.get_selector(),'')
878
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000879 def test_errors(self):
880 h = urllib2.HTTPErrorProcessor()
881 o = h.parent = MockOpener()
882
883 url = "http://example.com/"
884 req = Request(url)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000885 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000886 r = MockResponse(200, "OK", {}, "", url)
887 newr = h.http_response(req, r)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000888 self.assertTrue(r is newr)
889 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000890 r = MockResponse(202, "Accepted", {}, "", url)
891 newr = h.http_response(req, r)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000892 self.assertTrue(r is newr)
893 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000894 r = MockResponse(206, "Partial content", {}, "", url)
895 newr = h.http_response(req, r)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000896 self.assertTrue(r is newr)
897 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000898 # anything else calls o.error (and MockOpener returns None, here)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000899 r = MockResponse(502, "Bad gateway", {}, "", url)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000900 self.assertTrue(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000901 self.assertEqual(o.proto, "http") # o.error called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000902 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000903
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000904 def test_cookies(self):
905 cj = MockCookieJar()
906 h = urllib2.HTTPCookieProcessor(cj)
907 o = h.parent = MockOpener()
908
909 req = Request("http://example.com/")
910 r = MockResponse(200, "OK", {}, "")
911 newreq = h.http_request(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000912 self.assertTrue(cj.ach_req is req is newreq)
Ezio Melotti2623a372010-11-21 13:34:58 +0000913 self.assertEqual(req.get_origin_req_host(), "example.com")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000914 self.assertTrue(not req.is_unverifiable())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000915 newr = h.http_response(req, r)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000916 self.assertTrue(cj.ec_req is req)
917 self.assertTrue(cj.ec_r is r is newr)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000918
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000919 def test_redirect(self):
920 from_url = "http://example.com/a.html"
921 to_url = "http://example.com/b.html"
922 h = urllib2.HTTPRedirectHandler()
923 o = h.parent = MockOpener()
924
925 # ordinary redirect behaviour
926 for code in 301, 302, 303, 307:
927 for data in None, "blah\nblah\n":
928 method = getattr(h, "http_error_%s" % code)
929 req = Request(from_url, data)
930 req.add_header("Nonsense", "viking=withhold")
Senthil Kumaran5fee4602009-07-19 02:43:43 +0000931 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Facundo Batista86371d62008-02-07 19:06:52 +0000932 if data is not None:
933 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000934 req.add_unredirected_header("Spam", "spam")
935 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000936 method(req, MockFile(), code, "Blah",
937 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000938 except urllib2.HTTPError:
939 # 307 in response to POST requires user OK
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200940 self.assertEqual(code, 307)
941 self.assertIsNotNone(data)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000942 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000943 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000944 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000945 except AttributeError:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000946 self.assertTrue(not o.req.has_data())
Facundo Batista86371d62008-02-07 19:06:52 +0000947
948 # now it's a GET, there should not be headers regarding content
949 # (possibly dragged from before being a POST)
950 headers = [x.lower() for x in o.req.headers]
Ezio Melottiaa980582010-01-23 23:04:36 +0000951 self.assertNotIn("content-length", headers)
952 self.assertNotIn("content-type", headers)
Facundo Batista86371d62008-02-07 19:06:52 +0000953
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000954 self.assertEqual(o.req.headers["Nonsense"],
955 "viking=withhold")
Ezio Melottiaa980582010-01-23 23:04:36 +0000956 self.assertNotIn("Spam", o.req.headers)
957 self.assertNotIn("Spam", o.req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000958
959 # loop detection
960 req = Request(from_url)
Senthil Kumaran5fee4602009-07-19 02:43:43 +0000961 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000962 def redirect(h, req, url=to_url):
963 h.http_error_302(req, MockFile(), 302, "Blah",
964 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000965 # Note that the *original* request shares the same record of
966 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000967
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000968 # detect infinite loop redirect of a URL to itself
969 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000970 count = 0
Senthil Kumaran5fee4602009-07-19 02:43:43 +0000971 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000972 try:
973 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000974 redirect(h, req, "http://example.com/")
975 count = count + 1
976 except urllib2.HTTPError:
977 # don't stop until max_repeats, because cookies may introduce state
978 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
979
980 # detect endless non-repeating chain of redirects
981 req = Request(from_url, origin_req_host="example.com")
982 count = 0
Senthil Kumaran5fee4602009-07-19 02:43:43 +0000983 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000984 try:
985 while 1:
986 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000987 count = count + 1
988 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000989 self.assertEqual(count,
990 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000991
guido@google.comf1509302011-03-28 13:47:01 -0700992 def test_invalid_redirect(self):
993 from_url = "http://example.com/a.html"
994 valid_schemes = ['http', 'https', 'ftp']
995 invalid_schemes = ['file', 'imap', 'ldap']
996 schemeless_url = "example.com/b.html"
997 h = urllib2.HTTPRedirectHandler()
998 o = h.parent = MockOpener()
999 req = Request(from_url)
guido@google.com9a9fdfa2011-03-29 10:48:23 -07001000 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
guido@google.comf1509302011-03-28 13:47:01 -07001001
1002 for scheme in invalid_schemes:
1003 invalid_url = scheme + '://' + schemeless_url
1004 self.assertRaises(urllib2.HTTPError, h.http_error_302,
1005 req, MockFile(), 302, "Security Loophole",
1006 MockHeaders({"location": invalid_url}))
1007
1008 for scheme in valid_schemes:
1009 valid_url = scheme + '://' + schemeless_url
1010 h.http_error_302(req, MockFile(), 302, "That's fine",
1011 MockHeaders({"location": valid_url}))
1012 self.assertEqual(o.req.get_full_url(), valid_url)
1013
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001014 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001015 # cookies shouldn't leak into redirected requests
1016 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001017
Neal Norwitzb902f4e2006-04-03 04:45:34 +00001018 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001019
1020 cj = CookieJar()
1021 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Georg Brandlfa42bd72006-04-30 07:06:11 +00001022 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
1023 hdeh = urllib2.HTTPDefaultErrorHandler()
1024 hrh = urllib2.HTTPRedirectHandler()
1025 cp = urllib2.HTTPCookieProcessor(cj)
1026 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001027 o.open("http://www.example.com/")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001028 self.assertTrue(not hh.req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001029
Senthil Kumaran49c44082011-04-13 07:31:45 +08001030 def test_redirect_fragment(self):
1031 redirected_url = 'http://www.example.com/index.html#OK\r\n\r\n'
1032 hh = MockHTTPHandler(302, 'Location: ' + redirected_url)
1033 hdeh = urllib2.HTTPDefaultErrorHandler()
1034 hrh = urllib2.HTTPRedirectHandler()
1035 o = build_test_opener(hh, hdeh, hrh)
1036 fp = o.open('http://www.example.com')
1037 self.assertEqual(fp.geturl(), redirected_url.strip())
1038
Georg Brandl720096a2006-04-02 20:45:34 +00001039 def test_proxy(self):
1040 o = OpenerDirector()
1041 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
1042 o.add_handler(ph)
1043 meth_spec = [
1044 [("http_open", "return response")]
1045 ]
1046 handlers = add_ordered_mock_handlers(o, meth_spec)
1047
1048 req = Request("http://acme.example.com/")
1049 self.assertEqual(req.get_host(), "acme.example.com")
1050 r = o.open(req)
1051 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1052
1053 self.assertEqual([(handlers[0], "http_open")],
1054 [tup[0:2] for tup in o.calls])
1055
Senthil Kumaran27468662009-10-11 02:00:07 +00001056 def test_proxy_no_proxy(self):
1057 os.environ['no_proxy'] = 'python.org'
1058 o = OpenerDirector()
1059 ph = urllib2.ProxyHandler(dict(http="proxy.example.com"))
1060 o.add_handler(ph)
1061 req = Request("http://www.perl.org/")
1062 self.assertEqual(req.get_host(), "www.perl.org")
1063 r = o.open(req)
1064 self.assertEqual(req.get_host(), "proxy.example.com")
1065 req = Request("http://www.python.org")
1066 self.assertEqual(req.get_host(), "www.python.org")
1067 r = o.open(req)
1068 self.assertEqual(req.get_host(), "www.python.org")
1069 del os.environ['no_proxy']
1070
1071
Senthil Kumarane266f252009-05-24 09:14:50 +00001072 def test_proxy_https(self):
1073 o = OpenerDirector()
1074 ph = urllib2.ProxyHandler(dict(https='proxy.example.com:3128'))
1075 o.add_handler(ph)
1076 meth_spec = [
1077 [("https_open","return response")]
1078 ]
1079 handlers = add_ordered_mock_handlers(o, meth_spec)
1080 req = Request("https://www.example.com/")
1081 self.assertEqual(req.get_host(), "www.example.com")
1082 r = o.open(req)
1083 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1084 self.assertEqual([(handlers[0], "https_open")],
1085 [tup[0:2] for tup in o.calls])
1086
Senthil Kumaran7713acf2009-12-20 06:05:13 +00001087 def test_proxy_https_proxy_authorization(self):
1088 o = OpenerDirector()
1089 ph = urllib2.ProxyHandler(dict(https='proxy.example.com:3128'))
1090 o.add_handler(ph)
1091 https_handler = MockHTTPSHandler()
1092 o.add_handler(https_handler)
1093 req = Request("https://www.example.com/")
1094 req.add_header("Proxy-Authorization","FooBar")
1095 req.add_header("User-Agent","Grail")
1096 self.assertEqual(req.get_host(), "www.example.com")
1097 self.assertIsNone(req._tunnel_host)
1098 r = o.open(req)
1099 # Verify Proxy-Authorization gets tunneled to request.
1100 # httpsconn req_headers do not have the Proxy-Authorization header but
1101 # the req will have.
Ezio Melottiaa980582010-01-23 23:04:36 +00001102 self.assertNotIn(("Proxy-Authorization","FooBar"),
Senthil Kumaran7713acf2009-12-20 06:05:13 +00001103 https_handler.httpconn.req_headers)
Ezio Melottiaa980582010-01-23 23:04:36 +00001104 self.assertIn(("User-Agent","Grail"),
1105 https_handler.httpconn.req_headers)
Senthil Kumaran7713acf2009-12-20 06:05:13 +00001106 self.assertIsNotNone(req._tunnel_host)
1107 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1108 self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
1109
Georg Brandl33124322008-03-21 19:54:00 +00001110 def test_basic_auth(self, quote_char='"'):
Georg Brandlfa42bd72006-04-30 07:06:11 +00001111 opener = OpenerDirector()
1112 password_manager = MockPasswordManager()
1113 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
1114 realm = "ACME Widget Store"
1115 http_handler = MockHTTPHandler(
Georg Brandl33124322008-03-21 19:54:00 +00001116 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
1117 (quote_char, realm, quote_char) )
Georg Brandl261e2512006-05-29 20:52:54 +00001118 opener.add_handler(auth_handler)
1119 opener.add_handler(http_handler)
Georg Brandlfa42bd72006-04-30 07:06:11 +00001120 self._test_basic_auth(opener, auth_handler, "Authorization",
1121 realm, http_handler, password_manager,
1122 "http://acme.example.com/protected",
Senthil Kumaranb0d85fd2012-05-15 23:59:19 +08001123 "http://acme.example.com/protected"
1124 )
Georg Brandlfa42bd72006-04-30 07:06:11 +00001125
Georg Brandl33124322008-03-21 19:54:00 +00001126 def test_basic_auth_with_single_quoted_realm(self):
1127 self.test_basic_auth(quote_char="'")
1128
Senthil Kumaran6a2a6c22012-05-15 22:24:10 +08001129 def test_basic_auth_with_unquoted_realm(self):
1130 opener = OpenerDirector()
1131 password_manager = MockPasswordManager()
1132 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
1133 realm = "ACME Widget Store"
1134 http_handler = MockHTTPHandler(
1135 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm)
1136 opener.add_handler(auth_handler)
1137 opener.add_handler(http_handler)
Senthil Kumaranb0d85fd2012-05-15 23:59:19 +08001138 msg = "Basic Auth Realm was unquoted"
1139 with test_support.check_warnings((msg, UserWarning)):
1140 self._test_basic_auth(opener, auth_handler, "Authorization",
1141 realm, http_handler, password_manager,
1142 "http://acme.example.com/protected",
1143 "http://acme.example.com/protected"
1144 )
Senthil Kumaran6a2a6c22012-05-15 22:24:10 +08001145
1146
Georg Brandlfa42bd72006-04-30 07:06:11 +00001147 def test_proxy_basic_auth(self):
1148 opener = OpenerDirector()
1149 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
1150 opener.add_handler(ph)
1151 password_manager = MockPasswordManager()
1152 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
1153 realm = "ACME Networks"
1154 http_handler = MockHTTPHandler(
1155 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +00001156 opener.add_handler(auth_handler)
1157 opener.add_handler(http_handler)
Georg Brandl8c036cc2006-08-20 13:15:39 +00001158 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Georg Brandlfa42bd72006-04-30 07:06:11 +00001159 realm, http_handler, password_manager,
1160 "http://acme.example.com:3128/protected",
1161 "proxy.example.com:3128",
1162 )
1163
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001164 def test_basic_and_digest_auth_handlers(self):
Andrew Svetlovbacf1bf2012-12-19 22:49:01 +02001165 # HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001166 # response (http://python.org/sf/1479302), where it should instead
1167 # return None to allow another handler (especially
1168 # HTTPBasicAuthHandler) to handle the response.
Georg Brandl261e2512006-05-29 20:52:54 +00001169
1170 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
1171 # try digest first (since it's the strongest auth scheme), so we record
1172 # order of calls here to check digest comes first:
1173 class RecordingOpenerDirector(OpenerDirector):
1174 def __init__(self):
1175 OpenerDirector.__init__(self)
1176 self.recorded = []
1177 def record(self, info):
1178 self.recorded.append(info)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001179 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Georg Brandl261e2512006-05-29 20:52:54 +00001180 def http_error_401(self, *args, **kwds):
1181 self.parent.record("digest")
1182 urllib2.HTTPDigestAuthHandler.http_error_401(self,
1183 *args, **kwds)
1184 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
1185 def http_error_401(self, *args, **kwds):
1186 self.parent.record("basic")
1187 urllib2.HTTPBasicAuthHandler.http_error_401(self,
1188 *args, **kwds)
1189
1190 opener = RecordingOpenerDirector()
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001191 password_manager = MockPasswordManager()
1192 digest_handler = TestDigestAuthHandler(password_manager)
Georg Brandl261e2512006-05-29 20:52:54 +00001193 basic_handler = TestBasicAuthHandler(password_manager)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001194 realm = "ACME Networks"
1195 http_handler = MockHTTPHandler(
1196 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +00001197 opener.add_handler(basic_handler)
1198 opener.add_handler(digest_handler)
1199 opener.add_handler(http_handler)
1200
1201 # check basic auth isn't blocked by digest handler failing
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001202 self._test_basic_auth(opener, basic_handler, "Authorization",
1203 realm, http_handler, password_manager,
1204 "http://acme.example.com/protected",
1205 "http://acme.example.com/protected",
1206 )
Georg Brandl261e2512006-05-29 20:52:54 +00001207 # check digest was tried before basic (twice, because
1208 # _test_basic_auth called .open() twice)
1209 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001210
Georg Brandlfa42bd72006-04-30 07:06:11 +00001211 def _test_basic_auth(self, opener, auth_handler, auth_header,
1212 realm, http_handler, password_manager,
1213 request_url, protected_url):
Christian Heimesc5f05e42008-02-23 17:40:11 +00001214 import base64
Georg Brandlfa42bd72006-04-30 07:06:11 +00001215 user, password = "wile", "coyote"
Georg Brandlfa42bd72006-04-30 07:06:11 +00001216
1217 # .add_password() fed through to password manager
1218 auth_handler.add_password(realm, request_url, user, password)
1219 self.assertEqual(realm, password_manager.realm)
1220 self.assertEqual(request_url, password_manager.url)
1221 self.assertEqual(user, password_manager.user)
1222 self.assertEqual(password, password_manager.password)
1223
1224 r = opener.open(request_url)
1225
1226 # should have asked the password manager for the username/password
1227 self.assertEqual(password_manager.target_realm, realm)
1228 self.assertEqual(password_manager.target_url, protected_url)
1229
1230 # expect one request without authorization, then one with
1231 self.assertEqual(len(http_handler.requests), 2)
1232 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1233 userpass = '%s:%s' % (user, password)
1234 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
1235 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1236 auth_hdr_value)
Senthil Kumaran8526adf2010-02-24 16:45:46 +00001237 self.assertEqual(http_handler.requests[1].unredirected_hdrs[auth_header],
1238 auth_hdr_value)
Georg Brandlfa42bd72006-04-30 07:06:11 +00001239 # if the password manager can't find a password, the handler won't
1240 # handle the HTTP auth error
1241 password_manager.user = password_manager.password = None
1242 http_handler.reset()
1243 r = opener.open(request_url)
1244 self.assertEqual(len(http_handler.requests), 1)
1245 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1246
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001247class MiscTests(unittest.TestCase):
1248
1249 def test_build_opener(self):
1250 class MyHTTPHandler(urllib2.HTTPHandler): pass
1251 class FooHandler(urllib2.BaseHandler):
1252 def foo_open(self): pass
1253 class BarHandler(urllib2.BaseHandler):
1254 def bar_open(self): pass
1255
1256 build_opener = urllib2.build_opener
1257
1258 o = build_opener(FooHandler, BarHandler)
1259 self.opener_has_handler(o, FooHandler)
1260 self.opener_has_handler(o, BarHandler)
1261
1262 # can take a mix of classes and instances
1263 o = build_opener(FooHandler, BarHandler())
1264 self.opener_has_handler(o, FooHandler)
1265 self.opener_has_handler(o, BarHandler)
1266
1267 # subclasses of default handlers override default handlers
1268 o = build_opener(MyHTTPHandler)
1269 self.opener_has_handler(o, MyHTTPHandler)
1270
1271 # a particular case of overriding: default handlers can be passed
1272 # in explicitly
1273 o = build_opener()
1274 self.opener_has_handler(o, urllib2.HTTPHandler)
1275 o = build_opener(urllib2.HTTPHandler)
1276 self.opener_has_handler(o, urllib2.HTTPHandler)
1277 o = build_opener(urllib2.HTTPHandler())
1278 self.opener_has_handler(o, urllib2.HTTPHandler)
1279
Amaury Forgeot d'Arc96865852008-04-22 21:14:41 +00001280 # Issue2670: multiple handlers sharing the same base class
1281 class MyOtherHTTPHandler(urllib2.HTTPHandler): pass
1282 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1283 self.opener_has_handler(o, MyHTTPHandler)
1284 self.opener_has_handler(o, MyOtherHTTPHandler)
1285
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001286 def opener_has_handler(self, opener, handler_class):
1287 for h in opener.handlers:
1288 if h.__class__ == handler_class:
1289 break
1290 else:
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001291 self.assertTrue(False)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001292
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001293class RequestTests(unittest.TestCase):
1294
1295 def setUp(self):
1296 self.get = urllib2.Request("http://www.python.org/~jeremy/")
1297 self.post = urllib2.Request("http://www.python.org/~jeremy/",
1298 "data",
1299 headers={"X-Test": "test"})
1300
1301 def test_method(self):
1302 self.assertEqual("POST", self.post.get_method())
1303 self.assertEqual("GET", self.get.get_method())
1304
1305 def test_add_data(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001306 self.assertTrue(not self.get.has_data())
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001307 self.assertEqual("GET", self.get.get_method())
1308 self.get.add_data("spam")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001309 self.assertTrue(self.get.has_data())
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001310 self.assertEqual("POST", self.get.get_method())
1311
1312 def test_get_full_url(self):
1313 self.assertEqual("http://www.python.org/~jeremy/",
1314 self.get.get_full_url())
1315
1316 def test_selector(self):
1317 self.assertEqual("/~jeremy/", self.get.get_selector())
1318 req = urllib2.Request("http://www.python.org/")
1319 self.assertEqual("/", req.get_selector())
1320
1321 def test_get_type(self):
1322 self.assertEqual("http", self.get.get_type())
1323
1324 def test_get_host(self):
1325 self.assertEqual("www.python.org", self.get.get_host())
1326
1327 def test_get_host_unquote(self):
1328 req = urllib2.Request("http://www.%70ython.org/")
1329 self.assertEqual("www.python.org", req.get_host())
1330
1331 def test_proxy(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001332 self.assertTrue(not self.get.has_proxy())
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001333 self.get.set_proxy("www.perl.org", "http")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001334 self.assertTrue(self.get.has_proxy())
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001335 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1336 self.assertEqual("www.perl.org", self.get.get_host())
1337
Senthil Kumaranb4ec7ee2010-08-08 11:43:45 +00001338 def test_wrapped_url(self):
1339 req = Request("<URL:http://www.python.org>")
1340 self.assertEqual("www.python.org", req.get_host())
1341
Senthil Kumaran49c44082011-04-13 07:31:45 +08001342 def test_url_fragment(self):
Senthil Kumaranb4ec7ee2010-08-08 11:43:45 +00001343 req = Request("http://www.python.org/?qs=query#fragment=true")
1344 self.assertEqual("/?qs=query", req.get_selector())
1345 req = Request("http://www.python.org/#fun=true")
1346 self.assertEqual("/", req.get_selector())
1347
Senthil Kumaran49c44082011-04-13 07:31:45 +08001348 # Issue 11703: geturl() omits fragment in the original URL.
1349 url = 'http://docs.python.org/library/urllib2.html#OK'
1350 req = Request(url)
1351 self.assertEqual(req.get_full_url(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001352
Senthil Kumaranf8a6b002012-12-23 09:00:47 -08001353 def test_HTTPError_interface(self):
1354 """
1355 Issue 13211 reveals that HTTPError didn't implement the URLError
1356 interface even though HTTPError is a subclass of URLError.
Jason R. Coombs974d8632011-11-07 10:44:25 -05001357
Senthil Kumaranf8a6b002012-12-23 09:00:47 -08001358 >>> err = urllib2.HTTPError(msg='something bad happened', url=None, code=None, hdrs=None, fp=None)
1359 >>> assert hasattr(err, 'reason')
1360 >>> err.reason
1361 'something bad happened'
1362 """
1363
1364 def test_HTTPError_interface_call(self):
1365 """
1366 Issue 15701= - HTTPError interface has info method available from URLError.
1367 """
1368 err = urllib2.HTTPError(msg='something bad happened', url=None,
1369 code=None, hdrs='Content-Length:42', fp=None)
1370 self.assertTrue(hasattr(err, 'reason'))
1371 assert hasattr(err, 'reason')
1372 assert hasattr(err, 'info')
1373 assert callable(err.info)
1374 try:
1375 err.info()
1376 except AttributeError:
1377 self.fail("err.info() failed")
1378 self.assertEqual(err.info(), "Content-Length:42")
Jason R. Coombs974d8632011-11-07 10:44:25 -05001379
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001380def test_main(verbose=None):
Georg Brandlfa42bd72006-04-30 07:06:11 +00001381 from test import test_urllib2
1382 test_support.run_doctest(test_urllib2, verbose)
Georg Brandl720096a2006-04-02 20:45:34 +00001383 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001384 tests = (TrivialTests,
1385 OpenerDirectorTests,
1386 HandlerTests,
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001387 MiscTests,
1388 RequestTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001389 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001390
1391if __name__ == "__main__":
1392 test_main(verbose=True)