blob: 10d8c46dea6bed54508eec3b59e55e814c853272 [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
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)
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
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000239class MockResponse(StringIO.StringIO):
240 def __init__(self, code, msg, headers, data, url=None):
241 StringIO.StringIO.__init__(self, data)
242 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
356 from StringIO import StringIO
357 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
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000383 def test_badly_named_methods(self):
384 # test work-around for three methods that accidentally follow the
385 # naming conventions for handler methods
386 # (*_open() / *_request() / *_response())
387
388 # These used to call the accidentally-named methods, causing a
389 # TypeError in real code; here, returning self from these mock
390 # methods would either cause no exception, or AttributeError.
391
392 from urllib2 import URLError
393
394 o = OpenerDirector()
395 meth_spec = [
396 [("do_open", "return self"), ("proxy_open", "return self")],
397 [("redirect_request", "return self")],
398 ]
399 handlers = add_ordered_mock_handlers(o, meth_spec)
400 o.add_handler(urllib2.UnknownHandler())
401 for scheme in "do", "proxy", "redirect":
402 self.assertRaises(URLError, o.open, scheme+"://example.com/")
403
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000404 def test_handled(self):
405 # handler returning non-None means no more handlers will be called
406 o = OpenerDirector()
407 meth_spec = [
408 ["http_open", "ftp_open", "http_error_302"],
409 ["ftp_open"],
410 [("http_open", "return self")],
411 [("http_open", "return self")],
412 ]
413 handlers = add_ordered_mock_handlers(o, meth_spec)
414
415 req = Request("http://example.com/")
416 r = o.open(req)
417 # Second .http_open() gets called, third doesn't, since second returned
418 # non-None. Handlers without .http_open() never get any methods called
419 # on them.
420 # In fact, second mock handler defining .http_open() returns self
421 # (instead of response), which becomes the OpenerDirector's return
422 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000423 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000424 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
425 for expected, got in zip(calls, o.calls):
426 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000427 self.assertEqual((handler, name), expected)
428 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000429
430 def test_handler_order(self):
431 o = OpenerDirector()
432 handlers = []
433 for meths, handler_order in [
434 ([("http_open", "return self")], 500),
435 (["http_open"], 0),
436 ]:
437 class MockHandlerSubclass(MockHandler): pass
438 h = MockHandlerSubclass(meths)
439 h.handler_order = handler_order
440 handlers.append(h)
441 o.add_handler(h)
442
443 r = o.open("http://example.com/")
444 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000445 self.assertEqual(o.calls[0][0], handlers[1])
446 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000447
448 def test_raise(self):
449 # raising URLError stops processing of request
450 o = OpenerDirector()
451 meth_spec = [
452 [("http_open", "raise")],
453 [("http_open", "return self")],
454 ]
455 handlers = add_ordered_mock_handlers(o, meth_spec)
456
457 req = Request("http://example.com/")
458 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000459 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000460
461## def test_error(self):
462## # XXX this doesn't actually seem to be used in standard library,
463## # but should really be tested anyway...
464
465 def test_http_error(self):
466 # XXX http_error_default
467 # http errors are a special case
468 o = OpenerDirector()
469 meth_spec = [
470 [("http_open", "error 302")],
471 [("http_error_400", "raise"), "http_open"],
472 [("http_error_302", "return response"), "http_error_303",
473 "http_error"],
474 [("http_error_302")],
475 ]
476 handlers = add_ordered_mock_handlers(o, meth_spec)
477
478 class Unknown:
479 def __eq__(self, other): return True
480
481 req = Request("http://example.com/")
482 r = o.open(req)
483 assert len(o.calls) == 2
484 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000485 (handlers[2], "http_error_302",
486 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000487 for expected, got in zip(calls, o.calls):
488 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000489 self.assertEqual((handler, method_name), got[:2])
490 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000491
492 def test_processors(self):
493 # *_request / *_response methods get called appropriately
494 o = OpenerDirector()
495 meth_spec = [
496 [("http_request", "return request"),
497 ("http_response", "return response")],
498 [("http_request", "return request"),
499 ("http_response", "return response")],
500 ]
501 handlers = add_ordered_mock_handlers(o, meth_spec)
502
503 req = Request("http://example.com/")
504 r = o.open(req)
505 # processor methods are called on *all* handlers that define them,
506 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000507 calls = [
508 (handlers[0], "http_request"), (handlers[1], "http_request"),
509 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000510
511 for i, (handler, name, args, kwds) in enumerate(o.calls):
512 if i < 2:
513 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000514 self.assertEqual((handler, name), calls[i])
515 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000516 self.assert_(isinstance(args[0], Request))
517 else:
518 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000519 self.assertEqual((handler, name), calls[i])
520 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000521 self.assert_(isinstance(args[0], Request))
522 # response from opener.open is None, because there's no
523 # handler that defines http_open to handle it
524 self.assert_(args[1] is None or
525 isinstance(args[1], MockResponse))
526
527
Tim Peters58eb11c2004-01-18 20:29:55 +0000528def sanepathname2url(path):
529 import urllib
530 urlpath = urllib.pathname2url(path)
531 if os.name == "nt" and urlpath.startswith("///"):
532 urlpath = urlpath[2:]
533 # XXX don't ask me about the mac...
534 return urlpath
535
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000536class HandlerTests(unittest.TestCase):
537
538 def test_ftp(self):
539 class MockFTPWrapper:
540 def __init__(self, data): self.data = data
541 def retrfile(self, filename, filetype):
542 self.filename, self.filetype = filename, filetype
543 return StringIO.StringIO(self.data), len(self.data)
544
545 class NullFTPHandler(urllib2.FTPHandler):
546 def __init__(self, data): self.data = data
547 def connect_ftp(self, user, passwd, host, port, dirs):
548 self.user, self.passwd = user, passwd
549 self.host, self.port = host, port
550 self.dirs = dirs
551 self.ftpwrapper = MockFTPWrapper(self.data)
552 return self.ftpwrapper
553
554 import ftplib, socket
555 data = "rheum rhaponicum"
556 h = NullFTPHandler(data)
557 o = h.parent = MockOpener()
558
559 for url, host, port, type_, dirs, filename, mimetype in [
560 ("ftp://localhost/foo/bar/baz.html",
561 "localhost", ftplib.FTP_PORT, "I",
562 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000563 ("ftp://localhost:80/foo/bar/",
564 "localhost", 80, "D",
565 ["foo", "bar"], "", None),
566 ("ftp://localhost/baz.gif;type=a",
567 "localhost", ftplib.FTP_PORT, "A",
568 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000569 ]:
570 r = h.ftp_open(Request(url))
571 # ftp authentication not yet implemented by FTPHandler
572 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000573 self.assertEqual(h.host, socket.gethostbyname(host))
574 self.assertEqual(h.port, port)
575 self.assertEqual(h.dirs, dirs)
576 self.assertEqual(h.ftpwrapper.filename, filename)
577 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000578 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000579 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000580 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000581
582 def test_file(self):
583 import time, rfc822, socket
584 h = urllib2.FileHandler()
585 o = h.parent = MockOpener()
586
Tim Peters58eb11c2004-01-18 20:29:55 +0000587 TESTFN = test_support.TESTFN
588 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000589 towrite = "hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000590 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000591 "file://localhost%s" % urlpath,
592 "file://%s" % urlpath,
593 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000594 ]
595 try:
596 localaddr = socket.gethostbyname(socket.gethostname())
597 except socket.gaierror:
598 localaddr = ''
599 if localaddr:
600 urls.append("file://%s%s" % (localaddr, urlpath))
601
602 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000603 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000604 try:
605 try:
606 f.write(towrite)
607 finally:
608 f.close()
609
610 r = h.file_open(Request(url))
611 try:
612 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000613 headers = r.info()
614 newurl = r.geturl()
615 finally:
616 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000617 stats = os.stat(TESTFN)
618 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000619 finally:
620 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000621 self.assertEqual(data, towrite)
622 self.assertEqual(headers["Content-type"], "text/plain")
623 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000624 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000625
626 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000627 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000628 "file:///file_does_not_exist.txt",
629 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
630 os.getcwd(), TESTFN),
631 "file://somerandomhost.ontheinternet.com%s/%s" %
632 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000633 ]:
634 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000635 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000636 try:
637 f.write(towrite)
638 finally:
639 f.close()
640
641 self.assertRaises(urllib2.URLError,
642 h.file_open, Request(url))
643 finally:
644 os.remove(TESTFN)
645
646 h = urllib2.FileHandler()
647 o = h.parent = MockOpener()
648 # XXXX why does // mean ftp (and /// mean not ftp!), and where
649 # is file: scheme specified? I think this is really a bug, and
650 # what was intended was to distinguish between URLs like:
651 # file:/blah.txt (a file)
652 # file://localhost/blah.txt (a file)
653 # file:///blah.txt (a file)
654 # file://ftp.example.com/blah.txt (an ftp URL)
655 for url, ftp in [
656 ("file://ftp.example.com//foo.txt", True),
657 ("file://ftp.example.com///foo.txt", False),
658# XXXX bug: fails with OSError, should be URLError
659 ("file://ftp.example.com/foo.txt", False),
660 ]:
661 req = Request(url)
662 try:
663 h.file_open(req)
664 # XXXX remove OSError when bug fixed
665 except (urllib2.URLError, OSError):
666 self.assert_(not ftp)
667 else:
668 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000669 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000670
671 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000672 class MockHTTPResponse:
673 def __init__(self, fp, msg, status, reason):
674 self.fp = fp
675 self.msg = msg
676 self.status = status
677 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000678 def read(self):
679 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000680 class MockHTTPClass:
681 def __init__(self):
682 self.req_headers = []
683 self.data = None
684 self.raise_on_endheaders = False
685 def __call__(self, host):
686 self.host = host
687 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000688 def set_debuglevel(self, level):
689 self.level = level
690 def request(self, method, url, body=None, headers={}):
691 self.method = method
692 self.selector = url
693 self.req_headers += headers.items()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000694 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000695 if body:
696 self.data = body
697 if self.raise_on_endheaders:
698 import socket
699 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000700 def getresponse(self):
701 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000702
703 h = urllib2.AbstractHTTPHandler()
704 o = h.parent = MockOpener()
705
706 url = "http://example.com/"
707 for method, data in [("GET", None), ("POST", "blah")]:
708 req = Request(url, data, {"Foo": "bar"})
709 req.add_unredirected_header("Spam", "eggs")
710 http = MockHTTPClass()
711 r = h.do_open(http, req)
712
713 # result attributes
714 r.read; r.readline # wrapped MockFile methods
715 r.info; r.geturl # addinfourl methods
716 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
717 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000718 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000719 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000720
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000721 self.assertEqual(http.host, "example.com")
722 self.assertEqual(http.level, 0)
723 self.assertEqual(http.method, method)
724 self.assertEqual(http.selector, "/")
725 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000726 [("Connection", "close"),
727 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000728 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000729
730 # check socket.error converted to URLError
731 http.raise_on_endheaders = True
732 self.assertRaises(urllib2.URLError, h.do_open, http, req)
733
734 # check adding of standard headers
735 o.addheaders = [("Spam", "eggs")]
736 for data in "", None: # POST, GET
737 req = Request("http://example.com/", data)
738 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000739 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000740 if data is None: # GET
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000741 self.assert_("Content-length" not in req.unredirected_hdrs)
742 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000743 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000744 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
745 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000746 "application/x-www-form-urlencoded")
747 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000748 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
749 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000750
751 # don't clobber existing headers
752 req.add_unredirected_header("Content-length", "foo")
753 req.add_unredirected_header("Content-type", "bar")
754 req.add_unredirected_header("Host", "baz")
755 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000756 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000757 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
758 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000759 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
760 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000761
762 def test_errors(self):
763 h = urllib2.HTTPErrorProcessor()
764 o = h.parent = MockOpener()
765
766 url = "http://example.com/"
767 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000768 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000769 r = MockResponse(200, "OK", {}, "", url)
770 newr = h.http_response(req, r)
771 self.assert_(r is newr)
772 self.assert_(not hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000773 r = MockResponse(202, "Accepted", {}, "", url)
774 newr = h.http_response(req, r)
775 self.assert_(r is newr)
776 self.assert_(not hasattr(o, "proto")) # o.error not called
777 r = MockResponse(206, "Partial content", {}, "", url)
778 newr = h.http_response(req, r)
779 self.assert_(r is newr)
780 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000781 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000782 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000783 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000784 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000785 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000786
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000787 def test_cookies(self):
788 cj = MockCookieJar()
789 h = urllib2.HTTPCookieProcessor(cj)
790 o = h.parent = MockOpener()
791
792 req = Request("http://example.com/")
793 r = MockResponse(200, "OK", {}, "")
794 newreq = h.http_request(req)
795 self.assert_(cj.ach_req is req is newreq)
796 self.assertEquals(req.get_origin_req_host(), "example.com")
797 self.assert_(not req.is_unverifiable())
798 newr = h.http_response(req, r)
799 self.assert_(cj.ec_req is req)
800 self.assert_(cj.ec_r is r is newr)
801
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000802 def test_redirect(self):
803 from_url = "http://example.com/a.html"
804 to_url = "http://example.com/b.html"
805 h = urllib2.HTTPRedirectHandler()
806 o = h.parent = MockOpener()
807
808 # ordinary redirect behaviour
809 for code in 301, 302, 303, 307:
810 for data in None, "blah\nblah\n":
811 method = getattr(h, "http_error_%s" % code)
812 req = Request(from_url, data)
813 req.add_header("Nonsense", "viking=withhold")
814 req.add_unredirected_header("Spam", "spam")
815 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000816 method(req, MockFile(), code, "Blah",
817 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000818 except urllib2.HTTPError:
819 # 307 in response to POST requires user OK
820 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000821 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000822 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000823 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000824 except AttributeError:
825 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000826 self.assertEqual(o.req.headers["Nonsense"],
827 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000828 self.assert_("Spam" not in o.req.headers)
829 self.assert_("Spam" not in o.req.unredirected_hdrs)
830
831 # loop detection
832 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000833 def redirect(h, req, url=to_url):
834 h.http_error_302(req, MockFile(), 302, "Blah",
835 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000836 # Note that the *original* request shares the same record of
837 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000838
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000839 # detect infinite loop redirect of a URL to itself
840 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000841 count = 0
842 try:
843 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000844 redirect(h, req, "http://example.com/")
845 count = count + 1
846 except urllib2.HTTPError:
847 # don't stop until max_repeats, because cookies may introduce state
848 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
849
850 # detect endless non-repeating chain of redirects
851 req = Request(from_url, origin_req_host="example.com")
852 count = 0
853 try:
854 while 1:
855 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000856 count = count + 1
857 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000858 self.assertEqual(count,
859 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000860
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000861 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000862 # cookies shouldn't leak into redirected requests
863 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000864
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000865 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000866
867 cj = CookieJar()
868 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000869 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
870 hdeh = urllib2.HTTPDefaultErrorHandler()
871 hrh = urllib2.HTTPRedirectHandler()
872 cp = urllib2.HTTPCookieProcessor(cj)
873 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000874 o.open("http://www.example.com/")
875 self.assert_(not hh.req.has_header("Cookie"))
876
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000877 def test_proxy(self):
878 o = OpenerDirector()
879 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
880 o.add_handler(ph)
881 meth_spec = [
882 [("http_open", "return response")]
883 ]
884 handlers = add_ordered_mock_handlers(o, meth_spec)
885
886 req = Request("http://acme.example.com/")
887 self.assertEqual(req.get_host(), "acme.example.com")
888 r = o.open(req)
889 self.assertEqual(req.get_host(), "proxy.example.com:3128")
890
891 self.assertEqual([(handlers[0], "http_open")],
892 [tup[0:2] for tup in o.calls])
893
Thomas Wouters477c8d52006-05-27 19:21:47 +0000894 def test_basic_auth(self):
895 opener = OpenerDirector()
896 password_manager = MockPasswordManager()
897 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
898 realm = "ACME Widget Store"
899 http_handler = MockHTTPHandler(
900 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000901 opener.add_handler(auth_handler)
902 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000903 self._test_basic_auth(opener, auth_handler, "Authorization",
904 realm, http_handler, password_manager,
905 "http://acme.example.com/protected",
906 "http://acme.example.com/protected",
907 )
908
909 def test_proxy_basic_auth(self):
910 opener = OpenerDirector()
911 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
912 opener.add_handler(ph)
913 password_manager = MockPasswordManager()
914 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
915 realm = "ACME Networks"
916 http_handler = MockHTTPHandler(
917 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000918 opener.add_handler(auth_handler)
919 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000920 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000921 realm, http_handler, password_manager,
922 "http://acme.example.com:3128/protected",
923 "proxy.example.com:3128",
924 )
925
926 def test_basic_and_digest_auth_handlers(self):
927 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
928 # response (http://python.org/sf/1479302), where it should instead
929 # return None to allow another handler (especially
930 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000931
932 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
933 # try digest first (since it's the strongest auth scheme), so we record
934 # order of calls here to check digest comes first:
935 class RecordingOpenerDirector(OpenerDirector):
936 def __init__(self):
937 OpenerDirector.__init__(self)
938 self.recorded = []
939 def record(self, info):
940 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000941 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000942 def http_error_401(self, *args, **kwds):
943 self.parent.record("digest")
944 urllib2.HTTPDigestAuthHandler.http_error_401(self,
945 *args, **kwds)
946 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
947 def http_error_401(self, *args, **kwds):
948 self.parent.record("basic")
949 urllib2.HTTPBasicAuthHandler.http_error_401(self,
950 *args, **kwds)
951
952 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000953 password_manager = MockPasswordManager()
954 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000955 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000956 realm = "ACME Networks"
957 http_handler = MockHTTPHandler(
958 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000959 opener.add_handler(basic_handler)
960 opener.add_handler(digest_handler)
961 opener.add_handler(http_handler)
962
963 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000964 self._test_basic_auth(opener, basic_handler, "Authorization",
965 realm, http_handler, password_manager,
966 "http://acme.example.com/protected",
967 "http://acme.example.com/protected",
968 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000969 # check digest was tried before basic (twice, because
970 # _test_basic_auth called .open() twice)
971 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000972
973 def _test_basic_auth(self, opener, auth_handler, auth_header,
974 realm, http_handler, password_manager,
975 request_url, protected_url):
976 import base64, httplib
977 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978
979 # .add_password() fed through to password manager
980 auth_handler.add_password(realm, request_url, user, password)
981 self.assertEqual(realm, password_manager.realm)
982 self.assertEqual(request_url, password_manager.url)
983 self.assertEqual(user, password_manager.user)
984 self.assertEqual(password, password_manager.password)
985
986 r = opener.open(request_url)
987
988 # should have asked the password manager for the username/password
989 self.assertEqual(password_manager.target_realm, realm)
990 self.assertEqual(password_manager.target_url, protected_url)
991
992 # expect one request without authorization, then one with
993 self.assertEqual(len(http_handler.requests), 2)
994 self.assertFalse(http_handler.requests[0].has_header(auth_header))
995 userpass = '%s:%s' % (user, password)
996 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
997 self.assertEqual(http_handler.requests[1].get_header(auth_header),
998 auth_hdr_value)
999
1000 # if the password manager can't find a password, the handler won't
1001 # handle the HTTP auth error
1002 password_manager.user = password_manager.password = None
1003 http_handler.reset()
1004 r = opener.open(request_url)
1005 self.assertEqual(len(http_handler.requests), 1)
1006 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1007
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001008
1009class MiscTests(unittest.TestCase):
1010
1011 def test_build_opener(self):
1012 class MyHTTPHandler(urllib2.HTTPHandler): pass
1013 class FooHandler(urllib2.BaseHandler):
1014 def foo_open(self): pass
1015 class BarHandler(urllib2.BaseHandler):
1016 def bar_open(self): pass
1017
1018 build_opener = urllib2.build_opener
1019
1020 o = build_opener(FooHandler, BarHandler)
1021 self.opener_has_handler(o, FooHandler)
1022 self.opener_has_handler(o, BarHandler)
1023
1024 # can take a mix of classes and instances
1025 o = build_opener(FooHandler, BarHandler())
1026 self.opener_has_handler(o, FooHandler)
1027 self.opener_has_handler(o, BarHandler)
1028
1029 # subclasses of default handlers override default handlers
1030 o = build_opener(MyHTTPHandler)
1031 self.opener_has_handler(o, MyHTTPHandler)
1032
1033 # a particular case of overriding: default handlers can be passed
1034 # in explicitly
1035 o = build_opener()
1036 self.opener_has_handler(o, urllib2.HTTPHandler)
1037 o = build_opener(urllib2.HTTPHandler)
1038 self.opener_has_handler(o, urllib2.HTTPHandler)
1039 o = build_opener(urllib2.HTTPHandler())
1040 self.opener_has_handler(o, urllib2.HTTPHandler)
1041
1042 def opener_has_handler(self, opener, handler_class):
1043 for h in opener.handlers:
1044 if h.__class__ == handler_class:
1045 break
1046 else:
1047 self.assert_(False)
1048
1049
1050def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051 from test import test_urllib2
1052 test_support.run_doctest(test_urllib2, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001053 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001054 tests = (TrivialTests,
1055 OpenerDirectorTests,
1056 HandlerTests,
1057 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001058 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001059
1060if __name__ == "__main__":
1061 test_main(verbose=True)