blob: ff164c2058cc7dc303f163824b3a3ec4595a46a8 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Christian Heimesc5f05e42008-02-23 17:40:11 +00004import os
Facundo Batista4f1b1ed2008-05-29 16:39:26 +00005import socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00006import StringIO
Jeremy Hyltone3e61042001-05-09 15:50:25 +00007
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00008import urllib2
9from urllib2 import Request, OpenerDirector
Jeremy Hyltone3e61042001-05-09 15:50:25 +000010
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000011# XXX
12# Request
13# CacheFTPHandler (hard to write)
Georg Brandlfa42bd72006-04-30 07:06:11 +000014# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000015
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000016class TrivialTests(unittest.TestCase):
17 def test_trivial(self):
18 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000019
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000020 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000021
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000022 # XXX Name hacking to get this to work on Windows.
23 fname = os.path.abspath(urllib2.__file__).replace('\\', '/')
24 if fname[1:2] == ":":
25 fname = fname[2:]
26 # And more hacking to get it to work on MacOS. This assumes
27 # urllib.pathname2url works, unfortunately...
28 if os.name == 'mac':
29 fname = '/' + fname.replace(':', '/')
30 elif os.name == 'riscos':
31 import string
32 fname = os.expand(fname)
33 fname = fname.translate(string.maketrans("/.", "./"))
34
35 file_url = "file://%s" % fname
36 f = urllib2.urlopen(file_url)
37
38 buf = f.read()
39 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000040
Georg Brandle1b13d22005-08-24 22:20:32 +000041 def test_parse_http_list(self):
42 tests = [('a,b,c', ['a', 'b', 'c']),
43 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
44 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
45 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
46 for string, list in tests:
47 self.assertEquals(urllib2.parse_http_list(string), list)
48
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000049
Georg Brandl8c036cc2006-08-20 13:15:39 +000050def test_request_headers_dict():
51 """
52 The Request.headers dictionary is not a documented interface. It should
53 stay that way, because the complete set of headers are only accessible
54 through the .get_header(), .has_header(), .header_items() interface.
55 However, .headers pre-dates those methods, and so real code will be using
56 the dictionary.
57
58 The introduction in 2.4 of those methods was a mistake for the same reason:
59 code that previously saw all (urllib2 user)-provided headers in .headers
60 now sees only a subset (and the function interface is ugly and incomplete).
61 A better change would have been to replace .headers dict with a dict
62 subclass (or UserDict.DictMixin instance?) that preserved the .headers
63 interface and also provided access to the "unredirected" headers. It's
64 probably too late to fix that, though.
65
66
67 Check .capitalize() case normalization:
68
69 >>> url = "http://example.com"
70 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
71 'blah'
72 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
73 'blah'
74
75 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
76 but that could be changed in future.
77
78 """
79
80def test_request_headers_methods():
81 """
82 Note the case normalization of header names here, to .capitalize()-case.
83 This should be preserved for backwards-compatibility. (In the HTTP case,
84 normalization to .title()-case is done by urllib2 before sending headers to
85 httplib).
86
87 >>> url = "http://example.com"
88 >>> r = Request(url, headers={"Spam-eggs": "blah"})
89 >>> r.has_header("Spam-eggs")
90 True
91 >>> r.header_items()
92 [('Spam-eggs', 'blah')]
93 >>> r.add_header("Foo-Bar", "baz")
94 >>> items = r.header_items()
95 >>> items.sort()
96 >>> items
97 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
98
99 Note that e.g. r.has_header("spam-EggS") is currently False, and
100 r.get_header("spam-EggS") returns None, but that could be changed in
101 future.
102
103 >>> r.has_header("Not-there")
104 False
105 >>> print r.get_header("Not-there")
106 None
107 >>> r.get_header("Not-there", "default")
108 'default'
109
110 """
111
112
Georg Brandlfa42bd72006-04-30 07:06:11 +0000113def test_password_manager(self):
114 """
115 >>> mgr = urllib2.HTTPPasswordMgr()
116 >>> add = mgr.add_password
117 >>> add("Some Realm", "http://example.com/", "joe", "password")
118 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
119 >>> add("c", "http://example.com/foo", "foo", "ni")
120 >>> add("c", "http://example.com/bar", "bar", "nini")
121 >>> add("b", "http://example.com/", "first", "blah")
122 >>> add("b", "http://example.com/", "second", "spam")
123 >>> add("a", "http://example.com", "1", "a")
124 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
125 >>> add("Some Realm", "d.example.com", "4", "d")
126 >>> add("Some Realm", "e.example.com:3128", "5", "e")
127
128 >>> mgr.find_user_password("Some Realm", "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/")
133 ('joe', 'password')
134 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
135 ('joe', 'password')
136 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
137 ('joe', 'password')
138 >>> mgr.find_user_password("c", "http://example.com/foo")
139 ('foo', 'ni')
140 >>> mgr.find_user_password("c", "http://example.com/bar")
141 ('bar', 'nini')
142
Georg Brandl2b330372006-05-28 20:23:12 +0000143 Actually, this is really undefined ATM
144## Currently, we use the highest-level path where more than one match:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000145
Georg Brandl2b330372006-05-28 20:23:12 +0000146## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
147## ('joe', 'password')
Georg Brandlfa42bd72006-04-30 07:06:11 +0000148
149 Use latest add_password() in case of conflict:
150
151 >>> mgr.find_user_password("b", "http://example.com/")
152 ('second', 'spam')
153
154 No special relationship between a.example.com and example.com:
155
156 >>> mgr.find_user_password("a", "http://example.com/")
157 ('1', 'a')
158 >>> mgr.find_user_password("a", "http://a.example.com/")
159 (None, None)
160
161 Ports:
162
163 >>> mgr.find_user_password("Some Realm", "c.example.com")
164 (None, None)
165 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
166 ('3', 'c')
167 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
168 ('3', 'c')
169 >>> mgr.find_user_password("Some Realm", "d.example.com")
170 ('4', 'd')
171 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
172 ('5', 'e')
173
174 """
175 pass
176
177
Georg Brandl2b330372006-05-28 20:23:12 +0000178def test_password_manager_default_port(self):
179 """
180 >>> mgr = urllib2.HTTPPasswordMgr()
181 >>> add = mgr.add_password
182
183 The point to note here is that we can't guess the default port if there's
184 no scheme. This applies to both add_password and find_user_password.
185
186 >>> add("f", "http://g.example.com:80", "10", "j")
187 >>> add("g", "http://h.example.com", "11", "k")
188 >>> add("h", "i.example.com:80", "12", "l")
189 >>> add("i", "j.example.com", "13", "m")
190 >>> mgr.find_user_password("f", "g.example.com:100")
191 (None, None)
192 >>> mgr.find_user_password("f", "g.example.com:80")
193 ('10', 'j')
194 >>> mgr.find_user_password("f", "g.example.com")
195 (None, None)
196 >>> mgr.find_user_password("f", "http://g.example.com:100")
197 (None, None)
198 >>> mgr.find_user_password("f", "http://g.example.com:80")
199 ('10', 'j')
200 >>> mgr.find_user_password("f", "http://g.example.com")
201 ('10', 'j')
202 >>> mgr.find_user_password("g", "h.example.com")
203 ('11', 'k')
204 >>> mgr.find_user_password("g", "h.example.com:80")
205 ('11', 'k')
206 >>> mgr.find_user_password("g", "http://h.example.com:80")
207 ('11', 'k')
208 >>> mgr.find_user_password("h", "i.example.com")
209 (None, None)
210 >>> mgr.find_user_password("h", "i.example.com:80")
211 ('12', 'l')
212 >>> mgr.find_user_password("h", "http://i.example.com:80")
213 ('12', 'l')
214 >>> mgr.find_user_password("i", "j.example.com")
215 ('13', 'm')
216 >>> mgr.find_user_password("i", "j.example.com:80")
217 (None, None)
218 >>> mgr.find_user_password("i", "http://j.example.com")
219 ('13', 'm')
220 >>> mgr.find_user_password("i", "http://j.example.com:80")
221 (None, None)
222
223 """
224
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000225class MockOpener:
226 addheaders = []
227 def open(self, req, data=None):
228 self.req, self.data = req, data
229 def error(self, proto, *args):
230 self.proto, self.args = proto, args
231
232class MockFile:
233 def read(self, count=None): pass
234 def readline(self, count=None): pass
235 def close(self): pass
236
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000237class MockHeaders(dict):
238 def getheaders(self, name):
239 return self.values()
240
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000241class MockResponse(StringIO.StringIO):
242 def __init__(self, code, msg, headers, data, url=None):
243 StringIO.StringIO.__init__(self, data)
244 self.code, self.msg, self.headers, self.url = code, msg, headers, url
245 def info(self):
246 return self.headers
247 def geturl(self):
248 return self.url
249
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000250class MockCookieJar:
251 def add_cookie_header(self, request):
252 self.ach_req = request
253 def extract_cookies(self, response, request):
254 self.ec_req, self.ec_r = request, response
255
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000256class FakeMethod:
257 def __init__(self, meth_name, action, handle):
258 self.meth_name = meth_name
259 self.handle = handle
260 self.action = action
261 def __call__(self, *args):
262 return self.handle(self.meth_name, self.action, *args)
263
264class MockHandler:
Georg Brandlfa42bd72006-04-30 07:06:11 +0000265 # useful for testing handler machinery
266 # see add_ordered_mock_handlers() docstring
Georg Brandl720096a2006-04-02 20:45:34 +0000267 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000268 def __init__(self, methods):
269 self._define_methods(methods)
270 def _define_methods(self, methods):
271 for spec in methods:
272 if len(spec) == 2: name, action = spec
273 else: name, action = spec, None
274 meth = FakeMethod(name, action, self.handle)
275 setattr(self.__class__, name, meth)
276 def handle(self, fn_name, action, *args, **kwds):
277 self.parent.calls.append((self, fn_name, args, kwds))
278 if action is None:
279 return None
280 elif action == "return self":
281 return self
282 elif action == "return response":
283 res = MockResponse(200, "OK", {}, "")
284 return res
285 elif action == "return request":
286 return Request("http://blah/")
287 elif action.startswith("error"):
288 code = action[action.rfind(" ")+1:]
289 try:
290 code = int(code)
291 except ValueError:
292 pass
293 res = MockResponse(200, "OK", {}, "")
294 return self.parent.error("http", args[0], res, code, "", {})
295 elif action == "raise":
296 raise urllib2.URLError("blah")
297 assert False
298 def close(self): pass
299 def add_parent(self, parent):
300 self.parent = parent
301 self.parent.calls = []
302 def __lt__(self, other):
303 if not hasattr(other, "handler_order"):
304 # No handler_order, leave in original order. Yuck.
305 return True
306 return self.handler_order < other.handler_order
307
308def add_ordered_mock_handlers(opener, meth_spec):
309 """Create MockHandlers and add them to an OpenerDirector.
310
311 meth_spec: list of lists of tuples and strings defining methods to define
312 on handlers. eg:
313
314 [["http_error", "ftp_open"], ["http_open"]]
315
316 defines methods .http_error() and .ftp_open() on one handler, and
317 .http_open() on another. These methods just record their arguments and
318 return None. Using a tuple instead of a string causes the method to
319 perform some action (see MockHandler.handle()), eg:
320
321 [["http_error"], [("http_open", "return request")]]
322
323 defines .http_error() on one handler (which simply returns None), and
324 .http_open() on another handler, which returns a Request object.
325
326 """
327 handlers = []
328 count = 0
329 for meths in meth_spec:
330 class MockHandlerSubclass(MockHandler): pass
331 h = MockHandlerSubclass(meths)
Georg Brandl720096a2006-04-02 20:45:34 +0000332 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000333 h.add_parent(opener)
334 count = count + 1
335 handlers.append(h)
336 opener.add_handler(h)
337 return handlers
338
Georg Brandlfa42bd72006-04-30 07:06:11 +0000339def build_test_opener(*handler_instances):
340 opener = OpenerDirector()
341 for h in handler_instances:
342 opener.add_handler(h)
343 return opener
344
345class MockHTTPHandler(urllib2.BaseHandler):
346 # useful for testing redirections and auth
347 # sends supplied headers and code as first response
348 # sends 200 OK as second response
349 def __init__(self, code, headers):
350 self.code = code
351 self.headers = headers
352 self.reset()
353 def reset(self):
354 self._count = 0
355 self.requests = []
356 def http_open(self, req):
357 import mimetools, httplib, copy
358 from StringIO import StringIO
359 self.requests.append(copy.deepcopy(req))
360 if self._count == 0:
361 self._count = self._count + 1
362 name = httplib.responses[self.code]
363 msg = mimetools.Message(StringIO(self.headers))
364 return self.parent.error(
365 "http", req, MockFile(), self.code, name, msg)
366 else:
367 self.req = req
368 msg = mimetools.Message(StringIO("\r\n\r\n"))
369 return MockResponse(200, "OK", msg, "", req.get_full_url())
370
371class MockPasswordManager:
372 def add_password(self, realm, uri, user, password):
373 self.realm = realm
374 self.url = uri
375 self.user = user
376 self.password = password
377 def find_user_password(self, realm, authuri):
378 self.target_realm = realm
379 self.target_url = authuri
380 return self.user, self.password
381
382
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000383class OpenerDirectorTests(unittest.TestCase):
384
Georg Brandlf91149e2007-07-12 08:05:45 +0000385 def test_add_non_handler(self):
386 class NonHandler(object):
387 pass
388 self.assertRaises(TypeError,
389 OpenerDirector().add_handler, NonHandler())
390
Georg Brandl261e2512006-05-29 20:52:54 +0000391 def test_badly_named_methods(self):
392 # test work-around for three methods that accidentally follow the
393 # naming conventions for handler methods
394 # (*_open() / *_request() / *_response())
395
396 # These used to call the accidentally-named methods, causing a
397 # TypeError in real code; here, returning self from these mock
398 # methods would either cause no exception, or AttributeError.
399
400 from urllib2 import URLError
401
402 o = OpenerDirector()
403 meth_spec = [
404 [("do_open", "return self"), ("proxy_open", "return self")],
405 [("redirect_request", "return self")],
406 ]
407 handlers = add_ordered_mock_handlers(o, meth_spec)
408 o.add_handler(urllib2.UnknownHandler())
409 for scheme in "do", "proxy", "redirect":
410 self.assertRaises(URLError, o.open, scheme+"://example.com/")
411
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000412 def test_handled(self):
413 # handler returning non-None means no more handlers will be called
414 o = OpenerDirector()
415 meth_spec = [
416 ["http_open", "ftp_open", "http_error_302"],
417 ["ftp_open"],
418 [("http_open", "return self")],
419 [("http_open", "return self")],
420 ]
421 handlers = add_ordered_mock_handlers(o, meth_spec)
422
423 req = Request("http://example.com/")
424 r = o.open(req)
425 # Second .http_open() gets called, third doesn't, since second returned
426 # non-None. Handlers without .http_open() never get any methods called
427 # on them.
428 # In fact, second mock handler defining .http_open() returns self
429 # (instead of response), which becomes the OpenerDirector's return
430 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000431 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000432 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
433 for expected, got in zip(calls, o.calls):
434 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000435 self.assertEqual((handler, name), expected)
436 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000437
438 def test_handler_order(self):
439 o = OpenerDirector()
440 handlers = []
441 for meths, handler_order in [
442 ([("http_open", "return self")], 500),
443 (["http_open"], 0),
444 ]:
445 class MockHandlerSubclass(MockHandler): pass
446 h = MockHandlerSubclass(meths)
447 h.handler_order = handler_order
448 handlers.append(h)
449 o.add_handler(h)
450
451 r = o.open("http://example.com/")
452 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000453 self.assertEqual(o.calls[0][0], handlers[1])
454 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000455
456 def test_raise(self):
457 # raising URLError stops processing of request
458 o = OpenerDirector()
459 meth_spec = [
460 [("http_open", "raise")],
461 [("http_open", "return self")],
462 ]
463 handlers = add_ordered_mock_handlers(o, meth_spec)
464
465 req = Request("http://example.com/")
466 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000467 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000468
469## def test_error(self):
470## # XXX this doesn't actually seem to be used in standard library,
471## # but should really be tested anyway...
472
473 def test_http_error(self):
474 # XXX http_error_default
475 # http errors are a special case
476 o = OpenerDirector()
477 meth_spec = [
478 [("http_open", "error 302")],
479 [("http_error_400", "raise"), "http_open"],
480 [("http_error_302", "return response"), "http_error_303",
481 "http_error"],
482 [("http_error_302")],
483 ]
484 handlers = add_ordered_mock_handlers(o, meth_spec)
485
486 class Unknown:
487 def __eq__(self, other): return True
488
489 req = Request("http://example.com/")
490 r = o.open(req)
491 assert len(o.calls) == 2
492 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000493 (handlers[2], "http_error_302",
494 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000495 for expected, got in zip(calls, o.calls):
496 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000497 self.assertEqual((handler, method_name), got[:2])
498 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000499
500 def test_processors(self):
501 # *_request / *_response methods get called appropriately
502 o = OpenerDirector()
503 meth_spec = [
504 [("http_request", "return request"),
505 ("http_response", "return response")],
506 [("http_request", "return request"),
507 ("http_response", "return response")],
508 ]
509 handlers = add_ordered_mock_handlers(o, meth_spec)
510
511 req = Request("http://example.com/")
512 r = o.open(req)
513 # processor methods are called on *all* handlers that define them,
514 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000515 calls = [
516 (handlers[0], "http_request"), (handlers[1], "http_request"),
517 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000518
519 for i, (handler, name, args, kwds) in enumerate(o.calls):
520 if i < 2:
521 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000522 self.assertEqual((handler, name), calls[i])
523 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000524 self.assert_(isinstance(args[0], Request))
525 else:
526 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000527 self.assertEqual((handler, name), calls[i])
528 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000529 self.assert_(isinstance(args[0], Request))
530 # response from opener.open is None, because there's no
531 # handler that defines http_open to handle it
532 self.assert_(args[1] is None or
533 isinstance(args[1], MockResponse))
534
535
Tim Peters58eb11c2004-01-18 20:29:55 +0000536def sanepathname2url(path):
537 import urllib
538 urlpath = urllib.pathname2url(path)
539 if os.name == "nt" and urlpath.startswith("///"):
540 urlpath = urlpath[2:]
541 # XXX don't ask me about the mac...
542 return urlpath
543
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000544class HandlerTests(unittest.TestCase):
545
546 def test_ftp(self):
547 class MockFTPWrapper:
548 def __init__(self, data): self.data = data
549 def retrfile(self, filename, filetype):
550 self.filename, self.filetype = filename, filetype
551 return StringIO.StringIO(self.data), len(self.data)
552
553 class NullFTPHandler(urllib2.FTPHandler):
554 def __init__(self, data): self.data = data
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000555 def connect_ftp(self, user, passwd, host, port, dirs,
556 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000557 self.user, self.passwd = user, passwd
558 self.host, self.port = host, port
559 self.dirs = dirs
560 self.ftpwrapper = MockFTPWrapper(self.data)
561 return self.ftpwrapper
562
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000563 import ftplib
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000564 data = "rheum rhaponicum"
565 h = NullFTPHandler(data)
566 o = h.parent = MockOpener()
567
568 for url, host, port, type_, dirs, filename, mimetype in [
569 ("ftp://localhost/foo/bar/baz.html",
570 "localhost", ftplib.FTP_PORT, "I",
571 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000572 ("ftp://localhost:80/foo/bar/",
573 "localhost", 80, "D",
574 ["foo", "bar"], "", None),
575 ("ftp://localhost/baz.gif;type=a",
576 "localhost", ftplib.FTP_PORT, "A",
577 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000578 ]:
Facundo Batista10951d52007-06-06 17:15:23 +0000579 req = Request(url)
580 req.timeout = None
581 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000582 # ftp authentication not yet implemented by FTPHandler
583 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000584 self.assertEqual(h.host, socket.gethostbyname(host))
585 self.assertEqual(h.port, port)
586 self.assertEqual(h.dirs, dirs)
587 self.assertEqual(h.ftpwrapper.filename, filename)
588 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000589 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000590 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000591 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000592
593 def test_file(self):
Christian Heimesc5f05e42008-02-23 17:40:11 +0000594 import rfc822, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000595 h = urllib2.FileHandler()
596 o = h.parent = MockOpener()
597
Tim Peters58eb11c2004-01-18 20:29:55 +0000598 TESTFN = test_support.TESTFN
599 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000600 towrite = "hello, world\n"
Georg Brandldd2245f2006-03-31 17:18:06 +0000601 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000602 "file://localhost%s" % urlpath,
603 "file://%s" % urlpath,
604 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Georg Brandldd2245f2006-03-31 17:18:06 +0000605 ]
606 try:
Tim Peters480725d2006-04-03 02:46:44 +0000607 localaddr = socket.gethostbyname(socket.gethostname())
Georg Brandldd2245f2006-03-31 17:18:06 +0000608 except socket.gaierror:
609 localaddr = ''
610 if localaddr:
611 urls.append("file://%s%s" % (localaddr, urlpath))
Tim Peters480725d2006-04-03 02:46:44 +0000612
Georg Brandldd2245f2006-03-31 17:18:06 +0000613 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000614 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000615 try:
616 try:
617 f.write(towrite)
618 finally:
619 f.close()
620
621 r = h.file_open(Request(url))
622 try:
623 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000624 headers = r.info()
625 newurl = r.geturl()
626 finally:
627 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000628 stats = os.stat(TESTFN)
629 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000630 finally:
631 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000632 self.assertEqual(data, towrite)
633 self.assertEqual(headers["Content-type"], "text/plain")
634 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000635 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000636
637 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000638 "file://localhost:80%s" % urlpath,
Georg Brandlceede5c2007-03-13 08:14:27 +0000639 "file:///file_does_not_exist.txt",
640 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
641 os.getcwd(), TESTFN),
642 "file://somerandomhost.ontheinternet.com%s/%s" %
643 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000644 ]:
645 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000646 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000647 try:
648 f.write(towrite)
649 finally:
650 f.close()
651
652 self.assertRaises(urllib2.URLError,
653 h.file_open, Request(url))
654 finally:
655 os.remove(TESTFN)
656
657 h = urllib2.FileHandler()
658 o = h.parent = MockOpener()
659 # XXXX why does // mean ftp (and /// mean not ftp!), and where
660 # is file: scheme specified? I think this is really a bug, and
661 # what was intended was to distinguish between URLs like:
662 # file:/blah.txt (a file)
663 # file://localhost/blah.txt (a file)
664 # file:///blah.txt (a file)
665 # file://ftp.example.com/blah.txt (an ftp URL)
666 for url, ftp in [
667 ("file://ftp.example.com//foo.txt", True),
668 ("file://ftp.example.com///foo.txt", False),
669# XXXX bug: fails with OSError, should be URLError
670 ("file://ftp.example.com/foo.txt", False),
671 ]:
672 req = Request(url)
673 try:
674 h.file_open(req)
675 # XXXX remove OSError when bug fixed
676 except (urllib2.URLError, OSError):
677 self.assert_(not ftp)
678 else:
679 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000680 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000681
682 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000683 class MockHTTPResponse:
684 def __init__(self, fp, msg, status, reason):
685 self.fp = fp
686 self.msg = msg
687 self.status = status
688 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000689 def read(self):
690 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000691 class MockHTTPClass:
692 def __init__(self):
693 self.req_headers = []
694 self.data = None
695 self.raise_on_endheaders = False
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000696 def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000697 self.host = host
Facundo Batista10951d52007-06-06 17:15:23 +0000698 self.timeout = timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000699 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000700 def set_debuglevel(self, level):
701 self.level = level
702 def request(self, method, url, body=None, headers={}):
703 self.method = method
704 self.selector = url
705 self.req_headers += headers.items()
Armin Rigoa3f09272006-05-28 19:13:17 +0000706 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000707 if body:
708 self.data = body
709 if self.raise_on_endheaders:
710 import socket
711 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000712 def getresponse(self):
713 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000714
715 h = urllib2.AbstractHTTPHandler()
716 o = h.parent = MockOpener()
717
718 url = "http://example.com/"
719 for method, data in [("GET", None), ("POST", "blah")]:
720 req = Request(url, data, {"Foo": "bar"})
Facundo Batista10951d52007-06-06 17:15:23 +0000721 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000722 req.add_unredirected_header("Spam", "eggs")
723 http = MockHTTPClass()
724 r = h.do_open(http, req)
725
726 # result attributes
727 r.read; r.readline # wrapped MockFile methods
728 r.info; r.geturl # addinfourl methods
729 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
730 hdrs = r.info()
731 hdrs.get; hdrs.has_key # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000732 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000733
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000734 self.assertEqual(http.host, "example.com")
735 self.assertEqual(http.level, 0)
736 self.assertEqual(http.method, method)
737 self.assertEqual(http.selector, "/")
738 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000739 [("Connection", "close"),
740 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000741 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000742
743 # check socket.error converted to URLError
744 http.raise_on_endheaders = True
745 self.assertRaises(urllib2.URLError, h.do_open, http, req)
746
747 # check adding of standard headers
748 o.addheaders = [("Spam", "eggs")]
749 for data in "", None: # POST, GET
750 req = Request("http://example.com/", data)
751 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000752 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000753 if data is None: # GET
Georg Brandl8c036cc2006-08-20 13:15:39 +0000754 self.assert_("Content-length" not in req.unredirected_hdrs)
755 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000756 else: # POST
Georg Brandl8c036cc2006-08-20 13:15:39 +0000757 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
758 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000759 "application/x-www-form-urlencoded")
760 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000761 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
762 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000763
764 # don't clobber existing headers
765 req.add_unredirected_header("Content-length", "foo")
766 req.add_unredirected_header("Content-type", "bar")
767 req.add_unredirected_header("Host", "baz")
768 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000769 newreq = h.do_request_(req)
Georg Brandl8c036cc2006-08-20 13:15:39 +0000770 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
771 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000772 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
773 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000774
Facundo Batistaeb90b782008-08-16 14:44:07 +0000775 def test_http_doubleslash(self):
776 # Checks that the presence of an unnecessary double slash in a url doesn't break anything
777 # Previously, a double slash directly after the host could cause incorrect parsing of the url
778 h = urllib2.AbstractHTTPHandler()
779 o = h.parent = MockOpener()
780
781 data = ""
782 ds_urls = [
783 "http://example.com/foo/bar/baz.html",
784 "http://example.com//foo/bar/baz.html",
785 "http://example.com/foo//bar/baz.html",
786 "http://example.com/foo/bar//baz.html",
787 ]
788
789 for ds_url in ds_urls:
790 ds_req = Request(ds_url, data)
791
792 # Check whether host is determined correctly if there is no proxy
793 np_ds_req = h.do_request_(ds_req)
794 self.assertEqual(np_ds_req.unredirected_hdrs["Host"],"example.com")
795
796 # Check whether host is determined correctly if there is a proxy
797 ds_req.set_proxy("someproxy:3128",None)
798 p_ds_req = h.do_request_(ds_req)
799 self.assertEqual(p_ds_req.unredirected_hdrs["Host"],"example.com")
800
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000801 def test_errors(self):
802 h = urllib2.HTTPErrorProcessor()
803 o = h.parent = MockOpener()
804
805 url = "http://example.com/"
806 req = Request(url)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000807 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000808 r = MockResponse(200, "OK", {}, "", url)
809 newr = h.http_response(req, r)
810 self.assert_(r is newr)
811 self.assert_(not hasattr(o, "proto")) # o.error not called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000812 r = MockResponse(202, "Accepted", {}, "", url)
813 newr = h.http_response(req, r)
814 self.assert_(r is newr)
815 self.assert_(not hasattr(o, "proto")) # o.error not called
816 r = MockResponse(206, "Partial content", {}, "", url)
817 newr = h.http_response(req, r)
818 self.assert_(r is newr)
819 self.assert_(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000820 # anything else calls o.error (and MockOpener returns None, here)
Facundo Batista9fab9f12007-04-23 17:08:31 +0000821 r = MockResponse(502, "Bad gateway", {}, "", url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000822 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000823 self.assertEqual(o.proto, "http") # o.error called
Facundo Batista9fab9f12007-04-23 17:08:31 +0000824 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000825
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000826 def test_cookies(self):
827 cj = MockCookieJar()
828 h = urllib2.HTTPCookieProcessor(cj)
829 o = h.parent = MockOpener()
830
831 req = Request("http://example.com/")
832 r = MockResponse(200, "OK", {}, "")
833 newreq = h.http_request(req)
834 self.assert_(cj.ach_req is req is newreq)
835 self.assertEquals(req.get_origin_req_host(), "example.com")
836 self.assert_(not req.is_unverifiable())
837 newr = h.http_response(req, r)
838 self.assert_(cj.ec_req is req)
839 self.assert_(cj.ec_r is r is newr)
840
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000841 def test_redirect(self):
842 from_url = "http://example.com/a.html"
843 to_url = "http://example.com/b.html"
844 h = urllib2.HTTPRedirectHandler()
845 o = h.parent = MockOpener()
846
847 # ordinary redirect behaviour
848 for code in 301, 302, 303, 307:
849 for data in None, "blah\nblah\n":
850 method = getattr(h, "http_error_%s" % code)
851 req = Request(from_url, data)
852 req.add_header("Nonsense", "viking=withhold")
Facundo Batista86371d62008-02-07 19:06:52 +0000853 if data is not None:
854 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000855 req.add_unredirected_header("Spam", "spam")
856 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000857 method(req, MockFile(), code, "Blah",
858 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000859 except urllib2.HTTPError:
860 # 307 in response to POST requires user OK
861 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000862 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000863 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000864 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000865 except AttributeError:
866 self.assert_(not o.req.has_data())
Facundo Batista86371d62008-02-07 19:06:52 +0000867
868 # now it's a GET, there should not be headers regarding content
869 # (possibly dragged from before being a POST)
870 headers = [x.lower() for x in o.req.headers]
871 self.assertTrue("content-length" not in headers)
872 self.assertTrue("content-type" not in headers)
873
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000874 self.assertEqual(o.req.headers["Nonsense"],
875 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000876 self.assert_("Spam" not in o.req.headers)
877 self.assert_("Spam" not in o.req.unredirected_hdrs)
878
879 # loop detection
880 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000881 def redirect(h, req, url=to_url):
882 h.http_error_302(req, MockFile(), 302, "Blah",
883 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000884 # Note that the *original* request shares the same record of
885 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000886
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000887 # detect infinite loop redirect of a URL to itself
888 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000889 count = 0
890 try:
891 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000892 redirect(h, req, "http://example.com/")
893 count = count + 1
894 except urllib2.HTTPError:
895 # don't stop until max_repeats, because cookies may introduce state
896 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
897
898 # detect endless non-repeating chain of redirects
899 req = Request(from_url, origin_req_host="example.com")
900 count = 0
901 try:
902 while 1:
903 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000904 count = count + 1
905 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000906 self.assertEqual(count,
907 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000908
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000909 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000910 # cookies shouldn't leak into redirected requests
911 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000912
Neal Norwitzb902f4e2006-04-03 04:45:34 +0000913 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000914
915 cj = CookieJar()
916 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Georg Brandlfa42bd72006-04-30 07:06:11 +0000917 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
918 hdeh = urllib2.HTTPDefaultErrorHandler()
919 hrh = urllib2.HTTPRedirectHandler()
920 cp = urllib2.HTTPCookieProcessor(cj)
921 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000922 o.open("http://www.example.com/")
923 self.assert_(not hh.req.has_header("Cookie"))
924
Georg Brandl720096a2006-04-02 20:45:34 +0000925 def test_proxy(self):
926 o = OpenerDirector()
927 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
928 o.add_handler(ph)
929 meth_spec = [
930 [("http_open", "return response")]
931 ]
932 handlers = add_ordered_mock_handlers(o, meth_spec)
933
934 req = Request("http://acme.example.com/")
935 self.assertEqual(req.get_host(), "acme.example.com")
936 r = o.open(req)
937 self.assertEqual(req.get_host(), "proxy.example.com:3128")
938
939 self.assertEqual([(handlers[0], "http_open")],
940 [tup[0:2] for tup in o.calls])
941
Georg Brandl33124322008-03-21 19:54:00 +0000942 def test_basic_auth(self, quote_char='"'):
Georg Brandlfa42bd72006-04-30 07:06:11 +0000943 opener = OpenerDirector()
944 password_manager = MockPasswordManager()
945 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
946 realm = "ACME Widget Store"
947 http_handler = MockHTTPHandler(
Georg Brandl33124322008-03-21 19:54:00 +0000948 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
949 (quote_char, realm, quote_char) )
Georg Brandl261e2512006-05-29 20:52:54 +0000950 opener.add_handler(auth_handler)
951 opener.add_handler(http_handler)
Georg Brandlfa42bd72006-04-30 07:06:11 +0000952 self._test_basic_auth(opener, auth_handler, "Authorization",
953 realm, http_handler, password_manager,
954 "http://acme.example.com/protected",
955 "http://acme.example.com/protected",
956 )
957
Georg Brandl33124322008-03-21 19:54:00 +0000958 def test_basic_auth_with_single_quoted_realm(self):
959 self.test_basic_auth(quote_char="'")
960
Georg Brandlfa42bd72006-04-30 07:06:11 +0000961 def test_proxy_basic_auth(self):
962 opener = OpenerDirector()
963 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
964 opener.add_handler(ph)
965 password_manager = MockPasswordManager()
966 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
967 realm = "ACME Networks"
968 http_handler = MockHTTPHandler(
969 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +0000970 opener.add_handler(auth_handler)
971 opener.add_handler(http_handler)
Georg Brandl8c036cc2006-08-20 13:15:39 +0000972 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Georg Brandlfa42bd72006-04-30 07:06:11 +0000973 realm, http_handler, password_manager,
974 "http://acme.example.com:3128/protected",
975 "proxy.example.com:3128",
976 )
977
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000978 def test_basic_and_digest_auth_handlers(self):
979 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
980 # response (http://python.org/sf/1479302), where it should instead
981 # return None to allow another handler (especially
982 # HTTPBasicAuthHandler) to handle the response.
Georg Brandl261e2512006-05-29 20:52:54 +0000983
984 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
985 # try digest first (since it's the strongest auth scheme), so we record
986 # order of calls here to check digest comes first:
987 class RecordingOpenerDirector(OpenerDirector):
988 def __init__(self):
989 OpenerDirector.__init__(self)
990 self.recorded = []
991 def record(self, info):
992 self.recorded.append(info)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +0000993 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Georg Brandl261e2512006-05-29 20:52:54 +0000994 def http_error_401(self, *args, **kwds):
995 self.parent.record("digest")
996 urllib2.HTTPDigestAuthHandler.http_error_401(self,
997 *args, **kwds)
998 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
999 def http_error_401(self, *args, **kwds):
1000 self.parent.record("basic")
1001 urllib2.HTTPBasicAuthHandler.http_error_401(self,
1002 *args, **kwds)
1003
1004 opener = RecordingOpenerDirector()
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001005 password_manager = MockPasswordManager()
1006 digest_handler = TestDigestAuthHandler(password_manager)
Georg Brandl261e2512006-05-29 20:52:54 +00001007 basic_handler = TestBasicAuthHandler(password_manager)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001008 realm = "ACME Networks"
1009 http_handler = MockHTTPHandler(
1010 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Georg Brandl261e2512006-05-29 20:52:54 +00001011 opener.add_handler(basic_handler)
1012 opener.add_handler(digest_handler)
1013 opener.add_handler(http_handler)
1014
1015 # check basic auth isn't blocked by digest handler failing
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001016 self._test_basic_auth(opener, basic_handler, "Authorization",
1017 realm, http_handler, password_manager,
1018 "http://acme.example.com/protected",
1019 "http://acme.example.com/protected",
1020 )
Georg Brandl261e2512006-05-29 20:52:54 +00001021 # check digest was tried before basic (twice, because
1022 # _test_basic_auth called .open() twice)
1023 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Georg Brandlb5f2e5c2006-05-08 17:36:08 +00001024
Georg Brandlfa42bd72006-04-30 07:06:11 +00001025 def _test_basic_auth(self, opener, auth_handler, auth_header,
1026 realm, http_handler, password_manager,
1027 request_url, protected_url):
Christian Heimesc5f05e42008-02-23 17:40:11 +00001028 import base64
Georg Brandlfa42bd72006-04-30 07:06:11 +00001029 user, password = "wile", "coyote"
Georg Brandlfa42bd72006-04-30 07:06:11 +00001030
1031 # .add_password() fed through to password manager
1032 auth_handler.add_password(realm, request_url, user, password)
1033 self.assertEqual(realm, password_manager.realm)
1034 self.assertEqual(request_url, password_manager.url)
1035 self.assertEqual(user, password_manager.user)
1036 self.assertEqual(password, password_manager.password)
1037
1038 r = opener.open(request_url)
1039
1040 # should have asked the password manager for the username/password
1041 self.assertEqual(password_manager.target_realm, realm)
1042 self.assertEqual(password_manager.target_url, protected_url)
1043
1044 # expect one request without authorization, then one with
1045 self.assertEqual(len(http_handler.requests), 2)
1046 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1047 userpass = '%s:%s' % (user, password)
1048 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
1049 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1050 auth_hdr_value)
1051
1052 # if the password manager can't find a password, the handler won't
1053 # handle the HTTP auth error
1054 password_manager.user = password_manager.password = None
1055 http_handler.reset()
1056 r = opener.open(request_url)
1057 self.assertEqual(len(http_handler.requests), 1)
1058 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1059
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001060
1061class MiscTests(unittest.TestCase):
1062
1063 def test_build_opener(self):
1064 class MyHTTPHandler(urllib2.HTTPHandler): pass
1065 class FooHandler(urllib2.BaseHandler):
1066 def foo_open(self): pass
1067 class BarHandler(urllib2.BaseHandler):
1068 def bar_open(self): pass
1069
1070 build_opener = urllib2.build_opener
1071
1072 o = build_opener(FooHandler, BarHandler)
1073 self.opener_has_handler(o, FooHandler)
1074 self.opener_has_handler(o, BarHandler)
1075
1076 # can take a mix of classes and instances
1077 o = build_opener(FooHandler, BarHandler())
1078 self.opener_has_handler(o, FooHandler)
1079 self.opener_has_handler(o, BarHandler)
1080
1081 # subclasses of default handlers override default handlers
1082 o = build_opener(MyHTTPHandler)
1083 self.opener_has_handler(o, MyHTTPHandler)
1084
1085 # a particular case of overriding: default handlers can be passed
1086 # in explicitly
1087 o = build_opener()
1088 self.opener_has_handler(o, urllib2.HTTPHandler)
1089 o = build_opener(urllib2.HTTPHandler)
1090 self.opener_has_handler(o, urllib2.HTTPHandler)
1091 o = build_opener(urllib2.HTTPHandler())
1092 self.opener_has_handler(o, urllib2.HTTPHandler)
1093
Amaury Forgeot d'Arc96865852008-04-22 21:14:41 +00001094 # Issue2670: multiple handlers sharing the same base class
1095 class MyOtherHTTPHandler(urllib2.HTTPHandler): pass
1096 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1097 self.opener_has_handler(o, MyHTTPHandler)
1098 self.opener_has_handler(o, MyOtherHTTPHandler)
1099
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001100 def opener_has_handler(self, opener, handler_class):
1101 for h in opener.handlers:
1102 if h.__class__ == handler_class:
1103 break
1104 else:
1105 self.assert_(False)
1106
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001107class RequestTests(unittest.TestCase):
1108
1109 def setUp(self):
1110 self.get = urllib2.Request("http://www.python.org/~jeremy/")
1111 self.post = urllib2.Request("http://www.python.org/~jeremy/",
1112 "data",
1113 headers={"X-Test": "test"})
1114
1115 def test_method(self):
1116 self.assertEqual("POST", self.post.get_method())
1117 self.assertEqual("GET", self.get.get_method())
1118
1119 def test_add_data(self):
1120 self.assert_(not self.get.has_data())
1121 self.assertEqual("GET", self.get.get_method())
1122 self.get.add_data("spam")
1123 self.assert_(self.get.has_data())
1124 self.assertEqual("POST", self.get.get_method())
1125
1126 def test_get_full_url(self):
1127 self.assertEqual("http://www.python.org/~jeremy/",
1128 self.get.get_full_url())
1129
1130 def test_selector(self):
1131 self.assertEqual("/~jeremy/", self.get.get_selector())
1132 req = urllib2.Request("http://www.python.org/")
1133 self.assertEqual("/", req.get_selector())
1134
1135 def test_get_type(self):
1136 self.assertEqual("http", self.get.get_type())
1137
1138 def test_get_host(self):
1139 self.assertEqual("www.python.org", self.get.get_host())
1140
1141 def test_get_host_unquote(self):
1142 req = urllib2.Request("http://www.%70ython.org/")
1143 self.assertEqual("www.python.org", req.get_host())
1144
1145 def test_proxy(self):
1146 self.assert_(not self.get.has_proxy())
1147 self.get.set_proxy("www.perl.org", "http")
1148 self.assert_(self.get.has_proxy())
1149 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1150 self.assertEqual("www.perl.org", self.get.get_host())
1151
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001152
1153def test_main(verbose=None):
Georg Brandlfa42bd72006-04-30 07:06:11 +00001154 from test import test_urllib2
1155 test_support.run_doctest(test_urllib2, verbose)
Georg Brandl720096a2006-04-02 20:45:34 +00001156 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001157 tests = (TrivialTests,
1158 OpenerDirectorTests,
1159 HandlerTests,
Jeremy Hylton1868d7c2008-12-09 21:03:10 +00001160 MiscTests,
1161 RequestTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001162 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001163
1164if __name__ == "__main__":
1165 test_main(verbose=True)