summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2006-04-30 07:06:11 (GMT)
committerGeorg Brandl <georg@python.org>2006-04-30 07:06:11 (GMT)
commitfa42bd7af429a909e3e964ffc8dcfa457e007ac8 (patch)
treeb6ae48d30b6458f727e52788f465ed3c05e5e1d0 /Lib/test
parent5085fe2bcc1093eb4c0845a92c8e870421bd7f4e (diff)
downloadcpython-fa42bd7af429a909e3e964ffc8dcfa457e007ac8.zip
cpython-fa42bd7af429a909e3e964ffc8dcfa457e007ac8.tar.gz
cpython-fa42bd7af429a909e3e964ffc8dcfa457e007ac8.tar.bz2
Patch #1470846: fix urllib2 ProxyBasicAuthHandler.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_urllib2.py217
-rw-r--r--Lib/test/test_urllib2net.py43
2 files changed, 225 insertions, 35 deletions
diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py
index 64a2ee9..ab48fe9 100644
--- a/Lib/test/test_urllib2.py
+++ b/Lib/test/test_urllib2.py
@@ -10,10 +10,7 @@ from urllib2 import Request, OpenerDirector
# XXX
# Request
# CacheFTPHandler (hard to write)
-# parse_keqv_list, parse_http_list (I'm leaving this for Anthony Baxter
-# and Greg Stein, since they're doing Digest Authentication)
-# Authentication stuff (ditto)
-# CustomProxy, CustomProxyHandler
+# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
class TrivialTests(unittest.TestCase):
def test_trivial(self):
@@ -49,6 +46,70 @@ class TrivialTests(unittest.TestCase):
self.assertEquals(urllib2.parse_http_list(string), list)
+def test_password_manager(self):
+ """
+ >>> mgr = urllib2.HTTPPasswordMgr()
+ >>> add = mgr.add_password
+ >>> add("Some Realm", "http://example.com/", "joe", "password")
+ >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
+ >>> add("c", "http://example.com/foo", "foo", "ni")
+ >>> add("c", "http://example.com/bar", "bar", "nini")
+ >>> add("b", "http://example.com/", "first", "blah")
+ >>> add("b", "http://example.com/", "second", "spam")
+ >>> add("a", "http://example.com", "1", "a")
+ >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
+ >>> add("Some Realm", "d.example.com", "4", "d")
+ >>> add("Some Realm", "e.example.com:3128", "5", "e")
+
+ >>> mgr.find_user_password("Some Realm", "example.com")
+ ('joe', 'password')
+ >>> mgr.find_user_password("Some Realm", "http://example.com")
+ ('joe', 'password')
+ >>> mgr.find_user_password("Some Realm", "http://example.com/")
+ ('joe', 'password')
+ >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
+ ('joe', 'password')
+ >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
+ ('joe', 'password')
+ >>> mgr.find_user_password("c", "http://example.com/foo")
+ ('foo', 'ni')
+ >>> mgr.find_user_password("c", "http://example.com/bar")
+ ('bar', 'nini')
+
+ Currently, we use the highest-level path where more than one match:
+
+ >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
+ ('joe', 'password')
+
+ Use latest add_password() in case of conflict:
+
+ >>> mgr.find_user_password("b", "http://example.com/")
+ ('second', 'spam')
+
+ No special relationship between a.example.com and example.com:
+
+ >>> mgr.find_user_password("a", "http://example.com/")
+ ('1', 'a')
+ >>> mgr.find_user_password("a", "http://a.example.com/")
+ (None, None)
+
+ Ports:
+
+ >>> mgr.find_user_password("Some Realm", "c.example.com")
+ (None, None)
+ >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
+ ('3', 'c')
+ >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
+ ('3', 'c')
+ >>> mgr.find_user_password("Some Realm", "d.example.com")
+ ('4', 'd')
+ >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
+ ('5', 'e')
+
+ """
+ pass
+
+
class MockOpener:
addheaders = []
def open(self, req, data=None):
@@ -89,6 +150,8 @@ class FakeMethod:
return self.handle(self.meth_name, self.action, *args)
class MockHandler:
+ # useful for testing handler machinery
+ # see add_ordered_mock_handlers() docstring
handler_order = 500
def __init__(self, methods):
self._define_methods(methods)
@@ -161,6 +224,50 @@ def add_ordered_mock_handlers(opener, meth_spec):
opener.add_handler(h)
return handlers
+def build_test_opener(*handler_instances):
+ opener = OpenerDirector()
+ for h in handler_instances:
+ opener.add_handler(h)
+ return opener
+
+class MockHTTPHandler(urllib2.BaseHandler):
+ # useful for testing redirections and auth
+ # sends supplied headers and code as first response
+ # sends 200 OK as second response
+ def __init__(self, code, headers):
+ self.code = code
+ self.headers = headers
+ self.reset()
+ def reset(self):
+ self._count = 0
+ self.requests = []
+ def http_open(self, req):
+ import mimetools, httplib, copy
+ from StringIO import StringIO
+ self.requests.append(copy.deepcopy(req))
+ if self._count == 0:
+ self._count = self._count + 1
+ name = httplib.responses[self.code]
+ msg = mimetools.Message(StringIO(self.headers))
+ return self.parent.error(
+ "http", req, MockFile(), self.code, name, msg)
+ else:
+ self.req = req
+ msg = mimetools.Message(StringIO("\r\n\r\n"))
+ return MockResponse(200, "OK", msg, "", req.get_full_url())
+
+class MockPasswordManager:
+ def add_password(self, realm, uri, user, password):
+ self.realm = realm
+ self.url = uri
+ self.user = user
+ self.password = password
+ def find_user_password(self, realm, authuri):
+ self.target_realm = realm
+ self.target_url = authuri
+ return self.user, self.password
+
+
class OpenerDirectorTests(unittest.TestCase):
def test_handled(self):
@@ -612,33 +719,18 @@ class HandlerTests(unittest.TestCase):
urllib2.HTTPRedirectHandler.max_redirections)
def test_cookie_redirect(self):
- class MockHTTPHandler(urllib2.HTTPHandler):
- def __init__(self): self._count = 0
- def http_open(self, req):
- import mimetools
- from StringIO import StringIO
- if self._count == 0:
- self._count = self._count + 1
- msg = mimetools.Message(
- StringIO("Location: http://www.cracker.com/\r\n\r\n"))
- return self.parent.error(
- "http", req, MockFile(), 302, "Found", msg)
- else:
- self.req = req
- msg = mimetools.Message(StringIO("\r\n\r\n"))
- return MockResponse(200, "OK", msg, "", req.get_full_url())
# cookies shouldn't leak into redirected requests
from cookielib import CookieJar
- from urllib2 import build_opener, HTTPHandler, HTTPError, \
- HTTPCookieProcessor
from test.test_cookielib import interact_netscape
cj = CookieJar()
interact_netscape(cj, "http://www.example.com/", "spam=eggs")
- hh = MockHTTPHandler()
- cp = HTTPCookieProcessor(cj)
- o = build_opener(hh, cp)
+ hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
+ hdeh = urllib2.HTTPDefaultErrorHandler()
+ hrh = urllib2.HTTPRedirectHandler()
+ cp = urllib2.HTTPCookieProcessor(cj)
+ o = build_test_opener(hh, hdeh, hrh, cp)
o.open("http://www.example.com/")
self.assert_(not hh.req.has_header("Cookie"))
@@ -659,6 +751,71 @@ class HandlerTests(unittest.TestCase):
self.assertEqual([(handlers[0], "http_open")],
[tup[0:2] for tup in o.calls])
+ def test_basic_auth(self):
+ opener = OpenerDirector()
+ password_manager = MockPasswordManager()
+ auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
+ realm = "ACME Widget Store"
+ http_handler = MockHTTPHandler(
+ 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
+ self._test_basic_auth(opener, auth_handler, "Authorization",
+ realm, http_handler, password_manager,
+ "http://acme.example.com/protected",
+ "http://acme.example.com/protected",
+ )
+
+ def test_proxy_basic_auth(self):
+ opener = OpenerDirector()
+ ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
+ opener.add_handler(ph)
+ password_manager = MockPasswordManager()
+ auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
+ realm = "ACME Networks"
+ http_handler = MockHTTPHandler(
+ 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
+ self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
+ realm, http_handler, password_manager,
+ "http://acme.example.com:3128/protected",
+ "proxy.example.com:3128",
+ )
+
+ def _test_basic_auth(self, opener, auth_handler, auth_header,
+ realm, http_handler, password_manager,
+ request_url, protected_url):
+ import base64, httplib
+ user, password = "wile", "coyote"
+ opener.add_handler(auth_handler)
+ opener.add_handler(http_handler)
+
+ # .add_password() fed through to password manager
+ auth_handler.add_password(realm, request_url, user, password)
+ self.assertEqual(realm, password_manager.realm)
+ self.assertEqual(request_url, password_manager.url)
+ self.assertEqual(user, password_manager.user)
+ self.assertEqual(password, password_manager.password)
+
+ r = opener.open(request_url)
+
+ # should have asked the password manager for the username/password
+ self.assertEqual(password_manager.target_realm, realm)
+ self.assertEqual(password_manager.target_url, protected_url)
+
+ # expect one request without authorization, then one with
+ self.assertEqual(len(http_handler.requests), 2)
+ self.assertFalse(http_handler.requests[0].has_header(auth_header))
+ userpass = '%s:%s' % (user, password)
+ auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
+ self.assertEqual(http_handler.requests[1].get_header(auth_header),
+ auth_hdr_value)
+
+ # if the password manager can't find a password, the handler won't
+ # handle the HTTP auth error
+ password_manager.user = password_manager.password = None
+ http_handler.reset()
+ r = opener.open(request_url)
+ self.assertEqual(len(http_handler.requests), 1)
+ self.assertFalse(http_handler.requests[0].has_header(auth_header))
+
class MiscTests(unittest.TestCase):
@@ -830,20 +987,12 @@ class NetworkTests(unittest.TestCase):
cfh.setTimeout(1)
handlers.append(cfh)
-## # XXX try out some custom proxy objects too!
-## def at_cnri(req):
-## host = req.get_host()
-## debug(host)
-## if host[-18:] == '.cnri.reston.va.us':
-## return True
-## p = CustomProxy('http', at_cnri, 'proxy.cnri.reston.va.us')
-## ph = CustomProxyHandler(p)
-## handlers.append(ph)
-
return handlers
def test_main(verbose=None):
+ from test import test_urllib2
+ test_support.run_doctest(test_urllib2, verbose)
test_support.run_doctest(urllib2, verbose)
tests = (TrivialTests,
OpenerDirectorTests,
diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py
index 3c23246..665b6ad 100644
--- a/Lib/test/test_urllib2net.py
+++ b/Lib/test/test_urllib2net.py
@@ -23,6 +23,46 @@ class URLTimeoutTest(unittest.TestCase):
f = urllib2.urlopen("http://www.python.org/")
x = f.read()
+
+class AuthTests(unittest.TestCase):
+ """Tests urllib2 authentication features."""
+
+## Disabled at the moment since there is no page under python.org which
+## could be used to HTTP authentication.
+#
+# def test_basic_auth(self):
+# import httplib
+#
+# test_url = "http://www.python.org/test/test_urllib2/basic_auth"
+# test_hostport = "www.python.org"
+# test_realm = 'Test Realm'
+# test_user = 'test.test_urllib2net'
+# test_password = 'blah'
+#
+# # failure
+# try:
+# urllib2.urlopen(test_url)
+# except urllib2.HTTPError, exc:
+# self.assertEqual(exc.code, 401)
+# else:
+# self.fail("urlopen() should have failed with 401")
+#
+# # success
+# auth_handler = urllib2.HTTPBasicAuthHandler()
+# auth_handler.add_password(test_realm, test_hostport,
+# test_user, test_password)
+# opener = urllib2.build_opener(auth_handler)
+# f = opener.open('http://localhost/')
+# response = urllib2.urlopen("http://www.python.org/")
+#
+# # The 'userinfo' URL component is deprecated by RFC 3986 for security
+# # reasons, let's not implement it! (it's already implemented for proxy
+# # specification strings (that is, URLs or authorities specifying a
+# # proxy), so we must keep that)
+# self.assertRaises(httplib.InvalidURL,
+# urllib2.urlopen, "http://evil:thing@example.com")
+
+
class urlopenNetworkTests(unittest.TestCase):
"""Tests urllib2.urlopen using the network.
@@ -86,7 +126,8 @@ class urlopenNetworkTests(unittest.TestCase):
def test_main():
test_support.requires("network")
- test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests)
+ test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests,
+ AuthTests)
if __name__ == "__main__":
test_main()