From 38cc5a7aee655d743e654df3b49b4eb7ef9111c0 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Wed, 28 Oct 2026 12:16:22 -0700 Subject: [PATCH 1/2] Proxy Auth failure to raise 407 instead of 401 https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/407 is the correct status code to raise for the Proxy Authentication. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/401 is the generic code that can be raised for unauthorized response. --- Lib/test/test_urllib2.py | 18 ++++++++++++++++++ Lib/test/test_urllib2_localnet.py | 2 ++ Lib/urllib/request.py | 6 +++++- ...26-10-28-12-00-00.gh-issue-53852.Pr0xy4.rst | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-28-12-00-00.gh-issue-53852.Pr0xy4.rst diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 5bc8de9b82defbe..9439a687c96dd15 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1871,6 +1871,24 @@ def test_unsupported_auth_digest_handler(self): opener.add_handler(http_handler) self.assertRaises(ValueError, opener.open, "http://www.example.com") + def test_digest_auth_failure_code(self): + # gh-53852: a failed proxy digest auth must report 407, not 401. + headers = http.client.HTTPMessage() + for handler_class, auth_header, code in [ + (urllib.request.HTTPDigestAuthHandler, + 'www-authenticate', 401), + (urllib.request.ProxyDigestAuthHandler, + 'proxy-authenticate', 407)]: + with self.subTest(handler=handler_class.__name__): + handler = handler_class() + handler.retried = 6 + req = Request("http://www.example.com/") + with self.assertRaises(urllib.error.HTTPError) as cm: + handler.http_error_auth_reqed(auth_header, + "www.example.com", + req, headers) + self.assertEqual(cm.exception.code, code) + def test_unsupported_auth_basic_handler(self): # While using BasicAuthHandler opener = OpenerDirector() diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index c2646b9448f297f..8f93ee74252f5c6 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -368,12 +368,14 @@ def test_proxy_with_bad_password_raises_httperror(self): self.digest_auth_handler.set_qop("auth") with self.assertRaises(urllib.error.HTTPError) as cm: self.opener.open(self.URL) + self.assertEqual(cm.exception.code, 407) cm.exception.close() def test_proxy_with_no_password_raises_httperror(self): self.digest_auth_handler.set_qop("auth") with self.assertRaises(urllib.error.HTTPError) as cm: self.opener.open(self.URL) + self.assertEqual(cm.exception.code, 407) cm.exception.close() def test_proxy_qop_auth_works(self): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index c33e0aaac15de21..96ea88e39b9996f 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1093,7 +1093,11 @@ def http_error_auth_reqed(self, auth_header, host, req, headers): # prompting for the information. Crap. This isn't great # but it's better than the current 'repeat until recursion # depth exceeded' approach - raise HTTPError(req.full_url, 401, "digest auth failed", + if auth_header.lower() == 'proxy-authenticate': + code = 407 + else: + code = 401 + raise HTTPError(req.full_url, code, "digest auth failed", headers, None) else: self.retried += 1 diff --git a/Misc/NEWS.d/next/Library/2026-10-28-12-00-00.gh-issue-53852.Pr0xy4.rst b/Misc/NEWS.d/next/Library/2026-10-28-12-00-00.gh-issue-53852.Pr0xy4.rst new file mode 100644 index 000000000000000..6b6c7fa1e175135 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-28-12-00-00.gh-issue-53852.Pr0xy4.rst @@ -0,0 +1,3 @@ +:class:`urllib.request.ProxyDigestAuthHandler` now raises +:exc:`~urllib.error.HTTPError` with status code 407 (Proxy Authentication +Required) instead of 401 when proxy authentication fails. From d9d7d6b08ef982c6948b592ebef29ffdbe028346 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Thu, 29 Oct 2026 11:59:50 -0700 Subject: [PATCH 2/2] Close the context-manager. Address the env changed warning in the CI. --- Lib/test/test_urllib2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 9439a687c96dd15..8c402a2ef0eeedb 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1888,6 +1888,7 @@ def test_digest_auth_failure_code(self): "www.example.com", req, headers) self.assertEqual(cm.exception.code, code) + cm.exception.close() def test_unsupported_auth_basic_handler(self): # While using BasicAuthHandler