blob: 43485c20de4d80f4ae65e7a310840a911cd648b1 [file] [log] [blame]
Guido van Rossumcd16bf62007-06-13 18:07:49 +00001#!/usr/bin/env python
2
Barry Warsaw820c1202008-06-12 04:06:45 +00003import email
Guido van Rossumcd16bf62007-06-13 18:07:49 +00004import threading
Jeremy Hylton1afc1692008-06-18 20:49:58 +00005import urllib.parse
6import urllib.request
Georg Brandl24420152008-05-26 16:32:26 +00007import http.server
Guido van Rossumcd16bf62007-06-13 18:07:49 +00008import unittest
9import hashlib
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Guido van Rossumcd16bf62007-06-13 18:07:49 +000011
12# Loopback http server infrastructure
13
Georg Brandl24420152008-05-26 16:32:26 +000014class LoopbackHttpServer(http.server.HTTPServer):
Guido van Rossumcd16bf62007-06-13 18:07:49 +000015 """HTTP server w/ a few modifications that make it useful for
16 loopback testing purposes.
17 """
18
19 def __init__(self, server_address, RequestHandlerClass):
Georg Brandl24420152008-05-26 16:32:26 +000020 http.server.HTTPServer.__init__(self,
21 server_address,
22 RequestHandlerClass)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000023
24 # Set the timeout of our listening socket really low so
25 # that we can stop the server easily.
26 self.socket.settimeout(1.0)
27
28 def get_request(self):
Georg Brandl24420152008-05-26 16:32:26 +000029 """HTTPServer method, overridden."""
Guido van Rossumcd16bf62007-06-13 18:07:49 +000030
31 request, client_address = self.socket.accept()
32
33 # It's a loopback connection, so setting the timeout
34 # really low shouldn't affect anything, but should make
35 # deadlocks less likely to occur.
36 request.settimeout(10.0)
37
38 return (request, client_address)
39
40class LoopbackHttpServerThread(threading.Thread):
41 """Stoppable thread that runs a loopback http server."""
42
Guido van Rossum806c2462007-08-06 23:33:07 +000043 def __init__(self, request_handler):
Guido van Rossumcd16bf62007-06-13 18:07:49 +000044 threading.Thread.__init__(self)
Guido van Rossum4566c712007-08-21 03:36:47 +000045 self._stop_server = False
Guido van Rossumcd16bf62007-06-13 18:07:49 +000046 self.ready = threading.Event()
Guido van Rossum806c2462007-08-06 23:33:07 +000047 request_handler.protocol_version = "HTTP/1.0"
Jeremy Hylton1afc1692008-06-18 20:49:58 +000048 self.httpd = LoopbackHttpServer(("127.0.0.1", 0),
Guido van Rossum806c2462007-08-06 23:33:07 +000049 request_handler)
50 #print "Serving HTTP on %s port %s" % (self.httpd.server_name,
51 # self.httpd.server_port)
52 self.port = self.httpd.server_port
Guido van Rossumcd16bf62007-06-13 18:07:49 +000053
54 def stop(self):
55 """Stops the webserver if it's currently running."""
56
57 # Set the stop flag.
Guido van Rossum4566c712007-08-21 03:36:47 +000058 self._stop_server = True
Guido van Rossumcd16bf62007-06-13 18:07:49 +000059
60 self.join()
61
62 def run(self):
Guido van Rossumcd16bf62007-06-13 18:07:49 +000063 self.ready.set()
Guido van Rossum4566c712007-08-21 03:36:47 +000064 while not self._stop_server:
Guido van Rossum806c2462007-08-06 23:33:07 +000065 self.httpd.handle_request()
Guido van Rossumcd16bf62007-06-13 18:07:49 +000066
67# Authentication infrastructure
68
69class DigestAuthHandler:
70 """Handler for performing digest authentication."""
71
72 def __init__(self):
73 self._request_num = 0
74 self._nonces = []
75 self._users = {}
76 self._realm_name = "Test Realm"
77 self._qop = "auth"
78
79 def set_qop(self, qop):
80 self._qop = qop
81
82 def set_users(self, users):
83 assert isinstance(users, dict)
84 self._users = users
85
86 def set_realm(self, realm):
87 self._realm_name = realm
88
89 def _generate_nonce(self):
90 self._request_num += 1
Guido van Rossum81360142007-08-29 14:26:52 +000091 nonce = hashlib.md5(str(self._request_num).encode("ascii")).hexdigest()
Guido van Rossumcd16bf62007-06-13 18:07:49 +000092 self._nonces.append(nonce)
93 return nonce
94
95 def _create_auth_dict(self, auth_str):
96 first_space_index = auth_str.find(" ")
97 auth_str = auth_str[first_space_index+1:]
98
99 parts = auth_str.split(",")
100
101 auth_dict = {}
102 for part in parts:
103 name, value = part.split("=")
104 name = name.strip()
105 if value[0] == '"' and value[-1] == '"':
106 value = value[1:-1]
107 else:
108 value = value.strip()
109 auth_dict[name] = value
110 return auth_dict
111
112 def _validate_auth(self, auth_dict, password, method, uri):
113 final_dict = {}
114 final_dict.update(auth_dict)
115 final_dict["password"] = password
116 final_dict["method"] = method
117 final_dict["uri"] = uri
118 HA1_str = "%(username)s:%(realm)s:%(password)s" % final_dict
Guido van Rossum81360142007-08-29 14:26:52 +0000119 HA1 = hashlib.md5(HA1_str.encode("ascii")).hexdigest()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000120 HA2_str = "%(method)s:%(uri)s" % final_dict
Guido van Rossum81360142007-08-29 14:26:52 +0000121 HA2 = hashlib.md5(HA2_str.encode("ascii")).hexdigest()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000122 final_dict["HA1"] = HA1
123 final_dict["HA2"] = HA2
124 response_str = "%(HA1)s:%(nonce)s:%(nc)s:" \
125 "%(cnonce)s:%(qop)s:%(HA2)s" % final_dict
Guido van Rossum81360142007-08-29 14:26:52 +0000126 response = hashlib.md5(response_str.encode("ascii")).hexdigest()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000127
128 return response == auth_dict["response"]
129
130 def _return_auth_challenge(self, request_handler):
131 request_handler.send_response(407, "Proxy Authentication Required")
132 request_handler.send_header("Content-Type", "text/html")
133 request_handler.send_header(
134 'Proxy-Authenticate', 'Digest realm="%s", '
135 'qop="%s",'
136 'nonce="%s", ' % \
137 (self._realm_name, self._qop, self._generate_nonce()))
138 # XXX: Not sure if we're supposed to add this next header or
139 # not.
140 #request_handler.send_header('Connection', 'close')
141 request_handler.end_headers()
Guido van Rossum8a392d72007-11-21 22:09:45 +0000142 request_handler.wfile.write(b"Proxy Authentication Required.")
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000143 return False
144
145 def handle_request(self, request_handler):
146 """Performs digest authentication on the given HTTP request
147 handler. Returns True if authentication was successful, False
148 otherwise.
149
150 If no users have been set, then digest auth is effectively
151 disabled and this method will always return True.
152 """
153
154 if len(self._users) == 0:
155 return True
156
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000157 if "Proxy-Authorization" not in request_handler.headers:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000158 return self._return_auth_challenge(request_handler)
159 else:
160 auth_dict = self._create_auth_dict(
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000161 request_handler.headers["Proxy-Authorization"]
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000162 )
163 if auth_dict["username"] in self._users:
164 password = self._users[ auth_dict["username"] ]
165 else:
166 return self._return_auth_challenge(request_handler)
167 if not auth_dict.get("nonce") in self._nonces:
168 return self._return_auth_challenge(request_handler)
169 else:
170 self._nonces.remove(auth_dict["nonce"])
171
172 auth_validated = False
173
174 # MSIE uses short_path in its validation, but Python's
175 # urllib2 uses the full path, so we're going to see if
176 # either of them works here.
177
178 for path in [request_handler.path, request_handler.short_path]:
179 if self._validate_auth(auth_dict,
180 password,
181 request_handler.command,
182 path):
183 auth_validated = True
184
185 if not auth_validated:
186 return self._return_auth_challenge(request_handler)
187 return True
188
189# Proxy test infrastructure
190
Georg Brandl24420152008-05-26 16:32:26 +0000191class FakeProxyHandler(http.server.BaseHTTPRequestHandler):
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000192 """This is a 'fake proxy' that makes it look like the entire
193 internet has gone down due to a sudden zombie invasion. It main
194 utility is in providing us with authentication support for
195 testing.
196 """
197
Collin Winter9a4414d2009-05-18 22:32:26 +0000198 def __init__(self, digest_auth_handler, *args, **kwargs):
199 # This has to be set before calling our parent's __init__(), which will
200 # try to call do_GET().
201 self.digest_auth_handler = digest_auth_handler
202 http.server.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000203
204 def log_message(self, format, *args):
205 # Uncomment the next line for debugging.
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000206 # sys.stderr.write(format % args)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000207 pass
208
209 def do_GET(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000210 (scm, netloc, path, params, query, fragment) = urllib.parse.urlparse(
211 self.path, "http")
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000212 self.short_path = path
213 if self.digest_auth_handler.handle_request(self):
214 self.send_response(200, "OK")
215 self.send_header("Content-Type", "text/html")
216 self.end_headers()
Guido van Rossum8a392d72007-11-21 22:09:45 +0000217 self.wfile.write(bytes("You've reached %s!<BR>" % self.path,
218 "ascii"))
219 self.wfile.write(b"Our apologies, but our server is down due to "
220 b"a sudden zombie invasion.")
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000221
222# Test cases
223
224class ProxyAuthTests(unittest.TestCase):
Christian Heimesbbe741d2008-03-28 10:53:29 +0000225 URL = "http://localhost"
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000226
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000227 USER = "tester"
228 PASSWD = "test123"
229 REALM = "TestRealm"
230
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000231 def setUp(self):
Collin Winter9a4414d2009-05-18 22:32:26 +0000232 self.digest_auth_handler = DigestAuthHandler()
233 self.digest_auth_handler.set_users({self.USER: self.PASSWD})
234 self.digest_auth_handler.set_realm(self.REALM)
235 def create_fake_proxy_handler(*args, **kwargs):
236 return FakeProxyHandler(self.digest_auth_handler, *args, **kwargs)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000237
Collin Winter9a4414d2009-05-18 22:32:26 +0000238 self.server = LoopbackHttpServerThread(create_fake_proxy_handler)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000239 self.server.start()
240 self.server.ready.wait()
Guido van Rossum806c2462007-08-06 23:33:07 +0000241 proxy_url = "http://127.0.0.1:%d" % self.server.port
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000242 handler = urllib.request.ProxyHandler({"http" : proxy_url})
Collin Winter9a4414d2009-05-18 22:32:26 +0000243 self.proxy_digest_handler = urllib.request.ProxyDigestAuthHandler()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000244 self.opener = urllib.request.build_opener(
Collin Winter9a4414d2009-05-18 22:32:26 +0000245 handler, self.proxy_digest_handler)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000246
247 def tearDown(self):
248 self.server.stop()
249
250 def test_proxy_with_bad_password_raises_httperror(self):
Collin Winter9a4414d2009-05-18 22:32:26 +0000251 self.proxy_digest_handler.add_password(self.REALM, self.URL,
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000252 self.USER, self.PASSWD+"bad")
Collin Winter9a4414d2009-05-18 22:32:26 +0000253 self.digest_auth_handler.set_qop("auth")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000254 self.assertRaises(urllib.error.HTTPError,
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000255 self.opener.open,
256 self.URL)
257
258 def test_proxy_with_no_password_raises_httperror(self):
Collin Winter9a4414d2009-05-18 22:32:26 +0000259 self.digest_auth_handler.set_qop("auth")
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000260 self.assertRaises(urllib.error.HTTPError,
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000261 self.opener.open,
262 self.URL)
263
264 def test_proxy_qop_auth_works(self):
Collin Winter9a4414d2009-05-18 22:32:26 +0000265 self.proxy_digest_handler.add_password(self.REALM, self.URL,
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000266 self.USER, self.PASSWD)
Collin Winter9a4414d2009-05-18 22:32:26 +0000267 self.digest_auth_handler.set_qop("auth")
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000268 result = self.opener.open(self.URL)
269 while result.read():
270 pass
271 result.close()
272
273 def test_proxy_qop_auth_int_works_or_throws_urlerror(self):
Collin Winter9a4414d2009-05-18 22:32:26 +0000274 self.proxy_digest_handler.add_password(self.REALM, self.URL,
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000275 self.USER, self.PASSWD)
Collin Winter9a4414d2009-05-18 22:32:26 +0000276 self.digest_auth_handler.set_qop("auth-int")
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000277 try:
278 result = self.opener.open(self.URL)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000279 except urllib.error.URLError:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000280 # It's okay if we don't support auth-int, but we certainly
281 # shouldn't receive any kind of exception here other than
282 # a URLError.
283 result = None
284 if result:
285 while result.read():
286 pass
287 result.close()
288
Christian Heimesbbe741d2008-03-28 10:53:29 +0000289
290def GetRequestHandler(responses):
291
Georg Brandl24420152008-05-26 16:32:26 +0000292 class FakeHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
Christian Heimesbbe741d2008-03-28 10:53:29 +0000293
294 server_version = "TestHTTP/"
295 requests = []
296 headers_received = []
297 port = 80
298
299 def do_GET(self):
300 body = self.send_head()
301 if body:
302 self.wfile.write(body)
303
304 def do_POST(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000305 content_length = self.headers["Content-Length"]
Christian Heimesbbe741d2008-03-28 10:53:29 +0000306 post_data = self.rfile.read(int(content_length))
307 self.do_GET()
308 self.requests.append(post_data)
309
310 def send_head(self):
311 FakeHTTPRequestHandler.headers_received = self.headers
312 self.requests.append(self.path)
313 response_code, headers, body = responses.pop(0)
314
315 self.send_response(response_code)
316
317 for (header, value) in headers:
Antoine Pitroub353c122009-02-11 00:39:14 +0000318 self.send_header(header, value % {'port':self.port})
Christian Heimesbbe741d2008-03-28 10:53:29 +0000319 if body:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000320 self.send_header("Content-type", "text/plain")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000321 self.end_headers()
322 return body
323 self.end_headers()
324
325 def log_message(self, *args):
326 pass
327
328
329 return FakeHTTPRequestHandler
330
331
332class TestUrlopen(unittest.TestCase):
333 """Tests urllib2.urlopen using the network.
334
335 These tests are not exhaustive. Assuming that testing using files does a
336 good job overall of some of the basic interface features. There are no
337 tests exercising the optional 'data' and 'proxies' arguments. No tests
338 for transparent redirection have been written.
339 """
340
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000341 def setUp(self):
342 self.server = None
343
344 def tearDown(self):
345 if self.server is not None:
346 self.server.stop()
347
348 def urlopen(self, url, data=None):
Antoine Pitroub353c122009-02-11 00:39:14 +0000349 l = []
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000350 f = urllib.request.urlopen(url, data)
Antoine Pitroub353c122009-02-11 00:39:14 +0000351 try:
352 # Exercise various methods
353 l.extend(f.readlines(200))
354 l.append(f.readline())
355 l.append(f.read(1024))
356 l.append(f.read())
357 finally:
358 f.close()
359 return b"".join(l)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000360
361 def start_server(self, responses=None):
362 if responses is None:
363 responses = [(200, [], b"we don't care")]
Christian Heimesbbe741d2008-03-28 10:53:29 +0000364 handler = GetRequestHandler(responses)
365
366 self.server = LoopbackHttpServerThread(handler)
367 self.server.start()
368 self.server.ready.wait()
369 port = self.server.port
370 handler.port = port
371 return handler
372
Christian Heimesbbe741d2008-03-28 10:53:29 +0000373 def test_redirection(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000374 expected_response = b"We got here..."
Christian Heimesbbe741d2008-03-28 10:53:29 +0000375 responses = [
Antoine Pitroub353c122009-02-11 00:39:14 +0000376 (302, [("Location", "http://localhost:%(port)s/somewhere_else")],
377 ""),
Christian Heimesbbe741d2008-03-28 10:53:29 +0000378 (200, [], expected_response)
379 ]
380
381 handler = self.start_server(responses)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000382 data = self.urlopen("http://localhost:%s/" % handler.port)
383 self.assertEquals(data, expected_response)
384 self.assertEquals(handler.requests, ["/", "/somewhere_else"])
Christian Heimesbbe741d2008-03-28 10:53:29 +0000385
Antoine Pitroub353c122009-02-11 00:39:14 +0000386 def test_chunked(self):
387 expected_response = b"hello world"
388 chunked_start = (
389 b'a\r\n'
390 b'hello worl\r\n'
391 b'1\r\n'
392 b'd\r\n'
393 b'0\r\n'
394 )
395 response = [(200, [("Transfer-Encoding", "chunked")], chunked_start)]
396 handler = self.start_server(response)
397 data = self.urlopen("http://localhost:%s/" % handler.port)
398 self.assertEquals(data, expected_response)
399
Christian Heimesbbe741d2008-03-28 10:53:29 +0000400 def test_404(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000401 expected_response = b"Bad bad bad..."
Christian Heimesbbe741d2008-03-28 10:53:29 +0000402 handler = self.start_server([(404, [], expected_response)])
403
404 try:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000405 self.urlopen("http://localhost:%s/weeble" % handler.port)
406 except urllib.error.URLError as f:
407 data = f.read()
408 f.close()
409 else:
410 self.fail("404 should raise URLError")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000411
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000412 self.assertEquals(data, expected_response)
413 self.assertEquals(handler.requests, ["/weeble"])
Christian Heimesbbe741d2008-03-28 10:53:29 +0000414
415 def test_200(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000416 expected_response = b"pycon 2008..."
Christian Heimesbbe741d2008-03-28 10:53:29 +0000417 handler = self.start_server([(200, [], expected_response)])
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000418 data = self.urlopen("http://localhost:%s/bizarre" % handler.port)
419 self.assertEquals(data, expected_response)
420 self.assertEquals(handler.requests, ["/bizarre"])
Christian Heimesbbe741d2008-03-28 10:53:29 +0000421
422 def test_200_with_parameters(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000423 expected_response = b"pycon 2008..."
Christian Heimesbbe741d2008-03-28 10:53:29 +0000424 handler = self.start_server([(200, [], expected_response)])
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000425 data = self.urlopen("http://localhost:%s/bizarre" % handler.port,
426 b"get=with_feeling")
427 self.assertEquals(data, expected_response)
428 self.assertEquals(handler.requests, ["/bizarre", b"get=with_feeling"])
Christian Heimesbbe741d2008-03-28 10:53:29 +0000429
430 def test_sending_headers(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000431 handler = self.start_server()
432 req = urllib.request.Request("http://localhost:%s/" % handler.port,
433 headers={"Range": "bytes=20-39"})
434 urllib.request.urlopen(req)
435 self.assertEqual(handler.headers_received["Range"], "bytes=20-39")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000436
437 def test_basic(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000438 handler = self.start_server()
439 open_url = urllib.request.urlopen("http://localhost:%s" % handler.port)
440 for attr in ("read", "close", "info", "geturl"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000441 self.assertTrue(hasattr(open_url, attr), "object returned from "
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000442 "urlopen lacks the %s attribute" % attr)
Christian Heimesbbe741d2008-03-28 10:53:29 +0000443 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000444 self.assertTrue(open_url.read(), "calling 'read' failed")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000445 finally:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000446 open_url.close()
Christian Heimesbbe741d2008-03-28 10:53:29 +0000447
448 def test_info(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000449 handler = self.start_server()
Christian Heimesbbe741d2008-03-28 10:53:29 +0000450 try:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000451 open_url = urllib.request.urlopen(
452 "http://localhost:%s" % handler.port)
Christian Heimesbbe741d2008-03-28 10:53:29 +0000453 info_obj = open_url.info()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000454 self.assertTrue(isinstance(info_obj, email.message.Message),
Christian Heimesbbe741d2008-03-28 10:53:29 +0000455 "object returned by 'info' is not an instance of "
Barry Warsaw820c1202008-06-12 04:06:45 +0000456 "email.message.Message")
457 self.assertEqual(info_obj.get_content_subtype(), "plain")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000458 finally:
459 self.server.stop()
460
461 def test_geturl(self):
462 # Make sure same URL as opened is returned by geturl.
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000463 handler = self.start_server()
464 open_url = urllib.request.urlopen("http://localhost:%s" % handler.port)
465 url = open_url.geturl()
466 self.assertEqual(url, "http://localhost:%s" % handler.port)
Christian Heimesbbe741d2008-03-28 10:53:29 +0000467
468 def test_bad_address(self):
469 # Make sure proper exception is raised when connecting to a bogus
470 # address.
471 self.assertRaises(IOError,
472 # SF patch 809915: In Sep 2003, VeriSign started
473 # highjacking invalid .com and .net addresses to
474 # boost traffic to their own site. This test
475 # started failing then. One hopes the .invalid
476 # domain will be spared to serve its defined
477 # purpose.
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000478 urllib.request.urlopen,
Antoine Pitrou8fd33d32008-12-15 13:08:55 +0000479 "http://sadflkjsasf.i.nvali.d/")
Christian Heimesbbe741d2008-03-28 10:53:29 +0000480
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000481def test_main():
Collin Winter9a4414d2009-05-18 22:32:26 +0000482 support.run_unittest(ProxyAuthTests, TestUrlopen)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000483
484if __name__ == "__main__":
485 test_main()