blob: 91d6ba7770be2fc9ea2ea20ada8cbe3afa2a57d2 [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(':', '/')
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
Thomas Wouters00ee7ba2006-08-21 19:07:27 +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")
Guido van Rossumcc2b0162007-02-11 06:12:03 +000093 >>> items = sorted(r.header_items())
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000094 >>> items
95 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
96
97 Note that e.g. r.has_header("spam-EggS") is currently False, and
98 r.get_header("spam-EggS") returns None, but that could be changed in
99 future.
100
101 >>> r.has_header("Not-there")
102 False
Guido van Rossum7131f842007-02-09 20:13:25 +0000103 >>> print(r.get_header("Not-there"))
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000104 None
105 >>> r.get_header("Not-there", "default")
106 'default'
107
108 """
109
110
Thomas Wouters477c8d52006-05-27 19:21:47 +0000111def test_password_manager(self):
112 """
113 >>> mgr = urllib2.HTTPPasswordMgr()
114 >>> add = mgr.add_password
115 >>> add("Some Realm", "http://example.com/", "joe", "password")
116 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
117 >>> add("c", "http://example.com/foo", "foo", "ni")
118 >>> add("c", "http://example.com/bar", "bar", "nini")
119 >>> add("b", "http://example.com/", "first", "blah")
120 >>> add("b", "http://example.com/", "second", "spam")
121 >>> add("a", "http://example.com", "1", "a")
122 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
123 >>> add("Some Realm", "d.example.com", "4", "d")
124 >>> add("Some Realm", "e.example.com:3128", "5", "e")
125
126 >>> mgr.find_user_password("Some Realm", "example.com")
127 ('joe', 'password')
128 >>> mgr.find_user_password("Some Realm", "http://example.com")
129 ('joe', 'password')
130 >>> mgr.find_user_password("Some Realm", "http://example.com/")
131 ('joe', 'password')
132 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
133 ('joe', 'password')
134 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
135 ('joe', 'password')
136 >>> mgr.find_user_password("c", "http://example.com/foo")
137 ('foo', 'ni')
138 >>> mgr.find_user_password("c", "http://example.com/bar")
139 ('bar', 'nini')
140
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000141 Actually, this is really undefined ATM
142## Currently, we use the highest-level path where more than one match:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000144## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
145## ('joe', 'password')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146
147 Use latest add_password() in case of conflict:
148
149 >>> mgr.find_user_password("b", "http://example.com/")
150 ('second', 'spam')
151
152 No special relationship between a.example.com and example.com:
153
154 >>> mgr.find_user_password("a", "http://example.com/")
155 ('1', 'a')
156 >>> mgr.find_user_password("a", "http://a.example.com/")
157 (None, None)
158
159 Ports:
160
161 >>> mgr.find_user_password("Some Realm", "c.example.com")
162 (None, None)
163 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
164 ('3', 'c')
165 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
166 ('3', 'c')
167 >>> mgr.find_user_password("Some Realm", "d.example.com")
168 ('4', 'd')
169 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
170 ('5', 'e')
171
172 """
173 pass
174
175
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000176def test_password_manager_default_port(self):
177 """
178 >>> mgr = urllib2.HTTPPasswordMgr()
179 >>> add = mgr.add_password
180
181 The point to note here is that we can't guess the default port if there's
182 no scheme. This applies to both add_password and find_user_password.
183
184 >>> add("f", "http://g.example.com:80", "10", "j")
185 >>> add("g", "http://h.example.com", "11", "k")
186 >>> add("h", "i.example.com:80", "12", "l")
187 >>> add("i", "j.example.com", "13", "m")
188 >>> mgr.find_user_password("f", "g.example.com:100")
189 (None, None)
190 >>> mgr.find_user_password("f", "g.example.com:80")
191 ('10', 'j')
192 >>> mgr.find_user_password("f", "g.example.com")
193 (None, None)
194 >>> mgr.find_user_password("f", "http://g.example.com:100")
195 (None, None)
196 >>> mgr.find_user_password("f", "http://g.example.com:80")
197 ('10', 'j')
198 >>> mgr.find_user_password("f", "http://g.example.com")
199 ('10', 'j')
200 >>> mgr.find_user_password("g", "h.example.com")
201 ('11', 'k')
202 >>> mgr.find_user_password("g", "h.example.com:80")
203 ('11', 'k')
204 >>> mgr.find_user_password("g", "http://h.example.com:80")
205 ('11', 'k')
206 >>> mgr.find_user_password("h", "i.example.com")
207 (None, None)
208 >>> mgr.find_user_password("h", "i.example.com:80")
209 ('12', 'l')
210 >>> mgr.find_user_password("h", "http://i.example.com:80")
211 ('12', 'l')
212 >>> mgr.find_user_password("i", "j.example.com")
213 ('13', 'm')
214 >>> mgr.find_user_password("i", "j.example.com:80")
215 (None, None)
216 >>> mgr.find_user_password("i", "http://j.example.com")
217 ('13', 'm')
218 >>> mgr.find_user_password("i", "http://j.example.com:80")
219 (None, None)
220
221 """
222
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000223class MockOpener:
224 addheaders = []
225 def open(self, req, data=None):
226 self.req, self.data = req, data
227 def error(self, proto, *args):
228 self.proto, self.args = proto, args
229
230class MockFile:
231 def read(self, count=None): pass
232 def readline(self, count=None): pass
233 def close(self): pass
234
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000235class MockHeaders(dict):
236 def getheaders(self, name):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000237 return list(self.values())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000238
Guido van Rossum34d19282007-08-09 01:03:29 +0000239class MockResponse(io.StringIO):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000240 def __init__(self, code, msg, headers, data, url=None):
Guido van Rossum34d19282007-08-09 01:03:29 +0000241 io.StringIO.__init__(self, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000242 self.code, self.msg, self.headers, self.url = code, msg, headers, url
243 def info(self):
244 return self.headers
245 def geturl(self):
246 return self.url
247
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000248class MockCookieJar:
249 def add_cookie_header(self, request):
250 self.ach_req = request
251 def extract_cookies(self, response, request):
252 self.ec_req, self.ec_r = request, response
253
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000254class FakeMethod:
255 def __init__(self, meth_name, action, handle):
256 self.meth_name = meth_name
257 self.handle = handle
258 self.action = action
259 def __call__(self, *args):
260 return self.handle(self.meth_name, self.action, *args)
261
262class MockHandler:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000263 # useful for testing handler machinery
264 # see add_ordered_mock_handlers() docstring
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000265 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000266 def __init__(self, methods):
267 self._define_methods(methods)
268 def _define_methods(self, methods):
269 for spec in methods:
270 if len(spec) == 2: name, action = spec
271 else: name, action = spec, None
272 meth = FakeMethod(name, action, self.handle)
273 setattr(self.__class__, name, meth)
274 def handle(self, fn_name, action, *args, **kwds):
275 self.parent.calls.append((self, fn_name, args, kwds))
276 if action is None:
277 return None
278 elif action == "return self":
279 return self
280 elif action == "return response":
281 res = MockResponse(200, "OK", {}, "")
282 return res
283 elif action == "return request":
284 return Request("http://blah/")
285 elif action.startswith("error"):
286 code = action[action.rfind(" ")+1:]
287 try:
288 code = int(code)
289 except ValueError:
290 pass
291 res = MockResponse(200, "OK", {}, "")
292 return self.parent.error("http", args[0], res, code, "", {})
293 elif action == "raise":
294 raise urllib2.URLError("blah")
295 assert False
296 def close(self): pass
297 def add_parent(self, parent):
298 self.parent = parent
299 self.parent.calls = []
300 def __lt__(self, other):
301 if not hasattr(other, "handler_order"):
302 # No handler_order, leave in original order. Yuck.
303 return True
304 return self.handler_order < other.handler_order
305
306def add_ordered_mock_handlers(opener, meth_spec):
307 """Create MockHandlers and add them to an OpenerDirector.
308
309 meth_spec: list of lists of tuples and strings defining methods to define
310 on handlers. eg:
311
312 [["http_error", "ftp_open"], ["http_open"]]
313
314 defines methods .http_error() and .ftp_open() on one handler, and
315 .http_open() on another. These methods just record their arguments and
316 return None. Using a tuple instead of a string causes the method to
317 perform some action (see MockHandler.handle()), eg:
318
319 [["http_error"], [("http_open", "return request")]]
320
321 defines .http_error() on one handler (which simply returns None), and
322 .http_open() on another handler, which returns a Request object.
323
324 """
325 handlers = []
326 count = 0
327 for meths in meth_spec:
328 class MockHandlerSubclass(MockHandler): pass
329 h = MockHandlerSubclass(meths)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000330 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000331 h.add_parent(opener)
332 count = count + 1
333 handlers.append(h)
334 opener.add_handler(h)
335 return handlers
336
Thomas Wouters477c8d52006-05-27 19:21:47 +0000337def build_test_opener(*handler_instances):
338 opener = OpenerDirector()
339 for h in handler_instances:
340 opener.add_handler(h)
341 return opener
342
343class MockHTTPHandler(urllib2.BaseHandler):
344 # useful for testing redirections and auth
345 # sends supplied headers and code as first response
346 # sends 200 OK as second response
347 def __init__(self, code, headers):
348 self.code = code
349 self.headers = headers
350 self.reset()
351 def reset(self):
352 self._count = 0
353 self.requests = []
354 def http_open(self, req):
355 import mimetools, httplib, copy
Guido van Rossum34d19282007-08-09 01:03:29 +0000356 from io import StringIO
Thomas Wouters477c8d52006-05-27 19:21:47 +0000357 self.requests.append(copy.deepcopy(req))
358 if self._count == 0:
359 self._count = self._count + 1
360 name = httplib.responses[self.code]
361 msg = mimetools.Message(StringIO(self.headers))
362 return self.parent.error(
363 "http", req, MockFile(), self.code, name, msg)
364 else:
365 self.req = req
366 msg = mimetools.Message(StringIO("\r\n\r\n"))
367 return MockResponse(200, "OK", msg, "", req.get_full_url())
368
369class MockPasswordManager:
370 def add_password(self, realm, uri, user, password):
371 self.realm = realm
372 self.url = uri
373 self.user = user
374 self.password = password
375 def find_user_password(self, realm, authuri):
376 self.target_realm = realm
377 self.target_url = authuri
378 return self.user, self.password
379
380
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000381class OpenerDirectorTests(unittest.TestCase):
382
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000383 def test_add_non_handler(self):
384 class NonHandler(object):
385 pass
386 self.assertRaises(TypeError,
387 OpenerDirector().add_handler, NonHandler())
388
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000389 def test_badly_named_methods(self):
390 # test work-around for three methods that accidentally follow the
391 # naming conventions for handler methods
392 # (*_open() / *_request() / *_response())
393
394 # These used to call the accidentally-named methods, causing a
395 # TypeError in real code; here, returning self from these mock
396 # methods would either cause no exception, or AttributeError.
397
398 from urllib2 import URLError
399
400 o = OpenerDirector()
401 meth_spec = [
402 [("do_open", "return self"), ("proxy_open", "return self")],
403 [("redirect_request", "return self")],
404 ]
405 handlers = add_ordered_mock_handlers(o, meth_spec)
406 o.add_handler(urllib2.UnknownHandler())
407 for scheme in "do", "proxy", "redirect":
408 self.assertRaises(URLError, o.open, scheme+"://example.com/")
409
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000410 def test_handled(self):
411 # handler returning non-None means no more handlers will be called
412 o = OpenerDirector()
413 meth_spec = [
414 ["http_open", "ftp_open", "http_error_302"],
415 ["ftp_open"],
416 [("http_open", "return self")],
417 [("http_open", "return self")],
418 ]
419 handlers = add_ordered_mock_handlers(o, meth_spec)
420
421 req = Request("http://example.com/")
422 r = o.open(req)
423 # Second .http_open() gets called, third doesn't, since second returned
424 # non-None. Handlers without .http_open() never get any methods called
425 # on them.
426 # In fact, second mock handler defining .http_open() returns self
427 # (instead of response), which becomes the OpenerDirector's return
428 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000429 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000430 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
431 for expected, got in zip(calls, o.calls):
432 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000433 self.assertEqual((handler, name), expected)
434 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000435
436 def test_handler_order(self):
437 o = OpenerDirector()
438 handlers = []
439 for meths, handler_order in [
440 ([("http_open", "return self")], 500),
441 (["http_open"], 0),
442 ]:
443 class MockHandlerSubclass(MockHandler): pass
444 h = MockHandlerSubclass(meths)
445 h.handler_order = handler_order
446 handlers.append(h)
447 o.add_handler(h)
448
449 r = o.open("http://example.com/")
450 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000451 self.assertEqual(o.calls[0][0], handlers[1])
452 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000453
454 def test_raise(self):
455 # raising URLError stops processing of request
456 o = OpenerDirector()
457 meth_spec = [
458 [("http_open", "raise")],
459 [("http_open", "return self")],
460 ]
461 handlers = add_ordered_mock_handlers(o, meth_spec)
462
463 req = Request("http://example.com/")
464 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000465 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000466
467## def test_error(self):
468## # XXX this doesn't actually seem to be used in standard library,
469## # but should really be tested anyway...
470
471 def test_http_error(self):
472 # XXX http_error_default
473 # http errors are a special case
474 o = OpenerDirector()
475 meth_spec = [
476 [("http_open", "error 302")],
477 [("http_error_400", "raise"), "http_open"],
478 [("http_error_302", "return response"), "http_error_303",
479 "http_error"],
480 [("http_error_302")],
481 ]
482 handlers = add_ordered_mock_handlers(o, meth_spec)
483
484 class Unknown:
485 def __eq__(self, other): return True
486
487 req = Request("http://example.com/")
488 r = o.open(req)
489 assert len(o.calls) == 2
490 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000491 (handlers[2], "http_error_302",
492 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000493 for expected, got in zip(calls, o.calls):
494 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000495 self.assertEqual((handler, method_name), got[:2])
496 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000497
498 def test_processors(self):
499 # *_request / *_response methods get called appropriately
500 o = OpenerDirector()
501 meth_spec = [
502 [("http_request", "return request"),
503 ("http_response", "return response")],
504 [("http_request", "return request"),
505 ("http_response", "return response")],
506 ]
507 handlers = add_ordered_mock_handlers(o, meth_spec)
508
509 req = Request("http://example.com/")
510 r = o.open(req)
511 # processor methods are called on *all* handlers that define them,
512 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000513 calls = [
514 (handlers[0], "http_request"), (handlers[1], "http_request"),
515 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000516
517 for i, (handler, name, args, kwds) in enumerate(o.calls):
518 if i < 2:
519 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000520 self.assertEqual((handler, name), calls[i])
521 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000522 self.assert_(isinstance(args[0], Request))
523 else:
524 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000525 self.assertEqual((handler, name), calls[i])
526 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000527 self.assert_(isinstance(args[0], Request))
528 # response from opener.open is None, because there's no
529 # handler that defines http_open to handle it
530 self.assert_(args[1] is None or
531 isinstance(args[1], MockResponse))
532
533
Tim Peters58eb11c2004-01-18 20:29:55 +0000534def sanepathname2url(path):
535 import urllib
536 urlpath = urllib.pathname2url(path)
537 if os.name == "nt" and urlpath.startswith("///"):
538 urlpath = urlpath[2:]
539 # XXX don't ask me about the mac...
540 return urlpath
541
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000542class HandlerTests(unittest.TestCase):
543
544 def test_ftp(self):
545 class MockFTPWrapper:
546 def __init__(self, data): self.data = data
547 def retrfile(self, filename, filetype):
548 self.filename, self.filetype = filename, filetype
Guido van Rossum34d19282007-08-09 01:03:29 +0000549 return io.StringIO(self.data), len(self.data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000550
551 class NullFTPHandler(urllib2.FTPHandler):
552 def __init__(self, data): self.data = data
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000553 def connect_ftp(self, user, passwd, host, port, dirs, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000554 self.user, self.passwd = user, passwd
555 self.host, self.port = host, port
556 self.dirs = dirs
557 self.ftpwrapper = MockFTPWrapper(self.data)
558 return self.ftpwrapper
559
560 import ftplib, socket
561 data = "rheum rhaponicum"
562 h = NullFTPHandler(data)
563 o = h.parent = MockOpener()
564
565 for url, host, port, type_, dirs, filename, mimetype in [
566 ("ftp://localhost/foo/bar/baz.html",
567 "localhost", ftplib.FTP_PORT, "I",
568 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000569 ("ftp://localhost:80/foo/bar/",
570 "localhost", 80, "D",
571 ["foo", "bar"], "", None),
572 ("ftp://localhost/baz.gif;type=a",
573 "localhost", ftplib.FTP_PORT, "A",
574 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000575 ]:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000576 req = Request(url)
577 req.timeout = None
578 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000579 # ftp authentication not yet implemented by FTPHandler
580 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000581 self.assertEqual(h.host, socket.gethostbyname(host))
582 self.assertEqual(h.port, port)
583 self.assertEqual(h.dirs, dirs)
584 self.assertEqual(h.ftpwrapper.filename, filename)
585 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000586 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000587 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000588 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000589
590 def test_file(self):
591 import time, rfc822, socket
592 h = urllib2.FileHandler()
593 o = h.parent = MockOpener()
594
Tim Peters58eb11c2004-01-18 20:29:55 +0000595 TESTFN = test_support.TESTFN
596 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Guido van Rossum6a2ccd02007-07-16 20:51:57 +0000597 towrite = b"hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000598 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000599 "file://localhost%s" % urlpath,
600 "file://%s" % urlpath,
601 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000602 ]
603 try:
604 localaddr = socket.gethostbyname(socket.gethostname())
605 except socket.gaierror:
606 localaddr = ''
607 if localaddr:
608 urls.append("file://%s%s" % (localaddr, urlpath))
609
610 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000611 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000612 try:
613 try:
614 f.write(towrite)
615 finally:
616 f.close()
617
618 r = h.file_open(Request(url))
619 try:
620 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000621 headers = r.info()
622 newurl = r.geturl()
623 finally:
624 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000625 stats = os.stat(TESTFN)
626 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000627 finally:
628 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000629 self.assertEqual(data, towrite)
630 self.assertEqual(headers["Content-type"], "text/plain")
631 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000632 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000633
634 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000635 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000636 "file:///file_does_not_exist.txt",
637 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
638 os.getcwd(), TESTFN),
639 "file://somerandomhost.ontheinternet.com%s/%s" %
640 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000641 ]:
642 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000643 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000644 try:
645 f.write(towrite)
646 finally:
647 f.close()
648
649 self.assertRaises(urllib2.URLError,
650 h.file_open, Request(url))
651 finally:
652 os.remove(TESTFN)
653
654 h = urllib2.FileHandler()
655 o = h.parent = MockOpener()
656 # XXXX why does // mean ftp (and /// mean not ftp!), and where
657 # is file: scheme specified? I think this is really a bug, and
658 # what was intended was to distinguish between URLs like:
659 # file:/blah.txt (a file)
660 # file://localhost/blah.txt (a file)
661 # file:///blah.txt (a file)
662 # file://ftp.example.com/blah.txt (an ftp URL)
663 for url, ftp in [
664 ("file://ftp.example.com//foo.txt", True),
665 ("file://ftp.example.com///foo.txt", False),
666# XXXX bug: fails with OSError, should be URLError
667 ("file://ftp.example.com/foo.txt", False),
668 ]:
669 req = Request(url)
670 try:
671 h.file_open(req)
672 # XXXX remove OSError when bug fixed
673 except (urllib2.URLError, OSError):
674 self.assert_(not ftp)
675 else:
676 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000677 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000678
679 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000680 class MockHTTPResponse:
681 def __init__(self, fp, msg, status, reason):
682 self.fp = fp
683 self.msg = msg
684 self.status = status
685 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000686 def read(self):
687 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000688 class MockHTTPClass:
689 def __init__(self):
690 self.req_headers = []
691 self.data = None
692 self.raise_on_endheaders = False
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000693 def __call__(self, host, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000694 self.host = host
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000695 self.timeout = timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000696 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000697 def set_debuglevel(self, level):
698 self.level = level
699 def request(self, method, url, body=None, headers={}):
700 self.method = method
701 self.selector = url
702 self.req_headers += headers.items()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000703 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000704 if body:
705 self.data = body
706 if self.raise_on_endheaders:
707 import socket
708 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000709 def getresponse(self):
710 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000711
712 h = urllib2.AbstractHTTPHandler()
713 o = h.parent = MockOpener()
714
715 url = "http://example.com/"
716 for method, data in [("GET", None), ("POST", "blah")]:
717 req = Request(url, data, {"Foo": "bar"})
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000718 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000719 req.add_unredirected_header("Spam", "eggs")
720 http = MockHTTPClass()
721 r = h.do_open(http, req)
722
723 # result attributes
724 r.read; r.readline # wrapped MockFile methods
725 r.info; r.geturl # addinfourl methods
726 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
727 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000728 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000729 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000730
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000731 self.assertEqual(http.host, "example.com")
732 self.assertEqual(http.level, 0)
733 self.assertEqual(http.method, method)
734 self.assertEqual(http.selector, "/")
735 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000736 [("Connection", "close"),
737 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000738 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000739
740 # check socket.error converted to URLError
741 http.raise_on_endheaders = True
742 self.assertRaises(urllib2.URLError, h.do_open, http, req)
743
744 # check adding of standard headers
745 o.addheaders = [("Spam", "eggs")]
746 for data in "", None: # POST, GET
747 req = Request("http://example.com/", data)
748 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000749 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000750 if data is None: # GET
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000751 self.assert_("Content-length" not in req.unredirected_hdrs)
752 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000753 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000754 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
755 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000756 "application/x-www-form-urlencoded")
757 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000758 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
759 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000760
761 # don't clobber existing headers
762 req.add_unredirected_header("Content-length", "foo")
763 req.add_unredirected_header("Content-type", "bar")
764 req.add_unredirected_header("Host", "baz")
765 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000766 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000767 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
768 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000769 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
770 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000771
772 def test_errors(self):
773 h = urllib2.HTTPErrorProcessor()
774 o = h.parent = MockOpener()
775
776 url = "http://example.com/"
777 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000778 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000779 r = MockResponse(200, "OK", {}, "", 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
Guido van Rossumd8faa362007-04-27 19:54:29 +0000783 r = MockResponse(202, "Accepted", {}, "", 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
787 r = MockResponse(206, "Partial content", {}, "", url)
788 newr = h.http_response(req, r)
789 self.assert_(r is newr)
790 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000791 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000792 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000793 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000794 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000795 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000796
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000797 def test_cookies(self):
798 cj = MockCookieJar()
799 h = urllib2.HTTPCookieProcessor(cj)
800 o = h.parent = MockOpener()
801
802 req = Request("http://example.com/")
803 r = MockResponse(200, "OK", {}, "")
804 newreq = h.http_request(req)
805 self.assert_(cj.ach_req is req is newreq)
806 self.assertEquals(req.get_origin_req_host(), "example.com")
807 self.assert_(not req.is_unverifiable())
808 newr = h.http_response(req, r)
809 self.assert_(cj.ec_req is req)
810 self.assert_(cj.ec_r is r is newr)
811
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000812 def test_redirect(self):
813 from_url = "http://example.com/a.html"
814 to_url = "http://example.com/b.html"
815 h = urllib2.HTTPRedirectHandler()
816 o = h.parent = MockOpener()
817
818 # ordinary redirect behaviour
819 for code in 301, 302, 303, 307:
820 for data in None, "blah\nblah\n":
821 method = getattr(h, "http_error_%s" % code)
822 req = Request(from_url, data)
823 req.add_header("Nonsense", "viking=withhold")
824 req.add_unredirected_header("Spam", "spam")
825 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000826 method(req, MockFile(), code, "Blah",
827 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000828 except urllib2.HTTPError:
829 # 307 in response to POST requires user OK
830 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000831 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000832 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000833 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000834 except AttributeError:
835 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000836 self.assertEqual(o.req.headers["Nonsense"],
837 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000838 self.assert_("Spam" not in o.req.headers)
839 self.assert_("Spam" not in o.req.unredirected_hdrs)
840
841 # loop detection
842 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000843 def redirect(h, req, url=to_url):
844 h.http_error_302(req, MockFile(), 302, "Blah",
845 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000846 # Note that the *original* request shares the same record of
847 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000848
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000849 # detect infinite loop redirect of a URL to itself
850 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000851 count = 0
852 try:
853 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000854 redirect(h, req, "http://example.com/")
855 count = count + 1
856 except urllib2.HTTPError:
857 # don't stop until max_repeats, because cookies may introduce state
858 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
859
860 # detect endless non-repeating chain of redirects
861 req = Request(from_url, origin_req_host="example.com")
862 count = 0
863 try:
864 while 1:
865 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000866 count = count + 1
867 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000868 self.assertEqual(count,
869 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000870
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000871 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000872 # cookies shouldn't leak into redirected requests
873 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000874
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000875 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000876
877 cj = CookieJar()
878 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000879 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
880 hdeh = urllib2.HTTPDefaultErrorHandler()
881 hrh = urllib2.HTTPRedirectHandler()
882 cp = urllib2.HTTPCookieProcessor(cj)
883 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000884 o.open("http://www.example.com/")
885 self.assert_(not hh.req.has_header("Cookie"))
886
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000887 def test_proxy(self):
888 o = OpenerDirector()
889 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
890 o.add_handler(ph)
891 meth_spec = [
892 [("http_open", "return response")]
893 ]
894 handlers = add_ordered_mock_handlers(o, meth_spec)
895
896 req = Request("http://acme.example.com/")
897 self.assertEqual(req.get_host(), "acme.example.com")
898 r = o.open(req)
899 self.assertEqual(req.get_host(), "proxy.example.com:3128")
900
901 self.assertEqual([(handlers[0], "http_open")],
902 [tup[0:2] for tup in o.calls])
903
Thomas Wouters477c8d52006-05-27 19:21:47 +0000904 def test_basic_auth(self):
905 opener = OpenerDirector()
906 password_manager = MockPasswordManager()
907 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
908 realm = "ACME Widget Store"
909 http_handler = MockHTTPHandler(
910 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000911 opener.add_handler(auth_handler)
912 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000913 self._test_basic_auth(opener, auth_handler, "Authorization",
914 realm, http_handler, password_manager,
915 "http://acme.example.com/protected",
916 "http://acme.example.com/protected",
917 )
918
919 def test_proxy_basic_auth(self):
920 opener = OpenerDirector()
921 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
922 opener.add_handler(ph)
923 password_manager = MockPasswordManager()
924 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
925 realm = "ACME Networks"
926 http_handler = MockHTTPHandler(
927 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000928 opener.add_handler(auth_handler)
929 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000930 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000931 realm, http_handler, password_manager,
932 "http://acme.example.com:3128/protected",
933 "proxy.example.com:3128",
934 )
935
936 def test_basic_and_digest_auth_handlers(self):
937 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
938 # response (http://python.org/sf/1479302), where it should instead
939 # return None to allow another handler (especially
940 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000941
942 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
943 # try digest first (since it's the strongest auth scheme), so we record
944 # order of calls here to check digest comes first:
945 class RecordingOpenerDirector(OpenerDirector):
946 def __init__(self):
947 OpenerDirector.__init__(self)
948 self.recorded = []
949 def record(self, info):
950 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000951 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000952 def http_error_401(self, *args, **kwds):
953 self.parent.record("digest")
954 urllib2.HTTPDigestAuthHandler.http_error_401(self,
955 *args, **kwds)
956 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
957 def http_error_401(self, *args, **kwds):
958 self.parent.record("basic")
959 urllib2.HTTPBasicAuthHandler.http_error_401(self,
960 *args, **kwds)
961
962 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000963 password_manager = MockPasswordManager()
964 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000965 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000966 realm = "ACME Networks"
967 http_handler = MockHTTPHandler(
968 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000969 opener.add_handler(basic_handler)
970 opener.add_handler(digest_handler)
971 opener.add_handler(http_handler)
972
973 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000974 self._test_basic_auth(opener, basic_handler, "Authorization",
975 realm, http_handler, password_manager,
976 "http://acme.example.com/protected",
977 "http://acme.example.com/protected",
978 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000979 # check digest was tried before basic (twice, because
980 # _test_basic_auth called .open() twice)
981 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000982
983 def _test_basic_auth(self, opener, auth_handler, auth_header,
984 realm, http_handler, password_manager,
985 request_url, protected_url):
986 import base64, httplib
987 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000988
989 # .add_password() fed through to password manager
990 auth_handler.add_password(realm, request_url, user, password)
991 self.assertEqual(realm, password_manager.realm)
992 self.assertEqual(request_url, password_manager.url)
993 self.assertEqual(user, password_manager.user)
994 self.assertEqual(password, password_manager.password)
995
996 r = opener.open(request_url)
997
998 # should have asked the password manager for the username/password
999 self.assertEqual(password_manager.target_realm, realm)
1000 self.assertEqual(password_manager.target_url, protected_url)
1001
1002 # expect one request without authorization, then one with
1003 self.assertEqual(len(http_handler.requests), 2)
1004 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1005 userpass = '%s:%s' % (user, password)
Guido van Rossumc0f2d2d2007-08-03 19:19:24 +00001006 auth_hdr_value = 'Basic ' + str(base64.encodestring(userpass)).strip()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001007 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1008 auth_hdr_value)
1009
1010 # if the password manager can't find a password, the handler won't
1011 # handle the HTTP auth error
1012 password_manager.user = password_manager.password = None
1013 http_handler.reset()
1014 r = opener.open(request_url)
1015 self.assertEqual(len(http_handler.requests), 1)
1016 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1017
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001018
1019class MiscTests(unittest.TestCase):
1020
1021 def test_build_opener(self):
1022 class MyHTTPHandler(urllib2.HTTPHandler): pass
1023 class FooHandler(urllib2.BaseHandler):
1024 def foo_open(self): pass
1025 class BarHandler(urllib2.BaseHandler):
1026 def bar_open(self): pass
1027
1028 build_opener = urllib2.build_opener
1029
1030 o = build_opener(FooHandler, BarHandler)
1031 self.opener_has_handler(o, FooHandler)
1032 self.opener_has_handler(o, BarHandler)
1033
1034 # can take a mix of classes and instances
1035 o = build_opener(FooHandler, BarHandler())
1036 self.opener_has_handler(o, FooHandler)
1037 self.opener_has_handler(o, BarHandler)
1038
1039 # subclasses of default handlers override default handlers
1040 o = build_opener(MyHTTPHandler)
1041 self.opener_has_handler(o, MyHTTPHandler)
1042
1043 # a particular case of overriding: default handlers can be passed
1044 # in explicitly
1045 o = build_opener()
1046 self.opener_has_handler(o, urllib2.HTTPHandler)
1047 o = build_opener(urllib2.HTTPHandler)
1048 self.opener_has_handler(o, urllib2.HTTPHandler)
1049 o = build_opener(urllib2.HTTPHandler())
1050 self.opener_has_handler(o, urllib2.HTTPHandler)
1051
1052 def opener_has_handler(self, opener, handler_class):
1053 for h in opener.handlers:
1054 if h.__class__ == handler_class:
1055 break
1056 else:
1057 self.assert_(False)
1058
1059
1060def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001061 from test import test_urllib2
1062 test_support.run_doctest(test_urllib2, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001063 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001064 tests = (TrivialTests,
1065 OpenerDirectorTests,
1066 HandlerTests,
1067 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001068 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001069
1070if __name__ == "__main__":
1071 test_main(verbose=True)