blob: 5cbc6522084e22c8591daef860edad6b79717655 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +00004import os, socket
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):
587 import time, rfc822, socket
588 h = urllib2.FileHandler()
589 o = h.parent = MockOpener()
590
Tim Peters58eb11c2004-01-18 20:29:55 +0000591 TESTFN = test_support.TESTFN
592 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")
820 req.add_unredirected_header("Spam", "spam")
821 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000822 method(req, MockFile(), code, "Blah",
823 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000824 except urllib2.HTTPError:
825 # 307 in response to POST requires user OK
826 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000827 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000828 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000829 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000830 except AttributeError:
831 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000832 self.assertEqual(o.req.headers["Nonsense"],
833 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000834 self.assert_("Spam" not in o.req.headers)
835 self.assert_("Spam" not in o.req.unredirected_hdrs)
836
837 # loop detection
838 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000839 def redirect(h, req, url=to_url):
840 h.http_error_302(req, MockFile(), 302, "Blah",
841 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000842 # Note that the *original* request shares the same record of
843 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000844
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000845 # detect infinite loop redirect of a URL to itself
846 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000847 count = 0
848 try:
849 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000850 redirect(h, req, "http://example.com/")
851 count = count + 1
852 except urllib2.HTTPError:
853 # don't stop until max_repeats, because cookies may introduce state
854 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
855
856 # detect endless non-repeating chain of redirects
857 req = Request(from_url, origin_req_host="example.com")
858 count = 0
859 try:
860 while 1:
861 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000862 count = count + 1
863 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000864 self.assertEqual(count,
865 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000866
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000867 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000868 # cookies shouldn't leak into redirected requests
869 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000870
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000871 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000872
873 cj = CookieJar()
874 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000875 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
876 hdeh = urllib2.HTTPDefaultErrorHandler()
877 hrh = urllib2.HTTPRedirectHandler()
878 cp = urllib2.HTTPCookieProcessor(cj)
879 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000880 o.open("http://www.example.com/")
881 self.assert_(not hh.req.has_header("Cookie"))
882
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000883 def test_proxy(self):
884 o = OpenerDirector()
885 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
886 o.add_handler(ph)
887 meth_spec = [
888 [("http_open", "return response")]
889 ]
890 handlers = add_ordered_mock_handlers(o, meth_spec)
891
892 req = Request("http://acme.example.com/")
893 self.assertEqual(req.get_host(), "acme.example.com")
894 r = o.open(req)
895 self.assertEqual(req.get_host(), "proxy.example.com:3128")
896
897 self.assertEqual([(handlers[0], "http_open")],
898 [tup[0:2] for tup in o.calls])
899
Thomas Wouters477c8d52006-05-27 19:21:47 +0000900 def test_basic_auth(self):
901 opener = OpenerDirector()
902 password_manager = MockPasswordManager()
903 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
904 realm = "ACME Widget Store"
905 http_handler = MockHTTPHandler(
906 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000907 opener.add_handler(auth_handler)
908 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000909 self._test_basic_auth(opener, auth_handler, "Authorization",
910 realm, http_handler, password_manager,
911 "http://acme.example.com/protected",
912 "http://acme.example.com/protected",
913 )
914
915 def test_proxy_basic_auth(self):
916 opener = OpenerDirector()
917 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
918 opener.add_handler(ph)
919 password_manager = MockPasswordManager()
920 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
921 realm = "ACME Networks"
922 http_handler = MockHTTPHandler(
923 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000924 opener.add_handler(auth_handler)
925 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000926 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000927 realm, http_handler, password_manager,
928 "http://acme.example.com:3128/protected",
929 "proxy.example.com:3128",
930 )
931
932 def test_basic_and_digest_auth_handlers(self):
933 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
934 # response (http://python.org/sf/1479302), where it should instead
935 # return None to allow another handler (especially
936 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000937
938 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
939 # try digest first (since it's the strongest auth scheme), so we record
940 # order of calls here to check digest comes first:
941 class RecordingOpenerDirector(OpenerDirector):
942 def __init__(self):
943 OpenerDirector.__init__(self)
944 self.recorded = []
945 def record(self, info):
946 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000947 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000948 def http_error_401(self, *args, **kwds):
949 self.parent.record("digest")
950 urllib2.HTTPDigestAuthHandler.http_error_401(self,
951 *args, **kwds)
952 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
953 def http_error_401(self, *args, **kwds):
954 self.parent.record("basic")
955 urllib2.HTTPBasicAuthHandler.http_error_401(self,
956 *args, **kwds)
957
958 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000959 password_manager = MockPasswordManager()
960 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000961 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000962 realm = "ACME Networks"
963 http_handler = MockHTTPHandler(
964 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000965 opener.add_handler(basic_handler)
966 opener.add_handler(digest_handler)
967 opener.add_handler(http_handler)
968
969 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000970 self._test_basic_auth(opener, basic_handler, "Authorization",
971 realm, http_handler, password_manager,
972 "http://acme.example.com/protected",
973 "http://acme.example.com/protected",
974 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000975 # check digest was tried before basic (twice, because
976 # _test_basic_auth called .open() twice)
977 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978
979 def _test_basic_auth(self, opener, auth_handler, auth_header,
980 realm, http_handler, password_manager,
981 request_url, protected_url):
982 import base64, httplib
983 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000984
985 # .add_password() fed through to password manager
986 auth_handler.add_password(realm, request_url, user, password)
987 self.assertEqual(realm, password_manager.realm)
988 self.assertEqual(request_url, password_manager.url)
989 self.assertEqual(user, password_manager.user)
990 self.assertEqual(password, password_manager.password)
991
992 r = opener.open(request_url)
993
994 # should have asked the password manager for the username/password
995 self.assertEqual(password_manager.target_realm, realm)
996 self.assertEqual(password_manager.target_url, protected_url)
997
998 # expect one request without authorization, then one with
999 self.assertEqual(len(http_handler.requests), 2)
1000 self.assertFalse(http_handler.requests[0].has_header(auth_header))
Guido van Rossum98b349f2007-08-27 21:47:52 +00001001 userpass = bytes('%s:%s' % (user, password), "ascii")
Guido van Rossumc0f2d2d2007-08-03 19:19:24 +00001002 auth_hdr_value = 'Basic ' + str(base64.encodestring(userpass)).strip()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001003 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1004 auth_hdr_value)
1005
1006 # if the password manager can't find a password, the handler won't
1007 # handle the HTTP auth error
1008 password_manager.user = password_manager.password = None
1009 http_handler.reset()
1010 r = opener.open(request_url)
1011 self.assertEqual(len(http_handler.requests), 1)
1012 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1013
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001014
1015class MiscTests(unittest.TestCase):
1016
1017 def test_build_opener(self):
1018 class MyHTTPHandler(urllib2.HTTPHandler): pass
1019 class FooHandler(urllib2.BaseHandler):
1020 def foo_open(self): pass
1021 class BarHandler(urllib2.BaseHandler):
1022 def bar_open(self): pass
1023
1024 build_opener = urllib2.build_opener
1025
1026 o = build_opener(FooHandler, BarHandler)
1027 self.opener_has_handler(o, FooHandler)
1028 self.opener_has_handler(o, BarHandler)
1029
1030 # can take a mix of classes and instances
1031 o = build_opener(FooHandler, BarHandler())
1032 self.opener_has_handler(o, FooHandler)
1033 self.opener_has_handler(o, BarHandler)
1034
1035 # subclasses of default handlers override default handlers
1036 o = build_opener(MyHTTPHandler)
1037 self.opener_has_handler(o, MyHTTPHandler)
1038
1039 # a particular case of overriding: default handlers can be passed
1040 # in explicitly
1041 o = build_opener()
1042 self.opener_has_handler(o, urllib2.HTTPHandler)
1043 o = build_opener(urllib2.HTTPHandler)
1044 self.opener_has_handler(o, urllib2.HTTPHandler)
1045 o = build_opener(urllib2.HTTPHandler())
1046 self.opener_has_handler(o, urllib2.HTTPHandler)
1047
1048 def opener_has_handler(self, opener, handler_class):
1049 for h in opener.handlers:
1050 if h.__class__ == handler_class:
1051 break
1052 else:
1053 self.assert_(False)
1054
1055
1056def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001057 from test import test_urllib2
1058 test_support.run_doctest(test_urllib2, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001059 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001060 tests = (TrivialTests,
1061 OpenerDirectorTests,
1062 HandlerTests,
1063 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001064 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001065
1066if __name__ == "__main__":
1067 test_main(verbose=True)