blob: 70fbb64f558bf4ed4687da356bfab4dfede6ed3c [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00002from test import support
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00003
Christian Heimes05e8be12008-02-23 18:30:17 +00004import os
Guido van Rossum34d19282007-08-09 01:03:29 +00005import io
Georg Brandlf78e02b2008-06-10 17:40:04 +00006import socket
Senthil Kumaran7bc0d872010-12-19 10:49:52 +00007import array
Jeremy Hyltone3e61042001-05-09 15:50:25 +00008
Jeremy Hylton1afc1692008-06-18 20:49:58 +00009import urllib.request
Ronald Oussorene72e1612011-03-14 18:15:25 -040010# The proxy bypass method imported below has logic specific to the OSX
11# proxy config data structure but is testable on all platforms.
12from urllib.request import Request, OpenerDirector, _proxy_bypass_macosx_sysconf
guido@google.coma119df92011-03-29 11:41:02 -070013import urllib.error
Jeremy Hyltone3e61042001-05-09 15:50:25 +000014
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000015# XXX
16# Request
17# CacheFTPHandler (hard to write)
Thomas Wouters477c8d52006-05-27 19:21:47 +000018# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000019
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000020class TrivialTests(unittest.TestCase):
21 def test_trivial(self):
22 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000023
Jeremy Hylton1afc1692008-06-18 20:49:58 +000024 self.assertRaises(ValueError, urllib.request.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000025
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000026 # XXX Name hacking to get this to work on Windows.
Jeremy Hylton1afc1692008-06-18 20:49:58 +000027 fname = os.path.abspath(urllib.request.__file__).replace('\\', '/')
Senthil Kumarand587e302010-01-10 17:45:52 +000028
Senthil Kumarand587e302010-01-10 17:45:52 +000029 if os.name == 'nt':
30 file_url = "file:///%s" % fname
31 else:
32 file_url = "file://%s" % fname
33
Jeremy Hylton1afc1692008-06-18 20:49:58 +000034 f = urllib.request.urlopen(file_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000035
36 buf = f.read()
37 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000038
Georg Brandle1b13d22005-08-24 22:20:32 +000039 def test_parse_http_list(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +000040 tests = [
41 ('a,b,c', ['a', 'b', 'c']),
42 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
43 ('a, b, "c", "d", "e,f", g, h',
44 ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
45 ('a="b\\"c", d="e\\,f", g="h\\\\i"',
46 ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
Georg Brandle1b13d22005-08-24 22:20:32 +000047 for string, list in tests:
Florent Xicluna419e3842010-08-08 16:16:07 +000048 self.assertEqual(urllib.request.parse_http_list(string), list)
Georg Brandle1b13d22005-08-24 22:20:32 +000049
Senthil Kumaran843fae92013-03-19 13:43:42 -070050 def test_URLError_reasonstr(self):
51 err = urllib.error.URLError('reason')
52 self.assertIn(err.reason, str(err))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000053
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000054def test_request_headers_dict():
55 """
56 The Request.headers dictionary is not a documented interface. It should
57 stay that way, because the complete set of headers are only accessible
58 through the .get_header(), .has_header(), .header_items() interface.
59 However, .headers pre-dates those methods, and so real code will be using
60 the dictionary.
61
62 The introduction in 2.4 of those methods was a mistake for the same reason:
63 code that previously saw all (urllib2 user)-provided headers in .headers
64 now sees only a subset (and the function interface is ugly and incomplete).
65 A better change would have been to replace .headers dict with a dict
66 subclass (or UserDict.DictMixin instance?) that preserved the .headers
67 interface and also provided access to the "unredirected" headers. It's
68 probably too late to fix that, though.
69
70
71 Check .capitalize() case normalization:
72
73 >>> url = "http://example.com"
74 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
75 'blah'
76 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
77 'blah'
78
79 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
80 but that could be changed in future.
81
82 """
83
84def test_request_headers_methods():
85 """
86 Note the case normalization of header names here, to .capitalize()-case.
87 This should be preserved for backwards-compatibility. (In the HTTP case,
88 normalization to .title()-case is done by urllib2 before sending headers to
Georg Brandl24420152008-05-26 16:32:26 +000089 http.client).
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000090
91 >>> url = "http://example.com"
92 >>> r = Request(url, headers={"Spam-eggs": "blah"})
93 >>> r.has_header("Spam-eggs")
94 True
95 >>> r.header_items()
96 [('Spam-eggs', 'blah')]
97 >>> r.add_header("Foo-Bar", "baz")
Guido van Rossumcc2b0162007-02-11 06:12:03 +000098 >>> items = sorted(r.header_items())
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000099 >>> items
100 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
101
102 Note that e.g. r.has_header("spam-EggS") is currently False, and
103 r.get_header("spam-EggS") returns None, but that could be changed in
104 future.
105
106 >>> r.has_header("Not-there")
107 False
Guido van Rossum7131f842007-02-09 20:13:25 +0000108 >>> print(r.get_header("Not-there"))
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000109 None
110 >>> r.get_header("Not-there", "default")
111 'default'
112
113 """
114
115
Thomas Wouters477c8d52006-05-27 19:21:47 +0000116def test_password_manager(self):
117 """
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000118 >>> mgr = urllib.request.HTTPPasswordMgr()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000119 >>> add = mgr.add_password
120 >>> add("Some Realm", "http://example.com/", "joe", "password")
121 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
122 >>> add("c", "http://example.com/foo", "foo", "ni")
123 >>> add("c", "http://example.com/bar", "bar", "nini")
124 >>> add("b", "http://example.com/", "first", "blah")
125 >>> add("b", "http://example.com/", "second", "spam")
126 >>> add("a", "http://example.com", "1", "a")
127 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
128 >>> add("Some Realm", "d.example.com", "4", "d")
129 >>> add("Some Realm", "e.example.com:3128", "5", "e")
130
131 >>> mgr.find_user_password("Some Realm", "example.com")
132 ('joe', 'password')
133 >>> mgr.find_user_password("Some Realm", "http://example.com")
134 ('joe', 'password')
135 >>> mgr.find_user_password("Some Realm", "http://example.com/")
136 ('joe', 'password')
137 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
138 ('joe', 'password')
139 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
140 ('joe', 'password')
141 >>> mgr.find_user_password("c", "http://example.com/foo")
142 ('foo', 'ni')
143 >>> mgr.find_user_password("c", "http://example.com/bar")
144 ('bar', 'nini')
145
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000146 Actually, this is really undefined ATM
147## Currently, we use the highest-level path where more than one match:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000148
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000149## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
150## ('joe', 'password')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000151
152 Use latest add_password() in case of conflict:
153
154 >>> mgr.find_user_password("b", "http://example.com/")
155 ('second', 'spam')
156
157 No special relationship between a.example.com and example.com:
158
159 >>> mgr.find_user_password("a", "http://example.com/")
160 ('1', 'a')
161 >>> mgr.find_user_password("a", "http://a.example.com/")
162 (None, None)
163
164 Ports:
165
166 >>> mgr.find_user_password("Some Realm", "c.example.com")
167 (None, None)
168 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
169 ('3', 'c')
170 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
171 ('3', 'c')
172 >>> mgr.find_user_password("Some Realm", "d.example.com")
173 ('4', 'd')
174 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
175 ('5', 'e')
176
177 """
178 pass
179
180
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000181def test_password_manager_default_port(self):
182 """
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000183 >>> mgr = urllib.request.HTTPPasswordMgr()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000184 >>> add = mgr.add_password
185
186 The point to note here is that we can't guess the default port if there's
187 no scheme. This applies to both add_password and find_user_password.
188
189 >>> add("f", "http://g.example.com:80", "10", "j")
190 >>> add("g", "http://h.example.com", "11", "k")
191 >>> add("h", "i.example.com:80", "12", "l")
192 >>> add("i", "j.example.com", "13", "m")
193 >>> mgr.find_user_password("f", "g.example.com:100")
194 (None, None)
195 >>> mgr.find_user_password("f", "g.example.com:80")
196 ('10', 'j')
197 >>> mgr.find_user_password("f", "g.example.com")
198 (None, None)
199 >>> mgr.find_user_password("f", "http://g.example.com:100")
200 (None, None)
201 >>> mgr.find_user_password("f", "http://g.example.com:80")
202 ('10', 'j')
203 >>> mgr.find_user_password("f", "http://g.example.com")
204 ('10', 'j')
205 >>> mgr.find_user_password("g", "h.example.com")
206 ('11', 'k')
207 >>> mgr.find_user_password("g", "h.example.com:80")
208 ('11', 'k')
209 >>> mgr.find_user_password("g", "http://h.example.com:80")
210 ('11', 'k')
211 >>> mgr.find_user_password("h", "i.example.com")
212 (None, None)
213 >>> mgr.find_user_password("h", "i.example.com:80")
214 ('12', 'l')
215 >>> mgr.find_user_password("h", "http://i.example.com:80")
216 ('12', 'l')
217 >>> mgr.find_user_password("i", "j.example.com")
218 ('13', 'm')
219 >>> mgr.find_user_password("i", "j.example.com:80")
220 (None, None)
221 >>> mgr.find_user_password("i", "http://j.example.com")
222 ('13', 'm')
223 >>> mgr.find_user_password("i", "http://j.example.com:80")
224 (None, None)
225
226 """
227
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000228class MockOpener:
229 addheaders = []
Senthil Kumaranfb8cc2f2009-07-19 02:44:19 +0000230 def open(self, req, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
231 self.req, self.data, self.timeout = req, data, timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000232 def error(self, proto, *args):
233 self.proto, self.args = proto, args
234
235class MockFile:
236 def read(self, count=None): pass
237 def readline(self, count=None): pass
238 def close(self): pass
239
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000240class MockHeaders(dict):
241 def getheaders(self, name):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000242 return list(self.values())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000243
Guido van Rossum34d19282007-08-09 01:03:29 +0000244class MockResponse(io.StringIO):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000245 def __init__(self, code, msg, headers, data, url=None):
Guido van Rossum34d19282007-08-09 01:03:29 +0000246 io.StringIO.__init__(self, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000247 self.code, self.msg, self.headers, self.url = code, msg, headers, url
248 def info(self):
249 return self.headers
250 def geturl(self):
251 return self.url
252
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000253class MockCookieJar:
254 def add_cookie_header(self, request):
255 self.ach_req = request
256 def extract_cookies(self, response, request):
257 self.ec_req, self.ec_r = request, response
258
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000259class FakeMethod:
260 def __init__(self, meth_name, action, handle):
261 self.meth_name = meth_name
262 self.handle = handle
263 self.action = action
264 def __call__(self, *args):
265 return self.handle(self.meth_name, self.action, *args)
266
Senthil Kumaran47fff872009-12-20 07:10:31 +0000267class MockHTTPResponse(io.IOBase):
268 def __init__(self, fp, msg, status, reason):
269 self.fp = fp
270 self.msg = msg
271 self.status = status
272 self.reason = reason
273 self.code = 200
274
275 def read(self):
276 return ''
277
278 def info(self):
279 return {}
280
281 def geturl(self):
282 return self.url
283
284
285class MockHTTPClass:
286 def __init__(self):
287 self.level = 0
288 self.req_headers = []
289 self.data = None
290 self.raise_on_endheaders = False
291 self._tunnel_headers = {}
292
293 def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
294 self.host = host
295 self.timeout = timeout
296 return self
297
298 def set_debuglevel(self, level):
299 self.level = level
300
301 def set_tunnel(self, host, port=None, headers=None):
302 self._tunnel_host = host
303 self._tunnel_port = port
304 if headers:
305 self._tunnel_headers = headers
306 else:
307 self._tunnel_headers.clear()
308
Benjamin Peterson3d5b8db2009-12-24 01:14:05 +0000309 def request(self, method, url, body=None, headers=None):
Senthil Kumaran47fff872009-12-20 07:10:31 +0000310 self.method = method
311 self.selector = url
Benjamin Peterson3d5b8db2009-12-24 01:14:05 +0000312 if headers is not None:
313 self.req_headers += headers.items()
Senthil Kumaran47fff872009-12-20 07:10:31 +0000314 self.req_headers.sort()
315 if body:
316 self.data = body
317 if self.raise_on_endheaders:
318 import socket
319 raise socket.error()
320 def getresponse(self):
321 return MockHTTPResponse(MockFile(), {}, 200, "OK")
322
Victor Stinnera4c45d72011-06-17 14:01:18 +0200323 def close(self):
324 pass
325
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000326class MockHandler:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000327 # useful for testing handler machinery
328 # see add_ordered_mock_handlers() docstring
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000329 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000330 def __init__(self, methods):
331 self._define_methods(methods)
332 def _define_methods(self, methods):
333 for spec in methods:
334 if len(spec) == 2: name, action = spec
335 else: name, action = spec, None
336 meth = FakeMethod(name, action, self.handle)
337 setattr(self.__class__, name, meth)
338 def handle(self, fn_name, action, *args, **kwds):
339 self.parent.calls.append((self, fn_name, args, kwds))
340 if action is None:
341 return None
342 elif action == "return self":
343 return self
344 elif action == "return response":
345 res = MockResponse(200, "OK", {}, "")
346 return res
347 elif action == "return request":
348 return Request("http://blah/")
349 elif action.startswith("error"):
350 code = action[action.rfind(" ")+1:]
351 try:
352 code = int(code)
353 except ValueError:
354 pass
355 res = MockResponse(200, "OK", {}, "")
356 return self.parent.error("http", args[0], res, code, "", {})
357 elif action == "raise":
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000358 raise urllib.error.URLError("blah")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000359 assert False
360 def close(self): pass
361 def add_parent(self, parent):
362 self.parent = parent
363 self.parent.calls = []
364 def __lt__(self, other):
365 if not hasattr(other, "handler_order"):
366 # No handler_order, leave in original order. Yuck.
367 return True
368 return self.handler_order < other.handler_order
369
370def add_ordered_mock_handlers(opener, meth_spec):
371 """Create MockHandlers and add them to an OpenerDirector.
372
373 meth_spec: list of lists of tuples and strings defining methods to define
374 on handlers. eg:
375
376 [["http_error", "ftp_open"], ["http_open"]]
377
378 defines methods .http_error() and .ftp_open() on one handler, and
379 .http_open() on another. These methods just record their arguments and
380 return None. Using a tuple instead of a string causes the method to
381 perform some action (see MockHandler.handle()), eg:
382
383 [["http_error"], [("http_open", "return request")]]
384
385 defines .http_error() on one handler (which simply returns None), and
386 .http_open() on another handler, which returns a Request object.
387
388 """
389 handlers = []
390 count = 0
391 for meths in meth_spec:
392 class MockHandlerSubclass(MockHandler): pass
393 h = MockHandlerSubclass(meths)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000394 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000395 h.add_parent(opener)
396 count = count + 1
397 handlers.append(h)
398 opener.add_handler(h)
399 return handlers
400
Thomas Wouters477c8d52006-05-27 19:21:47 +0000401def build_test_opener(*handler_instances):
402 opener = OpenerDirector()
403 for h in handler_instances:
404 opener.add_handler(h)
405 return opener
406
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000407class MockHTTPHandler(urllib.request.BaseHandler):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000408 # useful for testing redirections and auth
409 # sends supplied headers and code as first response
410 # sends 200 OK as second response
411 def __init__(self, code, headers):
412 self.code = code
413 self.headers = headers
414 self.reset()
415 def reset(self):
416 self._count = 0
417 self.requests = []
418 def http_open(self, req):
Barry Warsaw820c1202008-06-12 04:06:45 +0000419 import email, http.client, copy
Guido van Rossum34d19282007-08-09 01:03:29 +0000420 from io import StringIO
Thomas Wouters477c8d52006-05-27 19:21:47 +0000421 self.requests.append(copy.deepcopy(req))
422 if self._count == 0:
423 self._count = self._count + 1
Georg Brandl24420152008-05-26 16:32:26 +0000424 name = http.client.responses[self.code]
Barry Warsaw820c1202008-06-12 04:06:45 +0000425 msg = email.message_from_string(self.headers)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426 return self.parent.error(
427 "http", req, MockFile(), self.code, name, msg)
428 else:
429 self.req = req
Barry Warsaw820c1202008-06-12 04:06:45 +0000430 msg = email.message_from_string("\r\n\r\n")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000431 return MockResponse(200, "OK", msg, "", req.get_full_url())
432
Senthil Kumaran47fff872009-12-20 07:10:31 +0000433class MockHTTPSHandler(urllib.request.AbstractHTTPHandler):
434 # Useful for testing the Proxy-Authorization request by verifying the
435 # properties of httpcon
Benjamin Peterson3d5b8db2009-12-24 01:14:05 +0000436
437 def __init__(self):
438 urllib.request.AbstractHTTPHandler.__init__(self)
439 self.httpconn = MockHTTPClass()
440
Senthil Kumaran47fff872009-12-20 07:10:31 +0000441 def https_open(self, req):
442 return self.do_open(self.httpconn, req)
443
Thomas Wouters477c8d52006-05-27 19:21:47 +0000444class MockPasswordManager:
445 def add_password(self, realm, uri, user, password):
446 self.realm = realm
447 self.url = uri
448 self.user = user
449 self.password = password
450 def find_user_password(self, realm, authuri):
451 self.target_realm = realm
452 self.target_url = authuri
453 return self.user, self.password
454
455
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000456class OpenerDirectorTests(unittest.TestCase):
457
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000458 def test_add_non_handler(self):
459 class NonHandler(object):
460 pass
461 self.assertRaises(TypeError,
462 OpenerDirector().add_handler, NonHandler())
463
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000464 def test_badly_named_methods(self):
465 # test work-around for three methods that accidentally follow the
466 # naming conventions for handler methods
467 # (*_open() / *_request() / *_response())
468
469 # These used to call the accidentally-named methods, causing a
470 # TypeError in real code; here, returning self from these mock
471 # methods would either cause no exception, or AttributeError.
472
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000473 from urllib.error import URLError
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000474
475 o = OpenerDirector()
476 meth_spec = [
477 [("do_open", "return self"), ("proxy_open", "return self")],
478 [("redirect_request", "return self")],
479 ]
480 handlers = add_ordered_mock_handlers(o, meth_spec)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000481 o.add_handler(urllib.request.UnknownHandler())
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000482 for scheme in "do", "proxy", "redirect":
483 self.assertRaises(URLError, o.open, scheme+"://example.com/")
484
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000485 def test_handled(self):
486 # handler returning non-None means no more handlers will be called
487 o = OpenerDirector()
488 meth_spec = [
489 ["http_open", "ftp_open", "http_error_302"],
490 ["ftp_open"],
491 [("http_open", "return self")],
492 [("http_open", "return self")],
493 ]
494 handlers = add_ordered_mock_handlers(o, meth_spec)
495
496 req = Request("http://example.com/")
497 r = o.open(req)
498 # Second .http_open() gets called, third doesn't, since second returned
499 # non-None. Handlers without .http_open() never get any methods called
500 # on them.
501 # In fact, second mock handler defining .http_open() returns self
502 # (instead of response), which becomes the OpenerDirector's return
503 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000504 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000505 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
506 for expected, got in zip(calls, o.calls):
507 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000508 self.assertEqual((handler, name), expected)
509 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000510
511 def test_handler_order(self):
512 o = OpenerDirector()
513 handlers = []
514 for meths, handler_order in [
515 ([("http_open", "return self")], 500),
516 (["http_open"], 0),
517 ]:
518 class MockHandlerSubclass(MockHandler): pass
519 h = MockHandlerSubclass(meths)
520 h.handler_order = handler_order
521 handlers.append(h)
522 o.add_handler(h)
523
524 r = o.open("http://example.com/")
525 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000526 self.assertEqual(o.calls[0][0], handlers[1])
527 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000528
529 def test_raise(self):
530 # raising URLError stops processing of request
531 o = OpenerDirector()
532 meth_spec = [
533 [("http_open", "raise")],
534 [("http_open", "return self")],
535 ]
536 handlers = add_ordered_mock_handlers(o, meth_spec)
537
538 req = Request("http://example.com/")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000539 self.assertRaises(urllib.error.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000540 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000541
542## def test_error(self):
543## # XXX this doesn't actually seem to be used in standard library,
544## # but should really be tested anyway...
545
546 def test_http_error(self):
547 # XXX http_error_default
548 # http errors are a special case
549 o = OpenerDirector()
550 meth_spec = [
551 [("http_open", "error 302")],
552 [("http_error_400", "raise"), "http_open"],
553 [("http_error_302", "return response"), "http_error_303",
554 "http_error"],
555 [("http_error_302")],
556 ]
557 handlers = add_ordered_mock_handlers(o, meth_spec)
558
559 class Unknown:
560 def __eq__(self, other): return True
561
562 req = Request("http://example.com/")
563 r = o.open(req)
564 assert len(o.calls) == 2
565 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000566 (handlers[2], "http_error_302",
567 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000568 for expected, got in zip(calls, o.calls):
569 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000570 self.assertEqual((handler, method_name), got[:2])
571 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000572
573 def test_processors(self):
574 # *_request / *_response methods get called appropriately
575 o = OpenerDirector()
576 meth_spec = [
577 [("http_request", "return request"),
578 ("http_response", "return response")],
579 [("http_request", "return request"),
580 ("http_response", "return response")],
581 ]
582 handlers = add_ordered_mock_handlers(o, meth_spec)
583
584 req = Request("http://example.com/")
585 r = o.open(req)
586 # processor methods are called on *all* handlers that define them,
587 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000588 calls = [
589 (handlers[0], "http_request"), (handlers[1], "http_request"),
590 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000591
592 for i, (handler, name, args, kwds) in enumerate(o.calls):
593 if i < 2:
594 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000595 self.assertEqual((handler, name), calls[i])
596 self.assertEqual(len(args), 1)
Ezio Melottie9615932010-01-24 19:26:24 +0000597 self.assertIsInstance(args[0], Request)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000598 else:
599 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000600 self.assertEqual((handler, name), calls[i])
601 self.assertEqual(len(args), 2)
Ezio Melottie9615932010-01-24 19:26:24 +0000602 self.assertIsInstance(args[0], Request)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000603 # response from opener.open is None, because there's no
604 # handler that defines http_open to handle it
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000605 self.assertTrue(args[1] is None or
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000606 isinstance(args[1], MockResponse))
607
608
Tim Peters58eb11c2004-01-18 20:29:55 +0000609def sanepathname2url(path):
Victor Stinner6c6f8512010-08-07 10:09:35 +0000610 try:
611 path.encode("utf8")
612 except UnicodeEncodeError:
613 raise unittest.SkipTest("path is not encodable to utf8")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000614 urlpath = urllib.request.pathname2url(path)
Tim Peters58eb11c2004-01-18 20:29:55 +0000615 if os.name == "nt" and urlpath.startswith("///"):
616 urlpath = urlpath[2:]
617 # XXX don't ask me about the mac...
618 return urlpath
619
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000620class HandlerTests(unittest.TestCase):
621
622 def test_ftp(self):
623 class MockFTPWrapper:
624 def __init__(self, data): self.data = data
625 def retrfile(self, filename, filetype):
626 self.filename, self.filetype = filename, filetype
Guido van Rossum34d19282007-08-09 01:03:29 +0000627 return io.StringIO(self.data), len(self.data)
Nadeem Vawda08f5f7a2011-07-23 14:03:00 +0200628 def close(self): pass
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000629
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000630 class NullFTPHandler(urllib.request.FTPHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000631 def __init__(self, data): self.data = data
Georg Brandlf78e02b2008-06-10 17:40:04 +0000632 def connect_ftp(self, user, passwd, host, port, dirs,
633 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000634 self.user, self.passwd = user, passwd
635 self.host, self.port = host, port
636 self.dirs = dirs
637 self.ftpwrapper = MockFTPWrapper(self.data)
638 return self.ftpwrapper
639
Georg Brandlf78e02b2008-06-10 17:40:04 +0000640 import ftplib
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000641 data = "rheum rhaponicum"
642 h = NullFTPHandler(data)
643 o = h.parent = MockOpener()
644
Senthil Kumarandaa29d02010-11-18 15:36:41 +0000645 for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000646 ("ftp://localhost/foo/bar/baz.html",
Senthil Kumarandaa29d02010-11-18 15:36:41 +0000647 "localhost", ftplib.FTP_PORT, "", "", "I",
648 ["foo", "bar"], "baz.html", "text/html"),
649 ("ftp://parrot@localhost/foo/bar/baz.html",
650 "localhost", ftplib.FTP_PORT, "parrot", "", "I",
651 ["foo", "bar"], "baz.html", "text/html"),
652 ("ftp://%25parrot@localhost/foo/bar/baz.html",
653 "localhost", ftplib.FTP_PORT, "%parrot", "", "I",
654 ["foo", "bar"], "baz.html", "text/html"),
655 ("ftp://%2542parrot@localhost/foo/bar/baz.html",
656 "localhost", ftplib.FTP_PORT, "%42parrot", "", "I",
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000657 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000658 ("ftp://localhost:80/foo/bar/",
Senthil Kumarandaa29d02010-11-18 15:36:41 +0000659 "localhost", 80, "", "", "D",
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000660 ["foo", "bar"], "", None),
661 ("ftp://localhost/baz.gif;type=a",
Senthil Kumarandaa29d02010-11-18 15:36:41 +0000662 "localhost", ftplib.FTP_PORT, "", "", "A",
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000663 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000664 ]:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000665 req = Request(url)
666 req.timeout = None
667 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000668 # ftp authentication not yet implemented by FTPHandler
Senthil Kumarandaa29d02010-11-18 15:36:41 +0000669 self.assertEqual(h.user, user)
670 self.assertEqual(h.passwd, passwd)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000671 self.assertEqual(h.host, socket.gethostbyname(host))
672 self.assertEqual(h.port, port)
673 self.assertEqual(h.dirs, dirs)
674 self.assertEqual(h.ftpwrapper.filename, filename)
675 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000676 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000677 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000678 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000679
680 def test_file(self):
Benjamin Petersona0c0a4a2008-06-12 22:15:50 +0000681 import email.utils, socket
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000682 h = urllib.request.FileHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000683 o = h.parent = MockOpener()
684
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000685 TESTFN = support.TESTFN
Tim Peters58eb11c2004-01-18 20:29:55 +0000686 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Guido van Rossum6a2ccd02007-07-16 20:51:57 +0000687 towrite = b"hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000688 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000689 "file://localhost%s" % urlpath,
690 "file://%s" % urlpath,
691 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000692 ]
693 try:
694 localaddr = socket.gethostbyname(socket.gethostname())
695 except socket.gaierror:
696 localaddr = ''
697 if localaddr:
698 urls.append("file://%s%s" % (localaddr, urlpath))
699
700 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000701 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000702 try:
703 try:
704 f.write(towrite)
705 finally:
706 f.close()
707
708 r = h.file_open(Request(url))
709 try:
710 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000711 headers = r.info()
Senthil Kumaran4fbed102010-05-08 03:29:09 +0000712 respurl = r.geturl()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000713 finally:
714 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000715 stats = os.stat(TESTFN)
Benjamin Petersona0c0a4a2008-06-12 22:15:50 +0000716 modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000717 finally:
718 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000719 self.assertEqual(data, towrite)
720 self.assertEqual(headers["Content-type"], "text/plain")
721 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000722 self.assertEqual(headers["Last-modified"], modified)
Senthil Kumaran4fbed102010-05-08 03:29:09 +0000723 self.assertEqual(respurl, url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000724
725 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000726 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000727 "file:///file_does_not_exist.txt",
728 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
729 os.getcwd(), TESTFN),
730 "file://somerandomhost.ontheinternet.com%s/%s" %
731 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000732 ]:
733 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000734 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000735 try:
736 f.write(towrite)
737 finally:
738 f.close()
739
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000740 self.assertRaises(urllib.error.URLError,
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000741 h.file_open, Request(url))
742 finally:
743 os.remove(TESTFN)
744
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000745 h = urllib.request.FileHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000746 o = h.parent = MockOpener()
747 # XXXX why does // mean ftp (and /// mean not ftp!), and where
748 # is file: scheme specified? I think this is really a bug, and
749 # what was intended was to distinguish between URLs like:
750 # file:/blah.txt (a file)
751 # file://localhost/blah.txt (a file)
752 # file:///blah.txt (a file)
753 # file://ftp.example.com/blah.txt (an ftp URL)
754 for url, ftp in [
Senthil Kumaran383c32d2010-10-14 11:57:35 +0000755 ("file://ftp.example.com//foo.txt", False),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000756 ("file://ftp.example.com///foo.txt", False),
757# XXXX bug: fails with OSError, should be URLError
758 ("file://ftp.example.com/foo.txt", False),
Senthil Kumaran383c32d2010-10-14 11:57:35 +0000759 ("file://somehost//foo/something.txt", False),
Senthil Kumaran2ef16322010-07-11 03:12:43 +0000760 ("file://localhost//foo/something.txt", False),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000761 ]:
762 req = Request(url)
763 try:
764 h.file_open(req)
765 # XXXX remove OSError when bug fixed
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000766 except (urllib.error.URLError, OSError):
Florent Xicluna419e3842010-08-08 16:16:07 +0000767 self.assertFalse(ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000768 else:
Florent Xicluna419e3842010-08-08 16:16:07 +0000769 self.assertIs(o.req, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000770 self.assertEqual(req.type, "ftp")
Łukasz Langad7e81cc2011-01-09 18:18:53 +0000771 self.assertEqual(req.type == "ftp", ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000772
773 def test_http(self):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000774
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000775 h = urllib.request.AbstractHTTPHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000776 o = h.parent = MockOpener()
777
778 url = "http://example.com/"
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000779 for method, data in [("GET", None), ("POST", b"blah")]:
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000780 req = Request(url, data, {"Foo": "bar"})
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000781 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000782 req.add_unredirected_header("Spam", "eggs")
783 http = MockHTTPClass()
784 r = h.do_open(http, req)
785
786 # result attributes
787 r.read; r.readline # wrapped MockFile methods
788 r.info; r.geturl # addinfourl methods
789 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
790 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000791 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000792 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000793
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000794 self.assertEqual(http.host, "example.com")
795 self.assertEqual(http.level, 0)
796 self.assertEqual(http.method, method)
797 self.assertEqual(http.selector, "/")
798 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000799 [("Connection", "close"),
800 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000801 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000802
803 # check socket.error converted to URLError
804 http.raise_on_endheaders = True
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000805 self.assertRaises(urllib.error.URLError, h.do_open, http, req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000806
Senthil Kumaran29333122011-02-11 11:25:47 +0000807 # Check for TypeError on POST data which is str.
808 req = Request("http://example.com/","badpost")
809 self.assertRaises(TypeError, h.do_request_, req)
810
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000811 # check adding of standard headers
812 o.addheaders = [("Spam", "eggs")]
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000813 for data in b"", None: # POST, GET
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000814 req = Request("http://example.com/", data)
815 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000816 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000817 if data is None: # GET
Benjamin Peterson577473f2010-01-19 00:09:57 +0000818 self.assertNotIn("Content-length", req.unredirected_hdrs)
819 self.assertNotIn("Content-type", req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000820 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000821 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
822 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000823 "application/x-www-form-urlencoded")
824 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000825 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
826 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000827
828 # don't clobber existing headers
829 req.add_unredirected_header("Content-length", "foo")
830 req.add_unredirected_header("Content-type", "bar")
831 req.add_unredirected_header("Host", "baz")
832 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000833 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000834 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
835 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000836 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
837 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000838
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000839 # Check iterable body support
840 def iterable_body():
841 yield b"one"
842 yield b"two"
843 yield b"three"
844
845 for headers in {}, {"Content-Length": 11}:
846 req = Request("http://example.com/", iterable_body(), headers)
847 if not headers:
848 # Having an iterable body without a Content-Length should
849 # raise an exception
850 self.assertRaises(ValueError, h.do_request_, req)
851 else:
852 newreq = h.do_request_(req)
853
Senthil Kumaran29333122011-02-11 11:25:47 +0000854 # A file object.
855 # Test only Content-Length attribute of request.
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000856
Senthil Kumaran29333122011-02-11 11:25:47 +0000857 file_obj = io.BytesIO()
858 file_obj.write(b"Something\nSomething\nSomething\n")
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000859
860 for headers in {}, {"Content-Length": 30}:
861 req = Request("http://example.com/", file_obj, headers)
862 if not headers:
863 # Having an iterable body without a Content-Length should
864 # raise an exception
865 self.assertRaises(ValueError, h.do_request_, req)
866 else:
867 newreq = h.do_request_(req)
868 self.assertEqual(int(newreq.get_header('Content-length')),30)
869
870 file_obj.close()
871
872 # array.array Iterable - Content Length is calculated
873
874 iterable_array = array.array("I",[1,2,3,4])
875
876 for headers in {}, {"Content-Length": 16}:
877 req = Request("http://example.com/", iterable_array, headers)
878 newreq = h.do_request_(req)
879 self.assertEqual(int(newreq.get_header('Content-length')),16)
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000880
Facundo Batista72dc1ea2008-08-16 14:44:32 +0000881 def test_http_doubleslash(self):
882 # Checks the presence of any unnecessary double slash in url does not
883 # break anything. Previously, a double slash directly after the host
Ezio Melottie130a522011-10-19 10:58:56 +0300884 # could cause incorrect parsing.
Facundo Batista72dc1ea2008-08-16 14:44:32 +0000885 h = urllib.request.AbstractHTTPHandler()
886 o = h.parent = MockOpener()
887
Senthil Kumaran7bc0d872010-12-19 10:49:52 +0000888 data = b""
Facundo Batista72dc1ea2008-08-16 14:44:32 +0000889 ds_urls = [
890 "http://example.com/foo/bar/baz.html",
891 "http://example.com//foo/bar/baz.html",
892 "http://example.com/foo//bar/baz.html",
893 "http://example.com/foo/bar//baz.html"
894 ]
895
896 for ds_url in ds_urls:
897 ds_req = Request(ds_url, data)
898
899 # Check whether host is determined correctly if there is no proxy
900 np_ds_req = h.do_request_(ds_req)
901 self.assertEqual(np_ds_req.unredirected_hdrs["Host"],"example.com")
902
903 # Check whether host is determined correctly if there is a proxy
904 ds_req.set_proxy("someproxy:3128",None)
905 p_ds_req = h.do_request_(ds_req)
906 self.assertEqual(p_ds_req.unredirected_hdrs["Host"],"example.com")
907
Senthil Kumaranc2958622010-11-22 04:48:26 +0000908 def test_fixpath_in_weirdurls(self):
909 # Issue4493: urllib2 to supply '/' when to urls where path does not
910 # start with'/'
911
912 h = urllib.request.AbstractHTTPHandler()
913 o = h.parent = MockOpener()
914
915 weird_url = 'http://www.python.org?getspam'
916 req = Request(weird_url)
917 newreq = h.do_request_(req)
918 self.assertEqual(newreq.host,'www.python.org')
919 self.assertEqual(newreq.selector,'/?getspam')
920
921 url_without_path = 'http://www.python.org'
922 req = Request(url_without_path)
923 newreq = h.do_request_(req)
924 self.assertEqual(newreq.host,'www.python.org')
925 self.assertEqual(newreq.selector,'')
926
Facundo Batista72dc1ea2008-08-16 14:44:32 +0000927
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000928 def test_errors(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000929 h = urllib.request.HTTPErrorProcessor()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000930 o = h.parent = MockOpener()
931
932 url = "http://example.com/"
933 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000934 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000935 r = MockResponse(200, "OK", {}, "", url)
936 newr = h.http_response(req, r)
Florent Xicluna419e3842010-08-08 16:16:07 +0000937 self.assertIs(r, newr)
938 self.assertFalse(hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000939 r = MockResponse(202, "Accepted", {}, "", url)
940 newr = h.http_response(req, r)
Florent Xicluna419e3842010-08-08 16:16:07 +0000941 self.assertIs(r, newr)
942 self.assertFalse(hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000943 r = MockResponse(206, "Partial content", {}, "", url)
944 newr = h.http_response(req, r)
Florent Xicluna419e3842010-08-08 16:16:07 +0000945 self.assertIs(r, newr)
946 self.assertFalse(hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000947 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000948 r = MockResponse(502, "Bad gateway", {}, "", url)
Florent Xicluna419e3842010-08-08 16:16:07 +0000949 self.assertIsNone(h.http_response(req, r))
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000950 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000951 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000952
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000953 def test_cookies(self):
954 cj = MockCookieJar()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000955 h = urllib.request.HTTPCookieProcessor(cj)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000956 o = h.parent = MockOpener()
957
958 req = Request("http://example.com/")
959 r = MockResponse(200, "OK", {}, "")
960 newreq = h.http_request(req)
Florent Xicluna419e3842010-08-08 16:16:07 +0000961 self.assertIs(cj.ach_req, req)
962 self.assertIs(cj.ach_req, newreq)
963 self.assertEqual(req.get_origin_req_host(), "example.com")
964 self.assertFalse(req.is_unverifiable())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000965 newr = h.http_response(req, r)
Florent Xicluna419e3842010-08-08 16:16:07 +0000966 self.assertIs(cj.ec_req, req)
967 self.assertIs(cj.ec_r, r)
968 self.assertIs(r, newr)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000969
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000970 def test_redirect(self):
971 from_url = "http://example.com/a.html"
972 to_url = "http://example.com/b.html"
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000973 h = urllib.request.HTTPRedirectHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000974 o = h.parent = MockOpener()
975
976 # ordinary redirect behaviour
977 for code in 301, 302, 303, 307:
978 for data in None, "blah\nblah\n":
979 method = getattr(h, "http_error_%s" % code)
980 req = Request(from_url, data)
Senthil Kumaranfb8cc2f2009-07-19 02:44:19 +0000981 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000982 req.add_header("Nonsense", "viking=withhold")
Christian Heimes77c02eb2008-02-09 02:18:51 +0000983 if data is not None:
984 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000985 req.add_unredirected_header("Spam", "spam")
986 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000987 method(req, MockFile(), code, "Blah",
988 MockHeaders({"location": to_url}))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000989 except urllib.error.HTTPError:
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000990 # 307 in response to POST requires user OK
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000991 self.assertTrue(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000992 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000993 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000994 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000995 except AttributeError:
Florent Xicluna419e3842010-08-08 16:16:07 +0000996 self.assertFalse(o.req.has_data())
Christian Heimes77c02eb2008-02-09 02:18:51 +0000997
998 # now it's a GET, there should not be headers regarding content
999 # (possibly dragged from before being a POST)
1000 headers = [x.lower() for x in o.req.headers]
Benjamin Peterson577473f2010-01-19 00:09:57 +00001001 self.assertNotIn("content-length", headers)
1002 self.assertNotIn("content-type", headers)
Christian Heimes77c02eb2008-02-09 02:18:51 +00001003
Jeremy Hyltondf38ea92003-12-17 20:42:38 +00001004 self.assertEqual(o.req.headers["Nonsense"],
1005 "viking=withhold")
Benjamin Peterson577473f2010-01-19 00:09:57 +00001006 self.assertNotIn("Spam", o.req.headers)
1007 self.assertNotIn("Spam", o.req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001008
1009 # loop detection
1010 req = Request(from_url)
Senthil Kumaranfb8cc2f2009-07-19 02:44:19 +00001011 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001012 def redirect(h, req, url=to_url):
1013 h.http_error_302(req, MockFile(), 302, "Blah",
1014 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001015 # Note that the *original* request shares the same record of
1016 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001017
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001018 # detect infinite loop redirect of a URL to itself
1019 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001020 count = 0
Senthil Kumaranfb8cc2f2009-07-19 02:44:19 +00001021 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001022 try:
1023 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001024 redirect(h, req, "http://example.com/")
1025 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001026 except urllib.error.HTTPError:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001027 # don't stop until max_repeats, because cookies may introduce state
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001028 self.assertEqual(count, urllib.request.HTTPRedirectHandler.max_repeats)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001029
1030 # detect endless non-repeating chain of redirects
1031 req = Request(from_url, origin_req_host="example.com")
1032 count = 0
Senthil Kumaranfb8cc2f2009-07-19 02:44:19 +00001033 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001034 try:
1035 while 1:
1036 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001037 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001038 except urllib.error.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +00001039 self.assertEqual(count,
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001040 urllib.request.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001041
guido@google.coma119df92011-03-29 11:41:02 -07001042
1043 def test_invalid_redirect(self):
1044 from_url = "http://example.com/a.html"
1045 valid_schemes = ['http','https','ftp']
1046 invalid_schemes = ['file','imap','ldap']
1047 schemeless_url = "example.com/b.html"
1048 h = urllib.request.HTTPRedirectHandler()
1049 o = h.parent = MockOpener()
1050 req = Request(from_url)
1051 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
1052
1053 for scheme in invalid_schemes:
1054 invalid_url = scheme + '://' + schemeless_url
1055 self.assertRaises(urllib.error.HTTPError, h.http_error_302,
1056 req, MockFile(), 302, "Security Loophole",
1057 MockHeaders({"location": invalid_url}))
1058
1059 for scheme in valid_schemes:
1060 valid_url = scheme + '://' + schemeless_url
1061 h.http_error_302(req, MockFile(), 302, "That's fine",
1062 MockHeaders({"location": valid_url}))
1063 self.assertEqual(o.req.get_full_url(), valid_url)
1064
Senthil Kumaran6497aa32012-01-04 13:46:59 +08001065 def test_relative_redirect(self):
1066 from_url = "http://example.com/a.html"
1067 relative_url = "/b.html"
1068 h = urllib.request.HTTPRedirectHandler()
1069 o = h.parent = MockOpener()
1070 req = Request(from_url)
1071 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
1072
1073 valid_url = urllib.parse.urljoin(from_url,relative_url)
1074 h.http_error_302(req, MockFile(), 302, "That's fine",
1075 MockHeaders({"location": valid_url}))
1076 self.assertEqual(o.req.get_full_url(), valid_url)
1077
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001078 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001079 # cookies shouldn't leak into redirected requests
Georg Brandl24420152008-05-26 16:32:26 +00001080 from http.cookiejar import CookieJar
1081 from test.test_http_cookiejar import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001082
1083 cj = CookieJar()
1084 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001085 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001086 hdeh = urllib.request.HTTPDefaultErrorHandler()
1087 hrh = urllib.request.HTTPRedirectHandler()
1088 cp = urllib.request.HTTPCookieProcessor(cj)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001090 o.open("http://www.example.com/")
Florent Xicluna419e3842010-08-08 16:16:07 +00001091 self.assertFalse(hh.req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001092
Senthil Kumaran26430412011-04-13 07:01:19 +08001093 def test_redirect_fragment(self):
1094 redirected_url = 'http://www.example.com/index.html#OK\r\n\r\n'
1095 hh = MockHTTPHandler(302, 'Location: ' + redirected_url)
1096 hdeh = urllib.request.HTTPDefaultErrorHandler()
1097 hrh = urllib.request.HTTPRedirectHandler()
1098 o = build_test_opener(hh, hdeh, hrh)
1099 fp = o.open('http://www.example.com')
1100 self.assertEqual(fp.geturl(), redirected_url.strip())
1101
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001102 def test_proxy(self):
1103 o = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001104 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001105 o.add_handler(ph)
1106 meth_spec = [
1107 [("http_open", "return response")]
1108 ]
1109 handlers = add_ordered_mock_handlers(o, meth_spec)
1110
1111 req = Request("http://acme.example.com/")
1112 self.assertEqual(req.get_host(), "acme.example.com")
1113 r = o.open(req)
1114 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1115
1116 self.assertEqual([(handlers[0], "http_open")],
1117 [tup[0:2] for tup in o.calls])
1118
Senthil Kumaran7bb04972009-10-11 04:58:55 +00001119 def test_proxy_no_proxy(self):
1120 os.environ['no_proxy'] = 'python.org'
1121 o = OpenerDirector()
1122 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com"))
1123 o.add_handler(ph)
1124 req = Request("http://www.perl.org/")
1125 self.assertEqual(req.get_host(), "www.perl.org")
1126 r = o.open(req)
1127 self.assertEqual(req.get_host(), "proxy.example.com")
1128 req = Request("http://www.python.org")
1129 self.assertEqual(req.get_host(), "www.python.org")
1130 r = o.open(req)
1131 self.assertEqual(req.get_host(), "www.python.org")
1132 del os.environ['no_proxy']
1133
Ronald Oussorene72e1612011-03-14 18:15:25 -04001134 def test_proxy_no_proxy_all(self):
1135 os.environ['no_proxy'] = '*'
1136 o = OpenerDirector()
1137 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com"))
1138 o.add_handler(ph)
1139 req = Request("http://www.python.org")
1140 self.assertEqual(req.get_host(), "www.python.org")
1141 r = o.open(req)
1142 self.assertEqual(req.get_host(), "www.python.org")
1143 del os.environ['no_proxy']
1144
Senthil Kumaran7bb04972009-10-11 04:58:55 +00001145
Senthil Kumaran97f0c6b2009-07-25 04:24:38 +00001146 def test_proxy_https(self):
1147 o = OpenerDirector()
1148 ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
1149 o.add_handler(ph)
1150 meth_spec = [
1151 [("https_open", "return response")]
1152 ]
1153 handlers = add_ordered_mock_handlers(o, meth_spec)
1154
1155 req = Request("https://www.example.com/")
1156 self.assertEqual(req.get_host(), "www.example.com")
1157 r = o.open(req)
1158 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1159 self.assertEqual([(handlers[0], "https_open")],
1160 [tup[0:2] for tup in o.calls])
1161
Senthil Kumaran47fff872009-12-20 07:10:31 +00001162 def test_proxy_https_proxy_authorization(self):
1163 o = OpenerDirector()
1164 ph = urllib.request.ProxyHandler(dict(https='proxy.example.com:3128'))
1165 o.add_handler(ph)
1166 https_handler = MockHTTPSHandler()
1167 o.add_handler(https_handler)
1168 req = Request("https://www.example.com/")
1169 req.add_header("Proxy-Authorization","FooBar")
1170 req.add_header("User-Agent","Grail")
1171 self.assertEqual(req.get_host(), "www.example.com")
1172 self.assertIsNone(req._tunnel_host)
1173 r = o.open(req)
1174 # Verify Proxy-Authorization gets tunneled to request.
1175 # httpsconn req_headers do not have the Proxy-Authorization header but
1176 # the req will have.
Ezio Melottib58e0bd2010-01-23 15:40:09 +00001177 self.assertNotIn(("Proxy-Authorization","FooBar"),
Senthil Kumaran47fff872009-12-20 07:10:31 +00001178 https_handler.httpconn.req_headers)
Ezio Melottib58e0bd2010-01-23 15:40:09 +00001179 self.assertIn(("User-Agent","Grail"),
1180 https_handler.httpconn.req_headers)
Senthil Kumaran47fff872009-12-20 07:10:31 +00001181 self.assertIsNotNone(req._tunnel_host)
1182 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1183 self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
Senthil Kumaran97f0c6b2009-07-25 04:24:38 +00001184
Ronald Oussorene72e1612011-03-14 18:15:25 -04001185 def test_osx_proxy_bypass(self):
1186 bypass = {
1187 'exclude_simple': False,
1188 'exceptions': ['foo.bar', '*.bar.com', '127.0.0.1', '10.10',
1189 '10.0/16']
1190 }
1191 # Check hosts that should trigger the proxy bypass
1192 for host in ('foo.bar', 'www.bar.com', '127.0.0.1', '10.10.0.1',
1193 '10.0.0.1'):
1194 self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass),
1195 'expected bypass of %s to be True' % host)
1196 # Check hosts that should not trigger the proxy bypass
1197 for host in ('abc.foo.bar', 'bar.com', '127.0.0.2', '10.11.0.1', 'test'):
1198 self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass),
1199 'expected bypass of %s to be False' % host)
1200
1201 # Check the exclude_simple flag
1202 bypass = {'exclude_simple': True, 'exceptions': []}
1203 self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass))
1204
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001205 def test_basic_auth(self, quote_char='"'):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001206 opener = OpenerDirector()
1207 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001208 auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001209 realm = "ACME Widget Store"
1210 http_handler = MockHTTPHandler(
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001211 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
1212 (quote_char, realm, quote_char) )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001213 opener.add_handler(auth_handler)
1214 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001215 self._test_basic_auth(opener, auth_handler, "Authorization",
1216 realm, http_handler, password_manager,
1217 "http://acme.example.com/protected",
1218 "http://acme.example.com/protected",
1219 )
1220
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001221 def test_basic_auth_with_single_quoted_realm(self):
1222 self.test_basic_auth(quote_char="'")
1223
Senthil Kumaran34f3fcc2012-05-15 22:30:25 +08001224 def test_basic_auth_with_unquoted_realm(self):
1225 opener = OpenerDirector()
1226 password_manager = MockPasswordManager()
1227 auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager)
1228 realm = "ACME Widget Store"
1229 http_handler = MockHTTPHandler(
1230 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm)
1231 opener.add_handler(auth_handler)
1232 opener.add_handler(http_handler)
Senthil Kumaran0ea91cb2012-05-15 23:59:42 +08001233 with self.assertWarns(UserWarning):
1234 self._test_basic_auth(opener, auth_handler, "Authorization",
1235 realm, http_handler, password_manager,
1236 "http://acme.example.com/protected",
1237 "http://acme.example.com/protected",
1238 )
Senthil Kumaran34f3fcc2012-05-15 22:30:25 +08001239
Thomas Wouters477c8d52006-05-27 19:21:47 +00001240 def test_proxy_basic_auth(self):
1241 opener = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001242 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001243 opener.add_handler(ph)
1244 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001245 auth_handler = urllib.request.ProxyBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001246 realm = "ACME Networks"
1247 http_handler = MockHTTPHandler(
1248 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001249 opener.add_handler(auth_handler)
1250 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001251 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001252 realm, http_handler, password_manager,
1253 "http://acme.example.com:3128/protected",
1254 "proxy.example.com:3128",
1255 )
1256
1257 def test_basic_and_digest_auth_handlers(self):
Andrew Svetlov7bd61cb2012-12-19 22:49:25 +02001258 # HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
Thomas Wouters477c8d52006-05-27 19:21:47 +00001259 # response (http://python.org/sf/1479302), where it should instead
1260 # return None to allow another handler (especially
1261 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001262
1263 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
1264 # try digest first (since it's the strongest auth scheme), so we record
1265 # order of calls here to check digest comes first:
1266 class RecordingOpenerDirector(OpenerDirector):
1267 def __init__(self):
1268 OpenerDirector.__init__(self)
1269 self.recorded = []
1270 def record(self, info):
1271 self.recorded.append(info)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001272 class TestDigestAuthHandler(urllib.request.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001273 def http_error_401(self, *args, **kwds):
1274 self.parent.record("digest")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001275 urllib.request.HTTPDigestAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001276 *args, **kwds)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001277 class TestBasicAuthHandler(urllib.request.HTTPBasicAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001278 def http_error_401(self, *args, **kwds):
1279 self.parent.record("basic")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001280 urllib.request.HTTPBasicAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001281 *args, **kwds)
1282
1283 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001284 password_manager = MockPasswordManager()
1285 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001286 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001287 realm = "ACME Networks"
1288 http_handler = MockHTTPHandler(
1289 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001290 opener.add_handler(basic_handler)
1291 opener.add_handler(digest_handler)
1292 opener.add_handler(http_handler)
1293
1294 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +00001295 self._test_basic_auth(opener, basic_handler, "Authorization",
1296 realm, http_handler, password_manager,
1297 "http://acme.example.com/protected",
1298 "http://acme.example.com/protected",
1299 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001300 # check digest was tried before basic (twice, because
1301 # _test_basic_auth called .open() twice)
1302 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001303
1304 def _test_basic_auth(self, opener, auth_handler, auth_header,
1305 realm, http_handler, password_manager,
1306 request_url, protected_url):
Christian Heimes05e8be12008-02-23 18:30:17 +00001307 import base64
Thomas Wouters477c8d52006-05-27 19:21:47 +00001308 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +00001309
1310 # .add_password() fed through to password manager
1311 auth_handler.add_password(realm, request_url, user, password)
1312 self.assertEqual(realm, password_manager.realm)
1313 self.assertEqual(request_url, password_manager.url)
1314 self.assertEqual(user, password_manager.user)
1315 self.assertEqual(password, password_manager.password)
1316
1317 r = opener.open(request_url)
1318
1319 # should have asked the password manager for the username/password
1320 self.assertEqual(password_manager.target_realm, realm)
1321 self.assertEqual(password_manager.target_url, protected_url)
1322
1323 # expect one request without authorization, then one with
1324 self.assertEqual(len(http_handler.requests), 2)
1325 self.assertFalse(http_handler.requests[0].has_header(auth_header))
Guido van Rossum98b349f2007-08-27 21:47:52 +00001326 userpass = bytes('%s:%s' % (user, password), "ascii")
Guido van Rossum98297ee2007-11-06 21:34:58 +00001327 auth_hdr_value = ('Basic ' +
Georg Brandl706824f2009-06-04 09:42:55 +00001328 base64.encodebytes(userpass).strip().decode())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001329 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1330 auth_hdr_value)
Senthil Kumaranca2fc9e2010-02-24 16:53:16 +00001331 self.assertEqual(http_handler.requests[1].unredirected_hdrs[auth_header],
1332 auth_hdr_value)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001333 # if the password manager can't find a password, the handler won't
1334 # handle the HTTP auth error
1335 password_manager.user = password_manager.password = None
1336 http_handler.reset()
1337 r = opener.open(request_url)
1338 self.assertEqual(len(http_handler.requests), 1)
1339 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1340
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001341class MiscTests(unittest.TestCase):
1342
1343 def test_build_opener(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001344 class MyHTTPHandler(urllib.request.HTTPHandler): pass
1345 class FooHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001346 def foo_open(self): pass
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001347 class BarHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001348 def bar_open(self): pass
1349
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001350 build_opener = urllib.request.build_opener
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001351
1352 o = build_opener(FooHandler, BarHandler)
1353 self.opener_has_handler(o, FooHandler)
1354 self.opener_has_handler(o, BarHandler)
1355
1356 # can take a mix of classes and instances
1357 o = build_opener(FooHandler, BarHandler())
1358 self.opener_has_handler(o, FooHandler)
1359 self.opener_has_handler(o, BarHandler)
1360
1361 # subclasses of default handlers override default handlers
1362 o = build_opener(MyHTTPHandler)
1363 self.opener_has_handler(o, MyHTTPHandler)
1364
1365 # a particular case of overriding: default handlers can be passed
1366 # in explicitly
1367 o = build_opener()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001368 self.opener_has_handler(o, urllib.request.HTTPHandler)
1369 o = build_opener(urllib.request.HTTPHandler)
1370 self.opener_has_handler(o, urllib.request.HTTPHandler)
1371 o = build_opener(urllib.request.HTTPHandler())
1372 self.opener_has_handler(o, urllib.request.HTTPHandler)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001373
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001374 # Issue2670: multiple handlers sharing the same base class
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001375 class MyOtherHTTPHandler(urllib.request.HTTPHandler): pass
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001376 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1377 self.opener_has_handler(o, MyHTTPHandler)
1378 self.opener_has_handler(o, MyOtherHTTPHandler)
1379
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001380 def opener_has_handler(self, opener, handler_class):
Florent Xicluna419e3842010-08-08 16:16:07 +00001381 self.assertTrue(any(h.__class__ == handler_class
1382 for h in opener.handlers))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001383
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001384class RequestTests(unittest.TestCase):
1385
1386 def setUp(self):
1387 self.get = Request("http://www.python.org/~jeremy/")
1388 self.post = Request("http://www.python.org/~jeremy/",
1389 "data",
1390 headers={"X-Test": "test"})
1391
1392 def test_method(self):
1393 self.assertEqual("POST", self.post.get_method())
1394 self.assertEqual("GET", self.get.get_method())
1395
1396 def test_add_data(self):
Florent Xicluna419e3842010-08-08 16:16:07 +00001397 self.assertFalse(self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001398 self.assertEqual("GET", self.get.get_method())
1399 self.get.add_data("spam")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001400 self.assertTrue(self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001401 self.assertEqual("POST", self.get.get_method())
1402
1403 def test_get_full_url(self):
1404 self.assertEqual("http://www.python.org/~jeremy/",
1405 self.get.get_full_url())
1406
1407 def test_selector(self):
1408 self.assertEqual("/~jeremy/", self.get.get_selector())
1409 req = Request("http://www.python.org/")
1410 self.assertEqual("/", req.get_selector())
1411
1412 def test_get_type(self):
1413 self.assertEqual("http", self.get.get_type())
1414
1415 def test_get_host(self):
1416 self.assertEqual("www.python.org", self.get.get_host())
1417
1418 def test_get_host_unquote(self):
1419 req = Request("http://www.%70ython.org/")
1420 self.assertEqual("www.python.org", req.get_host())
1421
1422 def test_proxy(self):
Florent Xicluna419e3842010-08-08 16:16:07 +00001423 self.assertFalse(self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001424 self.get.set_proxy("www.perl.org", "http")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001425 self.assertTrue(self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001426 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1427 self.assertEqual("www.perl.org", self.get.get_host())
1428
Senthil Kumarand95cc752010-08-08 11:27:53 +00001429 def test_wrapped_url(self):
1430 req = Request("<URL:http://www.python.org>")
1431 self.assertEqual("www.python.org", req.get_host())
1432
Senthil Kumaran26430412011-04-13 07:01:19 +08001433 def test_url_fragment(self):
Senthil Kumarand95cc752010-08-08 11:27:53 +00001434 req = Request("http://www.python.org/?qs=query#fragment=true")
1435 self.assertEqual("/?qs=query", req.get_selector())
1436 req = Request("http://www.python.org/#fun=true")
1437 self.assertEqual("/", req.get_selector())
1438
Senthil Kumaran26430412011-04-13 07:01:19 +08001439 # Issue 11703: geturl() omits fragment in the original URL.
1440 url = 'http://docs.python.org/library/urllib2.html#OK'
1441 req = Request(url)
1442 self.assertEqual(req.get_full_url(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001443
Senthil Kumaran41e66a22012-12-23 09:04:24 -08001444 def test_HTTPError_interface(self):
1445 """
1446 Issue 13211 reveals that HTTPError didn't implement the URLError
1447 interface even though HTTPError is a subclass of URLError.
Jason R. Coombsaa204db2011-11-07 10:50:32 -05001448
Senthil Kumaran2a4d2452013-03-19 16:11:07 -07001449 >>> msg = 'something bad happened'
1450 >>> url = code = fp = None
1451 >>> hdrs = 'Content-Length: 42'
1452 >>> err = urllib.error.HTTPError(url, code, msg, hdrs, fp)
Senthil Kumaran41e66a22012-12-23 09:04:24 -08001453 >>> assert hasattr(err, 'reason')
1454 >>> err.reason
1455 'something bad happened'
Senthil Kumaran2a4d2452013-03-19 16:11:07 -07001456 >>> assert hasattr(err, 'hdrs')
1457 >>> err.hdrs
1458 'Content-Length: 42'
1459 >>> expected_errmsg = 'HTTP Error %s: %s' % (err.code, err.msg)
1460 >>> assert str(err) == expected_errmsg
Senthil Kumaran41e66a22012-12-23 09:04:24 -08001461 """
1462
1463 def test_HTTPError_interface_call(self):
1464 """
1465 Issue 15701 - HTTPError interface has info method available from URLError
1466 """
1467 err = urllib.request.HTTPError(msg="something bad happened", url=None,
1468 code=None, hdrs='Content-Length:42', fp=None)
1469 self.assertTrue(hasattr(err, 'reason'))
1470 assert hasattr(err, 'reason')
1471 assert hasattr(err, 'info')
1472 assert callable(err.info)
1473 try:
1474 err.info()
1475 except AttributeError:
1476 self.fail('err.info call failed.')
1477 self.assertEqual(err.info(), "Content-Length:42")
Jason R. Coombsaa204db2011-11-07 10:50:32 -05001478
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001479def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001480 from test import test_urllib2
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001481 support.run_doctest(test_urllib2, verbose)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001482 support.run_doctest(urllib.request, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001483 tests = (TrivialTests,
1484 OpenerDirectorTests,
1485 HandlerTests,
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001486 MiscTests,
1487 RequestTests)
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001488 support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001489
1490if __name__ == "__main__":
1491 test_main(verbose=True)