changeset: 93477:fb3061ba6fd2 user: Nick Coghlan date: Wed Nov 12 23:33:50 2014 +1000 files: Doc/library/urllib.request.rst Doc/whatsnew/3.5.rst Lib/test/test_urllib2.py Lib/urllib/request.py Misc/NEWS description: Close #19494: add urrlib.request.HTTPBasicPriorAuthHandler This auth handler adds the Authorization header to the first HTTP request rather than waiting for a HTTP 401 Unauthorized response from the server as the default HTTPBasicAuthHandler does. This allows working with websites like https://api.github.com which do not follow the strict interpretation of RFC, but more the dicta in the end of section 2 of RFC 2617: > A client MAY preemptively send the corresponding Authorization > header with requests for resources in that space without receipt > of another challenge from the server. Similarly, when a client > sends a request to a proxy, it may reuse a userid and password in > the Proxy-Authorization header field without receiving another > challenge from the proxy server. See section 4 for security > considerations associated with Basic authentication. Patch by Matej Cepl. diff -r 30a6c74ad87f -r fb3061ba6fd2 Doc/library/urllib.request.rst --- a/Doc/library/urllib.request.rst Tue Nov 11 21:13:28 2014 +0200 +++ b/Doc/library/urllib.request.rst Wed Nov 12 23:33:50 2014 +1000 @@ -304,6 +304,17 @@ presented with a wrong Authentication scheme. +.. class:: HTTPBasicPriorAuthHandler(password_mgr=None) + + A variant of :class:`HTTPBasicAuthHandler` which automatically sends + authorization credentials with the first request, rather than waiting to + first receive a HTTP 401 "Unauthorised" error response. This allows + authentication to sites that don't provide a 401 response when receiving + a request without an Authorization header. Aside from this difference, + this behaves exactly as :class:`HTTPBasicAuthHandler`. + + .. versionadded:: 3.5 + .. class:: ProxyBasicAuthHandler(password_mgr=None) Handle authentication with the proxy. *password_mgr*, if given, should be diff -r 30a6c74ad87f -r fb3061ba6fd2 Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst Tue Nov 11 21:13:28 2014 +0200 +++ b/Doc/whatsnew/3.5.rst Wed Nov 12 23:33:50 2014 +1000 @@ -297,6 +297,15 @@ * The :func:`time.monotonic` function is now always available. (Contributed by Victor Stinner in :issue:`22043`.) +time +---- + +* A new :class:`urllib.request.HTTPBasicPriorAuthHandler` allows HTTP Basic + Authentication credentials to be sent unconditionally with the first HTTP + request, rather than waiting for a HTTP 401 Unauthorized response from the + server. + (Contributed by Matej Cepl in :issue:`19494`.) + wsgiref ------- diff -r 30a6c74ad87f -r fb3061ba6fd2 Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py Tue Nov 11 21:13:28 2014 +0200 +++ b/Lib/test/test_urllib2.py Wed Nov 12 23:33:50 2014 +1000 @@ -1422,6 +1422,21 @@ handler.do_open(conn, req) self.assertTrue(conn.fakesock.closed, "Connection not closed") + def test_auth_prior_handler(self): + pwd_manager = MockPasswordManager() + pwd_manager.add_password(None, 'https://example.com', + 'somebody', 'verysecret') + auth_prior_handler = urllib.request.HTTPBasicPriorAuthHandler( + pwd_manager) + http_hand = MockHTTPSHandler() + + opener = OpenerDirector() + opener.add_handler(http_hand) + opener.add_handler(auth_prior_handler) + + req = Request("https://example.com") + opener.open(req) + self.assertNotIn('Authorization', http_hand.httpconn.req_headers) class MiscTests(unittest.TestCase): diff -r 30a6c74ad87f -r fb3061ba6fd2 Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Nov 11 21:13:28 2014 +0200 +++ b/Lib/urllib/request.py Wed Nov 12 23:33:50 2014 +1000 @@ -916,6 +916,21 @@ return response +class HTTPBasicPriorAuthHandler(HTTPBasicAuthHandler): + handler_order = 400 + + def http_request(self, req): + if not req.has_header('Authorization'): + user, passwd = self.passwd.find_user_password(None, req.host) + credentials = '{0}:{1}'.format(user, passwd).encode() + auth_str = base64.standard_b64encode(credentials).decode() + req.add_unredirected_header('Authorization', + 'Basic {}'.format(auth_str.strip())) + return req + + https_request = http_request + + # Return n random bytes. _randombytes = os.urandom diff -r 30a6c74ad87f -r fb3061ba6fd2 Misc/NEWS --- a/Misc/NEWS Tue Nov 11 21:13:28 2014 +0200 +++ b/Misc/NEWS Wed Nov 12 23:33:50 2014 +1000 @@ -183,6 +183,9 @@ Library ------- +- Issue #19494: Added urllib.request.HTTPBasicPriorAuthHandler. Patch by + Matej Cepl. + - Issue #22578: Added attributes to the re.error class. - Issue #12728: Different Unicode characters having the same uppercase but