blob: cb730e2057492e44cbccc7ea58bc5c7f74f9de80 [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
Jeremy Hyltone3e61042001-05-09 15:50:25 +00006
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00007import urllib2
8from urllib2 import Request, OpenerDirector
Jeremy Hyltone3e61042001-05-09 15:50:25 +00009
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000010# XXX
11# Request
12# CacheFTPHandler (hard to write)
Thomas Wouters477c8d52006-05-27 19:21:47 +000013# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000014
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000015class TrivialTests(unittest.TestCase):
16 def test_trivial(self):
17 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000018
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000019 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000020
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000021 # XXX Name hacking to get this to work on Windows.
22 fname = os.path.abspath(urllib2.__file__).replace('\\', '/')
23 if fname[1:2] == ":":
24 fname = fname[2:]
25 # And more hacking to get it to work on MacOS. This assumes
26 # urllib.pathname2url works, unfortunately...
27 if os.name == 'mac':
28 fname = '/' + fname.replace(':', '/')
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000029
30 file_url = "file://%s" % fname
31 f = urllib2.urlopen(file_url)
32
33 buf = f.read()
34 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000035
Georg Brandle1b13d22005-08-24 22:20:32 +000036 def test_parse_http_list(self):
37 tests = [('a,b,c', ['a', 'b', 'c']),
38 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
39 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
40 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
41 for string, list in tests:
42 self.assertEquals(urllib2.parse_http_list(string), list)
43
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000044
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000045def test_request_headers_dict():
46 """
47 The Request.headers dictionary is not a documented interface. It should
48 stay that way, because the complete set of headers are only accessible
49 through the .get_header(), .has_header(), .header_items() interface.
50 However, .headers pre-dates those methods, and so real code will be using
51 the dictionary.
52
53 The introduction in 2.4 of those methods was a mistake for the same reason:
54 code that previously saw all (urllib2 user)-provided headers in .headers
55 now sees only a subset (and the function interface is ugly and incomplete).
56 A better change would have been to replace .headers dict with a dict
57 subclass (or UserDict.DictMixin instance?) that preserved the .headers
58 interface and also provided access to the "unredirected" headers. It's
59 probably too late to fix that, though.
60
61
62 Check .capitalize() case normalization:
63
64 >>> url = "http://example.com"
65 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
66 'blah'
67 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
68 'blah'
69
70 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
71 but that could be changed in future.
72
73 """
74
75def test_request_headers_methods():
76 """
77 Note the case normalization of header names here, to .capitalize()-case.
78 This should be preserved for backwards-compatibility. (In the HTTP case,
79 normalization to .title()-case is done by urllib2 before sending headers to
80 httplib).
81
82 >>> url = "http://example.com"
83 >>> r = Request(url, headers={"Spam-eggs": "blah"})
84 >>> r.has_header("Spam-eggs")
85 True
86 >>> r.header_items()
87 [('Spam-eggs', 'blah')]
88 >>> r.add_header("Foo-Bar", "baz")
Guido van Rossumcc2b0162007-02-11 06:12:03 +000089 >>> items = sorted(r.header_items())
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000090 >>> items
91 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
92
93 Note that e.g. r.has_header("spam-EggS") is currently False, and
94 r.get_header("spam-EggS") returns None, but that could be changed in
95 future.
96
97 >>> r.has_header("Not-there")
98 False
Guido van Rossum7131f842007-02-09 20:13:25 +000099 >>> print(r.get_header("Not-there"))
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000100 None
101 >>> r.get_header("Not-there", "default")
102 'default'
103
104 """
105
106
Thomas Wouters477c8d52006-05-27 19:21:47 +0000107def test_password_manager(self):
108 """
109 >>> mgr = urllib2.HTTPPasswordMgr()
110 >>> add = mgr.add_password
111 >>> add("Some Realm", "http://example.com/", "joe", "password")
112 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
113 >>> add("c", "http://example.com/foo", "foo", "ni")
114 >>> add("c", "http://example.com/bar", "bar", "nini")
115 >>> add("b", "http://example.com/", "first", "blah")
116 >>> add("b", "http://example.com/", "second", "spam")
117 >>> add("a", "http://example.com", "1", "a")
118 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
119 >>> add("Some Realm", "d.example.com", "4", "d")
120 >>> add("Some Realm", "e.example.com:3128", "5", "e")
121
122 >>> mgr.find_user_password("Some Realm", "example.com")
123 ('joe', 'password')
124 >>> mgr.find_user_password("Some Realm", "http://example.com")
125 ('joe', 'password')
126 >>> mgr.find_user_password("Some Realm", "http://example.com/")
127 ('joe', 'password')
128 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
129 ('joe', 'password')
130 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
131 ('joe', 'password')
132 >>> mgr.find_user_password("c", "http://example.com/foo")
133 ('foo', 'ni')
134 >>> mgr.find_user_password("c", "http://example.com/bar")
135 ('bar', 'nini')
136
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000137 Actually, this is really undefined ATM
138## Currently, we use the highest-level path where more than one match:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000139
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000140## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
141## ('joe', 'password')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000142
143 Use latest add_password() in case of conflict:
144
145 >>> mgr.find_user_password("b", "http://example.com/")
146 ('second', 'spam')
147
148 No special relationship between a.example.com and example.com:
149
150 >>> mgr.find_user_password("a", "http://example.com/")
151 ('1', 'a')
152 >>> mgr.find_user_password("a", "http://a.example.com/")
153 (None, None)
154
155 Ports:
156
157 >>> mgr.find_user_password("Some Realm", "c.example.com")
158 (None, None)
159 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
160 ('3', 'c')
161 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
162 ('3', 'c')
163 >>> mgr.find_user_password("Some Realm", "d.example.com")
164 ('4', 'd')
165 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
166 ('5', 'e')
167
168 """
169 pass
170
171
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000172def test_password_manager_default_port(self):
173 """
174 >>> mgr = urllib2.HTTPPasswordMgr()
175 >>> add = mgr.add_password
176
177 The point to note here is that we can't guess the default port if there's
178 no scheme. This applies to both add_password and find_user_password.
179
180 >>> add("f", "http://g.example.com:80", "10", "j")
181 >>> add("g", "http://h.example.com", "11", "k")
182 >>> add("h", "i.example.com:80", "12", "l")
183 >>> add("i", "j.example.com", "13", "m")
184 >>> mgr.find_user_password("f", "g.example.com:100")
185 (None, None)
186 >>> mgr.find_user_password("f", "g.example.com:80")
187 ('10', 'j')
188 >>> mgr.find_user_password("f", "g.example.com")
189 (None, None)
190 >>> mgr.find_user_password("f", "http://g.example.com:100")
191 (None, None)
192 >>> mgr.find_user_password("f", "http://g.example.com:80")
193 ('10', 'j')
194 >>> mgr.find_user_password("f", "http://g.example.com")
195 ('10', 'j')
196 >>> mgr.find_user_password("g", "h.example.com")
197 ('11', 'k')
198 >>> mgr.find_user_password("g", "h.example.com:80")
199 ('11', 'k')
200 >>> mgr.find_user_password("g", "http://h.example.com:80")
201 ('11', 'k')
202 >>> mgr.find_user_password("h", "i.example.com")
203 (None, None)
204 >>> mgr.find_user_password("h", "i.example.com:80")
205 ('12', 'l')
206 >>> mgr.find_user_password("h", "http://i.example.com:80")
207 ('12', 'l')
208 >>> mgr.find_user_password("i", "j.example.com")
209 ('13', 'm')
210 >>> mgr.find_user_password("i", "j.example.com:80")
211 (None, None)
212 >>> mgr.find_user_password("i", "http://j.example.com")
213 ('13', 'm')
214 >>> mgr.find_user_password("i", "http://j.example.com:80")
215 (None, None)
216
217 """
218
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000219class MockOpener:
220 addheaders = []
221 def open(self, req, data=None):
222 self.req, self.data = req, data
223 def error(self, proto, *args):
224 self.proto, self.args = proto, args
225
226class MockFile:
227 def read(self, count=None): pass
228 def readline(self, count=None): pass
229 def close(self): pass
230
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000231class MockHeaders(dict):
232 def getheaders(self, name):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000233 return list(self.values())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000234
Guido van Rossum34d19282007-08-09 01:03:29 +0000235class MockResponse(io.StringIO):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000236 def __init__(self, code, msg, headers, data, url=None):
Guido van Rossum34d19282007-08-09 01:03:29 +0000237 io.StringIO.__init__(self, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000238 self.code, self.msg, self.headers, self.url = code, msg, headers, url
239 def info(self):
240 return self.headers
241 def geturl(self):
242 return self.url
243
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000244class MockCookieJar:
245 def add_cookie_header(self, request):
246 self.ach_req = request
247 def extract_cookies(self, response, request):
248 self.ec_req, self.ec_r = request, response
249
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000250class FakeMethod:
251 def __init__(self, meth_name, action, handle):
252 self.meth_name = meth_name
253 self.handle = handle
254 self.action = action
255 def __call__(self, *args):
256 return self.handle(self.meth_name, self.action, *args)
257
258class MockHandler:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000259 # useful for testing handler machinery
260 # see add_ordered_mock_handlers() docstring
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000261 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000262 def __init__(self, methods):
263 self._define_methods(methods)
264 def _define_methods(self, methods):
265 for spec in methods:
266 if len(spec) == 2: name, action = spec
267 else: name, action = spec, None
268 meth = FakeMethod(name, action, self.handle)
269 setattr(self.__class__, name, meth)
270 def handle(self, fn_name, action, *args, **kwds):
271 self.parent.calls.append((self, fn_name, args, kwds))
272 if action is None:
273 return None
274 elif action == "return self":
275 return self
276 elif action == "return response":
277 res = MockResponse(200, "OK", {}, "")
278 return res
279 elif action == "return request":
280 return Request("http://blah/")
281 elif action.startswith("error"):
282 code = action[action.rfind(" ")+1:]
283 try:
284 code = int(code)
285 except ValueError:
286 pass
287 res = MockResponse(200, "OK", {}, "")
288 return self.parent.error("http", args[0], res, code, "", {})
289 elif action == "raise":
290 raise urllib2.URLError("blah")
291 assert False
292 def close(self): pass
293 def add_parent(self, parent):
294 self.parent = parent
295 self.parent.calls = []
296 def __lt__(self, other):
297 if not hasattr(other, "handler_order"):
298 # No handler_order, leave in original order. Yuck.
299 return True
300 return self.handler_order < other.handler_order
301
302def add_ordered_mock_handlers(opener, meth_spec):
303 """Create MockHandlers and add them to an OpenerDirector.
304
305 meth_spec: list of lists of tuples and strings defining methods to define
306 on handlers. eg:
307
308 [["http_error", "ftp_open"], ["http_open"]]
309
310 defines methods .http_error() and .ftp_open() on one handler, and
311 .http_open() on another. These methods just record their arguments and
312 return None. Using a tuple instead of a string causes the method to
313 perform some action (see MockHandler.handle()), eg:
314
315 [["http_error"], [("http_open", "return request")]]
316
317 defines .http_error() on one handler (which simply returns None), and
318 .http_open() on another handler, which returns a Request object.
319
320 """
321 handlers = []
322 count = 0
323 for meths in meth_spec:
324 class MockHandlerSubclass(MockHandler): pass
325 h = MockHandlerSubclass(meths)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000326 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000327 h.add_parent(opener)
328 count = count + 1
329 handlers.append(h)
330 opener.add_handler(h)
331 return handlers
332
Thomas Wouters477c8d52006-05-27 19:21:47 +0000333def build_test_opener(*handler_instances):
334 opener = OpenerDirector()
335 for h in handler_instances:
336 opener.add_handler(h)
337 return opener
338
339class MockHTTPHandler(urllib2.BaseHandler):
340 # useful for testing redirections and auth
341 # sends supplied headers and code as first response
342 # sends 200 OK as second response
343 def __init__(self, code, headers):
344 self.code = code
345 self.headers = headers
346 self.reset()
347 def reset(self):
348 self._count = 0
349 self.requests = []
350 def http_open(self, req):
351 import mimetools, httplib, copy
Guido van Rossum34d19282007-08-09 01:03:29 +0000352 from io import StringIO
Thomas Wouters477c8d52006-05-27 19:21:47 +0000353 self.requests.append(copy.deepcopy(req))
354 if self._count == 0:
355 self._count = self._count + 1
356 name = httplib.responses[self.code]
357 msg = mimetools.Message(StringIO(self.headers))
358 return self.parent.error(
359 "http", req, MockFile(), self.code, name, msg)
360 else:
361 self.req = req
362 msg = mimetools.Message(StringIO("\r\n\r\n"))
363 return MockResponse(200, "OK", msg, "", req.get_full_url())
364
365class MockPasswordManager:
366 def add_password(self, realm, uri, user, password):
367 self.realm = realm
368 self.url = uri
369 self.user = user
370 self.password = password
371 def find_user_password(self, realm, authuri):
372 self.target_realm = realm
373 self.target_url = authuri
374 return self.user, self.password
375
376
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000377class OpenerDirectorTests(unittest.TestCase):
378
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000379 def test_add_non_handler(self):
380 class NonHandler(object):
381 pass
382 self.assertRaises(TypeError,
383 OpenerDirector().add_handler, NonHandler())
384
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000385 def test_badly_named_methods(self):
386 # test work-around for three methods that accidentally follow the
387 # naming conventions for handler methods
388 # (*_open() / *_request() / *_response())
389
390 # These used to call the accidentally-named methods, causing a
391 # TypeError in real code; here, returning self from these mock
392 # methods would either cause no exception, or AttributeError.
393
394 from urllib2 import URLError
395
396 o = OpenerDirector()
397 meth_spec = [
398 [("do_open", "return self"), ("proxy_open", "return self")],
399 [("redirect_request", "return self")],
400 ]
401 handlers = add_ordered_mock_handlers(o, meth_spec)
402 o.add_handler(urllib2.UnknownHandler())
403 for scheme in "do", "proxy", "redirect":
404 self.assertRaises(URLError, o.open, scheme+"://example.com/")
405
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000406 def test_handled(self):
407 # handler returning non-None means no more handlers will be called
408 o = OpenerDirector()
409 meth_spec = [
410 ["http_open", "ftp_open", "http_error_302"],
411 ["ftp_open"],
412 [("http_open", "return self")],
413 [("http_open", "return self")],
414 ]
415 handlers = add_ordered_mock_handlers(o, meth_spec)
416
417 req = Request("http://example.com/")
418 r = o.open(req)
419 # Second .http_open() gets called, third doesn't, since second returned
420 # non-None. Handlers without .http_open() never get any methods called
421 # on them.
422 # In fact, second mock handler defining .http_open() returns self
423 # (instead of response), which becomes the OpenerDirector's return
424 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000425 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000426 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
427 for expected, got in zip(calls, o.calls):
428 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000429 self.assertEqual((handler, name), expected)
430 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000431
432 def test_handler_order(self):
433 o = OpenerDirector()
434 handlers = []
435 for meths, handler_order in [
436 ([("http_open", "return self")], 500),
437 (["http_open"], 0),
438 ]:
439 class MockHandlerSubclass(MockHandler): pass
440 h = MockHandlerSubclass(meths)
441 h.handler_order = handler_order
442 handlers.append(h)
443 o.add_handler(h)
444
445 r = o.open("http://example.com/")
446 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000447 self.assertEqual(o.calls[0][0], handlers[1])
448 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000449
450 def test_raise(self):
451 # raising URLError stops processing of request
452 o = OpenerDirector()
453 meth_spec = [
454 [("http_open", "raise")],
455 [("http_open", "return self")],
456 ]
457 handlers = add_ordered_mock_handlers(o, meth_spec)
458
459 req = Request("http://example.com/")
460 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000461 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000462
463## def test_error(self):
464## # XXX this doesn't actually seem to be used in standard library,
465## # but should really be tested anyway...
466
467 def test_http_error(self):
468 # XXX http_error_default
469 # http errors are a special case
470 o = OpenerDirector()
471 meth_spec = [
472 [("http_open", "error 302")],
473 [("http_error_400", "raise"), "http_open"],
474 [("http_error_302", "return response"), "http_error_303",
475 "http_error"],
476 [("http_error_302")],
477 ]
478 handlers = add_ordered_mock_handlers(o, meth_spec)
479
480 class Unknown:
481 def __eq__(self, other): return True
482
483 req = Request("http://example.com/")
484 r = o.open(req)
485 assert len(o.calls) == 2
486 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000487 (handlers[2], "http_error_302",
488 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000489 for expected, got in zip(calls, o.calls):
490 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000491 self.assertEqual((handler, method_name), got[:2])
492 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000493
494 def test_processors(self):
495 # *_request / *_response methods get called appropriately
496 o = OpenerDirector()
497 meth_spec = [
498 [("http_request", "return request"),
499 ("http_response", "return response")],
500 [("http_request", "return request"),
501 ("http_response", "return response")],
502 ]
503 handlers = add_ordered_mock_handlers(o, meth_spec)
504
505 req = Request("http://example.com/")
506 r = o.open(req)
507 # processor methods are called on *all* handlers that define them,
508 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000509 calls = [
510 (handlers[0], "http_request"), (handlers[1], "http_request"),
511 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000512
513 for i, (handler, name, args, kwds) in enumerate(o.calls):
514 if i < 2:
515 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000516 self.assertEqual((handler, name), calls[i])
517 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000518 self.assert_(isinstance(args[0], Request))
519 else:
520 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000521 self.assertEqual((handler, name), calls[i])
522 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000523 self.assert_(isinstance(args[0], Request))
524 # response from opener.open is None, because there's no
525 # handler that defines http_open to handle it
526 self.assert_(args[1] is None or
527 isinstance(args[1], MockResponse))
528
529
Tim Peters58eb11c2004-01-18 20:29:55 +0000530def sanepathname2url(path):
531 import urllib
532 urlpath = urllib.pathname2url(path)
533 if os.name == "nt" and urlpath.startswith("///"):
534 urlpath = urlpath[2:]
535 # XXX don't ask me about the mac...
536 return urlpath
537
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000538class HandlerTests(unittest.TestCase):
539
540 def test_ftp(self):
541 class MockFTPWrapper:
542 def __init__(self, data): self.data = data
543 def retrfile(self, filename, filetype):
544 self.filename, self.filetype = filename, filetype
Guido van Rossum34d19282007-08-09 01:03:29 +0000545 return io.StringIO(self.data), len(self.data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000546
547 class NullFTPHandler(urllib2.FTPHandler):
548 def __init__(self, data): self.data = data
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000549 def connect_ftp(self, user, passwd, host, port, dirs, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000550 self.user, self.passwd = user, passwd
551 self.host, self.port = host, port
552 self.dirs = dirs
553 self.ftpwrapper = MockFTPWrapper(self.data)
554 return self.ftpwrapper
555
556 import ftplib, socket
557 data = "rheum rhaponicum"
558 h = NullFTPHandler(data)
559 o = h.parent = MockOpener()
560
561 for url, host, port, type_, dirs, filename, mimetype in [
562 ("ftp://localhost/foo/bar/baz.html",
563 "localhost", ftplib.FTP_PORT, "I",
564 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000565 ("ftp://localhost:80/foo/bar/",
566 "localhost", 80, "D",
567 ["foo", "bar"], "", None),
568 ("ftp://localhost/baz.gif;type=a",
569 "localhost", ftplib.FTP_PORT, "A",
570 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000571 ]:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000572 req = Request(url)
573 req.timeout = None
574 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000575 # ftp authentication not yet implemented by FTPHandler
576 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000577 self.assertEqual(h.host, socket.gethostbyname(host))
578 self.assertEqual(h.port, port)
579 self.assertEqual(h.dirs, dirs)
580 self.assertEqual(h.ftpwrapper.filename, filename)
581 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000582 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000583 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000584 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000585
586 def test_file(self):
Christian Heimes05e8be12008-02-23 18:30:17 +0000587 import rfc822, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000588 h = urllib2.FileHandler()
589 o = h.parent = MockOpener()
590
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000591 TESTFN = support.TESTFN
Tim Peters58eb11c2004-01-18 20:29:55 +0000592 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Guido van Rossum6a2ccd02007-07-16 20:51:57 +0000593 towrite = b"hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000594 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000595 "file://localhost%s" % urlpath,
596 "file://%s" % urlpath,
597 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000598 ]
599 try:
600 localaddr = socket.gethostbyname(socket.gethostname())
601 except socket.gaierror:
602 localaddr = ''
603 if localaddr:
604 urls.append("file://%s%s" % (localaddr, urlpath))
605
606 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000607 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000608 try:
609 try:
610 f.write(towrite)
611 finally:
612 f.close()
613
614 r = h.file_open(Request(url))
615 try:
616 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000617 headers = r.info()
618 newurl = r.geturl()
619 finally:
620 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000621 stats = os.stat(TESTFN)
622 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000623 finally:
624 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000625 self.assertEqual(data, towrite)
626 self.assertEqual(headers["Content-type"], "text/plain")
627 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000628 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000629
630 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000631 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000632 "file:///file_does_not_exist.txt",
633 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
634 os.getcwd(), TESTFN),
635 "file://somerandomhost.ontheinternet.com%s/%s" %
636 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000637 ]:
638 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000639 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000640 try:
641 f.write(towrite)
642 finally:
643 f.close()
644
645 self.assertRaises(urllib2.URLError,
646 h.file_open, Request(url))
647 finally:
648 os.remove(TESTFN)
649
650 h = urllib2.FileHandler()
651 o = h.parent = MockOpener()
652 # XXXX why does // mean ftp (and /// mean not ftp!), and where
653 # is file: scheme specified? I think this is really a bug, and
654 # what was intended was to distinguish between URLs like:
655 # file:/blah.txt (a file)
656 # file://localhost/blah.txt (a file)
657 # file:///blah.txt (a file)
658 # file://ftp.example.com/blah.txt (an ftp URL)
659 for url, ftp in [
660 ("file://ftp.example.com//foo.txt", True),
661 ("file://ftp.example.com///foo.txt", False),
662# XXXX bug: fails with OSError, should be URLError
663 ("file://ftp.example.com/foo.txt", False),
664 ]:
665 req = Request(url)
666 try:
667 h.file_open(req)
668 # XXXX remove OSError when bug fixed
669 except (urllib2.URLError, OSError):
670 self.assert_(not ftp)
671 else:
672 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000673 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000674
675 def test_http(self):
Guido van Rossum700bd922007-08-27 18:10:06 +0000676 class MockHTTPResponse(io.IOBase):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000677 def __init__(self, fp, msg, status, reason):
678 self.fp = fp
679 self.msg = msg
680 self.status = status
681 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000682 def read(self):
683 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000684 class MockHTTPClass:
685 def __init__(self):
686 self.req_headers = []
687 self.data = None
688 self.raise_on_endheaders = False
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000689 def __call__(self, host, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000690 self.host = host
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000691 self.timeout = timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000692 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000693 def set_debuglevel(self, level):
694 self.level = level
695 def request(self, method, url, body=None, headers={}):
696 self.method = method
697 self.selector = url
698 self.req_headers += headers.items()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000699 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000700 if body:
701 self.data = body
702 if self.raise_on_endheaders:
703 import socket
704 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000705 def getresponse(self):
706 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000707
708 h = urllib2.AbstractHTTPHandler()
709 o = h.parent = MockOpener()
710
711 url = "http://example.com/"
712 for method, data in [("GET", None), ("POST", "blah")]:
713 req = Request(url, data, {"Foo": "bar"})
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000714 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000715 req.add_unredirected_header("Spam", "eggs")
716 http = MockHTTPClass()
717 r = h.do_open(http, req)
718
719 # result attributes
720 r.read; r.readline # wrapped MockFile methods
721 r.info; r.geturl # addinfourl methods
722 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
723 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000724 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000725 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000726
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000727 self.assertEqual(http.host, "example.com")
728 self.assertEqual(http.level, 0)
729 self.assertEqual(http.method, method)
730 self.assertEqual(http.selector, "/")
731 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000732 [("Connection", "close"),
733 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000734 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000735
736 # check socket.error converted to URLError
737 http.raise_on_endheaders = True
738 self.assertRaises(urllib2.URLError, h.do_open, http, req)
739
740 # check adding of standard headers
741 o.addheaders = [("Spam", "eggs")]
742 for data in "", None: # POST, GET
743 req = Request("http://example.com/", data)
744 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000745 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000746 if data is None: # GET
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000747 self.assert_("Content-length" not in req.unredirected_hdrs)
748 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000749 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000750 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
751 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000752 "application/x-www-form-urlencoded")
753 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000754 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
755 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000756
757 # don't clobber existing headers
758 req.add_unredirected_header("Content-length", "foo")
759 req.add_unredirected_header("Content-type", "bar")
760 req.add_unredirected_header("Host", "baz")
761 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000762 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000763 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
764 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000765 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
766 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000767
768 def test_errors(self):
769 h = urllib2.HTTPErrorProcessor()
770 o = h.parent = MockOpener()
771
772 url = "http://example.com/"
773 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000774 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000775 r = MockResponse(200, "OK", {}, "", url)
776 newr = h.http_response(req, r)
777 self.assert_(r is newr)
778 self.assert_(not hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000779 r = MockResponse(202, "Accepted", {}, "", url)
780 newr = h.http_response(req, r)
781 self.assert_(r is newr)
782 self.assert_(not hasattr(o, "proto")) # o.error not called
783 r = MockResponse(206, "Partial content", {}, "", url)
784 newr = h.http_response(req, r)
785 self.assert_(r is newr)
786 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000787 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000788 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000789 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000790 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000791 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000792
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000793 def test_cookies(self):
794 cj = MockCookieJar()
795 h = urllib2.HTTPCookieProcessor(cj)
796 o = h.parent = MockOpener()
797
798 req = Request("http://example.com/")
799 r = MockResponse(200, "OK", {}, "")
800 newreq = h.http_request(req)
801 self.assert_(cj.ach_req is req is newreq)
802 self.assertEquals(req.get_origin_req_host(), "example.com")
803 self.assert_(not req.is_unverifiable())
804 newr = h.http_response(req, r)
805 self.assert_(cj.ec_req is req)
806 self.assert_(cj.ec_r is r is newr)
807
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000808 def test_redirect(self):
809 from_url = "http://example.com/a.html"
810 to_url = "http://example.com/b.html"
811 h = urllib2.HTTPRedirectHandler()
812 o = h.parent = MockOpener()
813
814 # ordinary redirect behaviour
815 for code in 301, 302, 303, 307:
816 for data in None, "blah\nblah\n":
817 method = getattr(h, "http_error_%s" % code)
818 req = Request(from_url, data)
819 req.add_header("Nonsense", "viking=withhold")
Christian Heimes77c02eb2008-02-09 02:18:51 +0000820 if data is not None:
821 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000822 req.add_unredirected_header("Spam", "spam")
823 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000824 method(req, MockFile(), code, "Blah",
825 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000826 except urllib2.HTTPError:
827 # 307 in response to POST requires user OK
828 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000829 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000830 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000831 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000832 except AttributeError:
833 self.assert_(not o.req.has_data())
Christian Heimes77c02eb2008-02-09 02:18:51 +0000834
835 # now it's a GET, there should not be headers regarding content
836 # (possibly dragged from before being a POST)
837 headers = [x.lower() for x in o.req.headers]
838 self.assertTrue("content-length" not in headers)
839 self.assertTrue("content-type" not in headers)
840
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000841 self.assertEqual(o.req.headers["Nonsense"],
842 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000843 self.assert_("Spam" not in o.req.headers)
844 self.assert_("Spam" not in o.req.unredirected_hdrs)
845
846 # loop detection
847 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000848 def redirect(h, req, url=to_url):
849 h.http_error_302(req, MockFile(), 302, "Blah",
850 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000851 # Note that the *original* request shares the same record of
852 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000853
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000854 # detect infinite loop redirect of a URL to itself
855 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000856 count = 0
857 try:
858 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000859 redirect(h, req, "http://example.com/")
860 count = count + 1
861 except urllib2.HTTPError:
862 # don't stop until max_repeats, because cookies may introduce state
863 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
864
865 # detect endless non-repeating chain of redirects
866 req = Request(from_url, origin_req_host="example.com")
867 count = 0
868 try:
869 while 1:
870 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000871 count = count + 1
872 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000873 self.assertEqual(count,
874 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000875
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000876 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000877 # cookies shouldn't leak into redirected requests
878 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000879
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000880 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000881
882 cj = CookieJar()
883 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000884 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
885 hdeh = urllib2.HTTPDefaultErrorHandler()
886 hrh = urllib2.HTTPRedirectHandler()
887 cp = urllib2.HTTPCookieProcessor(cj)
888 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000889 o.open("http://www.example.com/")
890 self.assert_(not hh.req.has_header("Cookie"))
891
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000892 def test_proxy(self):
893 o = OpenerDirector()
894 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
895 o.add_handler(ph)
896 meth_spec = [
897 [("http_open", "return response")]
898 ]
899 handlers = add_ordered_mock_handlers(o, meth_spec)
900
901 req = Request("http://acme.example.com/")
902 self.assertEqual(req.get_host(), "acme.example.com")
903 r = o.open(req)
904 self.assertEqual(req.get_host(), "proxy.example.com:3128")
905
906 self.assertEqual([(handlers[0], "http_open")],
907 [tup[0:2] for tup in o.calls])
908
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000909 def test_basic_auth(self, quote_char='"'):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000910 opener = OpenerDirector()
911 password_manager = MockPasswordManager()
912 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
913 realm = "ACME Widget Store"
914 http_handler = MockHTTPHandler(
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000915 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
916 (quote_char, realm, quote_char) )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000917 opener.add_handler(auth_handler)
918 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000919 self._test_basic_auth(opener, auth_handler, "Authorization",
920 realm, http_handler, password_manager,
921 "http://acme.example.com/protected",
922 "http://acme.example.com/protected",
923 )
924
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000925 def test_basic_auth_with_single_quoted_realm(self):
926 self.test_basic_auth(quote_char="'")
927
Thomas Wouters477c8d52006-05-27 19:21:47 +0000928 def test_proxy_basic_auth(self):
929 opener = OpenerDirector()
930 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
931 opener.add_handler(ph)
932 password_manager = MockPasswordManager()
933 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
934 realm = "ACME Networks"
935 http_handler = MockHTTPHandler(
936 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000937 opener.add_handler(auth_handler)
938 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000939 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000940 realm, http_handler, password_manager,
941 "http://acme.example.com:3128/protected",
942 "proxy.example.com:3128",
943 )
944
945 def test_basic_and_digest_auth_handlers(self):
946 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
947 # response (http://python.org/sf/1479302), where it should instead
948 # return None to allow another handler (especially
949 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000950
951 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
952 # try digest first (since it's the strongest auth scheme), so we record
953 # order of calls here to check digest comes first:
954 class RecordingOpenerDirector(OpenerDirector):
955 def __init__(self):
956 OpenerDirector.__init__(self)
957 self.recorded = []
958 def record(self, info):
959 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000960 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000961 def http_error_401(self, *args, **kwds):
962 self.parent.record("digest")
963 urllib2.HTTPDigestAuthHandler.http_error_401(self,
964 *args, **kwds)
965 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
966 def http_error_401(self, *args, **kwds):
967 self.parent.record("basic")
968 urllib2.HTTPBasicAuthHandler.http_error_401(self,
969 *args, **kwds)
970
971 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000972 password_manager = MockPasswordManager()
973 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000974 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000975 realm = "ACME Networks"
976 http_handler = MockHTTPHandler(
977 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000978 opener.add_handler(basic_handler)
979 opener.add_handler(digest_handler)
980 opener.add_handler(http_handler)
981
982 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000983 self._test_basic_auth(opener, basic_handler, "Authorization",
984 realm, http_handler, password_manager,
985 "http://acme.example.com/protected",
986 "http://acme.example.com/protected",
987 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000988 # check digest was tried before basic (twice, because
989 # _test_basic_auth called .open() twice)
990 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000991
992 def _test_basic_auth(self, opener, auth_handler, auth_header,
993 realm, http_handler, password_manager,
994 request_url, protected_url):
Christian Heimes05e8be12008-02-23 18:30:17 +0000995 import base64
Thomas Wouters477c8d52006-05-27 19:21:47 +0000996 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000997
998 # .add_password() fed through to password manager
999 auth_handler.add_password(realm, request_url, user, password)
1000 self.assertEqual(realm, password_manager.realm)
1001 self.assertEqual(request_url, password_manager.url)
1002 self.assertEqual(user, password_manager.user)
1003 self.assertEqual(password, password_manager.password)
1004
1005 r = opener.open(request_url)
1006
1007 # should have asked the password manager for the username/password
1008 self.assertEqual(password_manager.target_realm, realm)
1009 self.assertEqual(password_manager.target_url, protected_url)
1010
1011 # expect one request without authorization, then one with
1012 self.assertEqual(len(http_handler.requests), 2)
1013 self.assertFalse(http_handler.requests[0].has_header(auth_header))
Guido van Rossum98b349f2007-08-27 21:47:52 +00001014 userpass = bytes('%s:%s' % (user, password), "ascii")
Guido van Rossum98297ee2007-11-06 21:34:58 +00001015 auth_hdr_value = ('Basic ' +
1016 base64.encodestring(userpass).strip().decode())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001017 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1018 auth_hdr_value)
1019
1020 # if the password manager can't find a password, the handler won't
1021 # handle the HTTP auth error
1022 password_manager.user = password_manager.password = None
1023 http_handler.reset()
1024 r = opener.open(request_url)
1025 self.assertEqual(len(http_handler.requests), 1)
1026 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1027
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001028
1029class MiscTests(unittest.TestCase):
1030
1031 def test_build_opener(self):
1032 class MyHTTPHandler(urllib2.HTTPHandler): pass
1033 class FooHandler(urllib2.BaseHandler):
1034 def foo_open(self): pass
1035 class BarHandler(urllib2.BaseHandler):
1036 def bar_open(self): pass
1037
1038 build_opener = urllib2.build_opener
1039
1040 o = build_opener(FooHandler, BarHandler)
1041 self.opener_has_handler(o, FooHandler)
1042 self.opener_has_handler(o, BarHandler)
1043
1044 # can take a mix of classes and instances
1045 o = build_opener(FooHandler, BarHandler())
1046 self.opener_has_handler(o, FooHandler)
1047 self.opener_has_handler(o, BarHandler)
1048
1049 # subclasses of default handlers override default handlers
1050 o = build_opener(MyHTTPHandler)
1051 self.opener_has_handler(o, MyHTTPHandler)
1052
1053 # a particular case of overriding: default handlers can be passed
1054 # in explicitly
1055 o = build_opener()
1056 self.opener_has_handler(o, urllib2.HTTPHandler)
1057 o = build_opener(urllib2.HTTPHandler)
1058 self.opener_has_handler(o, urllib2.HTTPHandler)
1059 o = build_opener(urllib2.HTTPHandler())
1060 self.opener_has_handler(o, urllib2.HTTPHandler)
1061
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001062 # Issue2670: multiple handlers sharing the same base class
1063 class MyOtherHTTPHandler(urllib2.HTTPHandler): pass
1064 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1065 self.opener_has_handler(o, MyHTTPHandler)
1066 self.opener_has_handler(o, MyOtherHTTPHandler)
1067
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001068 def opener_has_handler(self, opener, handler_class):
1069 for h in opener.handlers:
1070 if h.__class__ == handler_class:
1071 break
1072 else:
1073 self.assert_(False)
1074
1075
1076def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001077 from test import test_urllib2
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001078 support.run_doctest(test_urllib2, verbose)
1079 support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001080 tests = (TrivialTests,
1081 OpenerDirectorTests,
1082 HandlerTests,
1083 MiscTests)
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001084 support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001085
1086if __name__ == "__main__":
1087 test_main(verbose=True)