blob: 8d17e05725d7191c0d1f17da041540b9f7859ca1 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Christian Heimesc5f05e42008-02-23 17:40:11 +00004import os
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00005import StringIO
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)
Georg Brandlfa42bd72006-04-30 07:06:11 +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(':', '/')
29 elif os.name == 'riscos':
30 import string
31 fname = os.expand(fname)
32 fname = fname.translate(string.maketrans("/.", "./"))
33
34 file_url = "file://%s" % fname
35 f = urllib2.urlopen(file_url)
36
37 buf = f.read()
38 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000039
Georg Brandle1b13d22005-08-24 22:20:32 +000040 def test_parse_http_list(self):
41 tests = [('a,b,c', ['a', 'b', 'c']),
42 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
43 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
44 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
45 for string, list in tests:
46 self.assertEquals(urllib2.parse_http_list(string), list)
47
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000048
Georg Brandl8c036cc2006-08-20 13:15:39 +000049def test_request_headers_dict():
50 """
51 The Request.headers dictionary is not a documented interface. It should
52 stay that way, because the complete set of headers are only accessible
53 through the .get_header(), .has_header(), .header_items() interface.
54 However, .headers pre-dates those methods, and so real code will be using
55 the dictionary.
56
57 The introduction in 2.4 of those methods was a mistake for the same reason:
58 code that previously saw all (urllib2 user)-provided headers in .headers
59 now sees only a subset (and the function interface is ugly and incomplete).
60 A better change would have been to replace .headers dict with a dict
61 subclass (or UserDict.DictMixin instance?) that preserved the .headers
62 interface and also provided access to the "unredirected" headers. It's
63 probably too late to fix that, though.
64
65
66 Check .capitalize() case normalization:
67
68 >>> url = "http://example.com"
69 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
70 'blah'
71 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
72 'blah'
73
74 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
75 but that could be changed in future.
76
77 """
78
79def test_request_headers_methods():
80 """
81 Note the case normalization of header names here, to .capitalize()-case.
82 This should be preserved for backwards-compatibility. (In the HTTP case,
83 normalization to .title()-case is done by urllib2 before sending headers to
84 httplib).
85
86 >>> url = "http://example.com"
87 >>> r = Request(url, headers={"Spam-eggs": "blah"})
88 >>> r.has_header("Spam-eggs")
89 True
90 >>> r.header_items()
91 [('Spam-eggs', 'blah')]
92 >>> r.add_header("Foo-Bar", "baz")
93 >>> items = r.header_items()
94 >>> items.sort()
95 >>> items
96 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
97
98 Note that e.g. r.has_header("spam-EggS") is currently False, and
99 r.get_header("spam-EggS") returns None, but that could be changed in
100 future.
101
102 >>> r.has_header("Not-there")
103 False
104 >>> print r.get_header("Not-there")
105 None
106 >>> r.get_header("Not-there", "default")
107 'default'
108
109 """
110
111
Georg Brandlfa42bd72006-04-30 07:06:11 +0000112def test_password_manager(self):
113 """
114 >>> mgr = urllib2.HTTPPasswordMgr()
115 >>> add = mgr.add_password
116 >>> add("Some Realm", "http://example.com/", "joe", "password")
117 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
118 >>> add("c", "http://example.com/foo", "foo", "ni")
119 >>> add("c", "http://example.com/bar", "bar", "nini")
120 >>> add("b", "http://example.com/", "first", "blah")
121 >>> add("b", "http://example.com/", "second", "spam")
122 >>> add("a", "http://example.com", "1", "a")
123 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
124 >>> add("Some Realm", "d.example.com", "4", "d")
125 >>> add("Some Realm", "e.example.com:3128", "5", "e")
126
127 >>> mgr.find_user_password("Some Realm", "example.com")
128 ('joe', 'password')
129 >>> mgr.find_user_password("Some Realm", "http://example.com")
130 ('joe', 'password')
131 >>> mgr.find_user_password("Some Realm", "http://example.com/")
132 ('joe', 'password')
133 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
134 ('joe', 'password')
135 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
136 ('joe', 'password')
137 >>> mgr.find_user_password("c", "http://example.com/foo")
138 ('foo', 'ni')
139 >>> mgr.find_user_password("c", "http://example.com/bar")
140 ('bar', 'nini')
141
Georg Brandl2b330372006-05-28 20:23:12 +0000142 Actually, this is really undefined ATM
143## Currently, we use the highest-level path where more than one match:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000144
Georg Brandl2b330372006-05-28 20:23:12 +0000145## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
146## ('joe', 'password')
Georg Brandlfa42bd72006-04-30 07:06:11 +0000147
148 Use latest add_password() in case of conflict:
149
150 >>> mgr.find_user_password("b", "http://example.com/")
151 ('second', 'spam')
152
153 No special relationship between a.example.com and example.com:
154
155 >>> mgr.find_user_password("a", "http://example.com/")
156 ('1', 'a')
157 >>> mgr.find_user_password("a", "http://a.example.com/")
158 (None, None)
159
160 Ports:
161
162 >>> mgr.find_user_password("Some Realm", "c.example.com")
163 (None, None)
164 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
165 ('3', 'c')
166 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
167 ('3', 'c')
168 >>> mgr.find_user_password("Some Realm", "d.example.com")
169 ('4', 'd')
170 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
171 ('5', 'e')
172
173 """
174 pass
175
176
Georg Brandl2b330372006-05-28 20:23:12 +0000177def test_password_manager_default_port(self):
178 """
179 >>> mgr = urllib2.HTTPPasswordMgr()
180 >>> add = mgr.add_password
181
182 The point to note here is that we can't guess the default port if there's
183 no scheme. This applies to both add_password and find_user_password.
184
185 >>> add("f", "http://g.example.com:80", "10", "j")
186 >>> add("g", "http://h.example.com", "11", "k")
187 >>> add("h", "i.example.com:80", "12", "l")
188 >>> add("i", "j.example.com", "13", "m")
189 >>> mgr.find_user_password("f", "g.example.com:100")
190 (None, None)
191 >>> mgr.find_user_password("f", "g.example.com:80")
192 ('10', 'j')
193 >>> mgr.find_user_password("f", "g.example.com")
194 (None, None)
195 >>> mgr.find_user_password("f", "http://g.example.com:100")
196 (None, None)
197 >>> mgr.find_user_password("f", "http://g.example.com:80")
198 ('10', 'j')
199 >>> mgr.find_user_password("f", "http://g.example.com")
200 ('10', 'j')
201 >>> mgr.find_user_password("g", "h.example.com")
202 ('11', 'k')
203 >>> mgr.find_user_password("g", "h.example.com:80")
204 ('11', 'k')
205 >>> mgr.find_user_password("g", "http://h.example.com:80")
206 ('11', 'k')
207 >>> mgr.find_user_password("h", "i.example.com")
208 (None, None)
209 >>> mgr.find_user_password("h", "i.example.com:80")
210 ('12', 'l')
211 >>> mgr.find_user_password("h", "http://i.example.com:80")
212 ('12', 'l')
213 >>> mgr.find_user_password("i", "j.example.com")
214 ('13', 'm')
215 >>> mgr.find_user_password("i", "j.example.com:80")
216 (None, None)
217 >>> mgr.find_user_password("i", "http://j.example.com")
218 ('13', 'm')
219 >>> mgr.find_user_password("i", "http://j.example.com:80")
220 (None, None)
221
222 """
223
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000224class MockOpener:
225 addheaders = []
226 def open(self, req, data=None):
227 self.req, self.data = req, data
228 def error(self, proto, *args):
229 self.proto, self.args = proto, args
230
231class MockFile:
232 def read(self, count=None): pass
233 def readline(self, count=None): pass
234 def close(self): pass
235
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000236class MockHeaders(dict):
237 def getheaders(self, name):
238 return self.values()
239
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000240class MockResponse(StringIO.StringIO):
241 def __init__(self, code, msg, headers, data, url=None):
242 StringIO.StringIO.__init__(self, data)
243 self.code, self.msg, self.headers, self.url = code, msg, headers, url
244 def info(self):
245 return self.headers
246 def geturl(self):
247 return self.url
248
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000249class MockCookieJar:
250 def add_cookie_header(self, request):
251 self.ach_req = request
252 def extract_cookies(self, response, request):
253 self.ec_req, self.ec_r = request, response
254
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000255class FakeMethod:
256 def __init__(self, meth_name, action, handle):
257 self.meth_name = meth_name
258 self.handle = handle
259 self.action = action
260 def __call__(self, *args):
261 return self.handle(self.meth_name, self.action, *args)
262
263class MockHandler:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000264 # useful for testing handler machinery
265 # see add_ordered_mock_handlers() docstring
Georg Brandl720096a2006-04-02 20:45:34 +0000266 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000267 def __init__(self, methods):
268 self._define_methods(methods)
269 def _define_methods(self, methods):
270 for spec in methods:
271 if len(spec) == 2: name, action = spec
272 else: name, action = spec, None
273 meth = FakeMethod(name, action, self.handle)
274 setattr(self.__class__, name, meth)
275 def handle(self, fn_name, action, *args, **kwds):
276 self.parent.calls.append((self, fn_name, args, kwds))
277 if action is None:
278 return None
279 elif action == "return self":
280 return self
281 elif action == "return response":
282 res = MockResponse(200, "OK", {}, "")
283 return res
284 elif action == "return request":
285 return Request("http://blah/")
286 elif action.startswith("error"):
287 code = action[action.rfind(" ")+1:]
288 try:
289 code = int(code)
290 except ValueError:
291 pass
292 res = MockResponse(200, "OK", {}, "")
293 return self.parent.error("http", args[0], res, code, "", {})
294 elif action == "raise":
295 raise urllib2.URLError("blah")
296 assert False
297 def close(self): pass
298 def add_parent(self, parent):
299 self.parent = parent
300 self.parent.calls = []
301 def __lt__(self, other):
302 if not hasattr(other, "handler_order"):
303 # No handler_order, leave in original order. Yuck.
304 return True
305 return self.handler_order < other.handler_order
306
307def add_ordered_mock_handlers(opener, meth_spec):
308 """Create MockHandlers and add them to an OpenerDirector.
309
310 meth_spec: list of lists of tuples and strings defining methods to define
311 on handlers. eg:
312
313 [["http_error", "ftp_open"], ["http_open"]]
314
315 defines methods .http_error() and .ftp_open() on one handler, and
316 .http_open() on another. These methods just record their arguments and
317 return None. Using a tuple instead of a string causes the method to
318 perform some action (see MockHandler.handle()), eg:
319
320 [["http_error"], [("http_open", "return request")]]
321
322 defines .http_error() on one handler (which simply returns None), and
323 .http_open() on another handler, which returns a Request object.
324
325 """
326 handlers = []
327 count = 0
328 for meths in meth_spec:
329 class MockHandlerSubclass(MockHandler): pass
330 h = MockHandlerSubclass(meths)
Georg Brandl720096a2006-04-02 20:45:34 +0000331 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000332 h.add_parent(opener)
333 count = count + 1
334 handlers.append(h)
335 opener.add_handler(h)
336 return handlers
337
Georg Brandlfa42bd72006-04-30 07:06:11 +0000338def build_test_opener(*handler_instances):
339 opener = OpenerDirector()
340 for h in handler_instances:
341 opener.add_handler(h)
342 return opener
343
344class MockHTTPHandler(urllib2.BaseHandler):
345 # useful for testing redirections and auth
346 # sends supplied headers and code as first response
347 # sends 200 OK as second response
348 def __init__(self, code, headers):
349 self.code = code
350 self.headers = headers
351 self.reset()
352 def reset(self):
353 self._count = 0
354 self.requests = []
355 def http_open(self, req):
356 import mimetools, httplib, copy
357 from StringIO import StringIO
358 self.requests.append(copy.deepcopy(req))
359 if self._count == 0:
360 self._count = self._count + 1
361 name = httplib.responses[self.code]
362 msg = mimetools.Message(StringIO(self.headers))
363 return self.parent.error(
364 "http", req, MockFile(), self.code, name, msg)
365 else:
366 self.req = req
367 msg = mimetools.Message(StringIO("\r\n\r\n"))
368 return MockResponse(200, "OK", msg, "", req.get_full_url())
369
370class MockPasswordManager:
371 def add_password(self, realm, uri, user, password):
372 self.realm = realm
373 self.url = uri
374 self.user = user
375 self.password = password
376 def find_user_password(self, realm, authuri):
377 self.target_realm = realm
378 self.target_url = authuri
379 return self.user, self.password
380
381
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000382class OpenerDirectorTests(unittest.TestCase):
383
Georg Brandlf91149e2007-07-12 08:05:45 +0000384 def test_add_non_handler(self):
385 class NonHandler(object):
386 pass
387 self.assertRaises(TypeError,
388 OpenerDirector().add_handler, NonHandler())
389
Georg Brandl261e2512006-05-29 20:52:54 +0000390 def test_badly_named_methods(self):
391 # test work-around for three methods that accidentally follow the
392 # naming conventions for handler methods
393 # (*_open() / *_request() / *_response())
394
395 # These used to call the accidentally-named methods, causing a
396 # TypeError in real code; here, returning self from these mock
397 # methods would either cause no exception, or AttributeError.
398
399 from urllib2 import URLError
400
401 o = OpenerDirector()
402 meth_spec = [
403 [("do_open", "return self"), ("proxy_open", "return self")],
404 [("redirect_request", "return self")],
405 ]
406 handlers = add_ordered_mock_handlers(o, meth_spec)
407 o.add_handler(urllib2.UnknownHandler())
408 for scheme in "do", "proxy", "redirect":
409 self.assertRaises(URLError, o.open, scheme+"://example.com/")
410
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000411 def test_handled(self):
412 # handler returning non-None means no more handlers will be called
413 o = OpenerDirector()
414 meth_spec = [
415 ["http_open", "ftp_open", "http_error_302"],
416 ["ftp_open"],
417 [("http_open", "return self")],
418 [("http_open", "return self")],
419 ]
420 handlers = add_ordered_mock_handlers(o, meth_spec)
421
422 req = Request("http://example.com/")
423 r = o.open(req)
424 # Second .http_open() gets called, third doesn't, since second returned
425 # non-None. Handlers without .http_open() never get any methods called
426 # on them.
427 # In fact, second mock handler defining .http_open() returns self
428 # (instead of response), which becomes the OpenerDirector's return
429 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000430 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000431 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
432 for expected, got in zip(calls, o.calls):
433 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000434 self.assertEqual((handler, name), expected)
435 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000436
437 def test_handler_order(self):
438 o = OpenerDirector()
439 handlers = []
440 for meths, handler_order in [
441 ([("http_open", "return self")], 500),
442 (["http_open"], 0),
443 ]:
444 class MockHandlerSubclass(MockHandler): pass
445 h = MockHandlerSubclass(meths)
446 h.handler_order = handler_order
447 handlers.append(h)
448 o.add_handler(h)
449
450 r = o.open("http://example.com/")
451 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000452 self.assertEqual(o.calls[0][0], handlers[1])
453 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000454
455 def test_raise(self):
456 # raising URLError stops processing of request
457 o = OpenerDirector()
458 meth_spec = [
459 [("http_open", "raise")],
460 [("http_open", "return self")],
461 ]
462 handlers = add_ordered_mock_handlers(o, meth_spec)
463
464 req = Request("http://example.com/")
465 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000466 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000467
468## def test_error(self):
469## # XXX this doesn't actually seem to be used in standard library,
470## # but should really be tested anyway...
471
472 def test_http_error(self):
473 # XXX http_error_default
474 # http errors are a special case
475 o = OpenerDirector()
476 meth_spec = [
477 [("http_open", "error 302")],
478 [("http_error_400", "raise"), "http_open"],
479 [("http_error_302", "return response"), "http_error_303",
480 "http_error"],
481 [("http_error_302")],
482 ]
483 handlers = add_ordered_mock_handlers(o, meth_spec)
484
485 class Unknown:
486 def __eq__(self, other): return True
487
488 req = Request("http://example.com/")
489 r = o.open(req)
490 assert len(o.calls) == 2
491 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000492 (handlers[2], "http_error_302",
493 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000494 for expected, got in zip(calls, o.calls):
495 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000496 self.assertEqual((handler, method_name), got[:2])
497 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000498
499 def test_processors(self):
500 # *_request / *_response methods get called appropriately
501 o = OpenerDirector()
502 meth_spec = [
503 [("http_request", "return request"),
504 ("http_response", "return response")],
505 [("http_request", "return request"),
506 ("http_response", "return response")],
507 ]
508 handlers = add_ordered_mock_handlers(o, meth_spec)
509
510 req = Request("http://example.com/")
511 r = o.open(req)
512 # processor methods are called on *all* handlers that define them,
513 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000514 calls = [
515 (handlers[0], "http_request"), (handlers[1], "http_request"),
516 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000517
518 for i, (handler, name, args, kwds) in enumerate(o.calls):
519 if i < 2:
520 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000521 self.assertEqual((handler, name), calls[i])
522 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000523 self.assert_(isinstance(args[0], Request))
524 else:
525 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000526 self.assertEqual((handler, name), calls[i])
527 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000528 self.assert_(isinstance(args[0], Request))
529 # response from opener.open is None, because there's no
530 # handler that defines http_open to handle it
531 self.assert_(args[1] is None or
532 isinstance(args[1], MockResponse))
533
534
Tim Peters58eb11c2004-01-18 20:29:55 +0000535def sanepathname2url(path):
536 import urllib
537 urlpath = urllib.pathname2url(path)
538 if os.name == "nt" and urlpath.startswith("///"):
539 urlpath = urlpath[2:]
540 # XXX don't ask me about the mac...
541 return urlpath
542
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000543class HandlerTests(unittest.TestCase):
544
545 def test_ftp(self):
546 class MockFTPWrapper:
547 def __init__(self, data): self.data = data
548 def retrfile(self, filename, filetype):
549 self.filename, self.filetype = filename, filetype
550 return StringIO.StringIO(self.data), len(self.data)
551
552 class NullFTPHandler(urllib2.FTPHandler):
553 def __init__(self, data): self.data = data
Facundo Batista10951d52007-06-06 17:15:23 +0000554 def connect_ftp(self, user, passwd, host, port, dirs, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000555 self.user, self.passwd = user, passwd
556 self.host, self.port = host, port
557 self.dirs = dirs
558 self.ftpwrapper = MockFTPWrapper(self.data)
559 return self.ftpwrapper
560
561 import ftplib, socket
562 data = "rheum rhaponicum"
563 h = NullFTPHandler(data)
564 o = h.parent = MockOpener()
565
566 for url, host, port, type_, dirs, filename, mimetype in [
567 ("ftp://localhost/foo/bar/baz.html",
568 "localhost", ftplib.FTP_PORT, "I",
569 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000570 ("ftp://localhost:80/foo/bar/",
571 "localhost", 80, "D",
572 ["foo", "bar"], "", None),
573 ("ftp://localhost/baz.gif;type=a",
574 "localhost", ftplib.FTP_PORT, "A",
575 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000576 ]:
Facundo Batista10951d52007-06-06 17:15:23 +0000577 req = Request(url)
578 req.timeout = None
579 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000580 # ftp authentication not yet implemented by FTPHandler
581 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000582 self.assertEqual(h.host, socket.gethostbyname(host))
583 self.assertEqual(h.port, port)
584 self.assertEqual(h.dirs, dirs)
585 self.assertEqual(h.ftpwrapper.filename, filename)
586 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000587 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000588 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000589 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000590
591 def test_file(self):
Christian Heimesc5f05e42008-02-23 17:40:11 +0000592 import rfc822, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000593 h = urllib2.FileHandler()
594 o = h.parent = MockOpener()
595
Tim Peters58eb11c2004-01-18 20:29:55 +0000596 TESTFN = test_support.TESTFN
597 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000598 towrite = "hello, world\n"
Georg Brandldd2245f2006-03-31 17:18:06 +0000599 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000600 "file://localhost%s" % urlpath,
601 "file://%s" % urlpath,
602 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Georg Brandldd2245f2006-03-31 17:18:06 +0000603 ]
604 try:
Tim Peters480725d2006-04-03 02:46:44 +0000605 localaddr = socket.gethostbyname(socket.gethostname())
Georg Brandldd2245f2006-03-31 17:18:06 +0000606 except socket.gaierror:
607 localaddr = ''
608 if localaddr:
609 urls.append("file://%s%s" % (localaddr, urlpath))
Tim Peters480725d2006-04-03 02:46:44 +0000610
Georg Brandldd2245f2006-03-31 17:18:06 +0000611 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000612 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000613 try:
614 try:
615 f.write(towrite)
616 finally:
617 f.close()
618
619 r = h.file_open(Request(url))
620 try:
621 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000622 headers = r.info()
623 newurl = r.geturl()
624 finally:
625 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000626 stats = os.stat(TESTFN)
627 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000628 finally:
629 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000630 self.assertEqual(data, towrite)
631 self.assertEqual(headers["Content-type"], "text/plain")
632 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000633 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000634
635 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000636 "file://localhost:80%s" % urlpath,
Georg Brandlceede5c2007-03-13 08:14:27 +0000637 "file:///file_does_not_exist.txt",
638 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
639 os.getcwd(), TESTFN),
640 "file://somerandomhost.ontheinternet.com%s/%s" %
641 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000642 ]:
643 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000644 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000645 try:
646 f.write(towrite)
647 finally:
648 f.close()
649
650 self.assertRaises(urllib2.URLError,
651 h.file_open, Request(url))
652 finally:
653 os.remove(TESTFN)
654
655 h = urllib2.FileHandler()
656 o = h.parent = MockOpener()
657 # XXXX why does // mean ftp (and /// mean not ftp!), and where
658 # is file: scheme specified? I think this is really a bug, and
659 # what was intended was to distinguish between URLs like:
660 # file:/blah.txt (a file)
661 # file://localhost/blah.txt (a file)
662 # file:///blah.txt (a file)
663 # file://ftp.example.com/blah.txt (an ftp URL)
664 for url, ftp in [
665 ("file://ftp.example.com//foo.txt", True),
666 ("file://ftp.example.com///foo.txt", False),
667# XXXX bug: fails with OSError, should be URLError
668 ("file://ftp.example.com/foo.txt", False),
669 ]:
670 req = Request(url)
671 try:
672 h.file_open(req)
673 # XXXX remove OSError when bug fixed
674 except (urllib2.URLError, OSError):
675 self.assert_(not ftp)
676 else:
677 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000678 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000679
680 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000681 class MockHTTPResponse:
682 def __init__(self, fp, msg, status, reason):
683 self.fp = fp
684 self.msg = msg
685 self.status = status
686 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000687 def read(self):
688 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000689 class MockHTTPClass:
690 def __init__(self):
691 self.req_headers = []
692 self.data = None
693 self.raise_on_endheaders = False
Facundo Batista10951d52007-06-06 17:15:23 +0000694 def __call__(self, host, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000695 self.host = host
Facundo Batista10951d52007-06-06 17:15:23 +0000696 self.timeout = timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000697 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000698 def set_debuglevel(self, level):
699 self.level = level
700 def request(self, method, url, body=None, headers={}):
701 self.method = method
702 self.selector = url
703 self.req_headers += headers.items()
Armin Rigoa3f09272006-05-28 19:13:17 +0000704 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000705 if body:
706 self.data = body
707 if self.raise_on_endheaders:
708 import socket
709 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000710 def getresponse(self):
711 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000712
713 h = urllib2.AbstractHTTPHandler()
714 o = h.parent = MockOpener()
715
716 url = "http://example.com/"
717 for method, data in [("GET", None), ("POST", "blah")]:
718 req = Request(url, data, {"Foo": "bar"})
Facundo Batista10951d52007-06-06 17:15:23 +0000719 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000720 req.add_unredirected_header("Spam", "eggs")
721 http = MockHTTPClass()
722 r = h.do_open(http, req)
723
724 # result attributes
725 r.read; r.readline # wrapped MockFile methods
726 r.info; r.geturl # addinfourl methods
727 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
728 hdrs = r.info()
729 hdrs.get; hdrs.has_key # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000730 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000731
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000732 self.assertEqual(http.host, "example.com")
733 self.assertEqual(http.level, 0)
734 self.assertEqual(http.method, method)
735 self.assertEqual(http.selector, "/")
736 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000737 [("Connection", "close"),
738 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000739 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000740
741 # check socket.error converted to URLError
742 http.raise_on_endheaders = True
743 self.assertRaises(urllib2.URLError, h.do_open, http, req)
744
745 # check adding of standard headers
746 o.addheaders = [("Spam", "eggs")]
747 for data in "", None: # POST, GET
748 req = Request("http://example.com/", data)
749 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000750 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000751 if data is None: # GET
Georg Brandl8c036cc2006-08-20 13:15:39 +0000752 self.assert_("Content-length" not in req.unredirected_hdrs)
753 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000754 else: # POST
Georg Brandl8c036cc2006-08-20 13:15:39 +0000755 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
756 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000757 "application/x-www-form-urlencoded")
758 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000759 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
760 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000761
762 # don't clobber existing headers
763 req.add_unredirected_header("Content-length", "foo")
764 req.add_unredirected_header("Content-type", "bar")
765 req.add_unredirected_header("Host", "baz")
766 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000767 newreq = h.do_request_(req)
Georg Brandl8c036cc2006-08-20 13:15:39 +0000768 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
769 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000770 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
771 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000772
773 def test_errors(self):
774 h = urllib2.HTTPErrorProcessor()
775 o = h.parent = MockOpener()
776
777 url = "http://example.com/"
778 req = Request(url)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000779 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000780 r = MockResponse(200, "OK", {}, "", url)
781 newr = h.http_response(req, r)
782 self.assert_(r is newr)
783 self.assert_(not hasattr(o, "proto")) # o.error not called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000784 r = MockResponse(202, "Accepted", {}, "", url)
785 newr = h.http_response(req, r)
786 self.assert_(r is newr)
787 self.assert_(not hasattr(o, "proto")) # o.error not called
788 r = MockResponse(206, "Partial content", {}, "", url)
789 newr = h.http_response(req, r)
790 self.assert_(r is newr)
791 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000792 # anything else calls o.error (and MockOpener returns None, here)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000793 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000794 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000795 self.assertEqual(o.proto, "http") # o.error called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000796 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000797
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000798 def test_cookies(self):
799 cj = MockCookieJar()
800 h = urllib2.HTTPCookieProcessor(cj)
801 o = h.parent = MockOpener()
802
803 req = Request("http://example.com/")
804 r = MockResponse(200, "OK", {}, "")
805 newreq = h.http_request(req)
806 self.assert_(cj.ach_req is req is newreq)
807 self.assertEquals(req.get_origin_req_host(), "example.com")
808 self.assert_(not req.is_unverifiable())
809 newr = h.http_response(req, r)
810 self.assert_(cj.ec_req is req)
811 self.assert_(cj.ec_r is r is newr)
812
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000813 def test_redirect(self):
814 from_url = "http://example.com/a.html"
815 to_url = "http://example.com/b.html"
816 h = urllib2.HTTPRedirectHandler()
817 o = h.parent = MockOpener()
818
819 # ordinary redirect behaviour
820 for code in 301, 302, 303, 307:
821 for data in None, "blah\nblah\n":
822 method = getattr(h, "http_error_%s" % code)
823 req = Request(from_url, data)
824 req.add_header("Nonsense", "viking=withhold")
Facundo Batista86371d62008-02-07 19:06:52 +0000825 if data is not None:
826 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000827 req.add_unredirected_header("Spam", "spam")
828 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000829 method(req, MockFile(), code, "Blah",
830 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000831 except urllib2.HTTPError:
832 # 307 in response to POST requires user OK
833 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000834 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000835 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000836 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000837 except AttributeError:
838 self.assert_(not o.req.has_data())
Facundo Batista86371d62008-02-07 19:06:52 +0000839
840 # now it's a GET, there should not be headers regarding content
841 # (possibly dragged from before being a POST)
842 headers = [x.lower() for x in o.req.headers]
843 self.assertTrue("content-length" not in headers)
844 self.assertTrue("content-type" not in headers)
845
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000846 self.assertEqual(o.req.headers["Nonsense"],
847 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000848 self.assert_("Spam" not in o.req.headers)
849 self.assert_("Spam" not in o.req.unredirected_hdrs)
850
851 # loop detection
852 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000853 def redirect(h, req, url=to_url):
854 h.http_error_302(req, MockFile(), 302, "Blah",
855 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000856 # Note that the *original* request shares the same record of
857 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000858
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000859 # detect infinite loop redirect of a URL to itself
860 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000861 count = 0
862 try:
863 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000864 redirect(h, req, "http://example.com/")
865 count = count + 1
866 except urllib2.HTTPError:
867 # don't stop until max_repeats, because cookies may introduce state
868 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
869
870 # detect endless non-repeating chain of redirects
871 req = Request(from_url, origin_req_host="example.com")
872 count = 0
873 try:
874 while 1:
875 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000876 count = count + 1
877 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000878 self.assertEqual(count,
879 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000880
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000881 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000882 # cookies shouldn't leak into redirected requests
883 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000884
Neal Norwitzb902f4e2006-04-03 04:45:34 +0000885 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000886
887 cj = CookieJar()
888 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Georg Brandlfa42bd72006-04-30 07:06:11 +0000889 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
890 hdeh = urllib2.HTTPDefaultErrorHandler()
891 hrh = urllib2.HTTPRedirectHandler()
892 cp = urllib2.HTTPCookieProcessor(cj)
893 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000894 o.open("http://www.example.com/")
895 self.assert_(not hh.req.has_header("Cookie"))
896
Georg Brandl720096a2006-04-02 20:45:34 +0000897 def test_proxy(self):
898 o = OpenerDirector()
899 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
900 o.add_handler(ph)
901 meth_spec = [
902 [("http_open", "return response")]
903 ]
904 handlers = add_ordered_mock_handlers(o, meth_spec)
905
906 req = Request("http://acme.example.com/")
907 self.assertEqual(req.get_host(), "acme.example.com")
908 r = o.open(req)
909 self.assertEqual(req.get_host(), "proxy.example.com:3128")
910
911 self.assertEqual([(handlers[0], "http_open")],
912 [tup[0:2] for tup in o.calls])
913
Georg Brandl33124322008-03-21 19:54:00 +0000914 def test_basic_auth(self, quote_char='"'):
Georg Brandlfa42bd72006-04-30 07:06:11 +0000915 opener = OpenerDirector()
916 password_manager = MockPasswordManager()
917 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
918 realm = "ACME Widget Store"
919 http_handler = MockHTTPHandler(
Georg Brandl33124322008-03-21 19:54:00 +0000920 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
921 (quote_char, realm, quote_char) )
Georg Brandl261e2512006-05-29 20:52:54 +0000922 opener.add_handler(auth_handler)
923 opener.add_handler(http_handler)
Georg Brandlfa42bd72006-04-30 07:06:11 +0000924 self._test_basic_auth(opener, auth_handler, "Authorization",
925 realm, http_handler, password_manager,
926 "http://acme.example.com/protected",
927 "http://acme.example.com/protected",
928 )
929
Georg Brandl33124322008-03-21 19:54:00 +0000930 def test_basic_auth_with_single_quoted_realm(self):
931 self.test_basic_auth(quote_char="'")
932
Georg Brandlfa42bd72006-04-30 07:06:11 +0000933 def test_proxy_basic_auth(self):
934 opener = OpenerDirector()
935 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
936 opener.add_handler(ph)
937 password_manager = MockPasswordManager()
938 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
939 realm = "ACME Networks"
940 http_handler = MockHTTPHandler(
941 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +0000942 opener.add_handler(auth_handler)
943 opener.add_handler(http_handler)
Georg Brandl8c036cc2006-08-20 13:15:39 +0000944 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Georg Brandlfa42bd72006-04-30 07:06:11 +0000945 realm, http_handler, password_manager,
946 "http://acme.example.com:3128/protected",
947 "proxy.example.com:3128",
948 )
949
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000950 def test_basic_and_digest_auth_handlers(self):
951 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
952 # response (http://python.org/sf/1479302), where it should instead
953 # return None to allow another handler (especially
954 # HTTPBasicAuthHandler) to handle the response.
Georg Brandl261e2512006-05-29 20:52:54 +0000955
956 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
957 # try digest first (since it's the strongest auth scheme), so we record
958 # order of calls here to check digest comes first:
959 class RecordingOpenerDirector(OpenerDirector):
960 def __init__(self):
961 OpenerDirector.__init__(self)
962 self.recorded = []
963 def record(self, info):
964 self.recorded.append(info)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000965 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Georg Brandl261e2512006-05-29 20:52:54 +0000966 def http_error_401(self, *args, **kwds):
967 self.parent.record("digest")
968 urllib2.HTTPDigestAuthHandler.http_error_401(self,
969 *args, **kwds)
970 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
971 def http_error_401(self, *args, **kwds):
972 self.parent.record("basic")
973 urllib2.HTTPBasicAuthHandler.http_error_401(self,
974 *args, **kwds)
975
976 opener = RecordingOpenerDirector()
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000977 password_manager = MockPasswordManager()
978 digest_handler = TestDigestAuthHandler(password_manager)
Georg Brandl261e2512006-05-29 20:52:54 +0000979 basic_handler = TestBasicAuthHandler(password_manager)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000980 realm = "ACME Networks"
981 http_handler = MockHTTPHandler(
982 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +0000983 opener.add_handler(basic_handler)
984 opener.add_handler(digest_handler)
985 opener.add_handler(http_handler)
986
987 # check basic auth isn't blocked by digest handler failing
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000988 self._test_basic_auth(opener, basic_handler, "Authorization",
989 realm, http_handler, password_manager,
990 "http://acme.example.com/protected",
991 "http://acme.example.com/protected",
992 )
Georg Brandl261e2512006-05-29 20:52:54 +0000993 # check digest was tried before basic (twice, because
994 # _test_basic_auth called .open() twice)
995 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000996
Georg Brandlfa42bd72006-04-30 07:06:11 +0000997 def _test_basic_auth(self, opener, auth_handler, auth_header,
998 realm, http_handler, password_manager,
999 request_url, protected_url):
Christian Heimesc5f05e42008-02-23 17:40:11 +00001000 import base64
Georg Brandlfa42bd72006-04-30 07:06:11 +00001001 user, password = "wile", "coyote"
Georg Brandlfa42bd72006-04-30 07:06:11 +00001002
1003 # .add_password() fed through to password manager
1004 auth_handler.add_password(realm, request_url, user, password)
1005 self.assertEqual(realm, password_manager.realm)
1006 self.assertEqual(request_url, password_manager.url)
1007 self.assertEqual(user, password_manager.user)
1008 self.assertEqual(password, password_manager.password)
1009
1010 r = opener.open(request_url)
1011
1012 # should have asked the password manager for the username/password
1013 self.assertEqual(password_manager.target_realm, realm)
1014 self.assertEqual(password_manager.target_url, protected_url)
1015
1016 # expect one request without authorization, then one with
1017 self.assertEqual(len(http_handler.requests), 2)
1018 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1019 userpass = '%s:%s' % (user, password)
1020 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
1021 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1022 auth_hdr_value)
1023
1024 # if the password manager can't find a password, the handler won't
1025 # handle the HTTP auth error
1026 password_manager.user = password_manager.password = None
1027 http_handler.reset()
1028 r = opener.open(request_url)
1029 self.assertEqual(len(http_handler.requests), 1)
1030 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1031
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001032
1033class MiscTests(unittest.TestCase):
1034
1035 def test_build_opener(self):
1036 class MyHTTPHandler(urllib2.HTTPHandler): pass
1037 class FooHandler(urllib2.BaseHandler):
1038 def foo_open(self): pass
1039 class BarHandler(urllib2.BaseHandler):
1040 def bar_open(self): pass
1041
1042 build_opener = urllib2.build_opener
1043
1044 o = build_opener(FooHandler, BarHandler)
1045 self.opener_has_handler(o, FooHandler)
1046 self.opener_has_handler(o, BarHandler)
1047
1048 # can take a mix of classes and instances
1049 o = build_opener(FooHandler, BarHandler())
1050 self.opener_has_handler(o, FooHandler)
1051 self.opener_has_handler(o, BarHandler)
1052
1053 # subclasses of default handlers override default handlers
1054 o = build_opener(MyHTTPHandler)
1055 self.opener_has_handler(o, MyHTTPHandler)
1056
1057 # a particular case of overriding: default handlers can be passed
1058 # in explicitly
1059 o = build_opener()
1060 self.opener_has_handler(o, urllib2.HTTPHandler)
1061 o = build_opener(urllib2.HTTPHandler)
1062 self.opener_has_handler(o, urllib2.HTTPHandler)
1063 o = build_opener(urllib2.HTTPHandler())
1064 self.opener_has_handler(o, urllib2.HTTPHandler)
1065
Amaury Forgeot d'Arc96865852008-04-22 21:14:41 +00001066 # Issue2670: multiple handlers sharing the same base class
1067 class MyOtherHTTPHandler(urllib2.HTTPHandler): pass
1068 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1069 self.opener_has_handler(o, MyHTTPHandler)
1070 self.opener_has_handler(o, MyOtherHTTPHandler)
1071
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001072 def opener_has_handler(self, opener, handler_class):
1073 for h in opener.handlers:
1074 if h.__class__ == handler_class:
1075 break
1076 else:
1077 self.assert_(False)
1078
1079
1080def test_main(verbose=None):
Georg Brandlfa42bd72006-04-30 07:06:11 +00001081 from test import test_urllib2
1082 test_support.run_doctest(test_urllib2, verbose)
Georg Brandl720096a2006-04-02 20:45:34 +00001083 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001084 tests = (TrivialTests,
1085 OpenerDirectorTests,
1086 HandlerTests,
1087 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001088 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001089
1090if __name__ == "__main__":
1091 test_main(verbose=True)