<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>