blob: 0f8395e8ca181547c9c2d654bca55f0afb8afed0 [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:
Florent Xiclunab4efb3d2010-08-14 18:24:40 +000049 self.assertEqual(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):
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000747 self.assertFalse(ftp)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000748 else:
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000749 self.assertIs(o.req, 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)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000852 self.assertIs(r, newr)
853 self.assertFalse(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)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000856 self.assertIs(r, newr)
857 self.assertFalse(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)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000860 self.assertIs(r, newr)
861 self.assertFalse(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)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000864 self.assertIsNone(h.http_response(req, r))
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)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000876 self.assertIs(cj.ach_req, req)
877 self.assertIs(cj.ach_req, newreq)
878 self.assertEqual(req.get_origin_req_host(), "example.com")
879 self.assertFalse(req.is_unverifiable())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000880 newr = h.http_response(req, r)
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000881 self.assertIs(cj.ec_req, req)
882 self.assertIs(cj.ec_r, r)
883 self.assertIs(r, newr)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000884
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000885 def test_redirect(self):
886 from_url = "http://example.com/a.html"
887 to_url = "http://example.com/b.html"
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000888 h = urllib.request.HTTPRedirectHandler()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000889 o = h.parent = MockOpener()
890
891 # ordinary redirect behaviour
892 for code in 301, 302, 303, 307:
893 for data in None, "blah\nblah\n":
894 method = getattr(h, "http_error_%s" % code)
895 req = Request(from_url, data)
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000896 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000897 req.add_header("Nonsense", "viking=withhold")
Christian Heimes77c02eb2008-02-09 02:18:51 +0000898 if data is not None:
899 req.add_header("Content-Length", str(len(data)))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000900 req.add_unredirected_header("Spam", "spam")
901 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000902 method(req, MockFile(), code, "Blah",
903 MockHeaders({"location": to_url}))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000904 except urllib.error.HTTPError:
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000905 # 307 in response to POST requires user OK
Georg Brandlab91fde2009-08-13 08:51:18 +0000906 self.assertTrue(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000907 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000908 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000909 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000910 except AttributeError:
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000911 self.assertFalse(o.req.has_data())
Christian Heimes77c02eb2008-02-09 02:18:51 +0000912
913 # now it's a GET, there should not be headers regarding content
914 # (possibly dragged from before being a POST)
915 headers = [x.lower() for x in o.req.headers]
916 self.assertTrue("content-length" not in headers)
917 self.assertTrue("content-type" not in headers)
918
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000919 self.assertEqual(o.req.headers["Nonsense"],
920 "viking=withhold")
Georg Brandlab91fde2009-08-13 08:51:18 +0000921 self.assertTrue("Spam" not in o.req.headers)
922 self.assertTrue("Spam" not in o.req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000923
924 # loop detection
925 req = Request(from_url)
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000926 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000927 def redirect(h, req, url=to_url):
928 h.http_error_302(req, MockFile(), 302, "Blah",
929 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000930 # Note that the *original* request shares the same record of
931 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000932
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000933 # detect infinite loop redirect of a URL to itself
934 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000935 count = 0
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000936 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000937 try:
938 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000939 redirect(h, req, "http://example.com/")
940 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000941 except urllib.error.HTTPError:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000942 # don't stop until max_repeats, because cookies may introduce state
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000943 self.assertEqual(count, urllib.request.HTTPRedirectHandler.max_repeats)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000944
945 # detect endless non-repeating chain of redirects
946 req = Request(from_url, origin_req_host="example.com")
947 count = 0
Senthil Kumarane9da06f2009-07-19 04:20:12 +0000948 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000949 try:
950 while 1:
951 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000952 count = count + 1
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000953 except urllib.error.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000954 self.assertEqual(count,
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000955 urllib.request.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000956
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000957 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000958 # cookies shouldn't leak into redirected requests
Georg Brandl24420152008-05-26 16:32:26 +0000959 from http.cookiejar import CookieJar
960 from test.test_http_cookiejar import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000961
962 cj = CookieJar()
963 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000964 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000965 hdeh = urllib.request.HTTPDefaultErrorHandler()
966 hrh = urllib.request.HTTPRedirectHandler()
967 cp = urllib.request.HTTPCookieProcessor(cj)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000968 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000969 o.open("http://www.example.com/")
Florent Xiclunab4efb3d2010-08-14 18:24:40 +0000970 self.assertFalse(hh.req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000971
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000972 def test_proxy(self):
973 o = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000974 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000975 o.add_handler(ph)
976 meth_spec = [
977 [("http_open", "return response")]
978 ]
979 handlers = add_ordered_mock_handlers(o, meth_spec)
980
981 req = Request("http://acme.example.com/")
982 self.assertEqual(req.get_host(), "acme.example.com")
983 r = o.open(req)
984 self.assertEqual(req.get_host(), "proxy.example.com:3128")
985
986 self.assertEqual([(handlers[0], "http_open")],
987 [tup[0:2] for tup in o.calls])
988
Senthil Kumaran11301632009-10-11 06:07:46 +0000989 def test_proxy_no_proxy(self):
990 os.environ['no_proxy'] = 'python.org'
991 o = OpenerDirector()
992 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com"))
993 o.add_handler(ph)
994 req = Request("http://www.perl.org/")
995 self.assertEqual(req.get_host(), "www.perl.org")
996 r = o.open(req)
997 self.assertEqual(req.get_host(), "proxy.example.com")
998 req = Request("http://www.python.org")
999 self.assertEqual(req.get_host(), "www.python.org")
1000 r = o.open(req)
1001 self.assertEqual(req.get_host(), "www.python.org")
1002 del os.environ['no_proxy']
1003
1004
Senthil Kumaran0ac1f832009-07-26 12:39:47 +00001005 def test_proxy_https(self):
1006 o = OpenerDirector()
1007 ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
1008 o.add_handler(ph)
1009 meth_spec = [
1010 [("https_open", "return response")]
1011 ]
1012 handlers = add_ordered_mock_handlers(o, meth_spec)
1013
1014 req = Request("https://www.example.com/")
1015 self.assertEqual(req.get_host(), "www.example.com")
1016 r = o.open(req)
1017 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1018 self.assertEqual([(handlers[0], "https_open")],
1019 [tup[0:2] for tup in o.calls])
1020
Senthil Kumaran4b9fbeb2009-12-20 07:18:22 +00001021 def test_proxy_https_proxy_authorization(self):
1022 o = OpenerDirector()
1023 ph = urllib.request.ProxyHandler(dict(https='proxy.example.com:3128'))
1024 o.add_handler(ph)
1025 https_handler = MockHTTPSHandler()
1026 o.add_handler(https_handler)
1027 req = Request("https://www.example.com/")
1028 req.add_header("Proxy-Authorization","FooBar")
1029 req.add_header("User-Agent","Grail")
1030 self.assertEqual(req.get_host(), "www.example.com")
1031 self.assertIsNone(req._tunnel_host)
1032 r = o.open(req)
1033 # Verify Proxy-Authorization gets tunneled to request.
1034 # httpsconn req_headers do not have the Proxy-Authorization header but
1035 # the req will have.
1036 self.assertFalse(("Proxy-Authorization","FooBar") in
1037 https_handler.httpconn.req_headers)
1038 self.assertTrue(("User-Agent","Grail") in
1039 https_handler.httpconn.req_headers)
1040 self.assertIsNotNone(req._tunnel_host)
1041 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1042 self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
Senthil Kumaran0ac1f832009-07-26 12:39:47 +00001043
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001044 def test_basic_auth(self, quote_char='"'):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001045 opener = OpenerDirector()
1046 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001047 auth_handler = urllib.request.HTTPBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001048 realm = "ACME Widget Store"
1049 http_handler = MockHTTPHandler(
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001050 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
1051 (quote_char, realm, quote_char) )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001052 opener.add_handler(auth_handler)
1053 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001054 self._test_basic_auth(opener, auth_handler, "Authorization",
1055 realm, http_handler, password_manager,
1056 "http://acme.example.com/protected",
1057 "http://acme.example.com/protected",
1058 )
1059
Christian Heimes4fbc72b2008-03-22 00:47:35 +00001060 def test_basic_auth_with_single_quoted_realm(self):
1061 self.test_basic_auth(quote_char="'")
1062
Thomas Wouters477c8d52006-05-27 19:21:47 +00001063 def test_proxy_basic_auth(self):
1064 opener = OpenerDirector()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001065 ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001066 opener.add_handler(ph)
1067 password_manager = MockPasswordManager()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001068 auth_handler = urllib.request.ProxyBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001069 realm = "ACME Networks"
1070 http_handler = MockHTTPHandler(
1071 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001072 opener.add_handler(auth_handler)
1073 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001074 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001075 realm, http_handler, password_manager,
1076 "http://acme.example.com:3128/protected",
1077 "proxy.example.com:3128",
1078 )
1079
1080 def test_basic_and_digest_auth_handlers(self):
1081 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
1082 # response (http://python.org/sf/1479302), where it should instead
1083 # return None to allow another handler (especially
1084 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001085
1086 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
1087 # try digest first (since it's the strongest auth scheme), so we record
1088 # order of calls here to check digest comes first:
1089 class RecordingOpenerDirector(OpenerDirector):
1090 def __init__(self):
1091 OpenerDirector.__init__(self)
1092 self.recorded = []
1093 def record(self, info):
1094 self.recorded.append(info)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001095 class TestDigestAuthHandler(urllib.request.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001096 def http_error_401(self, *args, **kwds):
1097 self.parent.record("digest")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001098 urllib.request.HTTPDigestAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001099 *args, **kwds)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001100 class TestBasicAuthHandler(urllib.request.HTTPBasicAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001101 def http_error_401(self, *args, **kwds):
1102 self.parent.record("basic")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001103 urllib.request.HTTPBasicAuthHandler.http_error_401(self,
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001104 *args, **kwds)
1105
1106 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107 password_manager = MockPasswordManager()
1108 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001109 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001110 realm = "ACME Networks"
1111 http_handler = MockHTTPHandler(
1112 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001113 opener.add_handler(basic_handler)
1114 opener.add_handler(digest_handler)
1115 opener.add_handler(http_handler)
1116
1117 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +00001118 self._test_basic_auth(opener, basic_handler, "Authorization",
1119 realm, http_handler, password_manager,
1120 "http://acme.example.com/protected",
1121 "http://acme.example.com/protected",
1122 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001123 # check digest was tried before basic (twice, because
1124 # _test_basic_auth called .open() twice)
1125 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001126
1127 def _test_basic_auth(self, opener, auth_handler, auth_header,
1128 realm, http_handler, password_manager,
1129 request_url, protected_url):
Christian Heimes05e8be12008-02-23 18:30:17 +00001130 import base64
Thomas Wouters477c8d52006-05-27 19:21:47 +00001131 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +00001132
1133 # .add_password() fed through to password manager
1134 auth_handler.add_password(realm, request_url, user, password)
1135 self.assertEqual(realm, password_manager.realm)
1136 self.assertEqual(request_url, password_manager.url)
1137 self.assertEqual(user, password_manager.user)
1138 self.assertEqual(password, password_manager.password)
1139
1140 r = opener.open(request_url)
1141
1142 # should have asked the password manager for the username/password
1143 self.assertEqual(password_manager.target_realm, realm)
1144 self.assertEqual(password_manager.target_url, protected_url)
1145
1146 # expect one request without authorization, then one with
1147 self.assertEqual(len(http_handler.requests), 2)
1148 self.assertFalse(http_handler.requests[0].has_header(auth_header))
Guido van Rossum98b349f2007-08-27 21:47:52 +00001149 userpass = bytes('%s:%s' % (user, password), "ascii")
Guido van Rossum98297ee2007-11-06 21:34:58 +00001150 auth_hdr_value = ('Basic ' +
Georg Brandl706824f2009-06-04 09:42:55 +00001151 base64.encodebytes(userpass).strip().decode())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1153 auth_hdr_value)
Senthil Kumaranefcd8832010-02-24 16:56:20 +00001154 self.assertEqual(http_handler.requests[1].unredirected_hdrs[auth_header],
1155 auth_hdr_value)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001156 # if the password manager can't find a password, the handler won't
1157 # handle the HTTP auth error
1158 password_manager.user = password_manager.password = None
1159 http_handler.reset()
1160 r = opener.open(request_url)
1161 self.assertEqual(len(http_handler.requests), 1)
1162 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1163
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001164class MiscTests(unittest.TestCase):
1165
1166 def test_build_opener(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001167 class MyHTTPHandler(urllib.request.HTTPHandler): pass
1168 class FooHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001169 def foo_open(self): pass
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001170 class BarHandler(urllib.request.BaseHandler):
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001171 def bar_open(self): pass
1172
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001173 build_opener = urllib.request.build_opener
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001174
1175 o = build_opener(FooHandler, BarHandler)
1176 self.opener_has_handler(o, FooHandler)
1177 self.opener_has_handler(o, BarHandler)
1178
1179 # can take a mix of classes and instances
1180 o = build_opener(FooHandler, BarHandler())
1181 self.opener_has_handler(o, FooHandler)
1182 self.opener_has_handler(o, BarHandler)
1183
1184 # subclasses of default handlers override default handlers
1185 o = build_opener(MyHTTPHandler)
1186 self.opener_has_handler(o, MyHTTPHandler)
1187
1188 # a particular case of overriding: default handlers can be passed
1189 # in explicitly
1190 o = build_opener()
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001191 self.opener_has_handler(o, urllib.request.HTTPHandler)
1192 o = build_opener(urllib.request.HTTPHandler)
1193 self.opener_has_handler(o, urllib.request.HTTPHandler)
1194 o = build_opener(urllib.request.HTTPHandler())
1195 self.opener_has_handler(o, urllib.request.HTTPHandler)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001196
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001197 # Issue2670: multiple handlers sharing the same base class
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001198 class MyOtherHTTPHandler(urllib.request.HTTPHandler): pass
Christian Heimes81ee3ef2008-05-04 22:42:01 +00001199 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1200 self.opener_has_handler(o, MyHTTPHandler)
1201 self.opener_has_handler(o, MyOtherHTTPHandler)
1202
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001203 def opener_has_handler(self, opener, handler_class):
Florent Xiclunab4efb3d2010-08-14 18:24:40 +00001204 self.assertTrue(any(h.__class__ == handler_class
1205 for h in opener.handlers))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001206
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001207class RequestTests(unittest.TestCase):
1208
1209 def setUp(self):
1210 self.get = Request("http://www.python.org/~jeremy/")
1211 self.post = Request("http://www.python.org/~jeremy/",
1212 "data",
1213 headers={"X-Test": "test"})
1214
1215 def test_method(self):
1216 self.assertEqual("POST", self.post.get_method())
1217 self.assertEqual("GET", self.get.get_method())
1218
1219 def test_add_data(self):
Florent Xiclunab4efb3d2010-08-14 18:24:40 +00001220 self.assertFalse(self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001221 self.assertEqual("GET", self.get.get_method())
1222 self.get.add_data("spam")
Georg Brandlab91fde2009-08-13 08:51:18 +00001223 self.assertTrue(self.get.has_data())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001224 self.assertEqual("POST", self.get.get_method())
1225
1226 def test_get_full_url(self):
1227 self.assertEqual("http://www.python.org/~jeremy/",
1228 self.get.get_full_url())
1229
1230 def test_selector(self):
1231 self.assertEqual("/~jeremy/", self.get.get_selector())
1232 req = Request("http://www.python.org/")
1233 self.assertEqual("/", req.get_selector())
1234
1235 def test_get_type(self):
1236 self.assertEqual("http", self.get.get_type())
1237
1238 def test_get_host(self):
1239 self.assertEqual("www.python.org", self.get.get_host())
1240
1241 def test_get_host_unquote(self):
1242 req = Request("http://www.%70ython.org/")
1243 self.assertEqual("www.python.org", req.get_host())
1244
1245 def test_proxy(self):
Florent Xiclunab4efb3d2010-08-14 18:24:40 +00001246 self.assertFalse(self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001247 self.get.set_proxy("www.perl.org", "http")
Georg Brandlab91fde2009-08-13 08:51:18 +00001248 self.assertTrue(self.get.has_proxy())
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001249 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1250 self.assertEqual("www.perl.org", self.get.get_host())
1251
Senthil Kumaran4c88db72010-08-08 11:30:58 +00001252 def test_wrapped_url(self):
1253 req = Request("<URL:http://www.python.org>")
1254 self.assertEqual("www.python.org", req.get_host())
1255
1256 def test_urlwith_fragment(self):
1257 req = Request("http://www.python.org/?qs=query#fragment=true")
1258 self.assertEqual("/?qs=query", req.get_selector())
1259 req = Request("http://www.python.org/#fun=true")
1260 self.assertEqual("/", req.get_selector())
1261
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001262
1263def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001264 from test import test_urllib2
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001265 support.run_doctest(test_urllib2, verbose)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001266 support.run_doctest(urllib.request, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001267 tests = (TrivialTests,
1268 OpenerDirectorTests,
1269 HandlerTests,
Benjamin Peterson6ebe78f2008-12-21 00:06:59 +00001270 MiscTests,
1271 RequestTests)
Benjamin Petersonee8712c2008-05-20 21:35:26 +00001272 support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001273
1274if __name__ == "__main__":
1275 test_main(verbose=True)