<div dir="ltr">I added some extra coverage for basic auth in the tests and I notice that in buildbots, some of them are throwing "<span style="color:red;font-family:'Courier New',courier,monotype;font-size:12px">error: [Errno 32] Broken pipe" </span>error.<div>
<br></div><div>I am looking into this and will fix this.</div><div><br></div><div>Thanks,</div><div>Senthil</div><div><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Sat, Aug 16, 2014 at 2:19 PM, senthil.kumaran <span dir="ltr"><<a href="mailto:python-checkins@python.org" target="_blank">python-checkins@python.org</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><a href="http://hg.python.org/cpython/rev/e0510a3bdf8f" target="_blank">http://hg.python.org/cpython/rev/e0510a3bdf8f</a><br>

changeset:   92111:e0510a3bdf8f<br>
branch:      2.7<br>
parent:      92097:6d41f139709b<br>
user:        Senthil Kumaran <<a href="mailto:senthil@uthcode.com">senthil@uthcode.com</a>><br>
date:        Sat Aug 16 14:16:14 2014 +0530<br>
summary:<br>
  Fix Issue #8797: Raise HTTPError on failed Basic Authentication immediately. Initial patch by Sam Bull.<br>
<br>
files:<br>
  Lib/test/test_urllib2_localnet.py |  86 ++++++++++++++++++-<br>
  Lib/urllib2.py                    |  19 +---<br>
  Misc/NEWS                         |   3 +<br>
  3 files changed, 90 insertions(+), 18 deletions(-)<br>
<br>
<br>
diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py<br>
--- a/Lib/test/test_urllib2_localnet.py<br>
+++ b/Lib/test/test_urllib2_localnet.py<br>
@@ -1,6 +1,8 @@<br>
+import base64<br>
 import urlparse<br>
 import urllib2<br>
 import BaseHTTPServer<br>
+import SimpleHTTPServer<br>
 import unittest<br>
 import hashlib<br>
<br>
@@ -66,6 +68,48 @@<br>
<br>
 # Authentication infrastructure<br>
<br>
+<br>
+class BasicAuthHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):<br>
+    """Handler for performing Basic Authentication."""<br>
+    # Server side values<br>
+    USER = "testUser"<br>
+    PASSWD = "testPass"<br>
+    REALM = "Test"<br>
+    USER_PASSWD = "%s:%s" % (USER, PASSWD)<br>
+    ENCODED_AUTH = base64.b64encode(USER_PASSWD)<br>
+<br>
+    def __init__(self, *args, **kwargs):<br>
+        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args,<br>
+                                                           **kwargs)<br>
+<br>
+    def log_message(self, format, *args):<br>
+        # Supress the HTTP Console log output<br>
+        pass<br>
+<br>
+    def do_HEAD(self):<br>
+        self.send_response(200)<br>
+        self.send_header("Content-type", "text/html")<br>
+        self.end_headers()<br>
+<br>
+    def do_AUTHHEAD(self):<br>
+        self.send_response(401)<br>
+        self.send_header("WWW-Authenticate", "Basic realm=\"%s\"" % self.REALM)<br>
+        self.send_header("Content-type", "text/html")<br>
+        self.end_headers()<br>
+<br>
+    def do_GET(self):<br>
+        if self.headers.getheader("Authorization") == None:<br>
+            self.do_AUTHHEAD()<br>
+            self.wfile.write("No Auth Header Received")<br>
+        elif self.headers.getheader(<br>
+                "Authorization") == "Basic " + self.ENCODED_AUTH:<br>
+            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)<br>
+        else:<br>
+            self.do_AUTHHEAD()<br>
+            self.wfile.write(self.headers.getheader("Authorization"))<br>
+            self.wfile.write("Not Authenticated")<br>
+<br>
+<br>
 class DigestAuthHandler:<br>
     """Handler for performing digest authentication."""<br>
<br>
@@ -228,6 +272,45 @@<br>
         test_support.threading_cleanup(*self._threads)<br>
<br>
<br>
+class BasicAuthTests(BaseTestCase):<br>
+    USER = "testUser"<br>
+    PASSWD = "testPass"<br>
+    INCORRECT_PASSWD = "Incorrect"<br>
+    REALM = "Test"<br>
+<br>
+    def setUp(self):<br>
+        super(BasicAuthTests, self).setUp()<br>
+        # With Basic Authentication<br>
+        def http_server_with_basic_auth_handler(*args, **kwargs):<br>
+            return BasicAuthHandler(*args, **kwargs)<br>
+        self.server = LoopbackHttpServerThread(http_server_with_basic_auth_handler)<br>
+        self.server_url = 'http://127.0.0.1:%s' % self.server.port<br>
+        self.server.start()<br>
+        self.server.ready.wait()<br>
+<br>
+    def tearDown(self):<br>
+        self.server.stop()<br>
+        super(BasicAuthTests, self).tearDown()<br>
+<br>
+    def test_basic_auth_success(self):<br>
+        ah = urllib2.HTTPBasicAuthHandler()<br>
+        ah.add_password(self.REALM, self.server_url, self.USER, self.PASSWD)<br>
+        urllib2.install_opener(urllib2.build_opener(ah))<br>
+        try:<br>
+            self.assertTrue(urllib2.urlopen(self.server_url))<br>
+        except urllib2.HTTPError:<br>
+            self.fail("Basic Auth Failed for url: %s" % self.server_url)<br>
+        except Exception as e:<br>
+            raise e<br>
+<br>
+    def test_basic_auth_httperror(self):<br>
+        ah = urllib2.HTTPBasicAuthHandler()<br>
+        ah.add_password(self.REALM, self.server_url, self.USER,<br>
+                        self.INCORRECT_PASSWD)<br>
+        urllib2.install_opener(urllib2.build_opener(ah))<br>
+        self.assertRaises(urllib2.HTTPError, urllib2.urlopen, self.server_url)<br>
+<br>
+<br>
 class ProxyAuthTests(BaseTestCase):<br>
     URL = "<a href="http://localhost" target="_blank">http://localhost</a>"<br>
<br>
@@ -240,6 +323,7 @@<br>
         self.digest_auth_handler = DigestAuthHandler()<br>
         self.digest_auth_handler.set_users({self.USER: self.PASSWD})<br>
         self.digest_auth_handler.set_realm(self.REALM)<br>
+        # With Digest Authentication<br>
         def create_fake_proxy_handler(*args, **kwargs):<br>
             return FakeProxyHandler(self.digest_auth_handler, *args, **kwargs)<br>
<br>
@@ -544,7 +628,7 @@<br>
     # the next line.<br>
     #test_support.requires("network")<br>
<br>
-    test_support.run_unittest(ProxyAuthTests, TestUrlopen)<br>
+    test_support.run_unittest(BasicAuthTests, ProxyAuthTests, TestUrlopen)<br>
<br>
 if __name__ == "__main__":<br>
     test_main()<br>
diff --git a/Lib/urllib2.py b/Lib/urllib2.py<br>
--- a/Lib/urllib2.py<br>
+++ b/Lib/urllib2.py<br>
@@ -843,10 +843,7 @@<br>
             password_mgr = HTTPPasswordMgr()<br>
         self.passwd = password_mgr<br>
         self.add_password = self.passwd.add_password<br>
-        self.retried = 0<br>
<br>
-    def reset_retry_count(self):<br>
-        self.retried = 0<br>
<br>
     def http_error_auth_reqed(self, authreq, host, req, headers):<br>
         # host may be an authority (without userinfo) or a URL with an<br>
@@ -854,13 +851,6 @@<br>
         # XXX could be multiple headers<br>
         authreq = headers.get(authreq, None)<br>
<br>
-        if self.retried > 5:<br>
-            # retry sending the username:password 5 times before failing.<br>
-            raise HTTPError(req.get_full_url(), 401, "basic auth failed",<br>
-                            headers, None)<br>
-        else:<br>
-            self.retried += 1<br>
-<br>
         if authreq:<br>
             mo = AbstractBasicAuthHandler.rx.search(authreq)<br>
             if mo:<br>
@@ -869,17 +859,14 @@<br>
                     warnings.warn("Basic Auth Realm was unquoted",<br>
                                   UserWarning, 2)<br>
                 if scheme.lower() == 'basic':<br>
-                    response = self.retry_http_basic_auth(host, req, realm)<br>
-                    if response and response.code != 401:<br>
-                        self.retried = 0<br>
-                    return response<br>
+                    return self.retry_http_basic_auth(host, req, realm)<br>
<br>
     def retry_http_basic_auth(self, host, req, realm):<br>
         user, pw = self.passwd.find_user_password(realm, host)<br>
         if pw is not None:<br>
             raw = "%s:%s" % (user, pw)<br>
             auth = 'Basic %s' % base64.b64encode(raw).strip()<br>
-            if req.headers.get(self.auth_header, None) == auth:<br>
+            if req.get_header(self.auth_header, None) == auth:<br>
                 return None<br>
             req.add_unredirected_header(self.auth_header, auth)<br>
             return self.parent.open(req, timeout=req.timeout)<br>
@@ -895,7 +882,6 @@<br>
         url = req.get_full_url()<br>
         response = self.http_error_auth_reqed('www-authenticate',<br>
                                               url, req, headers)<br>
-        self.reset_retry_count()<br>
         return response<br>
<br>
<br>
@@ -911,7 +897,6 @@<br>
         authority = req.get_host()<br>
         response = self.http_error_auth_reqed('proxy-authenticate',<br>
                                           authority, req, headers)<br>
-        self.reset_retry_count()<br>
         return response<br>
<br>
<br>
diff --git a/Misc/NEWS b/Misc/NEWS<br>
--- a/Misc/NEWS<br>
+++ b/Misc/NEWS<br>
@@ -19,6 +19,9 @@<br>
 Library<br>
 -------<br>
<br>
+- Issue #8797: Raise HTTPError on failed Basic Authentication immediately.<br>
+  Initial patch by Sam Bull.<br>
+<br>
 - Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when<br>
   parsing long line.  Original patch by Raymond Hettinger.<br>
<span class=""><font color="#888888"><br>
<br>
--<br>
Repository URL: <a href="http://hg.python.org/cpython" target="_blank">http://hg.python.org/cpython</a><br>
</font></span><br>_______________________________________________<br>
Python-checkins mailing list<br>
<a href="mailto:Python-checkins@python.org">Python-checkins@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-checkins" target="_blank">https://mail.python.org/mailman/listinfo/python-checkins</a><br>
<br></blockquote></div><br></div></div></div>