source: python/trunk/Lib/test/test_urllib2.py

Last change on this file was 391, checked in by dmik, 11 years ago

python: Merge vendor 2.7.6 to trunk.

  • Property svn:eol-style set to native
File size: 53.0 KB
Line 
1import unittest
2from test import test_support
3
4import os
5import socket
6import StringIO
7
8import urllib2
9from urllib2 import Request, OpenerDirector
10
11# XXX
12# Request
13# CacheFTPHandler (hard to write)
14# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
15
16class TrivialTests(unittest.TestCase):
17 def test_trivial(self):
18 # A couple trivial tests
19
20 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
21
22 # XXX Name hacking to get this to work on Windows.
23 fname = os.path.abspath(urllib2.__file__).replace('\\', '/')
24
25 # And more hacking to get it to work on MacOS. This assumes
26 # urllib.pathname2url works, unfortunately...
27 if os.name == 'riscos':
28 import string
29 fname = os.expand(fname)
30 fname = fname.translate(string.maketrans("/.", "./"))
31
32 if os.name == 'nt':
33 file_url = "file:///%s" % fname
34 else:
35 file_url = "file://%s" % fname
36
37 f = urllib2.urlopen(file_url)
38
39 buf = f.read()
40 f.close()
41
42 def test_parse_http_list(self):
43 tests = [('a,b,c', ['a', 'b', 'c']),
44 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
45 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
46 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
47 for string, list in tests:
48 self.assertEqual(urllib2.parse_http_list(string), list)
49
50
51def test_request_headers_dict():
52 """
53 The Request.headers dictionary is not a documented interface. It should
54 stay that way, because the complete set of headers are only accessible
55 through the .get_header(), .has_header(), .header_items() interface.
56 However, .headers pre-dates those methods, and so real code will be using
57 the dictionary.
58
59 The introduction in 2.4 of those methods was a mistake for the same reason:
60 code that previously saw all (urllib2 user)-provided headers in .headers
61 now sees only a subset (and the function interface is ugly and incomplete).
62 A better change would have been to replace .headers dict with a dict
63 subclass (or UserDict.DictMixin instance?) that preserved the .headers
64 interface and also provided access to the "unredirected" headers. It's
65 probably too late to fix that, though.
66
67
68 Check .capitalize() case normalization:
69
70 >>> url = "http://example.com"
71 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
72 'blah'
73 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
74 'blah'
75
76 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
77 but that could be changed in future.
78
79 """
80
81def test_request_headers_methods():
82 """
83 Note the case normalization of header names here, to .capitalize()-case.
84 This should be preserved for backwards-compatibility. (In the HTTP case,
85 normalization to .title()-case is done by urllib2 before sending headers to
86 httplib).
87
88 >>> url = "http://example.com"
89 >>> r = Request(url, headers={"Spam-eggs": "blah"})
90 >>> r.has_header("Spam-eggs")
91 True
92 >>> r.header_items()
93 [('Spam-eggs', 'blah')]
94 >>> r.add_header("Foo-Bar", "baz")
95 >>> items = r.header_items()
96 >>> items.sort()
97 >>> 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
106 >>> print r.get_header("Not-there")
107 None
108 >>> r.get_header("Not-there", "default")
109 'default'
110
111 """
112
113
114def test_password_manager(self):
115 """
116 >>> mgr = urllib2.HTTPPasswordMgr()
117 >>> 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
144 Actually, this is really undefined ATM
145## Currently, we use the highest-level path where more than one match:
146
147## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
148## ('joe', 'password')
149
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
179def test_password_manager_default_port(self):
180 """
181 >>> mgr = urllib2.HTTPPasswordMgr()
182 >>> 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
226class MockOpener:
227 addheaders = []
228 def open(self, req, data=None,timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
229 self.req, self.data, self.timeout = req, data, timeout
230 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
238class MockHeaders(dict):
239 def getheaders(self, name):
240 return self.values()
241
242class MockResponse(StringIO.StringIO):
243 def __init__(self, code, msg, headers, data, url=None):
244 StringIO.StringIO.__init__(self, data)
245 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
251class 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
257class 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
265class MockHTTPResponse:
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 def read(self):
272 return ''
273
274class MockHTTPClass:
275 def __init__(self):
276 self.req_headers = []
277 self.data = None
278 self.raise_on_endheaders = False
279 self._tunnel_headers = {}
280
281 def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
282 self.host = host
283 self.timeout = timeout
284 return self
285
286 def set_debuglevel(self, level):
287 self.level = level
288
289 def set_tunnel(self, host, port=None, headers=None):
290 self._tunnel_host = host
291 self._tunnel_port = port
292 if headers:
293 self._tunnel_headers = headers
294 else:
295 self._tunnel_headers.clear()
296
297 def request(self, method, url, body=None, headers=None):
298 self.method = method
299 self.selector = url
300 if headers is not None:
301 self.req_headers += headers.items()
302 self.req_headers.sort()
303 if body:
304 self.data = body
305 if self.raise_on_endheaders:
306 import socket
307 raise socket.error()
308
309 def getresponse(self):
310 return MockHTTPResponse(MockFile(), {}, 200, "OK")
311
312 def close(self):
313 pass
314
315class MockHandler:
316 # useful for testing handler machinery
317 # see add_ordered_mock_handlers() docstring
318 handler_order = 500
319 def __init__(self, methods):
320 self._define_methods(methods)
321 def _define_methods(self, methods):
322 for spec in methods:
323 if len(spec) == 2: name, action = spec
324 else: name, action = spec, None
325 meth = FakeMethod(name, action, self.handle)
326 setattr(self.__class__, name, meth)
327 def handle(self, fn_name, action, *args, **kwds):
328 self.parent.calls.append((self, fn_name, args, kwds))
329 if action is None:
330 return None
331 elif action == "return self":
332 return self
333 elif action == "return response":
334 res = MockResponse(200, "OK", {}, "")
335 return res
336 elif action == "return request":
337 return Request("http://blah/")
338 elif action.startswith("error"):
339 code = action[action.rfind(" ")+1:]
340 try:
341 code = int(code)
342 except ValueError:
343 pass
344 res = MockResponse(200, "OK", {}, "")
345 return self.parent.error("http", args[0], res, code, "", {})
346 elif action == "raise":
347 raise urllib2.URLError("blah")
348 assert False
349 def close(self): pass
350 def add_parent(self, parent):
351 self.parent = parent
352 self.parent.calls = []
353 def __lt__(self, other):
354 if not hasattr(other, "handler_order"):
355 # No handler_order, leave in original order. Yuck.
356 return True
357 return self.handler_order < other.handler_order
358
359def add_ordered_mock_handlers(opener, meth_spec):
360 """Create MockHandlers and add them to an OpenerDirector.
361
362 meth_spec: list of lists of tuples and strings defining methods to define
363 on handlers. eg:
364
365 [["http_error", "ftp_open"], ["http_open"]]
366
367 defines methods .http_error() and .ftp_open() on one handler, and
368 .http_open() on another. These methods just record their arguments and
369 return None. Using a tuple instead of a string causes the method to
370 perform some action (see MockHandler.handle()), eg:
371
372 [["http_error"], [("http_open", "return request")]]
373
374 defines .http_error() on one handler (which simply returns None), and
375 .http_open() on another handler, which returns a Request object.
376
377 """
378 handlers = []
379 count = 0
380 for meths in meth_spec:
381 class MockHandlerSubclass(MockHandler): pass
382 h = MockHandlerSubclass(meths)
383 h.handler_order += count
384 h.add_parent(opener)
385 count = count + 1
386 handlers.append(h)
387 opener.add_handler(h)
388 return handlers
389
390def build_test_opener(*handler_instances):
391 opener = OpenerDirector()
392 for h in handler_instances:
393 opener.add_handler(h)
394 return opener
395
396class MockHTTPHandler(urllib2.BaseHandler):
397 # useful for testing redirections and auth
398 # sends supplied headers and code as first response
399 # sends 200 OK as second response
400 def __init__(self, code, headers):
401 self.code = code
402 self.headers = headers
403 self.reset()
404 def reset(self):
405 self._count = 0
406 self.requests = []
407 def http_open(self, req):
408 import mimetools, httplib, copy
409 from StringIO import StringIO
410 self.requests.append(copy.deepcopy(req))
411 if self._count == 0:
412 self._count = self._count + 1
413 name = httplib.responses[self.code]
414 msg = mimetools.Message(StringIO(self.headers))
415 return self.parent.error(
416 "http", req, MockFile(), self.code, name, msg)
417 else:
418 self.req = req
419 msg = mimetools.Message(StringIO("\r\n\r\n"))
420 return MockResponse(200, "OK", msg, "", req.get_full_url())
421
422class MockHTTPSHandler(urllib2.AbstractHTTPHandler):
423 # Useful for testing the Proxy-Authorization request by verifying the
424 # properties of httpcon
425
426 def __init__(self):
427 urllib2.AbstractHTTPHandler.__init__(self)
428 self.httpconn = MockHTTPClass()
429
430 def https_open(self, req):
431 return self.do_open(self.httpconn, req)
432
433class MockPasswordManager:
434 def add_password(self, realm, uri, user, password):
435 self.realm = realm
436 self.url = uri
437 self.user = user
438 self.password = password
439 def find_user_password(self, realm, authuri):
440 self.target_realm = realm
441 self.target_url = authuri
442 return self.user, self.password
443
444
445class OpenerDirectorTests(unittest.TestCase):
446
447 def test_add_non_handler(self):
448 class NonHandler(object):
449 pass
450 self.assertRaises(TypeError,
451 OpenerDirector().add_handler, NonHandler())
452
453 def test_badly_named_methods(self):
454 # test work-around for three methods that accidentally follow the
455 # naming conventions for handler methods
456 # (*_open() / *_request() / *_response())
457
458 # These used to call the accidentally-named methods, causing a
459 # TypeError in real code; here, returning self from these mock
460 # methods would either cause no exception, or AttributeError.
461
462 from urllib2 import URLError
463
464 o = OpenerDirector()
465 meth_spec = [
466 [("do_open", "return self"), ("proxy_open", "return self")],
467 [("redirect_request", "return self")],
468 ]
469 handlers = add_ordered_mock_handlers(o, meth_spec)
470 o.add_handler(urllib2.UnknownHandler())
471 for scheme in "do", "proxy", "redirect":
472 self.assertRaises(URLError, o.open, scheme+"://example.com/")
473
474 def test_handled(self):
475 # handler returning non-None means no more handlers will be called
476 o = OpenerDirector()
477 meth_spec = [
478 ["http_open", "ftp_open", "http_error_302"],
479 ["ftp_open"],
480 [("http_open", "return self")],
481 [("http_open", "return self")],
482 ]
483 handlers = add_ordered_mock_handlers(o, meth_spec)
484
485 req = Request("http://example.com/")
486 r = o.open(req)
487 # Second .http_open() gets called, third doesn't, since second returned
488 # non-None. Handlers without .http_open() never get any methods called
489 # on them.
490 # In fact, second mock handler defining .http_open() returns self
491 # (instead of response), which becomes the OpenerDirector's return
492 # value.
493 self.assertEqual(r, handlers[2])
494 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
495 for expected, got in zip(calls, o.calls):
496 handler, name, args, kwds = got
497 self.assertEqual((handler, name), expected)
498 self.assertEqual(args, (req,))
499
500 def test_handler_order(self):
501 o = OpenerDirector()
502 handlers = []
503 for meths, handler_order in [
504 ([("http_open", "return self")], 500),
505 (["http_open"], 0),
506 ]:
507 class MockHandlerSubclass(MockHandler): pass
508 h = MockHandlerSubclass(meths)
509 h.handler_order = handler_order
510 handlers.append(h)
511 o.add_handler(h)
512
513 r = o.open("http://example.com/")
514 # handlers called in reverse order, thanks to their sort order
515 self.assertEqual(o.calls[0][0], handlers[1])
516 self.assertEqual(o.calls[1][0], handlers[0])
517
518 def test_raise(self):
519 # raising URLError stops processing of request
520 o = OpenerDirector()
521 meth_spec = [
522 [("http_open", "raise")],
523 [("http_open", "return self")],
524 ]
525 handlers = add_ordered_mock_handlers(o, meth_spec)
526
527 req = Request("http://example.com/")
528 self.assertRaises(urllib2.URLError, o.open, req)
529 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
530
531## def test_error(self):
532## # XXX this doesn't actually seem to be used in standard library,
533## # but should really be tested anyway...
534
535 def test_http_error(self):
536 # XXX http_error_default
537 # http errors are a special case
538 o = OpenerDirector()
539 meth_spec = [
540 [("http_open", "error 302")],
541 [("http_error_400", "raise"), "http_open"],
542 [("http_error_302", "return response"), "http_error_303",
543 "http_error"],
544 [("http_error_302")],
545 ]
546 handlers = add_ordered_mock_handlers(o, meth_spec)
547
548 class Unknown:
549 def __eq__(self, other): return True
550
551 req = Request("http://example.com/")
552 r = o.open(req)
553 assert len(o.calls) == 2
554 calls = [(handlers[0], "http_open", (req,)),
555 (handlers[2], "http_error_302",
556 (req, Unknown(), 302, "", {}))]
557 for expected, got in zip(calls, o.calls):
558 handler, method_name, args = expected
559 self.assertEqual((handler, method_name), got[:2])
560 self.assertEqual(args, got[2])
561
562 def test_processors(self):
563 # *_request / *_response methods get called appropriately
564 o = OpenerDirector()
565 meth_spec = [
566 [("http_request", "return request"),
567 ("http_response", "return response")],
568 [("http_request", "return request"),
569 ("http_response", "return response")],
570 ]
571 handlers = add_ordered_mock_handlers(o, meth_spec)
572
573 req = Request("http://example.com/")
574 r = o.open(req)
575 # processor methods are called on *all* handlers that define them,
576 # not just the first handler that handles the request
577 calls = [
578 (handlers[0], "http_request"), (handlers[1], "http_request"),
579 (handlers[0], "http_response"), (handlers[1], "http_response")]
580
581 for i, (handler, name, args, kwds) in enumerate(o.calls):
582 if i < 2:
583 # *_request
584 self.assertEqual((handler, name), calls[i])
585 self.assertEqual(len(args), 1)
586 self.assertIsInstance(args[0], Request)
587 else:
588 # *_response
589 self.assertEqual((handler, name), calls[i])
590 self.assertEqual(len(args), 2)
591 self.assertIsInstance(args[0], Request)
592 # response from opener.open is None, because there's no
593 # handler that defines http_open to handle it
594 self.assertTrue(args[1] is None or
595 isinstance(args[1], MockResponse))
596
597
598def sanepathname2url(path):
599 import urllib
600 urlpath = urllib.pathname2url(path)
601 if os.name == "nt" and urlpath.startswith("///"):
602 urlpath = urlpath[2:]
603 # XXX don't ask me about the mac...
604 return urlpath
605
606class HandlerTests(unittest.TestCase):
607
608 def test_ftp(self):
609 class MockFTPWrapper:
610 def __init__(self, data): self.data = data
611 def retrfile(self, filename, filetype):
612 self.filename, self.filetype = filename, filetype
613 return StringIO.StringIO(self.data), len(self.data)
614 def close(self): pass
615
616 class NullFTPHandler(urllib2.FTPHandler):
617 def __init__(self, data): self.data = data
618 def connect_ftp(self, user, passwd, host, port, dirs,
619 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
620 self.user, self.passwd = user, passwd
621 self.host, self.port = host, port
622 self.dirs = dirs
623 self.ftpwrapper = MockFTPWrapper(self.data)
624 return self.ftpwrapper
625
626 import ftplib
627 data = "rheum rhaponicum"
628 h = NullFTPHandler(data)
629 o = h.parent = MockOpener()
630
631 for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
632 ("ftp://localhost/foo/bar/baz.html",
633 "localhost", ftplib.FTP_PORT, "", "", "I",
634 ["foo", "bar"], "baz.html", "text/html"),
635 ("ftp://parrot@localhost/foo/bar/baz.html",
636 "localhost", ftplib.FTP_PORT, "parrot", "", "I",
637 ["foo", "bar"], "baz.html", "text/html"),
638 ("ftp://%25parrot@localhost/foo/bar/baz.html",
639 "localhost", ftplib.FTP_PORT, "%parrot", "", "I",
640 ["foo", "bar"], "baz.html", "text/html"),
641 ("ftp://%2542parrot@localhost/foo/bar/baz.html",
642 "localhost", ftplib.FTP_PORT, "%42parrot", "", "I",
643 ["foo", "bar"], "baz.html", "text/html"),
644 ("ftp://localhost:80/foo/bar/",
645 "localhost", 80, "", "", "D",
646 ["foo", "bar"], "", None),
647 ("ftp://localhost/baz.gif;type=a",
648 "localhost", ftplib.FTP_PORT, "", "", "A",
649 [], "baz.gif", None), # XXX really this should guess image/gif
650 ]:
651 req = Request(url)
652 req.timeout = None
653 r = h.ftp_open(req)
654 # ftp authentication not yet implemented by FTPHandler
655 self.assertEqual(h.user, user)
656 self.assertEqual(h.passwd, passwd)
657 self.assertEqual(h.host, socket.gethostbyname(host))
658 self.assertEqual(h.port, port)
659 self.assertEqual(h.dirs, dirs)
660 self.assertEqual(h.ftpwrapper.filename, filename)
661 self.assertEqual(h.ftpwrapper.filetype, type_)
662 headers = r.info()
663 self.assertEqual(headers.get("Content-type"), mimetype)
664 self.assertEqual(int(headers["Content-length"]), len(data))
665
666 def test_file(self):
667 import rfc822, socket
668 h = urllib2.FileHandler()
669 o = h.parent = MockOpener()
670
671 TESTFN = test_support.TESTFN
672 urlpath = sanepathname2url(os.path.abspath(TESTFN))
673 towrite = "hello, world\n"
674 urls = [
675 "file://localhost%s" % urlpath,
676 "file://%s" % urlpath,
677 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
678 ]
679 try:
680 localaddr = socket.gethostbyname(socket.gethostname())
681 except socket.gaierror:
682 localaddr = ''
683 if localaddr:
684 urls.append("file://%s%s" % (localaddr, urlpath))
685
686 for url in urls:
687 f = open(TESTFN, "wb")
688 try:
689 try:
690 f.write(towrite)
691 finally:
692 f.close()
693
694 r = h.file_open(Request(url))
695 try:
696 data = r.read()
697 headers = r.info()
698 respurl = r.geturl()
699 finally:
700 r.close()
701 stats = os.stat(TESTFN)
702 modified = rfc822.formatdate(stats.st_mtime)
703 finally:
704 os.remove(TESTFN)
705 self.assertEqual(data, towrite)
706 self.assertEqual(headers["Content-type"], "text/plain")
707 self.assertEqual(headers["Content-length"], "13")
708 self.assertEqual(headers["Last-modified"], modified)
709 self.assertEqual(respurl, url)
710
711 for url in [
712 "file://localhost:80%s" % urlpath,
713 "file:///file_does_not_exist.txt",
714 "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
715 os.getcwd(), TESTFN),
716 "file://somerandomhost.ontheinternet.com%s/%s" %
717 (os.getcwd(), TESTFN),
718 ]:
719 try:
720 f = open(TESTFN, "wb")
721 try:
722 f.write(towrite)
723 finally:
724 f.close()
725
726 self.assertRaises(urllib2.URLError,
727 h.file_open, Request(url))
728 finally:
729 os.remove(TESTFN)
730
731 h = urllib2.FileHandler()
732 o = h.parent = MockOpener()
733 # XXXX why does // mean ftp (and /// mean not ftp!), and where
734 # is file: scheme specified? I think this is really a bug, and
735 # what was intended was to distinguish between URLs like:
736 # file:/blah.txt (a file)
737 # file://localhost/blah.txt (a file)
738 # file:///blah.txt (a file)
739 # file://ftp.example.com/blah.txt (an ftp URL)
740 for url, ftp in [
741 ("file://ftp.example.com//foo.txt", True),
742 ("file://ftp.example.com///foo.txt", False),
743# XXXX bug: fails with OSError, should be URLError
744 ("file://ftp.example.com/foo.txt", False),
745 ("file://somehost//foo/something.txt", True),
746 ("file://localhost//foo/something.txt", False),
747 ]:
748 req = Request(url)
749 try:
750 h.file_open(req)
751 # XXXX remove OSError when bug fixed
752 except (urllib2.URLError, OSError):
753 self.assertTrue(not ftp)
754 else:
755 self.assertTrue(o.req is req)
756 self.assertEqual(req.type, "ftp")
757 self.assertEqual(req.type == "ftp", ftp)
758
759 def test_http(self):
760
761 h = urllib2.AbstractHTTPHandler()
762 o = h.parent = MockOpener()
763
764 url = "http://example.com/"
765 for method, data in [("GET", None), ("POST", "blah")]:
766 req = Request(url, data, {"Foo": "bar"})
767 req.timeout = None
768 req.add_unredirected_header("Spam", "eggs")
769 http = MockHTTPClass()
770 r = h.do_open(http, req)
771
772 # result attributes
773 r.read; r.readline # wrapped MockFile methods
774 r.info; r.geturl # addinfourl methods
775 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
776 hdrs = r.info()
777 hdrs.get; hdrs.has_key # r.info() gives dict from .getreply()
778 self.assertEqual(r.geturl(), url)
779
780 self.assertEqual(http.host, "example.com")
781 self.assertEqual(http.level, 0)
782 self.assertEqual(http.method, method)
783 self.assertEqual(http.selector, "/")
784 self.assertEqual(http.req_headers,
785 [("Connection", "close"),
786 ("Foo", "bar"), ("Spam", "eggs")])
787 self.assertEqual(http.data, data)
788
789 # check socket.error converted to URLError
790 http.raise_on_endheaders = True
791 self.assertRaises(urllib2.URLError, h.do_open, http, req)
792
793 # check adding of standard headers
794 o.addheaders = [("Spam", "eggs")]
795 for data in "", None: # POST, GET
796 req = Request("http://example.com/", data)
797 r = MockResponse(200, "OK", {}, "")
798 newreq = h.do_request_(req)
799 if data is None: # GET
800 self.assertNotIn("Content-length", req.unredirected_hdrs)
801 self.assertNotIn("Content-type", req.unredirected_hdrs)
802 else: # POST
803 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
804 self.assertEqual(req.unredirected_hdrs["Content-type"],
805 "application/x-www-form-urlencoded")
806 # XXX the details of Host could be better tested
807 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
808 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
809
810 # don't clobber existing headers
811 req.add_unredirected_header("Content-length", "foo")
812 req.add_unredirected_header("Content-type", "bar")
813 req.add_unredirected_header("Host", "baz")
814 req.add_unredirected_header("Spam", "foo")
815 newreq = h.do_request_(req)
816 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
817 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
818 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
819 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
820
821 def test_http_doubleslash(self):
822 # Checks that the presence of an unnecessary double slash in a url doesn't break anything
823 # Previously, a double slash directly after the host could cause incorrect parsing of the url
824 h = urllib2.AbstractHTTPHandler()
825 o = h.parent = MockOpener()
826
827 data = ""
828 ds_urls = [
829 "http://example.com/foo/bar/baz.html",
830 "http://example.com//foo/bar/baz.html",
831 "http://example.com/foo//bar/baz.html",
832 "http://example.com/foo/bar//baz.html",
833 ]
834
835 for ds_url in ds_urls:
836 ds_req = Request(ds_url, data)
837
838 # Check whether host is determined correctly if there is no proxy
839 np_ds_req = h.do_request_(ds_req)
840 self.assertEqual(np_ds_req.unredirected_hdrs["Host"],"example.com")
841
842 # Check whether host is determined correctly if there is a proxy
843 ds_req.set_proxy("someproxy:3128",None)
844 p_ds_req = h.do_request_(ds_req)
845 self.assertEqual(p_ds_req.unredirected_hdrs["Host"],"example.com")
846
847 def test_fixpath_in_weirdurls(self):
848 # Issue4493: urllib2 to supply '/' when to urls where path does not
849 # start with'/'
850
851 h = urllib2.AbstractHTTPHandler()
852 o = h.parent = MockOpener()
853
854 weird_url = 'http://www.python.org?getspam'
855 req = Request(weird_url)
856 newreq = h.do_request_(req)
857 self.assertEqual(newreq.get_host(),'www.python.org')
858 self.assertEqual(newreq.get_selector(),'/?getspam')
859
860 url_without_path = 'http://www.python.org'
861 req = Request(url_without_path)
862 newreq = h.do_request_(req)
863 self.assertEqual(newreq.get_host(),'www.python.org')
864 self.assertEqual(newreq.get_selector(),'')
865
866 def test_errors(self):
867 h = urllib2.HTTPErrorProcessor()
868 o = h.parent = MockOpener()
869
870 url = "http://example.com/"
871 req = Request(url)
872 # all 2xx are passed through
873 r = MockResponse(200, "OK", {}, "", url)
874 newr = h.http_response(req, r)
875 self.assertTrue(r is newr)
876 self.assertTrue(not hasattr(o, "proto")) # o.error not called
877 r = MockResponse(202, "Accepted", {}, "", url)
878 newr = h.http_response(req, r)
879 self.assertTrue(r is newr)
880 self.assertTrue(not hasattr(o, "proto")) # o.error not called
881 r = MockResponse(206, "Partial content", {}, "", url)
882 newr = h.http_response(req, r)
883 self.assertTrue(r is newr)
884 self.assertTrue(not hasattr(o, "proto")) # o.error not called
885 # anything else calls o.error (and MockOpener returns None, here)
886 r = MockResponse(502, "Bad gateway", {}, "", url)
887 self.assertTrue(h.http_response(req, r) is None)
888 self.assertEqual(o.proto, "http") # o.error called
889 self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
890
891 def test_cookies(self):
892 cj = MockCookieJar()
893 h = urllib2.HTTPCookieProcessor(cj)
894 o = h.parent = MockOpener()
895
896 req = Request("http://example.com/")
897 r = MockResponse(200, "OK", {}, "")
898 newreq = h.http_request(req)
899 self.assertTrue(cj.ach_req is req is newreq)
900 self.assertEqual(req.get_origin_req_host(), "example.com")
901 self.assertTrue(not req.is_unverifiable())
902 newr = h.http_response(req, r)
903 self.assertTrue(cj.ec_req is req)
904 self.assertTrue(cj.ec_r is r is newr)
905
906 def test_redirect(self):
907 from_url = "http://example.com/a.html"
908 to_url = "http://example.com/b.html"
909 h = urllib2.HTTPRedirectHandler()
910 o = h.parent = MockOpener()
911
912 # ordinary redirect behaviour
913 for code in 301, 302, 303, 307:
914 for data in None, "blah\nblah\n":
915 method = getattr(h, "http_error_%s" % code)
916 req = Request(from_url, data)
917 req.add_header("Nonsense", "viking=withhold")
918 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
919 if data is not None:
920 req.add_header("Content-Length", str(len(data)))
921 req.add_unredirected_header("Spam", "spam")
922 try:
923 method(req, MockFile(), code, "Blah",
924 MockHeaders({"location": to_url}))
925 except urllib2.HTTPError:
926 # 307 in response to POST requires user OK
927 self.assertTrue(code == 307 and data is not None)
928 self.assertEqual(o.req.get_full_url(), to_url)
929 try:
930 self.assertEqual(o.req.get_method(), "GET")
931 except AttributeError:
932 self.assertTrue(not o.req.has_data())
933
934 # now it's a GET, there should not be headers regarding content
935 # (possibly dragged from before being a POST)
936 headers = [x.lower() for x in o.req.headers]
937 self.assertNotIn("content-length", headers)
938 self.assertNotIn("content-type", headers)
939
940 self.assertEqual(o.req.headers["Nonsense"],
941 "viking=withhold")
942 self.assertNotIn("Spam", o.req.headers)
943 self.assertNotIn("Spam", o.req.unredirected_hdrs)
944
945 # loop detection
946 req = Request(from_url)
947 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
948 def redirect(h, req, url=to_url):
949 h.http_error_302(req, MockFile(), 302, "Blah",
950 MockHeaders({"location": url}))
951 # Note that the *original* request shares the same record of
952 # redirections with the sub-requests caused by the redirections.
953
954 # detect infinite loop redirect of a URL to itself
955 req = Request(from_url, origin_req_host="example.com")
956 count = 0
957 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
958 try:
959 while 1:
960 redirect(h, req, "http://example.com/")
961 count = count + 1
962 except urllib2.HTTPError:
963 # don't stop until max_repeats, because cookies may introduce state
964 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
965
966 # detect endless non-repeating chain of redirects
967 req = Request(from_url, origin_req_host="example.com")
968 count = 0
969 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
970 try:
971 while 1:
972 redirect(h, req, "http://example.com/%d" % count)
973 count = count + 1
974 except urllib2.HTTPError:
975 self.assertEqual(count,
976 urllib2.HTTPRedirectHandler.max_redirections)
977
978 def test_invalid_redirect(self):
979 from_url = "http://example.com/a.html"
980 valid_schemes = ['http', 'https', 'ftp']
981 invalid_schemes = ['file', 'imap', 'ldap']
982 schemeless_url = "example.com/b.html"
983 h = urllib2.HTTPRedirectHandler()
984 o = h.parent = MockOpener()
985 req = Request(from_url)
986 req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
987
988 for scheme in invalid_schemes:
989 invalid_url = scheme + '://' + schemeless_url
990 self.assertRaises(urllib2.HTTPError, h.http_error_302,
991 req, MockFile(), 302, "Security Loophole",
992 MockHeaders({"location": invalid_url}))
993
994 for scheme in valid_schemes:
995 valid_url = scheme + '://' + schemeless_url
996 h.http_error_302(req, MockFile(), 302, "That's fine",
997 MockHeaders({"location": valid_url}))
998 self.assertEqual(o.req.get_full_url(), valid_url)
999
1000 def test_cookie_redirect(self):
1001 # cookies shouldn't leak into redirected requests
1002 from cookielib import CookieJar
1003
1004 from test.test_cookielib import interact_netscape
1005
1006 cj = CookieJar()
1007 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
1008 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
1009 hdeh = urllib2.HTTPDefaultErrorHandler()
1010 hrh = urllib2.HTTPRedirectHandler()
1011 cp = urllib2.HTTPCookieProcessor(cj)
1012 o = build_test_opener(hh, hdeh, hrh, cp)
1013 o.open("http://www.example.com/")
1014 self.assertTrue(not hh.req.has_header("Cookie"))
1015
1016 def test_redirect_fragment(self):
1017 redirected_url = 'http://www.example.com/index.html#OK\r\n\r\n'
1018 hh = MockHTTPHandler(302, 'Location: ' + redirected_url)
1019 hdeh = urllib2.HTTPDefaultErrorHandler()
1020 hrh = urllib2.HTTPRedirectHandler()
1021 o = build_test_opener(hh, hdeh, hrh)
1022 fp = o.open('http://www.example.com')
1023 self.assertEqual(fp.geturl(), redirected_url.strip())
1024
1025 def test_proxy(self):
1026 o = OpenerDirector()
1027 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
1028 o.add_handler(ph)
1029 meth_spec = [
1030 [("http_open", "return response")]
1031 ]
1032 handlers = add_ordered_mock_handlers(o, meth_spec)
1033
1034 req = Request("http://acme.example.com/")
1035 self.assertEqual(req.get_host(), "acme.example.com")
1036 r = o.open(req)
1037 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1038
1039 self.assertEqual([(handlers[0], "http_open")],
1040 [tup[0:2] for tup in o.calls])
1041
1042 def test_proxy_no_proxy(self):
1043 os.environ['no_proxy'] = 'python.org'
1044 o = OpenerDirector()
1045 ph = urllib2.ProxyHandler(dict(http="proxy.example.com"))
1046 o.add_handler(ph)
1047 req = Request("http://www.perl.org/")
1048 self.assertEqual(req.get_host(), "www.perl.org")
1049 r = o.open(req)
1050 self.assertEqual(req.get_host(), "proxy.example.com")
1051 req = Request("http://www.python.org")
1052 self.assertEqual(req.get_host(), "www.python.org")
1053 r = o.open(req)
1054 self.assertEqual(req.get_host(), "www.python.org")
1055 del os.environ['no_proxy']
1056
1057
1058 def test_proxy_https(self):
1059 o = OpenerDirector()
1060 ph = urllib2.ProxyHandler(dict(https='proxy.example.com:3128'))
1061 o.add_handler(ph)
1062 meth_spec = [
1063 [("https_open","return response")]
1064 ]
1065 handlers = add_ordered_mock_handlers(o, meth_spec)
1066 req = Request("https://www.example.com/")
1067 self.assertEqual(req.get_host(), "www.example.com")
1068 r = o.open(req)
1069 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1070 self.assertEqual([(handlers[0], "https_open")],
1071 [tup[0:2] for tup in o.calls])
1072
1073 def test_proxy_https_proxy_authorization(self):
1074 o = OpenerDirector()
1075 ph = urllib2.ProxyHandler(dict(https='proxy.example.com:3128'))
1076 o.add_handler(ph)
1077 https_handler = MockHTTPSHandler()
1078 o.add_handler(https_handler)
1079 req = Request("https://www.example.com/")
1080 req.add_header("Proxy-Authorization","FooBar")
1081 req.add_header("User-Agent","Grail")
1082 self.assertEqual(req.get_host(), "www.example.com")
1083 self.assertIsNone(req._tunnel_host)
1084 r = o.open(req)
1085 # Verify Proxy-Authorization gets tunneled to request.
1086 # httpsconn req_headers do not have the Proxy-Authorization header but
1087 # the req will have.
1088 self.assertNotIn(("Proxy-Authorization","FooBar"),
1089 https_handler.httpconn.req_headers)
1090 self.assertIn(("User-Agent","Grail"),
1091 https_handler.httpconn.req_headers)
1092 self.assertIsNotNone(req._tunnel_host)
1093 self.assertEqual(req.get_host(), "proxy.example.com:3128")
1094 self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
1095
1096 def test_basic_auth(self, quote_char='"'):
1097 opener = OpenerDirector()
1098 password_manager = MockPasswordManager()
1099 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
1100 realm = "ACME Widget Store"
1101 http_handler = MockHTTPHandler(
1102 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
1103 (quote_char, realm, quote_char) )
1104 opener.add_handler(auth_handler)
1105 opener.add_handler(http_handler)
1106 self._test_basic_auth(opener, auth_handler, "Authorization",
1107 realm, http_handler, password_manager,
1108 "http://acme.example.com/protected",
1109 "http://acme.example.com/protected"
1110 )
1111
1112 def test_basic_auth_with_single_quoted_realm(self):
1113 self.test_basic_auth(quote_char="'")
1114
1115 def test_basic_auth_with_unquoted_realm(self):
1116 opener = OpenerDirector()
1117 password_manager = MockPasswordManager()
1118 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
1119 realm = "ACME Widget Store"
1120 http_handler = MockHTTPHandler(
1121 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm)
1122 opener.add_handler(auth_handler)
1123 opener.add_handler(http_handler)
1124 msg = "Basic Auth Realm was unquoted"
1125 with test_support.check_warnings((msg, UserWarning)):
1126 self._test_basic_auth(opener, auth_handler, "Authorization",
1127 realm, http_handler, password_manager,
1128 "http://acme.example.com/protected",
1129 "http://acme.example.com/protected"
1130 )
1131
1132
1133 def test_proxy_basic_auth(self):
1134 opener = OpenerDirector()
1135 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
1136 opener.add_handler(ph)
1137 password_manager = MockPasswordManager()
1138 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
1139 realm = "ACME Networks"
1140 http_handler = MockHTTPHandler(
1141 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
1142 opener.add_handler(auth_handler)
1143 opener.add_handler(http_handler)
1144 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
1145 realm, http_handler, password_manager,
1146 "http://acme.example.com:3128/protected",
1147 "proxy.example.com:3128",
1148 )
1149
1150 def test_basic_and_digest_auth_handlers(self):
1151 # HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
1152 # response (http://python.org/sf/1479302), where it should instead
1153 # return None to allow another handler (especially
1154 # HTTPBasicAuthHandler) to handle the response.
1155
1156 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
1157 # try digest first (since it's the strongest auth scheme), so we record
1158 # order of calls here to check digest comes first:
1159 class RecordingOpenerDirector(OpenerDirector):
1160 def __init__(self):
1161 OpenerDirector.__init__(self)
1162 self.recorded = []
1163 def record(self, info):
1164 self.recorded.append(info)
1165 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
1166 def http_error_401(self, *args, **kwds):
1167 self.parent.record("digest")
1168 urllib2.HTTPDigestAuthHandler.http_error_401(self,
1169 *args, **kwds)
1170 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
1171 def http_error_401(self, *args, **kwds):
1172 self.parent.record("basic")
1173 urllib2.HTTPBasicAuthHandler.http_error_401(self,
1174 *args, **kwds)
1175
1176 opener = RecordingOpenerDirector()
1177 password_manager = MockPasswordManager()
1178 digest_handler = TestDigestAuthHandler(password_manager)
1179 basic_handler = TestBasicAuthHandler(password_manager)
1180 realm = "ACME Networks"
1181 http_handler = MockHTTPHandler(
1182 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
1183 opener.add_handler(basic_handler)
1184 opener.add_handler(digest_handler)
1185 opener.add_handler(http_handler)
1186
1187 # check basic auth isn't blocked by digest handler failing
1188 self._test_basic_auth(opener, basic_handler, "Authorization",
1189 realm, http_handler, password_manager,
1190 "http://acme.example.com/protected",
1191 "http://acme.example.com/protected",
1192 )
1193 # check digest was tried before basic (twice, because
1194 # _test_basic_auth called .open() twice)
1195 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
1196
1197 def _test_basic_auth(self, opener, auth_handler, auth_header,
1198 realm, http_handler, password_manager,
1199 request_url, protected_url):
1200 import base64
1201 user, password = "wile", "coyote"
1202
1203 # .add_password() fed through to password manager
1204 auth_handler.add_password(realm, request_url, user, password)
1205 self.assertEqual(realm, password_manager.realm)
1206 self.assertEqual(request_url, password_manager.url)
1207 self.assertEqual(user, password_manager.user)
1208 self.assertEqual(password, password_manager.password)
1209
1210 r = opener.open(request_url)
1211
1212 # should have asked the password manager for the username/password
1213 self.assertEqual(password_manager.target_realm, realm)
1214 self.assertEqual(password_manager.target_url, protected_url)
1215
1216 # expect one request without authorization, then one with
1217 self.assertEqual(len(http_handler.requests), 2)
1218 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1219 userpass = '%s:%s' % (user, password)
1220 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
1221 self.assertEqual(http_handler.requests[1].get_header(auth_header),
1222 auth_hdr_value)
1223 self.assertEqual(http_handler.requests[1].unredirected_hdrs[auth_header],
1224 auth_hdr_value)
1225 # if the password manager can't find a password, the handler won't
1226 # handle the HTTP auth error
1227 password_manager.user = password_manager.password = None
1228 http_handler.reset()
1229 r = opener.open(request_url)
1230 self.assertEqual(len(http_handler.requests), 1)
1231 self.assertFalse(http_handler.requests[0].has_header(auth_header))
1232
1233class MiscTests(unittest.TestCase):
1234
1235 def test_build_opener(self):
1236 class MyHTTPHandler(urllib2.HTTPHandler): pass
1237 class FooHandler(urllib2.BaseHandler):
1238 def foo_open(self): pass
1239 class BarHandler(urllib2.BaseHandler):
1240 def bar_open(self): pass
1241
1242 build_opener = urllib2.build_opener
1243
1244 o = build_opener(FooHandler, BarHandler)
1245 self.opener_has_handler(o, FooHandler)
1246 self.opener_has_handler(o, BarHandler)
1247
1248 # can take a mix of classes and instances
1249 o = build_opener(FooHandler, BarHandler())
1250 self.opener_has_handler(o, FooHandler)
1251 self.opener_has_handler(o, BarHandler)
1252
1253 # subclasses of default handlers override default handlers
1254 o = build_opener(MyHTTPHandler)
1255 self.opener_has_handler(o, MyHTTPHandler)
1256
1257 # a particular case of overriding: default handlers can be passed
1258 # in explicitly
1259 o = build_opener()
1260 self.opener_has_handler(o, urllib2.HTTPHandler)
1261 o = build_opener(urllib2.HTTPHandler)
1262 self.opener_has_handler(o, urllib2.HTTPHandler)
1263 o = build_opener(urllib2.HTTPHandler())
1264 self.opener_has_handler(o, urllib2.HTTPHandler)
1265
1266 # Issue2670: multiple handlers sharing the same base class
1267 class MyOtherHTTPHandler(urllib2.HTTPHandler): pass
1268 o = build_opener(MyHTTPHandler, MyOtherHTTPHandler)
1269 self.opener_has_handler(o, MyHTTPHandler)
1270 self.opener_has_handler(o, MyOtherHTTPHandler)
1271
1272 def opener_has_handler(self, opener, handler_class):
1273 for h in opener.handlers:
1274 if h.__class__ == handler_class:
1275 break
1276 else:
1277 self.assertTrue(False)
1278
1279class RequestTests(unittest.TestCase):
1280
1281 def setUp(self):
1282 self.get = urllib2.Request("http://www.python.org/~jeremy/")
1283 self.post = urllib2.Request("http://www.python.org/~jeremy/",
1284 "data",
1285 headers={"X-Test": "test"})
1286
1287 def test_method(self):
1288 self.assertEqual("POST", self.post.get_method())
1289 self.assertEqual("GET", self.get.get_method())
1290
1291 def test_add_data(self):
1292 self.assertTrue(not self.get.has_data())
1293 self.assertEqual("GET", self.get.get_method())
1294 self.get.add_data("spam")
1295 self.assertTrue(self.get.has_data())
1296 self.assertEqual("POST", self.get.get_method())
1297
1298 def test_get_full_url(self):
1299 self.assertEqual("http://www.python.org/~jeremy/",
1300 self.get.get_full_url())
1301
1302 def test_selector(self):
1303 self.assertEqual("/~jeremy/", self.get.get_selector())
1304 req = urllib2.Request("http://www.python.org/")
1305 self.assertEqual("/", req.get_selector())
1306
1307 def test_get_type(self):
1308 self.assertEqual("http", self.get.get_type())
1309
1310 def test_get_host(self):
1311 self.assertEqual("www.python.org", self.get.get_host())
1312
1313 def test_get_host_unquote(self):
1314 req = urllib2.Request("http://www.%70ython.org/")
1315 self.assertEqual("www.python.org", req.get_host())
1316
1317 def test_proxy(self):
1318 self.assertTrue(not self.get.has_proxy())
1319 self.get.set_proxy("www.perl.org", "http")
1320 self.assertTrue(self.get.has_proxy())
1321 self.assertEqual("www.python.org", self.get.get_origin_req_host())
1322 self.assertEqual("www.perl.org", self.get.get_host())
1323
1324 def test_wrapped_url(self):
1325 req = Request("<URL:http://www.python.org>")
1326 self.assertEqual("www.python.org", req.get_host())
1327
1328 def test_url_fragment(self):
1329 req = Request("http://www.python.org/?qs=query#fragment=true")
1330 self.assertEqual("/?qs=query", req.get_selector())
1331 req = Request("http://www.python.org/#fun=true")
1332 self.assertEqual("/", req.get_selector())
1333
1334 # Issue 11703: geturl() omits fragment in the original URL.
1335 url = 'http://docs.python.org/library/urllib2.html#OK'
1336 req = Request(url)
1337 self.assertEqual(req.get_full_url(), url)
1338
1339 def test_HTTPError_interface(self):
1340 """
1341 Issue 13211 reveals that HTTPError didn't implement the URLError
1342 interface even though HTTPError is a subclass of URLError.
1343
1344 >>> err = urllib2.HTTPError(msg='something bad happened', url=None, code=None, hdrs=None, fp=None)
1345 >>> assert hasattr(err, 'reason')
1346 >>> err.reason
1347 'something bad happened'
1348 """
1349
1350 def test_HTTPError_interface_call(self):
1351 """
1352 Issue 15701= - HTTPError interface has info method available from URLError.
1353 """
1354 err = urllib2.HTTPError(msg='something bad happened', url=None,
1355 code=None, hdrs='Content-Length:42', fp=None)
1356 self.assertTrue(hasattr(err, 'reason'))
1357 assert hasattr(err, 'reason')
1358 assert hasattr(err, 'info')
1359 assert callable(err.info)
1360 try:
1361 err.info()
1362 except AttributeError:
1363 self.fail("err.info() failed")
1364 self.assertEqual(err.info(), "Content-Length:42")
1365
1366def test_main(verbose=None):
1367 from test import test_urllib2
1368 test_support.run_doctest(test_urllib2, verbose)
1369 test_support.run_doctest(urllib2, verbose)
1370 tests = (TrivialTests,
1371 OpenerDirectorTests,
1372 HandlerTests,
1373 MiscTests,
1374 RequestTests)
1375 test_support.run_unittest(*tests)
1376
1377if __name__ == "__main__":
1378 test_main(verbose=True)
Note: See TracBrowser for help on using the repository browser.