blob: dbcfe26de3ca518100eb401b947fcbc2a644b54d [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
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000547 def connect_ftp(self, user, passwd, host, port, dirs, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000548 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 ]:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000570 req = Request(url)
571 req.timeout = None
572 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000573 # ftp authentication not yet implemented by FTPHandler
574 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000575 self.assertEqual(h.host, socket.gethostbyname(host))
576 self.assertEqual(h.port, port)
577 self.assertEqual(h.dirs, dirs)
578 self.assertEqual(h.ftpwrapper.filename, filename)
579 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000580 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000581 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000582 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000583
584 def test_file(self):
585 import time, rfc822, socket
586 h = urllib2.FileHandler()
587 o = h.parent = MockOpener()
588
Tim Peters58eb11c2004-01-18 20:29:55 +0000589 TESTFN = test_support.TESTFN
590 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000591 towrite = "hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000592 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000593 "file://localhost%s" % urlpath,
594 "file://%s" % urlpath,
595 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000596 ]
597 try:
598 localaddr = socket.gethostbyname(socket.gethostname())
599 except socket.gaierror:
600 localaddr = ''
601 if localaddr:
602 urls.append("file://%s%s" % (localaddr, urlpath))
603
604 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000605 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000606 try:
607 try:
608 f.write(towrite)
609 finally:
610 f.close()
611
612 r = h.file_open(Request(url))
613 try:
614 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000615 headers = r.info()
616 newurl = r.geturl()
617 finally:
618 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000619 stats = os.stat(TESTFN)
620 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000621 finally:
622 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000623 self.assertEqual(data, towrite)
624 self.assertEqual(headers["Content-type"], "text/plain")
625 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000626 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000627
628 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000629 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000630 "file:///file_does_not_exist.txt",
631 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
632 os.getcwd(), TESTFN),
633 "file://somerandomhost.ontheinternet.com%s/%s" %
634 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000635 ]:
636 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000637 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000638 try:
639 f.write(towrite)
640 finally:
641 f.close()
642
643 self.assertRaises(urllib2.URLError,
644 h.file_open, Request(url))
645 finally:
646 os.remove(TESTFN)
647
648 h = urllib2.FileHandler()
649 o = h.parent = MockOpener()
650 # XXXX why does // mean ftp (and /// mean not ftp!), and where
651 # is file: scheme specified? I think this is really a bug, and
652 # what was intended was to distinguish between URLs like:
653 # file:/blah.txt (a file)
654 # file://localhost/blah.txt (a file)
655 # file:///blah.txt (a file)
656 # file://ftp.example.com/blah.txt (an ftp URL)
657 for url, ftp in [
658 ("file://ftp.example.com//foo.txt", True),
659 ("file://ftp.example.com///foo.txt", False),
660# XXXX bug: fails with OSError, should be URLError
661 ("file://ftp.example.com/foo.txt", False),
662 ]:
663 req = Request(url)
664 try:
665 h.file_open(req)
666 # XXXX remove OSError when bug fixed
667 except (urllib2.URLError, OSError):
668 self.assert_(not ftp)
669 else:
670 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000671 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000672
673 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000674 class MockHTTPResponse:
675 def __init__(self, fp, msg, status, reason):
676 self.fp = fp
677 self.msg = msg
678 self.status = status
679 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000680 def read(self):
681 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000682 class MockHTTPClass:
683 def __init__(self):
684 self.req_headers = []
685 self.data = None
686 self.raise_on_endheaders = False
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000687 def __call__(self, host, timeout=None):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000688 self.host = host
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000689 self.timeout = timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000690 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000691 def set_debuglevel(self, level):
692 self.level = level
693 def request(self, method, url, body=None, headers={}):
694 self.method = method
695 self.selector = url
696 self.req_headers += headers.items()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000697 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000698 if body:
699 self.data = body
700 if self.raise_on_endheaders:
701 import socket
702 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000703 def getresponse(self):
704 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000705
706 h = urllib2.AbstractHTTPHandler()
707 o = h.parent = MockOpener()
708
709 url = "http://example.com/"
710 for method, data in [("GET", None), ("POST", "blah")]:
711 req = Request(url, data, {"Foo": "bar"})
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000712 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000713 req.add_unredirected_header("Spam", "eggs")
714 http = MockHTTPClass()
715 r = h.do_open(http, req)
716
717 # result attributes
718 r.read; r.readline # wrapped MockFile methods
719 r.info; r.geturl # addinfourl methods
720 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
721 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000722 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000723 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000724
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000725 self.assertEqual(http.host, "example.com")
726 self.assertEqual(http.level, 0)
727 self.assertEqual(http.method, method)
728 self.assertEqual(http.selector, "/")
729 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000730 [("Connection", "close"),
731 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000732 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000733
734 # check socket.error converted to URLError
735 http.raise_on_endheaders = True
736 self.assertRaises(urllib2.URLError, h.do_open, http, req)
737
738 # check adding of standard headers
739 o.addheaders = [("Spam", "eggs")]
740 for data in "", None: # POST, GET
741 req = Request("http://example.com/", data)
742 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000743 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000744 if data is None: # GET
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000745 self.assert_("Content-length" not in req.unredirected_hdrs)
746 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000747 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000748 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
749 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000750 "application/x-www-form-urlencoded")
751 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000752 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
753 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000754
755 # don't clobber existing headers
756 req.add_unredirected_header("Content-length", "foo")
757 req.add_unredirected_header("Content-type", "bar")
758 req.add_unredirected_header("Host", "baz")
759 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000760 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000761 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
762 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000763 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
764 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000765
766 def test_errors(self):
767 h = urllib2.HTTPErrorProcessor()
768 o = h.parent = MockOpener()
769
770 url = "http://example.com/"
771 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000772 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000773 r = MockResponse(200, "OK", {}, "", 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
Guido van Rossumd8faa362007-04-27 19:54:29 +0000777 r = MockResponse(202, "Accepted", {}, "", 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
781 r = MockResponse(206, "Partial content", {}, "", url)
782 newr = h.http_response(req, r)
783 self.assert_(r is newr)
784 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000785 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000786 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000787 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000788 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000789 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000790
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000791 def test_cookies(self):
792 cj = MockCookieJar()
793 h = urllib2.HTTPCookieProcessor(cj)
794 o = h.parent = MockOpener()
795
796 req = Request("http://example.com/")
797 r = MockResponse(200, "OK", {}, "")
798 newreq = h.http_request(req)
799 self.assert_(cj.ach_req is req is newreq)
800 self.assertEquals(req.get_origin_req_host(), "example.com")
801 self.assert_(not req.is_unverifiable())
802 newr = h.http_response(req, r)
803 self.assert_(cj.ec_req is req)
804 self.assert_(cj.ec_r is r is newr)
805
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000806 def test_redirect(self):
807 from_url = "http://example.com/a.html"
808 to_url = "http://example.com/b.html"
809 h = urllib2.HTTPRedirectHandler()
810 o = h.parent = MockOpener()
811
812 # ordinary redirect behaviour
813 for code in 301, 302, 303, 307:
814 for data in None, "blah\nblah\n":
815 method = getattr(h, "http_error_%s" % code)
816 req = Request(from_url, data)
817 req.add_header("Nonsense", "viking=withhold")
818 req.add_unredirected_header("Spam", "spam")
819 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000820 method(req, MockFile(), code, "Blah",
821 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000822 except urllib2.HTTPError:
823 # 307 in response to POST requires user OK
824 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000825 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000826 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000827 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000828 except AttributeError:
829 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000830 self.assertEqual(o.req.headers["Nonsense"],
831 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000832 self.assert_("Spam" not in o.req.headers)
833 self.assert_("Spam" not in o.req.unredirected_hdrs)
834
835 # loop detection
836 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000837 def redirect(h, req, url=to_url):
838 h.http_error_302(req, MockFile(), 302, "Blah",
839 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000840 # Note that the *original* request shares the same record of
841 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000842
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000843 # detect infinite loop redirect of a URL to itself
844 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000845 count = 0
846 try:
847 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000848 redirect(h, req, "http://example.com/")
849 count = count + 1
850 except urllib2.HTTPError:
851 # don't stop until max_repeats, because cookies may introduce state
852 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
853
854 # detect endless non-repeating chain of redirects
855 req = Request(from_url, origin_req_host="example.com")
856 count = 0
857 try:
858 while 1:
859 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000860 count = count + 1
861 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000862 self.assertEqual(count,
863 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000864
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000865 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000866 # cookies shouldn't leak into redirected requests
867 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000868
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000869 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000870
871 cj = CookieJar()
872 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000873 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
874 hdeh = urllib2.HTTPDefaultErrorHandler()
875 hrh = urllib2.HTTPRedirectHandler()
876 cp = urllib2.HTTPCookieProcessor(cj)
877 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000878 o.open("http://www.example.com/")
879 self.assert_(not hh.req.has_header("Cookie"))
880
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000881 def test_proxy(self):
882 o = OpenerDirector()
883 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
884 o.add_handler(ph)
885 meth_spec = [
886 [("http_open", "return response")]
887 ]
888 handlers = add_ordered_mock_handlers(o, meth_spec)
889
890 req = Request("http://acme.example.com/")
891 self.assertEqual(req.get_host(), "acme.example.com")
892 r = o.open(req)
893 self.assertEqual(req.get_host(), "proxy.example.com:3128")
894
895 self.assertEqual([(handlers[0], "http_open")],
896 [tup[0:2] for tup in o.calls])
897
Thomas Wouters477c8d52006-05-27 19:21:47 +0000898 def test_basic_auth(self):
899 opener = OpenerDirector()
900 password_manager = MockPasswordManager()
901 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
902 realm = "ACME Widget Store"
903 http_handler = MockHTTPHandler(
904 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000905 opener.add_handler(auth_handler)
906 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000907 self._test_basic_auth(opener, auth_handler, "Authorization",
908 realm, http_handler, password_manager,
909 "http://acme.example.com/protected",
910 "http://acme.example.com/protected",
911 )
912
913 def test_proxy_basic_auth(self):
914 opener = OpenerDirector()
915 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
916 opener.add_handler(ph)
917 password_manager = MockPasswordManager()
918 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
919 realm = "ACME Networks"
920 http_handler = MockHTTPHandler(
921 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000922 opener.add_handler(auth_handler)
923 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000924 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000925 realm, http_handler, password_manager,
926 "http://acme.example.com:3128/protected",
927 "proxy.example.com:3128",
928 )
929
930 def test_basic_and_digest_auth_handlers(self):
931 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
932 # response (http://python.org/sf/1479302), where it should instead
933 # return None to allow another handler (especially
934 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000935
936 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
937 # try digest first (since it's the strongest auth scheme), so we record
938 # order of calls here to check digest comes first:
939 class RecordingOpenerDirector(OpenerDirector):
940 def __init__(self):
941 OpenerDirector.__init__(self)
942 self.recorded = []
943 def record(self, info):
944 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000945 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000946 def http_error_401(self, *args, **kwds):
947 self.parent.record("digest")
948 urllib2.HTTPDigestAuthHandler.http_error_401(self,
949 *args, **kwds)
950 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
951 def http_error_401(self, *args, **kwds):
952 self.parent.record("basic")
953 urllib2.HTTPBasicAuthHandler.http_error_401(self,
954 *args, **kwds)
955
956 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000957 password_manager = MockPasswordManager()
958 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000959 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000960 realm = "ACME Networks"
961 http_handler = MockHTTPHandler(
962 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000963 opener.add_handler(basic_handler)
964 opener.add_handler(digest_handler)
965 opener.add_handler(http_handler)
966
967 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000968 self._test_basic_auth(opener, basic_handler, "Authorization",
969 realm, http_handler, password_manager,
970 "http://acme.example.com/protected",
971 "http://acme.example.com/protected",
972 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000973 # check digest was tried before basic (twice, because
974 # _test_basic_auth called .open() twice)
975 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000976
977 def _test_basic_auth(self, opener, auth_handler, auth_header,
978 realm, http_handler, password_manager,
979 request_url, protected_url):
980 import base64, httplib
981 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000982
983 # .add_password() fed through to password manager
984 auth_handler.add_password(realm, request_url, user, password)
985 self.assertEqual(realm, password_manager.realm)
986 self.assertEqual(request_url, password_manager.url)
987 self.assertEqual(user, password_manager.user)
988 self.assertEqual(password, password_manager.password)
989
990 r = opener.open(request_url)
991
992 # should have asked the password manager for the username/password
993 self.assertEqual(password_manager.target_realm, realm)
994 self.assertEqual(password_manager.target_url, protected_url)
995
996 # expect one request without authorization, then one with
997 self.assertEqual(len(http_handler.requests), 2)
998 self.assertFalse(http_handler.requests[0].has_header(auth_header))
999 userpass = '%s:%s' % (user, password)
1000 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
1001 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1002 auth_hdr_value)
1003
1004 # if the password manager can't find a password, the handler won't
1005 # handle the HTTP auth error
1006 password_manager.user = password_manager.password = None
1007 http_handler.reset()
1008 r = opener.open(request_url)
1009 self.assertEqual(len(http_handler.requests), 1)
1010 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1011
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001012
1013class MiscTests(unittest.TestCase):
1014
1015 def test_build_opener(self):
1016 class MyHTTPHandler(urllib2.HTTPHandler): pass
1017 class FooHandler(urllib2.BaseHandler):
1018 def foo_open(self): pass
1019 class BarHandler(urllib2.BaseHandler):
1020 def bar_open(self): pass
1021
1022 build_opener = urllib2.build_opener
1023
1024 o = build_opener(FooHandler, BarHandler)
1025 self.opener_has_handler(o, FooHandler)
1026 self.opener_has_handler(o, BarHandler)
1027
1028 # can take a mix of classes and instances
1029 o = build_opener(FooHandler, BarHandler())
1030 self.opener_has_handler(o, FooHandler)
1031 self.opener_has_handler(o, BarHandler)
1032
1033 # subclasses of default handlers override default handlers
1034 o = build_opener(MyHTTPHandler)
1035 self.opener_has_handler(o, MyHTTPHandler)
1036
1037 # a particular case of overriding: default handlers can be passed
1038 # in explicitly
1039 o = build_opener()
1040 self.opener_has_handler(o, urllib2.HTTPHandler)
1041 o = build_opener(urllib2.HTTPHandler)
1042 self.opener_has_handler(o, urllib2.HTTPHandler)
1043 o = build_opener(urllib2.HTTPHandler())
1044 self.opener_has_handler(o, urllib2.HTTPHandler)
1045
1046 def opener_has_handler(self, opener, handler_class):
1047 for h in opener.handlers:
1048 if h.__class__ == handler_class:
1049 break
1050 else:
1051 self.assert_(False)
1052
1053
1054def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001055 from test import test_urllib2
1056 test_support.run_doctest(test_urllib2, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001057 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001058 tests = (TrivialTests,
1059 OpenerDirectorTests,
1060 HandlerTests,
1061 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001062 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001063
1064if __name__ == "__main__":
1065 test_main(verbose=True)