blob: 58b54c1f9a0cede16d0d9f74b0270ff3438b91c8 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +00004import os, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00005import StringIO
Jeremy Hyltone3e61042001-05-09 15:50:25 +00006
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00007import urllib2
8from urllib2 import Request, OpenerDirector
Jeremy Hyltone3e61042001-05-09 15:50:25 +00009
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000010# XXX
11# Request
12# CacheFTPHandler (hard to write)
13# parse_keqv_list, parse_http_list (I'm leaving this for Anthony Baxter
14# and Greg Stein, since they're doing Digest Authentication)
15# Authentication stuff (ditto)
Georg Brandl720096a2006-04-02 20:45:34 +000016# CustomProxy, CustomProxyHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000017
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000018class TrivialTests(unittest.TestCase):
19 def test_trivial(self):
20 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000021
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000022 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000023
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000024 # XXX Name hacking to get this to work on Windows.
25 fname = os.path.abspath(urllib2.__file__).replace('\\', '/')
26 if fname[1:2] == ":":
27 fname = fname[2:]
28 # And more hacking to get it to work on MacOS. This assumes
29 # urllib.pathname2url works, unfortunately...
30 if os.name == 'mac':
31 fname = '/' + fname.replace(':', '/')
32 elif os.name == 'riscos':
33 import string
34 fname = os.expand(fname)
35 fname = fname.translate(string.maketrans("/.", "./"))
36
37 file_url = "file://%s" % fname
38 f = urllib2.urlopen(file_url)
39
40 buf = f.read()
41 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000042
Georg Brandle1b13d22005-08-24 22:20:32 +000043 def test_parse_http_list(self):
44 tests = [('a,b,c', ['a', 'b', 'c']),
45 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
46 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
47 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
48 for string, list in tests:
49 self.assertEquals(urllib2.parse_http_list(string), list)
50
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000051
52class MockOpener:
53 addheaders = []
54 def open(self, req, data=None):
55 self.req, self.data = req, data
56 def error(self, proto, *args):
57 self.proto, self.args = proto, args
58
59class MockFile:
60 def read(self, count=None): pass
61 def readline(self, count=None): pass
62 def close(self): pass
63
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000064class MockHeaders(dict):
65 def getheaders(self, name):
66 return self.values()
67
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000068class MockResponse(StringIO.StringIO):
69 def __init__(self, code, msg, headers, data, url=None):
70 StringIO.StringIO.__init__(self, data)
71 self.code, self.msg, self.headers, self.url = code, msg, headers, url
72 def info(self):
73 return self.headers
74 def geturl(self):
75 return self.url
76
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000077class MockCookieJar:
78 def add_cookie_header(self, request):
79 self.ach_req = request
80 def extract_cookies(self, response, request):
81 self.ec_req, self.ec_r = request, response
82
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000083class FakeMethod:
84 def __init__(self, meth_name, action, handle):
85 self.meth_name = meth_name
86 self.handle = handle
87 self.action = action
88 def __call__(self, *args):
89 return self.handle(self.meth_name, self.action, *args)
90
91class MockHandler:
Georg Brandl720096a2006-04-02 20:45:34 +000092 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000093 def __init__(self, methods):
94 self._define_methods(methods)
95 def _define_methods(self, methods):
96 for spec in methods:
97 if len(spec) == 2: name, action = spec
98 else: name, action = spec, None
99 meth = FakeMethod(name, action, self.handle)
100 setattr(self.__class__, name, meth)
101 def handle(self, fn_name, action, *args, **kwds):
102 self.parent.calls.append((self, fn_name, args, kwds))
103 if action is None:
104 return None
105 elif action == "return self":
106 return self
107 elif action == "return response":
108 res = MockResponse(200, "OK", {}, "")
109 return res
110 elif action == "return request":
111 return Request("http://blah/")
112 elif action.startswith("error"):
113 code = action[action.rfind(" ")+1:]
114 try:
115 code = int(code)
116 except ValueError:
117 pass
118 res = MockResponse(200, "OK", {}, "")
119 return self.parent.error("http", args[0], res, code, "", {})
120 elif action == "raise":
121 raise urllib2.URLError("blah")
122 assert False
123 def close(self): pass
124 def add_parent(self, parent):
125 self.parent = parent
126 self.parent.calls = []
127 def __lt__(self, other):
128 if not hasattr(other, "handler_order"):
129 # No handler_order, leave in original order. Yuck.
130 return True
131 return self.handler_order < other.handler_order
132
133def add_ordered_mock_handlers(opener, meth_spec):
134 """Create MockHandlers and add them to an OpenerDirector.
135
136 meth_spec: list of lists of tuples and strings defining methods to define
137 on handlers. eg:
138
139 [["http_error", "ftp_open"], ["http_open"]]
140
141 defines methods .http_error() and .ftp_open() on one handler, and
142 .http_open() on another. These methods just record their arguments and
143 return None. Using a tuple instead of a string causes the method to
144 perform some action (see MockHandler.handle()), eg:
145
146 [["http_error"], [("http_open", "return request")]]
147
148 defines .http_error() on one handler (which simply returns None), and
149 .http_open() on another handler, which returns a Request object.
150
151 """
152 handlers = []
153 count = 0
154 for meths in meth_spec:
155 class MockHandlerSubclass(MockHandler): pass
156 h = MockHandlerSubclass(meths)
Georg Brandl720096a2006-04-02 20:45:34 +0000157 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000158 h.add_parent(opener)
159 count = count + 1
160 handlers.append(h)
161 opener.add_handler(h)
162 return handlers
163
164class OpenerDirectorTests(unittest.TestCase):
165
166 def test_handled(self):
167 # handler returning non-None means no more handlers will be called
168 o = OpenerDirector()
169 meth_spec = [
170 ["http_open", "ftp_open", "http_error_302"],
171 ["ftp_open"],
172 [("http_open", "return self")],
173 [("http_open", "return self")],
174 ]
175 handlers = add_ordered_mock_handlers(o, meth_spec)
176
177 req = Request("http://example.com/")
178 r = o.open(req)
179 # Second .http_open() gets called, third doesn't, since second returned
180 # non-None. Handlers without .http_open() never get any methods called
181 # on them.
182 # In fact, second mock handler defining .http_open() returns self
183 # (instead of response), which becomes the OpenerDirector's return
184 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000185 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000186 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
187 for expected, got in zip(calls, o.calls):
188 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000189 self.assertEqual((handler, name), expected)
190 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000191
192 def test_handler_order(self):
193 o = OpenerDirector()
194 handlers = []
195 for meths, handler_order in [
196 ([("http_open", "return self")], 500),
197 (["http_open"], 0),
198 ]:
199 class MockHandlerSubclass(MockHandler): pass
200 h = MockHandlerSubclass(meths)
201 h.handler_order = handler_order
202 handlers.append(h)
203 o.add_handler(h)
204
205 r = o.open("http://example.com/")
206 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000207 self.assertEqual(o.calls[0][0], handlers[1])
208 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000209
210 def test_raise(self):
211 # raising URLError stops processing of request
212 o = OpenerDirector()
213 meth_spec = [
214 [("http_open", "raise")],
215 [("http_open", "return self")],
216 ]
217 handlers = add_ordered_mock_handlers(o, meth_spec)
218
219 req = Request("http://example.com/")
220 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000221 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000222
223## def test_error(self):
224## # XXX this doesn't actually seem to be used in standard library,
225## # but should really be tested anyway...
226
227 def test_http_error(self):
228 # XXX http_error_default
229 # http errors are a special case
230 o = OpenerDirector()
231 meth_spec = [
232 [("http_open", "error 302")],
233 [("http_error_400", "raise"), "http_open"],
234 [("http_error_302", "return response"), "http_error_303",
235 "http_error"],
236 [("http_error_302")],
237 ]
238 handlers = add_ordered_mock_handlers(o, meth_spec)
239
240 class Unknown:
241 def __eq__(self, other): return True
242
243 req = Request("http://example.com/")
244 r = o.open(req)
245 assert len(o.calls) == 2
246 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000247 (handlers[2], "http_error_302",
248 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000249 for expected, got in zip(calls, o.calls):
250 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000251 self.assertEqual((handler, method_name), got[:2])
252 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000253
254 def test_processors(self):
255 # *_request / *_response methods get called appropriately
256 o = OpenerDirector()
257 meth_spec = [
258 [("http_request", "return request"),
259 ("http_response", "return response")],
260 [("http_request", "return request"),
261 ("http_response", "return response")],
262 ]
263 handlers = add_ordered_mock_handlers(o, meth_spec)
264
265 req = Request("http://example.com/")
266 r = o.open(req)
267 # processor methods are called on *all* handlers that define them,
268 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000269 calls = [
270 (handlers[0], "http_request"), (handlers[1], "http_request"),
271 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000272
273 for i, (handler, name, args, kwds) in enumerate(o.calls):
274 if i < 2:
275 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000276 self.assertEqual((handler, name), calls[i])
277 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000278 self.assert_(isinstance(args[0], Request))
279 else:
280 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000281 self.assertEqual((handler, name), calls[i])
282 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000283 self.assert_(isinstance(args[0], Request))
284 # response from opener.open is None, because there's no
285 # handler that defines http_open to handle it
286 self.assert_(args[1] is None or
287 isinstance(args[1], MockResponse))
288
289
Tim Peters58eb11c2004-01-18 20:29:55 +0000290def sanepathname2url(path):
291 import urllib
292 urlpath = urllib.pathname2url(path)
293 if os.name == "nt" and urlpath.startswith("///"):
294 urlpath = urlpath[2:]
295 # XXX don't ask me about the mac...
296 return urlpath
297
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000298class HandlerTests(unittest.TestCase):
299
300 def test_ftp(self):
301 class MockFTPWrapper:
302 def __init__(self, data): self.data = data
303 def retrfile(self, filename, filetype):
304 self.filename, self.filetype = filename, filetype
305 return StringIO.StringIO(self.data), len(self.data)
306
307 class NullFTPHandler(urllib2.FTPHandler):
308 def __init__(self, data): self.data = data
309 def connect_ftp(self, user, passwd, host, port, dirs):
310 self.user, self.passwd = user, passwd
311 self.host, self.port = host, port
312 self.dirs = dirs
313 self.ftpwrapper = MockFTPWrapper(self.data)
314 return self.ftpwrapper
315
316 import ftplib, socket
317 data = "rheum rhaponicum"
318 h = NullFTPHandler(data)
319 o = h.parent = MockOpener()
320
321 for url, host, port, type_, dirs, filename, mimetype in [
322 ("ftp://localhost/foo/bar/baz.html",
323 "localhost", ftplib.FTP_PORT, "I",
324 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000325 ("ftp://localhost:80/foo/bar/",
326 "localhost", 80, "D",
327 ["foo", "bar"], "", None),
328 ("ftp://localhost/baz.gif;type=a",
329 "localhost", ftplib.FTP_PORT, "A",
330 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000331 ]:
332 r = h.ftp_open(Request(url))
333 # ftp authentication not yet implemented by FTPHandler
334 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000335 self.assertEqual(h.host, socket.gethostbyname(host))
336 self.assertEqual(h.port, port)
337 self.assertEqual(h.dirs, dirs)
338 self.assertEqual(h.ftpwrapper.filename, filename)
339 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000340 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000341 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000342 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000343
344 def test_file(self):
345 import time, rfc822, socket
346 h = urllib2.FileHandler()
347 o = h.parent = MockOpener()
348
Tim Peters58eb11c2004-01-18 20:29:55 +0000349 TESTFN = test_support.TESTFN
350 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000351 towrite = "hello, world\n"
Georg Brandldd2245f2006-03-31 17:18:06 +0000352 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000353 "file://localhost%s" % urlpath,
354 "file://%s" % urlpath,
355 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Georg Brandldd2245f2006-03-31 17:18:06 +0000356 ]
357 try:
Tim Peters480725d2006-04-03 02:46:44 +0000358 localaddr = socket.gethostbyname(socket.gethostname())
Georg Brandldd2245f2006-03-31 17:18:06 +0000359 except socket.gaierror:
360 localaddr = ''
361 if localaddr:
362 urls.append("file://%s%s" % (localaddr, urlpath))
Tim Peters480725d2006-04-03 02:46:44 +0000363
Georg Brandldd2245f2006-03-31 17:18:06 +0000364 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000365 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000366 try:
367 try:
368 f.write(towrite)
369 finally:
370 f.close()
371
372 r = h.file_open(Request(url))
373 try:
374 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000375 headers = r.info()
376 newurl = r.geturl()
377 finally:
378 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000379 stats = os.stat(TESTFN)
380 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000381 finally:
382 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000383 self.assertEqual(data, towrite)
384 self.assertEqual(headers["Content-type"], "text/plain")
385 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000386 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000387
388 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000389 "file://localhost:80%s" % urlpath,
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000390# XXXX bug: these fail with socket.gaierror, should be URLError
391## "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
392## os.getcwd(), TESTFN),
393## "file://somerandomhost.ontheinternet.com%s/%s" %
394## (os.getcwd(), TESTFN),
395 ]:
396 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000397 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000398 try:
399 f.write(towrite)
400 finally:
401 f.close()
402
403 self.assertRaises(urllib2.URLError,
404 h.file_open, Request(url))
405 finally:
406 os.remove(TESTFN)
407
408 h = urllib2.FileHandler()
409 o = h.parent = MockOpener()
410 # XXXX why does // mean ftp (and /// mean not ftp!), and where
411 # is file: scheme specified? I think this is really a bug, and
412 # what was intended was to distinguish between URLs like:
413 # file:/blah.txt (a file)
414 # file://localhost/blah.txt (a file)
415 # file:///blah.txt (a file)
416 # file://ftp.example.com/blah.txt (an ftp URL)
417 for url, ftp in [
418 ("file://ftp.example.com//foo.txt", True),
419 ("file://ftp.example.com///foo.txt", False),
420# XXXX bug: fails with OSError, should be URLError
421 ("file://ftp.example.com/foo.txt", False),
422 ]:
423 req = Request(url)
424 try:
425 h.file_open(req)
426 # XXXX remove OSError when bug fixed
427 except (urllib2.URLError, OSError):
428 self.assert_(not ftp)
429 else:
430 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000431 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000432
433 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000434 class MockHTTPResponse:
435 def __init__(self, fp, msg, status, reason):
436 self.fp = fp
437 self.msg = msg
438 self.status = status
439 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000440 def read(self):
441 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000442 class MockHTTPClass:
443 def __init__(self):
444 self.req_headers = []
445 self.data = None
446 self.raise_on_endheaders = False
447 def __call__(self, host):
448 self.host = host
449 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000450 def set_debuglevel(self, level):
451 self.level = level
452 def request(self, method, url, body=None, headers={}):
453 self.method = method
454 self.selector = url
455 self.req_headers += headers.items()
456 if body:
457 self.data = body
458 if self.raise_on_endheaders:
459 import socket
460 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000461 def getresponse(self):
462 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000463
464 h = urllib2.AbstractHTTPHandler()
465 o = h.parent = MockOpener()
466
467 url = "http://example.com/"
468 for method, data in [("GET", None), ("POST", "blah")]:
469 req = Request(url, data, {"Foo": "bar"})
470 req.add_unredirected_header("Spam", "eggs")
471 http = MockHTTPClass()
472 r = h.do_open(http, req)
473
474 # result attributes
475 r.read; r.readline # wrapped MockFile methods
476 r.info; r.geturl # addinfourl methods
477 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
478 hdrs = r.info()
479 hdrs.get; hdrs.has_key # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000480 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000481
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000482 self.assertEqual(http.host, "example.com")
483 self.assertEqual(http.level, 0)
484 self.assertEqual(http.method, method)
485 self.assertEqual(http.selector, "/")
486 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000487 [("Connection", "close"),
488 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000489 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000490
491 # check socket.error converted to URLError
492 http.raise_on_endheaders = True
493 self.assertRaises(urllib2.URLError, h.do_open, http, req)
494
495 # check adding of standard headers
496 o.addheaders = [("Spam", "eggs")]
497 for data in "", None: # POST, GET
498 req = Request("http://example.com/", data)
499 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000500 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000501 if data is None: # GET
Georg Brandl80bb2bb2006-03-28 19:19:56 +0000502 self.assert_("Content-length" not in req.unredirected_hdrs)
503 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000504 else: # POST
Georg Brandl80bb2bb2006-03-28 19:19:56 +0000505 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
506 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000507 "application/x-www-form-urlencoded")
508 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000509 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
510 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000511
512 # don't clobber existing headers
513 req.add_unredirected_header("Content-length", "foo")
514 req.add_unredirected_header("Content-type", "bar")
515 req.add_unredirected_header("Host", "baz")
516 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000517 newreq = h.do_request_(req)
Georg Brandl80bb2bb2006-03-28 19:19:56 +0000518 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
519 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000520 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
521 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000522
523 def test_errors(self):
524 h = urllib2.HTTPErrorProcessor()
525 o = h.parent = MockOpener()
526
527 url = "http://example.com/"
528 req = Request(url)
529 # 200 OK is passed through
530 r = MockResponse(200, "OK", {}, "", url)
531 newr = h.http_response(req, r)
532 self.assert_(r is newr)
533 self.assert_(not hasattr(o, "proto")) # o.error not called
534 # anything else calls o.error (and MockOpener returns None, here)
535 r = MockResponse(201, "Created", {}, "", url)
536 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000537 self.assertEqual(o.proto, "http") # o.error called
538 self.assertEqual(o.args, (req, r, 201, "Created", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000539
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000540 def test_cookies(self):
541 cj = MockCookieJar()
542 h = urllib2.HTTPCookieProcessor(cj)
543 o = h.parent = MockOpener()
544
545 req = Request("http://example.com/")
546 r = MockResponse(200, "OK", {}, "")
547 newreq = h.http_request(req)
548 self.assert_(cj.ach_req is req is newreq)
549 self.assertEquals(req.get_origin_req_host(), "example.com")
550 self.assert_(not req.is_unverifiable())
551 newr = h.http_response(req, r)
552 self.assert_(cj.ec_req is req)
553 self.assert_(cj.ec_r is r is newr)
554
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000555 def test_redirect(self):
556 from_url = "http://example.com/a.html"
557 to_url = "http://example.com/b.html"
558 h = urllib2.HTTPRedirectHandler()
559 o = h.parent = MockOpener()
560
561 # ordinary redirect behaviour
562 for code in 301, 302, 303, 307:
563 for data in None, "blah\nblah\n":
564 method = getattr(h, "http_error_%s" % code)
565 req = Request(from_url, data)
566 req.add_header("Nonsense", "viking=withhold")
567 req.add_unredirected_header("Spam", "spam")
568 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000569 method(req, MockFile(), code, "Blah",
570 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000571 except urllib2.HTTPError:
572 # 307 in response to POST requires user OK
573 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000574 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000575 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000576 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000577 except AttributeError:
578 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000579 self.assertEqual(o.req.headers["Nonsense"],
580 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000581 self.assert_("Spam" not in o.req.headers)
582 self.assert_("Spam" not in o.req.unredirected_hdrs)
583
584 # loop detection
585 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000586 def redirect(h, req, url=to_url):
587 h.http_error_302(req, MockFile(), 302, "Blah",
588 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000589 # Note that the *original* request shares the same record of
590 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000591
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000592 # detect infinite loop redirect of a URL to itself
593 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000594 count = 0
595 try:
596 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000597 redirect(h, req, "http://example.com/")
598 count = count + 1
599 except urllib2.HTTPError:
600 # don't stop until max_repeats, because cookies may introduce state
601 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
602
603 # detect endless non-repeating chain of redirects
604 req = Request(from_url, origin_req_host="example.com")
605 count = 0
606 try:
607 while 1:
608 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000609 count = count + 1
610 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000611 self.assertEqual(count,
612 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000613
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000614 def test_cookie_redirect(self):
615 class MockHTTPHandler(urllib2.HTTPHandler):
616 def __init__(self): self._count = 0
617 def http_open(self, req):
618 import mimetools
619 from StringIO import StringIO
620 if self._count == 0:
621 self._count = self._count + 1
622 msg = mimetools.Message(
623 StringIO("Location: http://www.cracker.com/\r\n\r\n"))
624 return self.parent.error(
625 "http", req, MockFile(), 302, "Found", msg)
626 else:
627 self.req = req
628 msg = mimetools.Message(StringIO("\r\n\r\n"))
629 return MockResponse(200, "OK", msg, "", req.get_full_url())
630 # cookies shouldn't leak into redirected requests
631 from cookielib import CookieJar
632 from urllib2 import build_opener, HTTPHandler, HTTPError, \
633 HTTPCookieProcessor
634
635 from test_cookielib import interact_netscape
636
637 cj = CookieJar()
638 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
639 hh = MockHTTPHandler()
640 cp = HTTPCookieProcessor(cj)
641 o = build_opener(hh, cp)
642 o.open("http://www.example.com/")
643 self.assert_(not hh.req.has_header("Cookie"))
644
Georg Brandl720096a2006-04-02 20:45:34 +0000645 def test_proxy(self):
646 o = OpenerDirector()
647 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
648 o.add_handler(ph)
649 meth_spec = [
650 [("http_open", "return response")]
651 ]
652 handlers = add_ordered_mock_handlers(o, meth_spec)
653
654 req = Request("http://acme.example.com/")
655 self.assertEqual(req.get_host(), "acme.example.com")
656 r = o.open(req)
657 self.assertEqual(req.get_host(), "proxy.example.com:3128")
658
659 self.assertEqual([(handlers[0], "http_open")],
660 [tup[0:2] for tup in o.calls])
661
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000662
663class MiscTests(unittest.TestCase):
664
665 def test_build_opener(self):
666 class MyHTTPHandler(urllib2.HTTPHandler): pass
667 class FooHandler(urllib2.BaseHandler):
668 def foo_open(self): pass
669 class BarHandler(urllib2.BaseHandler):
670 def bar_open(self): pass
671
672 build_opener = urllib2.build_opener
673
674 o = build_opener(FooHandler, BarHandler)
675 self.opener_has_handler(o, FooHandler)
676 self.opener_has_handler(o, BarHandler)
677
678 # can take a mix of classes and instances
679 o = build_opener(FooHandler, BarHandler())
680 self.opener_has_handler(o, FooHandler)
681 self.opener_has_handler(o, BarHandler)
682
683 # subclasses of default handlers override default handlers
684 o = build_opener(MyHTTPHandler)
685 self.opener_has_handler(o, MyHTTPHandler)
686
687 # a particular case of overriding: default handlers can be passed
688 # in explicitly
689 o = build_opener()
690 self.opener_has_handler(o, urllib2.HTTPHandler)
691 o = build_opener(urllib2.HTTPHandler)
692 self.opener_has_handler(o, urllib2.HTTPHandler)
693 o = build_opener(urllib2.HTTPHandler())
694 self.opener_has_handler(o, urllib2.HTTPHandler)
695
696 def opener_has_handler(self, opener, handler_class):
697 for h in opener.handlers:
698 if h.__class__ == handler_class:
699 break
700 else:
701 self.assert_(False)
702
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +0000703class NetworkTests(unittest.TestCase):
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +0000704 def setUp(self):
705 if 0: # for debugging
706 import logging
707 logger = logging.getLogger("test_urllib2")
708 logger.addHandler(logging.StreamHandler())
709
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +0000710 def test_range (self):
711 req = urllib2.Request("http://www.python.org",
712 headers={'Range': 'bytes=20-39'})
713 result = urllib2.urlopen(req)
714 data = result.read()
715 self.assertEqual(len(data), 20)
716
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +0000717 # XXX The rest of these tests aren't very good -- they don't check much.
718 # They do sometimes catch some major disasters, though.
719
720 def test_ftp(self):
721 urls = [
722 'ftp://www.python.org/pub/python/misc/sousa.au',
723 'ftp://www.python.org/pub/tmp/blat',
724 'ftp://gatekeeper.research.compaq.com/pub/DEC/SRC'
725 '/research-reports/00README-Legal-Rules-Regs',
726 ]
727 self._test_urls(urls, self._extra_handlers())
728
729 def test_gopher(self):
Tim Peters2a8ec992006-02-19 05:09:00 +0000730 import warnings
731 warnings.filterwarnings("ignore",
732 "the gopherlib module is deprecated",
733 DeprecationWarning,
734 "urllib2$")
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +0000735 urls = [
736 # Thanks to Fred for finding these!
737 'gopher://gopher.lib.ncsu.edu/11/library/stacks/Alex',
738 'gopher://gopher.vt.edu:10010/10/33',
739 ]
740 self._test_urls(urls, self._extra_handlers())
741
742 def test_file(self):
743 TESTFN = test_support.TESTFN
744 f = open(TESTFN, 'w')
745 try:
746 f.write('hi there\n')
747 f.close()
748 urls = [
749 'file:'+sanepathname2url(os.path.abspath(TESTFN)),
750
751 # XXX bug, should raise URLError
752 #('file://nonsensename/etc/passwd', None, urllib2.URLError)
753 ('file://nonsensename/etc/passwd', None, (OSError, socket.error))
754 ]
755 self._test_urls(urls, self._extra_handlers())
756 finally:
757 os.remove(TESTFN)
758
759 def test_http(self):
760 urls = [
761 'http://www.espn.com/', # redirect
762 'http://www.python.org/Spanish/Inquistion/',
763 ('http://www.python.org/cgi-bin/faqw.py',
764 'query=pythonistas&querytype=simple&casefold=yes&req=search', None),
765 'http://www.python.org/',
766 ]
767 self._test_urls(urls, self._extra_handlers())
768
769 # XXX Following test depends on machine configurations that are internal
770 # to CNRI. Need to set up a public server with the right authentication
771 # configuration for test purposes.
772
773## def test_cnri(self):
774## if socket.gethostname() == 'bitdiddle':
775## localhost = 'bitdiddle.cnri.reston.va.us'
776## elif socket.gethostname() == 'bitdiddle.concentric.net':
777## localhost = 'localhost'
778## else:
779## localhost = None
780## if localhost is not None:
781## urls = [
782## 'file://%s/etc/passwd' % localhost,
783## 'http://%s/simple/' % localhost,
784## 'http://%s/digest/' % localhost,
785## 'http://%s/not/found.h' % localhost,
786## ]
787
788## bauth = HTTPBasicAuthHandler()
789## bauth.add_password('basic_test_realm', localhost, 'jhylton',
790## 'password')
791## dauth = HTTPDigestAuthHandler()
792## dauth.add_password('digest_test_realm', localhost, 'jhylton',
793## 'password')
794
795## self._test_urls(urls, self._extra_handlers()+[bauth, dauth])
796
797 def _test_urls(self, urls, handlers):
798 import socket
799 import time
800 import logging
801 debug = logging.getLogger("test_urllib2").debug
802
803 urllib2.install_opener(urllib2.build_opener(*handlers))
804
805 for url in urls:
806 if isinstance(url, tuple):
807 url, req, expected_err = url
808 else:
809 req = expected_err = None
810 debug(url)
811 try:
812 f = urllib2.urlopen(url, req)
813 except (IOError, socket.error, OSError), err:
814 debug(err)
815 if expected_err:
816 self.assert_(isinstance(err, expected_err))
817 else:
818 buf = f.read()
819 f.close()
820 debug("read %d bytes" % len(buf))
821 debug("******** next url coming up...")
822 time.sleep(0.1)
823
824 def _extra_handlers(self):
825 handlers = []
826
827 handlers.append(urllib2.GopherHandler)
828
829 cfh = urllib2.CacheFTPHandler()
830 cfh.setTimeout(1)
831 handlers.append(cfh)
832
833## # XXX try out some custom proxy objects too!
834## def at_cnri(req):
835## host = req.get_host()
836## debug(host)
837## if host[-18:] == '.cnri.reston.va.us':
838## return True
839## p = CustomProxy('http', at_cnri, 'proxy.cnri.reston.va.us')
840## ph = CustomProxyHandler(p)
841## handlers.append(ph)
842
843 return handlers
Tim Peters27f88362004-07-08 04:22:35 +0000844
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000845
846def test_main(verbose=None):
Georg Brandl720096a2006-04-02 20:45:34 +0000847 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +0000848 tests = (TrivialTests,
849 OpenerDirectorTests,
850 HandlerTests,
851 MiscTests)
852 if test_support.is_resource_enabled('network'):
853 tests += (NetworkTests,)
854 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000855
856if __name__ == "__main__":
857 test_main(verbose=True)