blob: 83bd467177bd2e5e1c0a14d78d68b93ea9b8dc17 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00002from test import support
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00003
Christian Heimes05e8be12008-02-23 18:30:17 +00004import os
Guido van Rossum34d19282007-08-09 01:03:29 +00005import io
Georg Brandlf78e02b2008-06-10 17:40:04 +00006import socket
Jeremy Hyltone3e61042001-05-09 15:50:25 +00007
Jeremy Hylton1afc1692008-06-18 20:49:58 +00008import urllib.request
9from urllib.request 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)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Hylton1afc1692008-06-18 20:49:58 +000020 self.assertRaises(ValueError, urllib.request.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.
Jeremy Hylton1afc1692008-06-18 20:49:58 +000023 fname = os.path.abspath(urllib.request.__file__).replace('\\', '/')
Senthil Kumaran673d7e92010-01-10 17:48:37 +000024
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000025 # 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(':', '/')
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000029
Senthil Kumaran673d7e92010-01-10 17:48:37 +000030 if os.name == 'nt':
31 file_url = "file:///%s" % fname
32 else:
33 file_url = "file://%s" % fname
34
Jeremy Hylton1afc1692008-06-18 20:49:58 +000035 f = urllib.request.urlopen(file_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000036
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):
Jeremy Hylton1afc1692008-06-18 20:49:58 +000041 tests = [
42 ('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',
45 ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
46 ('a="b\\"c", d="e\\,f", g="h\\\\i"',
47 ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
Georg Brandle1b13d22005-08-24 22:20:32 +000048 for string, list in tests:
Jeremy Hylton1afc1692008-06-18 20:49:58 +000049 self.assertEquals(urllib.request.parse_http_list(string), list)
Georg Brandle1b13d22005-08-24 22:20:32 +000050
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000051
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000052def test_request_headers_dict():
53 """
54 The Request.headers dictionary is not a documented interface. It should
55 stay that way, because the complete set of headers are only accessible
56 through the .get_header(), .has_header(), .header_items() interface.
57 However, .headers pre-dates those methods, and so real code will be using
58 the dictionary.
59
60 The introduction in 2.4 of those methods was a mistake for the same reason:
61 code that previously saw all (urllib2 user)-provided headers in .headers
62 now sees only a subset (and the function interface is ugly and incomplete).
63 A better change would have been to replace .headers dict with a dict
64 subclass (or UserDict.DictMixin instance?) that preserved the .headers
65 interface and also provided access to the "unredirected" headers. It's
66 probably too late to fix that, though.
67
68
69 Check .capitalize() case normalization:
70
71 >>> url = "http://example.com"
72 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
73 'blah'
74 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
75 'blah'
76
77 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
78 but that could be changed in future.
79
80 """
81
82def test_request_headers_methods():
83 """
84 Note the case normalization of header names here, to .capitalize()-case.
85 This should be preserved for backwards-compatibility. (In the HTTP case,
86 normalization to .title()-case is done by urllib2 before sending headers to
Georg Brandl24420152008-05-26 16:32:26 +000087 http.client).
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000088
89 >>> url = "http://example.com"
90 >>> r = Request(url, headers={"Spam-eggs": "blah"})
91 >>> r.has_header("Spam-eggs")
92 True
93 >>> r.header_items()
94 [('Spam-eggs', 'blah')]
95 >>> r.add_header("Foo-Bar", "baz")
Guido van Rossumcc2b0162007-02-11 06:12:03 +000096 >>> items = sorted(r.header_items())
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000097 >>> items
98 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
99
100 Note that e.g. r.has_header("spam-EggS") is currently False, and
101 r.get_header("spam-EggS") returns None, but that could be changed in
102 future.
103
104 >>> r.has_header("Not-there")
105 False
Guido van Rossum7131f842007-02-09 20:13:25 +0000106 >>> print(r.get_header("Not-there"))
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000107 None
108 >>> r.get_header("Not-there", "default")
109 'default'
110
111 """
112
113
Thomas Wouters477c8d52006-05-27 19:21:47 +0000114def test_password_manager(self):
115 """
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000116 >>> mgr = urllib.request.HTTPPasswordMgr()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000117 >>> add = mgr.add_password
118 >>> add("Some Realm", "http://example.com/", "joe", "password")
119 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
120 >>> add("c", "http://example.com/foo", "foo", "ni")
121 >>> add("c", "http://example.com/bar", "bar", "nini")
122 >>> add("b", "http://example.com/", "first", "blah")
123 >>> add("b", "http://example.com/", "second", "spam")
124 >>> add("a", "http://example.com", "1", "a")
125 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
126 >>> add("Some Realm", "d.example.com", "4", "d")
127 >>> add("Some Realm", "e.example.com:3128", "5", "e")
128
129 >>> mgr.find_user_password("Some Realm", "example.com")
130 ('joe', 'password')
131 >>> mgr.find_user_password("Some Realm", "http://example.com")
132 ('joe', 'password')
133 >>> mgr.find_user_password("Some Realm", "http://example.com/")
134 ('joe', 'password')
135 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
136 ('joe', 'password')
137 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
138 ('joe', 'password')
139 >>> mgr.find_user_password("c", "http://example.com/foo")
140 ('foo', 'ni')
141 >>> mgr.find_user_password("c", "http://example.com/bar")
142 ('bar', 'nini')
143
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000144 Actually, this is really undefined ATM
145## Currently, we use the highest-level path where more than one match:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000147## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
148## ('joe', 'password')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149
150 Use latest add_password() in case of conflict:
151
152 >>> mgr.find_user_password("b", "http://example.com/")
153 ('second', 'spam')
154
155 No special relationship between a.example.com and example.com:
156
157 >>> mgr.find_user_password("a", "http://example.com/")
158 ('1', 'a')
159 >>> mgr.find_user_password("a", "http://a.example.com/")
160 (None, None)
161
162 Ports:
163
164 >>> mgr.find_user_password("Some Realm", "c.example.com")
165 (None, None)
166 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
167 ('3', 'c')
168 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
169 ('3', 'c')
170 >>> mgr.find_user_password("Some Realm", "d.example.com")
171 ('4', 'd')
172 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
173 ('5', 'e')
174
175 """
176 pass
177
178
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000179def test_password_manager_default_port(self):
180 """
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000181 >>> mgr = urllib.request.HTTPPasswordMgr()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000182 >>> add = mgr.add_password
183
184 The point to note here is that we can't guess the default port if there's
185 no scheme. This applies to both add_password and find_user_password.
186
187 >>> add("f", "http://g.example.com:80", "10", "j")
188 >>> add("g", "http://h.example.com", "11", "k")
189 >>> add("h", "i.example.com:80", "12", "l")
190 >>> add("i", "j.example.com", "13", "m")
191 >>> mgr.find_user_password("f", "g.example.com:100")
192 (None, None)
193 >>> mgr.find_user_password("f", "g.example.com:80")
194 ('10', 'j')
195 >>> mgr.find_user_password("f", "g.example.com")
196 (None, None)
197 >>> mgr.find_user_password("f", "http://g.example.com:100")
198 (None, None)
199 >>> mgr.find_user_password("f", "http://g.example.com:80")
200 ('10', 'j')
201 >>> mgr.find_user_password("f", "http://g.example.com")
202 ('10', 'j')
203 >>> mgr.find_user_password("g", "h.example.com")
204 ('11', 'k')
205 >>> mgr.find_user_password("g", "h.example.com:80")
206 ('11', 'k')
207 >>> mgr.find_user_password("g", "http://h.example.com:80")
208 ('11', 'k')
209 >>> mgr.find_user_password("h", "i.example.com")
210 (None, None)
211 >>> mgr.find_user_password("h", "i.example.com:80")
212 ('12', 'l')
213 >>> mgr.find_user_password("h", "http://i.example.com:80")
214 ('12', 'l')
215 >>> mgr.find_user_password("i", "j.example.com")
216 ('13', 'm')
217 >>> mgr.find_user_password("i", "j.example.com:80")
218 (None, None)
219 >>> mgr.find_user_password("i", "http://j.example.com")
220 ('13', 'm')
221 >>> mgr.find_user_password("i", "http://j.example.com:80")
222 (None, None)
223
224 """
225
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000226class MockOpener:
227 addheaders = []
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000228 def open(self, req, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
229 self.req, self.data, self.timeout = req, data, timeout
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000230 def error(self, proto, *args):
231 self.proto, self.args = proto, args
232
233class MockFile:
234 def read(self, count=None): pass
235 def readline(self, count=None): pass
236 def close(self): pass
237
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000238class MockHeaders(dict):
239 def getheaders(self, name):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000240 return list(self.values())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000241
Guido van Rossum34d19282007-08-09 01:03:29 +0000242class MockResponse(io.StringIO):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000243 def __init__(self, code, msg, headers, data, url=None):
Guido van Rossum34d19282007-08-09 01:03:29 +0000244 io.StringIO.__init__(self, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000245 self.code, self.msg, self.headers, self.url = code, msg, headers, url
246 def info(self):
247 return self.headers
248 def geturl(self):
249 return self.url
250
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000251class MockCookieJar:
252 def add_cookie_header(self, request):
253 self.ach_req = request
254 def extract_cookies(self, response, request):
255 self.ec_req, self.ec_r = request, response
256
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000257class FakeMethod:
258 def __init__(self, meth_name, action, handle):
259 self.meth_name = meth_name
260 self.handle = handle
261 self.action = action
262 def __call__(self, *args):
263 return self.handle(self.meth_name, self.action, *args)
264
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +0000265class MockHTTPResponse(io.IOBase):
266 def __init__(self, fp, msg, status, reason):
267 self.fp = fp
268 self.msg = msg
269 self.status = status
270 self.reason = reason
271 self.code = 200
272
273 def read(self):
274 return ''
275
276 def info(self):
277 return {}
278
279 def geturl(self):
280 return self.url
281
282
283class MockHTTPClass:
284 def __init__(self):
285 self.level = 0
286 self.req_headers = []
287 self.data = None
288 self.raise_on_endheaders = False
289 self._tunnel_headers = {}
290
291 def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
292 self.host = host
293 self.timeout = timeout
294 return self
295
296 def set_debuglevel(self, level):
297 self.level = level
298
299 def _set_tunnel(self, host, port=None, headers=None):
300 self._tunnel_host = host
301 self._tunnel_port = port
302 if headers:
303 self._tunnel_headers = headers
304 else:
305 self._tunnel_headers.clear()
306
Benjamin Peterson794921a2009-12-24 01:18:13 +0000307 def request(self, method, url, body=None, headers=None):
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +0000308 self.method = method
309 self.selector = url
Benjamin Peterson794921a2009-12-24 01:18:13 +0000310 if headers is not None:
311 self.req_headers += headers.items()
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +0000312 self.req_headers.sort()
313 if body:
314 self.data = body
315 if self.raise_on_endheaders:
316 import socket
317 raise socket.error()
318 def getresponse(self):
319 return MockHTTPResponse(MockFile(), {}, 200, "OK")
320
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000321class MockHandler:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000322 # useful for testing handler machinery
323 # see add_ordered_mock_handlers() docstring
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000324 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000325 def __init__(self, methods):
326 self._define_methods(methods)
327 def _define_methods(self, methods):
328 for spec in methods:
329 if len(spec) == 2: name, action = spec
330 else: name, action = spec, None
331 meth = FakeMethod(name, action, self.handle)
332 setattr(self.__class__, name, meth)
333 def handle(self, fn_name, action, *args, **kwds):
334 self.parent.calls.append((self, fn_name, args, kwds))
335 if action is None:
336 return None
337 elif action == "return self":
338 return self
339 elif action == "return response":
340 res = MockResponse(200, "OK", {}, "")
341 return res
342 elif action == "return request":
343 return Request("http://blah/")
344 elif action.startswith("error"):
345 code = action[action.rfind(" ")+1:]
346 try:
347 code = int(code)
348 except ValueError:
349 pass
350 res = MockResponse(200, "OK", {}, "")
351 return self.parent.error("http", args[0], res, code, "", {})
352 elif action == "raise":
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000353 raise urllib.error.URLError("blah")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000354 assert False
355 def close(self): pass
356 def add_parent(self, parent):
357 self.parent = parent
358 self.parent.calls = []
359 def __lt__(self, other):
360 if not hasattr(other, "handler_order"):
361 # No handler_order, leave in original order. Yuck.
362 return True
363 return self.handler_order < other.handler_order
364
365def add_ordered_mock_handlers(opener, meth_spec):
366 """Create MockHandlers and add them to an OpenerDirector.
367
368 meth_spec: list of lists of tuples and strings defining methods to define
369 on handlers. eg:
370
371 [["http_error", "ftp_open"], ["http_open"]]
372
373 defines methods .http_error() and .ftp_open() on one handler, and
374 .http_open() on another. These methods just record their arguments and
375 return None. Using a tuple instead of a string causes the method to
376 perform some action (see MockHandler.handle()), eg:
377
378 [["http_error"], [("http_open", "return request")]]
379
380 defines .http_error() on one handler (which simply returns None), and
381 .http_open() on another handler, which returns a Request object.
382
383 """
384 handlers = []
385 count = 0
386 for meths in meth_spec:
387 class MockHandlerSubclass(MockHandler): pass
388 h = MockHandlerSubclass(meths)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000389 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000390 h.add_parent(opener)
391 count = count + 1
392 handlers.append(h)
393 opener.add_handler(h)
394 return handlers
395
Thomas Wouters477c8d52006-05-27 19:21:47 +0000396def build_test_opener(*handler_instances):
397 opener = OpenerDirector()
398 for h in handler_instances:
399 opener.add_handler(h)
400 return opener
401
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000402class MockHTTPHandler(urllib.request.BaseHandler):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000403 # useful for testing redirections and auth
404 # sends supplied headers and code as first response
405 # sends 200 OK as second response
406 def __init__(self, code, headers):
407 self.code = code
408 self.headers = headers
409 self.reset()
410 def reset(self):
411 self._count = 0
412 self.requests = []
413 def http_open(self, req):
Barry Warsaw820c1202008-06-12 04:06:45 +0000414 import email, http.client, copy
Guido van Rossum34d19282007-08-09 01:03:29 +0000415 from io import StringIO
Thomas Wouters477c8d52006-05-27 19:21:47 +0000416 self.requests.append(copy.deepcopy(req))
417 if self._count == 0:
418 self._count = self._count + 1
Georg Brandl24420152008-05-26 16:32:26 +0000419 name = http.client.responses[self.code]
Barry Warsaw820c1202008-06-12 04:06:45 +0000420 msg = email.message_from_string(self.headers)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000421 return self.parent.error(
422 "http", req, MockFile(), self.code, name, msg)
423 else:
424 self.req = req
Barry Warsaw820c1202008-06-12 04:06:45 +0000425 msg = email.message_from_string("\r\n\r\n")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426 return MockResponse(200, "OK", msg, "", req.get_full_url())
427
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +0000428class MockHTTPSHandler(urllib.request.AbstractHTTPHandler):
429 # Useful for testing the Proxy-Authorization request by verifying the
430 # properties of httpcon
Benjamin Peterson794921a2009-12-24 01:18:13 +0000431
432 def __init__(self):
433 urllib.request.AbstractHTTPHandler.__init__(self)
434 self.httpconn = MockHTTPClass()
435
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +0000436 def https_open(self, req):
437 return self.do_open(self.httpconn, req)
438
Thomas Wouters477c8d52006-05-27 19:21:47 +0000439class MockPasswordManager:
440 def add_password(self, realm, uri, user, password):
441 self.realm = realm
442 self.url = uri
443 self.user = user
444 self.password = password
445 def find_user_password(self, realm, authuri):
446 self.target_realm = realm
447 self.target_url = authuri
448 return self.user, self.password
449
450
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000451class OpenerDirectorTests(unittest.TestCase):
452
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000453 def test_add_non_handler(self):
454 class NonHandler(object):
455 pass
456 self.assertRaises(TypeError,
457 OpenerDirector().add_handler, NonHandler())
458
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000459 def test_badly_named_methods(self):
460 # test work-around for three methods that accidentally follow the
461 # naming conventions for handler methods
462 # (*_open() / *_request() / *_response())
463
464 # These used to call the accidentally-named methods, causing a
465 # TypeError in real code; here, returning self from these mock
466 # methods would either cause no exception, or AttributeError.
467
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000468 from urllib.error import URLError
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000469
470 o = OpenerDirector()
471 meth_spec = [
472 [("do_open", "return self"), ("proxy_open", "return self")],
473 [("redirect_request", "return self")],
474 ]
475 handlers = add_ordered_mock_handlers(o, meth_spec)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000476 o.add_handler(urllib.request.UnknownHandler())
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000477 for scheme in "do", "proxy", "redirect":
478 self.assertRaises(URLError, o.open, scheme+"://example.com/")
479
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000480 def test_handled(self):
481 # handler returning non-None means no more handlers will be called
482 o = OpenerDirector()
483 meth_spec = [
484 ["http_open", "ftp_open", "http_error_302"],
485 ["ftp_open"],
486 [("http_open", "return self")],
487 [("http_open", "return self")],
488 ]
489 handlers = add_ordered_mock_handlers(o, meth_spec)
490
491 req = Request("http://example.com/")
492 r = o.open(req)
493 # Second .http_open() gets called, third doesn't, since second returned
494 # non-None. Handlers without .http_open() never get any methods called
495 # on them.
496 # In fact, second mock handler defining .http_open() returns self
497 # (instead of response), which becomes the OpenerDirector's return
498 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000499 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000500 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
501 for expected, got in zip(calls, o.calls):
502 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000503 self.assertEqual((handler, name), expected)
504 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000505
506 def test_handler_order(self):
507 o = OpenerDirector()
508 handlers = []
509 for meths, handler_order in [
510 ([("http_open", "return self")], 500),
511 (["http_open"], 0),
512 ]:
513 class MockHandlerSubclass(MockHandler): pass
514 h = MockHandlerSubclass(meths)
515 h.handler_order = handler_order
516 handlers.append(h)
517 o.add_handler(h)
518
519 r = o.open("http://example.com/")
520 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000521 self.assertEqual(o.calls[0][0], handlers[1])
522 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000523
524 def test_raise(self):
525 # raising URLError stops processing of request
526 o = OpenerDirector()
527 meth_spec = [
528 [("http_open", "raise")],
529 [("http_open", "return self")],
530 ]
531 handlers = add_ordered_mock_handlers(o, meth_spec)
532
533 req = Request("http://example.com/")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000534 self.assertRaises(urllib.error.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000535 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000536
537## def test_error(self):
538## # XXX this doesn't actually seem to be used in standard library,
539## # but should really be tested anyway...
540
541 def test_http_error(self):
542 # XXX http_error_default
543 # http errors are a special case
544 o = OpenerDirector()
545 meth_spec = [
546 [("http_open", "error 302")],
547 [("http_error_400", "raise"), "http_open"],
548 [("http_error_302", "return response"), "http_error_303",
549 "http_error"],
550 [("http_error_302")],
551 ]
552 handlers = add_ordered_mock_handlers(o, meth_spec)
553
554 class Unknown:
555 def __eq__(self, other): return True
556
557 req = Request("http://example.com/")
558 r = o.open(req)
559 assert len(o.calls) == 2
560 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000561 (handlers[2], "http_error_302",
562 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000563 for expected, got in zip(calls, o.calls):
564 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000565 self.assertEqual((handler, method_name), got[:2])
566 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000567
568 def test_processors(self):
569 # *_request / *_response methods get called appropriately
570 o = OpenerDirector()
571 meth_spec = [
572 [("http_request", "return request"),
573 ("http_response", "return response")],
574 [("http_request", "return request"),
575 ("http_response", "return response")],
576 ]
577 handlers = add_ordered_mock_handlers(o, meth_spec)
578
579 req = Request("http://example.com/")
580 r = o.open(req)
581 # processor methods are called on *all* handlers that define them,
582 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000583 calls = [
584 (handlers[0], "http_request"), (handlers[1], "http_request"),
585 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000586
587 for i, (handler, name, args, kwds) in enumerate(o.calls):
588 if i < 2:
589 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000590 self.assertEqual((handler, name), calls[i])
591 self.assertEqual(len(args), 1)
Georg Brandlab91fde2009-08-13 08:51:18 +0000592 self.assertTrue(isinstance(args[0], Request))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000593 else:
594 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000595 self.assertEqual((handler, name), calls[i])
596 self.assertEqual(len(args), 2)
Georg Brandlab91fde2009-08-13 08:51:18 +0000597 self.assertTrue(isinstance(args[0], Request))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000598 # response from opener.open is None, because there's no
599 # handler that defines http_open to handle it
Georg Brandlab91fde2009-08-13 08:51:18 +0000600 self.assertTrue(args[1] is None or
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000601 isinstance(args[1], MockResponse))
602
603
Tim Peters58eb11c2004-01-18 20:29:55 +0000604def sanepathname2url(path):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000605 urlpath = urllib.request.pathname2url(path)
Tim Peters58eb11c2004-01-18 20:29:55 +0000606 if os.name == "nt" and urlpath.startswith("///"):
607 urlpath = urlpath[2:]
608 # XXX don't ask me about the mac...
609 return urlpath
610
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000611class HandlerTests(unittest.TestCase):
612
613 def test_ftp(self):
614 class MockFTPWrapper:
615 def __init__(self, data): self.data = data
616 def retrfile(self, filename, filetype):
617 self.filename, self.filetype = filename, filetype
Guido van Rossum34d19282007-08-09 01:03:29 +0000618 return io.StringIO(self.data), len(self.data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000619
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000620 class NullFTPHandler(urllib.request.FTPHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000621 def __init__(self, data): self.data = data
Georg Brandlf78e02b2008-06-10 17:40:04 +0000622 def connect_ftp(self, user, passwd, host, port, dirs,
623 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000624 self.user, self.passwd = user, passwd
625 self.host, self.port = host, port
626 self.dirs = dirs
627 self.ftpwrapper = MockFTPWrapper(self.data)
628 return self.ftpwrapper
629
Georg Brandlf78e02b2008-06-10 17:40:04 +0000630 import ftplib
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000631 data = "rheum rhaponicum"
632 h = NullFTPHandler(data)
633 o = h.parent = MockOpener()
634
635 for url, host, port, type_, dirs, filename, mimetype in [
636 ("ftp://localhost/foo/bar/baz.html",
637 "localhost", ftplib.FTP_PORT, "I",
638 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000639 ("ftp://localhost:80/foo/bar/",
640 "localhost", 80, "D",
641 ["foo", "bar"], "", None),
642 ("ftp://localhost/baz.gif;type=a",
643 "localhost", ftplib.FTP_PORT, "A",
644 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000645 ]:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000646 req = Request(url)
647 req.timeout = None
648 r = h.ftp_open(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000649 # ftp authentication not yet implemented by FTPHandler
Georg Brandlab91fde2009-08-13 08:51:18 +0000650 self.assertTrue(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000651 self.assertEqual(h.host, socket.gethostbyname(host))
652 self.assertEqual(h.port, port)
653 self.assertEqual(h.dirs, dirs)
654 self.assertEqual(h.ftpwrapper.filename, filename)
655 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000656 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000657 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000658 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000659
660 def test_file(self):
Benjamin Petersona0c0a4a2008-06-12 22:15:50 +0000661 import email.utils, socket
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000662 h = urllib.request.FileHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000663 o = h.parent = MockOpener()
664
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000665 TESTFN = support.TESTFN
Tim Peters58eb11c2004-01-18 20:29:55 +0000666 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Guido van Rossum6a2ccd02007-07-16 20:51:57 +0000667 towrite = b"hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000668 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000669 "file://localhost%s" % urlpath,
670 "file://%s" % urlpath,
671 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000672 ]
673 try:
674 localaddr = socket.gethostbyname(socket.gethostname())
675 except socket.gaierror:
676 localaddr = ''
677 if localaddr:
678 urls.append("file://%s%s" % (localaddr, urlpath))
679
680 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000681 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000682 try:
683 try:
684 f.write(towrite)
685 finally:
686 f.close()
687
688 r = h.file_open(Request(url))
689 try:
690 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000691 headers = r.info()
Senthil Kumaran51964772010-05-08 03:31:56 +0000692 respurl = r.geturl()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000693 finally:
694 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000695 stats = os.stat(TESTFN)
Benjamin Petersona0c0a4a2008-06-12 22:15:50 +0000696 modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000697 finally:
698 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000699 self.assertEqual(data, towrite)
700 self.assertEqual(headers["Content-type"], "text/plain")
701 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000702 self.assertEqual(headers["Last-modified"], modified)
Senthil Kumaran51964772010-05-08 03:31:56 +0000703 self.assertEqual(respurl, url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000704
705 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000706 "file://localhost:80%s" % urlpath,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000707 "file:///file_does_not_exist.txt",
708 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
709 os.getcwd(), TESTFN),
710 "file://somerandomhost.ontheinternet.com%s/%s" %
711 (os.getcwd(), TESTFN),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000712 ]:
713 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000714 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000715 try:
716 f.write(towrite)
717 finally:
718 f.close()
719
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000720 self.assertRaises(urllib.error.URLError,
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000721 h.file_open, Request(url))
722 finally:
723 os.remove(TESTFN)
724
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000725 h = urllib.request.FileHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000726 o = h.parent = MockOpener()
727 # XXXX why does // mean ftp (and /// mean not ftp!), and where
728 # is file: scheme specified? I think this is really a bug, and
729 # what was intended was to distinguish between URLs like:
730 # file:/blah.txt (a file)
731 # file://localhost/blah.txt (a file)
732 # file:///blah.txt (a file)
733 # file://ftp.example.com/blah.txt (an ftp URL)
734 for url, ftp in [
735 ("file://ftp.example.com//foo.txt", True),
736 ("file://ftp.example.com///foo.txt", False),
737# XXXX bug: fails with OSError, should be URLError
738 ("file://ftp.example.com/foo.txt", False),
Senthil Kumaran34024142010-07-11 03:15:25 +0000739 ("file://somehost//foo/something.txt", True),
740 ("file://localhost//foo/something.txt", False),
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000741 ]:
742 req = Request(url)
743 try:
744 h.file_open(req)
745 # XXXX remove OSError when bug fixed
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000746 except (urllib.error.URLError, OSError):
Georg Brandlab91fde2009-08-13 08:51:18 +0000747 self.assertTrue(not ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000748 else:
Georg Brandlab91fde2009-08-13 08:51:18 +0000749 self.assertTrue(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000750 self.assertEqual(req.type, "ftp")
Senthil Kumaran34024142010-07-11 03:15:25 +0000751 self.assertEqual(req.type is "ftp", ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000752
753 def test_http(self):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000754
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000755 h = urllib.request.AbstractHTTPHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000756 o = h.parent = MockOpener()
757
758 url = "http://example.com/"
759 for method, data in [("GET", None), ("POST", "blah")]:
760 req = Request(url, data, {"Foo": "bar"})
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000761 req.timeout = None
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000762 req.add_unredirected_header("Spam", "eggs")
763 http = MockHTTPClass()
764 r = h.do_open(http, req)
765
766 # result attributes
767 r.read; r.readline # wrapped MockFile methods
768 r.info; r.geturl # addinfourl methods
769 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
770 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000771 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000772 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000773
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000774 self.assertEqual(http.host, "example.com")
775 self.assertEqual(http.level, 0)
776 self.assertEqual(http.method, method)
777 self.assertEqual(http.selector, "/")
778 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000779 [("Connection", "close"),
780 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000781 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000782
783 # check socket.error converted to URLError
784 http.raise_on_endheaders = True
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000785 self.assertRaises(urllib.error.URLError, h.do_open, http, req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000786
787 # check adding of standard headers
788 o.addheaders = [("Spam", "eggs")]
789 for data in "", None: # POST, GET
790 req = Request("http://example.com/", data)
791 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000792 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000793 if data is None: # GET
Georg Brandlab91fde2009-08-13 08:51:18 +0000794 self.assertTrue("Content-length" not in req.unredirected_hdrs)
795 self.assertTrue("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000796 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000797 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
798 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000799 "application/x-www-form-urlencoded")
800 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000801 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
802 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000803
804 # don't clobber existing headers
805 req.add_unredirected_header("Content-length", "foo")
806 req.add_unredirected_header("Content-type", "bar")
807 req.add_unredirected_header("Host", "baz")
808 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000809 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000810 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
811 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000812 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
813 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000814
Facundo Batista72dc1ea2008-08-16 14:44:32 +0000815 def test_http_doubleslash(self):
816 # Checks the presence of any unnecessary double slash in url does not
817 # break anything. Previously, a double slash directly after the host
818 # could could cause incorrect parsing.
819 h = urllib.request.AbstractHTTPHandler()
820 o = h.parent = MockOpener()
821
822 data = ""
823 ds_urls = [
824 "http://example.com/foo/bar/baz.html",
825 "http://example.com//foo/bar/baz.html",
826 "http://example.com/foo//bar/baz.html",
827 "http://example.com/foo/bar//baz.html"
828 ]
829
830 for ds_url in ds_urls:
831 ds_req = Request(ds_url, data)
832
833 # Check whether host is determined correctly if there is no proxy
834 np_ds_req = h.do_request_(ds_req)
835 self.assertEqual(np_ds_req.unredirected_hdrs["Host"],"example.com")
836
837 # Check whether host is determined correctly if there is a proxy
838 ds_req.set_proxy("someproxy:3128",None)
839 p_ds_req = h.do_request_(ds_req)
840 self.assertEqual(p_ds_req.unredirected_hdrs["Host"],"example.com")
841
842
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000843 def test_errors(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000844 h = urllib.request.HTTPErrorProcessor()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000845 o = h.parent = MockOpener()
846
847 url = "http://example.com/"
848 req = Request(url)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000849 # all 2xx are passed through
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000850 r = MockResponse(200, "OK", {}, "", url)
851 newr = h.http_response(req, r)
Georg Brandlab91fde2009-08-13 08:51:18 +0000852 self.assertTrue(r is newr)
853 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000854 r = MockResponse(202, "Accepted", {}, "", url)
855 newr = h.http_response(req, r)
Georg Brandlab91fde2009-08-13 08:51:18 +0000856 self.assertTrue(r is newr)
857 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000858 r = MockResponse(206, "Partial content", {}, "", url)
859 newr = h.http_response(req, r)
Georg Brandlab91fde2009-08-13 08:51:18 +0000860 self.assertTrue(r is newr)
861 self.assertTrue(not hasattr(o, "proto")) # o.error not called
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000862 # anything else calls o.error (and MockOpener returns None, here)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000863 r = MockResponse(502, "Bad gateway", {}, "", url)
Georg Brandlab91fde2009-08-13 08:51:18 +0000864 self.assertTrue(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000865 self.assertEqual(o.proto, "http") # o.error called
Guido van Rossumd8faa362007-04-27 19:54:29 +0000866 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000867
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000868 def test_cookies(self):
869 cj = MockCookieJar()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000870 h = urllib.request.HTTPCookieProcessor(cj)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000871 o = h.parent = MockOpener()
872
873 req = Request("http://example.com/")
874 r = MockResponse(200, "OK", {}, "")
875 newreq = h.http_request(req)
Georg Brandlab91fde2009-08-13 08:51:18 +0000876 self.assertTrue(cj.ach_req is req is newreq)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000877 self.assertEquals(req.get_origin_req_host(), "example.com")
Georg Brandlab91fde2009-08-13 08:51:18 +0000878 self.assertTrue(not req.is_unverifiable())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000879 newr = h.http_response(req, r)
Georg Brandlab91fde2009-08-13 08:51:18 +0000880 self.assertTrue(cj.ec_req is req)
881 self.assertTrue(cj.ec_r is r is newr)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000882
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000883 def test_redirect(self):
884 from_url = "http://example.com/a.html"
885 to_url = "http://example.com/b.html"
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000886 h = urllib.request.HTTPRedirectHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000887 o = h.parent = MockOpener()
888
889 # ordinary redirect behaviour
890 for code in 301, 302, 303, 307:
891 for data in None, "blah\nblah\n":
892 method = getattr(h, "http_error_%s" % code)
893 req = Request(from_url, data)
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000894 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000895 req.add_header("Nonsense", "viking=withhold")
Christian Heimes77c02eb2008-02-09 02:18:51 +0000896 if data is not None:
897 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000898 req.add_unredirected_header("Spam", "spam")
899 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000900 method(req, MockFile(), code, "Blah",
901 MockHeaders({"location": to_url}))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000902 except urllib.error.HTTPError:
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000903 # 307 in response to POST requires user OK
Georg Brandlab91fde2009-08-13 08:51:18 +0000904 self.assertTrue(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000905 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000906 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000907 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000908 except AttributeError:
Georg Brandlab91fde2009-08-13 08:51:18 +0000909 self.assertTrue(not o.req.has_data())
Christian Heimes77c02eb2008-02-09 02:18:51 +0000910
911 # now it's a GET, there should not be headers regarding content
912 # (possibly dragged from before being a POST)
913 headers = [x.lower() for x in o.req.headers]
914 self.assertTrue("content-length" not in headers)
915 self.assertTrue("content-type" not in headers)
916
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000917 self.assertEqual(o.req.headers["Nonsense"],
918 "viking=withhold")
Georg Brandlab91fde2009-08-13 08:51:18 +0000919 self.assertTrue("Spam" not in o.req.headers)
920 self.assertTrue("Spam" not in o.req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000921
922 # loop detection
923 req = Request(from_url)
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000924 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000925 def redirect(h, req, url=to_url):
926 h.http_error_302(req, MockFile(), 302, "Blah",
927 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000928 # Note that the *original* request shares the same record of
929 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000930
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000931 # detect infinite loop redirect of a URL to itself
932 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000933 count = 0
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000934 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000935 try:
936 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000937 redirect(h, req, "http://example.com/")
938 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000939 except urllib.error.HTTPError:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000940 # don't stop until max_repeats, because cookies may introduce state
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000941 self.assertEqual(count, urllib.request.HTTPRedirectHandler.max_repeats)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000942
943 # detect endless non-repeating chain of redirects
944 req = Request(from_url, origin_req_host="example.com")
945 count = 0
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000946 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000947 try:
948 while 1:
949 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000950 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000951 except urllib.error.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000952 self.assertEqual(count,
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000953 urllib.request.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000954
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000955 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000956 # cookies shouldn't leak into redirected requests
Georg Brandl24420152008-05-26 16:32:26 +0000957 from http.cookiejar import CookieJar
958 from test.test_http_cookiejar import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000959
960 cj = CookieJar()
961 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000962 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000963 hdeh = urllib.request.HTTPDefaultErrorHandler()
964 hrh = urllib.request.HTTPRedirectHandler()
965 cp = urllib.request.HTTPCookieProcessor(cj)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000966 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000967 o.open("http://www.example.com/")
Georg Brandlab91fde2009-08-13 08:51:18 +0000968 self.assertTrue(not hh.req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000969
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000970 def test_proxy(self):
971 o = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000972 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000973 o.add_handler(ph)
974 meth_spec = [
975 [("http_open", "return response")]
976 ]
977 handlers = add_ordered_mock_handlers(o, meth_spec)
978
979 req = Request("http://acme.example.com/")
980 self.assertEqual(req.get_host(), "acme.example.com")
981 r = o.open(req)
982 self.assertEqual(req.get_host(), "proxy.example.com:3128")
983
984 self.assertEqual([(handlers[0], "http_open")],
985 [tup[0:2] for tup in o.calls])
986
Senthil Kumaran11301632009-10-11 06:07:46 +0000987 def test_proxy_no_proxy(self):
988 os.environ['no_proxy'] = 'python.org'
989 o = OpenerDirector()
990 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com"))
991 o.add_handler(ph)
992 req = Request("http://www.perl.org/")
993 self.assertEqual(req.get_host(), "www.perl.org")
994 r = o.open(req)
995 self.assertEqual(req.get_host(), "proxy.example.com")
996 req = Request("http://www.python.org")
997 self.assertEqual(req.get_host(), "www.python.org")
998 r = o.open(req)
999 self.assertEqual(req.get_host(), "www.python.org")
1000 del os.environ['no_proxy']
1001
1002
Senthil Kumaran0ac1f832009-07-26 12:39:47 +00001003 def test_proxy_https(self):
1004 o = OpenerDirector()
1005 ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
1006 o.add_handler(ph)
1007 meth_spec = [
1008 [("https_open", "return response")]
1009 ]
1010 handlers = add_ordered_mock_handlers(o, meth_spec)
1011
1012 req = Request("https://www.example.com/")
1013 self.assertEqual(req.get_host(), "www.example.com")
1014 r = o.open(req)
1015 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1016 self.assertEqual([(handlers[0], "https_open")],
1017 [tup[0:2] for tup in o.calls])
1018
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +00001019 def test_proxy_https_proxy_authorization(self):
1020 o = OpenerDirector()
1021 ph = urllib.request.ProxyHandler(dict(https='proxy.example.com:3128'))
1022 o.add_handler(ph)
1023 https_handler = MockHTTPSHandler()
1024 o.add_handler(https_handler)
1025 req = Request("https://www.example.com/")
1026 req.add_header("Proxy-Authorization","FooBar")
1027 req.add_header("User-Agent","Grail")
1028 self.assertEqual(req.get_host(), "www.example.com")
1029 self.assertIsNone(req._tunnel_host)
1030 r = o.open(req)
1031 # Verify Proxy-Authorization gets tunneled to request.
1032 # httpsconn req_headers do not have the Proxy-Authorization header but
1033 # the req will have.
1034 self.assertFalse(("Proxy-Authorization","FooBar") in
1035 https_handler.httpconn.req_headers)
1036 self.assertTrue(("User-Agent","Grail") in
1037 https_handler.httpconn.req_headers)
1038 self.assertIsNotNone(req._tunnel_host)
1039 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1040 self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
Senthil Kumaran0ac1f832009-07-26 12:39:47 +00001041
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001042 def test_basic_auth(self, quote_char='"'):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001043 opener = OpenerDirector()
1044 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001045 auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001046 realm = "ACME Widget Store"
1047 http_handler = MockHTTPHandler(
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001048 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
1049 (quote_char, realm, quote_char) )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001050 opener.add_handler(auth_handler)
1051 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001052 self._test_basic_auth(opener, auth_handler, "Authorization",
1053 realm, http_handler, password_manager,
1054 "http://acme.example.com/protected",
1055 "http://acme.example.com/protected",
1056 )
1057
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001058 def test_basic_auth_with_single_quoted_realm(self):
1059 self.test_basic_auth(quote_char="'")
1060
Thomas Wouters477c8d52006-05-27 19:21:47 +00001061 def test_proxy_basic_auth(self):
1062 opener = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001063 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001064 opener.add_handler(ph)
1065 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001066 auth_handler = urllib.request.ProxyBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001067 realm = "ACME Networks"
1068 http_handler = MockHTTPHandler(
1069 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001070 opener.add_handler(auth_handler)
1071 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001072 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001073 realm, http_handler, password_manager,
1074 "http://acme.example.com:3128/protected",
1075 "proxy.example.com:3128",
1076 )
1077
1078 def test_basic_and_digest_auth_handlers(self):
1079 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
1080 # response (http://python.org/sf/1479302), where it should instead
1081 # return None to allow another handler (especially
1082 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001083
1084 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
1085 # try digest first (since it's the strongest auth scheme), so we record
1086 # order of calls here to check digest comes first:
1087 class RecordingOpenerDirector(OpenerDirector):
1088 def __init__(self):
1089 OpenerDirector.__init__(self)
1090 self.recorded = []
1091 def record(self, info):
1092 self.recorded.append(info)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001093 class TestDigestAuthHandler(urllib.request.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001094 def http_error_401(self, *args, **kwds):
1095 self.parent.record("digest")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001096 urllib.request.HTTPDigestAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001097 *args, **kwds)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001098 class TestBasicAuthHandler(urllib.request.HTTPBasicAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001099 def http_error_401(self, *args, **kwds):
1100 self.parent.record("basic")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001101 urllib.request.HTTPBasicAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001102 *args, **kwds)
1103
1104 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001105 password_manager = MockPasswordManager()
1106 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001107 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001108 realm = "ACME Networks"
1109 http_handler = MockHTTPHandler(
1110 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001111 opener.add_handler(basic_handler)
1112 opener.add_handler(digest_handler)
1113 opener.add_handler(http_handler)
1114
1115 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +00001116 self._test_basic_auth(opener, basic_handler, "Authorization",
1117 realm, http_handler, password_manager,
1118 "http://acme.example.com/protected",
1119 "http://acme.example.com/protected",
1120 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001121 # check digest was tried before basic (twice, because
1122 # _test_basic_auth called .open() twice)
1123 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001124
1125 def _test_basic_auth(self, opener, auth_handler, auth_header,
1126 realm, http_handler, password_manager,
1127 request_url, protected_url):
Christian Heimes05e8be12008-02-23 18:30:17 +00001128 import base64
Thomas Wouters477c8d52006-05-27 19:21:47 +00001129 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +00001130
1131 # .add_password() fed through to password manager
1132 auth_handler.add_password(realm, request_url, user, password)
1133 self.assertEqual(realm, password_manager.realm)
1134 self.assertEqual(request_url, password_manager.url)
1135 self.assertEqual(user, password_manager.user)
1136 self.assertEqual(password, password_manager.password)
1137
1138 r = opener.open(request_url)
1139
1140 # should have asked the password manager for the username/password
1141 self.assertEqual(password_manager.target_realm, realm)
1142 self.assertEqual(password_manager.target_url, protected_url)
1143
1144 # expect one request without authorization, then one with
1145 self.assertEqual(len(http_handler.requests), 2)
1146 self.assertFalse(http_handler.requests[0].has_header(auth_header))
Guido van Rossum98b349f2007-08-27 21:47:52 +00001147 userpass = bytes('%s:%s' % (user, password), "ascii")
Guido van Rossum98297ee2007-11-06 21:34:58 +00001148 auth_hdr_value = ('Basic ' +
Georg Brandl706824f2009-06-04 09:42:55 +00001149 base64.encodebytes(userpass).strip().decode())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001150 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1151 auth_hdr_value)
Senthil Kumaranefcd8832010-02-24 16:56:20 +00001152 self.assertEqual(http_handler.requests[1].unredirected_hdrs[auth_header],
1153 auth_hdr_value)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001154 # if the password manager can't find a password, the handler won't
1155 # handle the HTTP auth error
1156 password_manager.user = password_manager.password = None
1157 http_handler.reset()
1158 r = opener.open(request_url)
1159 self.assertEqual(len(http_handler.requests), 1)
1160 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1161
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001162class MiscTests(unittest.TestCase):
1163
1164 def test_build_opener(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001165 class MyHTTPHandler(urllib.request.HTTPHandler): pass
1166 class FooHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001167 def foo_open(self): pass
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001168 class BarHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001169 def bar_open(self): pass
1170
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001171 build_opener = urllib.request.build_opener
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001172
1173 o = build_opener(FooHandler, BarHandler)
1174 self.opener_has_handler(o, FooHandler)
1175 self.opener_has_handler(o, BarHandler)
1176
1177 # can take a mix of classes and instances
1178 o = build_opener(FooHandler, BarHandler())
1179 self.opener_has_handler(o, FooHandler)
1180 self.opener_has_handler(o, BarHandler)
1181
1182 # subclasses of default handlers override default handlers
1183 o = build_opener(MyHTTPHandler)
1184 self.opener_has_handler(o, MyHTTPHandler)
1185
1186 # a particular case of overriding: default handlers can be passed
1187 # in explicitly
1188 o = build_opener()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001189 self.opener_has_handler(o, urllib.request.HTTPHandler)
1190 o = build_opener(urllib.request.HTTPHandler)
1191 self.opener_has_handler(o, urllib.request.HTTPHandler)
1192 o = build_opener(urllib.request.HTTPHandler())
1193 self.opener_has_handler(o, urllib.request.HTTPHandler)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001194
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001195 # Issue2670: multiple handlers sharing the same base class
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001196 class MyOtherHTTPHandler(urllib.request.HTTPHandler): pass
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001197 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1198 self.opener_has_handler(o, MyHTTPHandler)
1199 self.opener_has_handler(o, MyOtherHTTPHandler)
1200
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001201 def opener_has_handler(self, opener, handler_class):
1202 for h in opener.handlers:
1203 if h.__class__ == handler_class:
1204 break
1205 else:
Georg Brandlab91fde2009-08-13 08:51:18 +00001206 self.assertTrue(False)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001207
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001208class RequestTests(unittest.TestCase):
1209
1210 def setUp(self):
1211 self.get = Request("http://www.python.org/~jeremy/")
1212 self.post = Request("http://www.python.org/~jeremy/",
1213 "data",
1214 headers={"X-Test": "test"})
1215
1216 def test_method(self):
1217 self.assertEqual("POST", self.post.get_method())
1218 self.assertEqual("GET", self.get.get_method())
1219
1220 def test_add_data(self):
Georg Brandlab91fde2009-08-13 08:51:18 +00001221 self.assertTrue(not self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001222 self.assertEqual("GET", self.get.get_method())
1223 self.get.add_data("spam")
Georg Brandlab91fde2009-08-13 08:51:18 +00001224 self.assertTrue(self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001225 self.assertEqual("POST", self.get.get_method())
1226
1227 def test_get_full_url(self):
1228 self.assertEqual("http://www.python.org/~jeremy/",
1229 self.get.get_full_url())
1230
1231 def test_selector(self):
1232 self.assertEqual("/~jeremy/", self.get.get_selector())
1233 req = Request("http://www.python.org/")
1234 self.assertEqual("/", req.get_selector())
1235
1236 def test_get_type(self):
1237 self.assertEqual("http", self.get.get_type())
1238
1239 def test_get_host(self):
1240 self.assertEqual("www.python.org", self.get.get_host())
1241
1242 def test_get_host_unquote(self):
1243 req = Request("http://www.%70ython.org/")
1244 self.assertEqual("www.python.org", req.get_host())
1245
1246 def test_proxy(self):
Georg Brandlab91fde2009-08-13 08:51:18 +00001247 self.assertTrue(not self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001248 self.get.set_proxy("www.perl.org", "http")
Georg Brandlab91fde2009-08-13 08:51:18 +00001249 self.assertTrue(self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001250 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1251 self.assertEqual("www.perl.org", self.get.get_host())
1252
Senthil Kumaran4c88db72010-08-08 11:30:58 +00001253 def test_wrapped_url(self):
1254 req = Request("<URL:http://www.python.org>")
1255 self.assertEqual("www.python.org", req.get_host())
1256
1257 def test_urlwith_fragment(self):
1258 req = Request("http://www.python.org/?qs=query#fragment=true")
1259 self.assertEqual("/?qs=query", req.get_selector())
1260 req = Request("http://www.python.org/#fun=true")
1261 self.assertEqual("/", req.get_selector())
1262
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001263
1264def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001265 from test import test_urllib2
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001266 support.run_doctest(test_urllib2, verbose)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001267 support.run_doctest(urllib.request, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001268 tests = (TrivialTests,
1269 OpenerDirectorTests,
1270 HandlerTests,
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001271 MiscTests,
1272 RequestTests)
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001273 support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001274
1275if __name__ == "__main__":
1276 test_main(verbose=True)