@@ -82,6 +82,27 @@ def test_parse_http_list(self):
8282 def test_URLError_reasonstr (self ):
8383 err = urllib .error .URLError ('reason' )
8484 self .assertIn (err .reason , str (err ))
85+ self .assertIsNone (err .errno )
86+ self .assertIsNone (err .strerror )
87+
88+ def test_URLError_OSError_attributes (self ):
89+ # gh-50720: errno and strerror are copied from a wrapped OSError.
90+ socket_error = socket .gaierror (- 2 , 'Name or service not known' )
91+
92+ class FailingHTTPConnection (MockHTTPClass ):
93+ def request (self , * args , ** kwargs ):
94+ raise socket_error
95+
96+ h = urllib .request .AbstractHTTPHandler ()
97+ req = Request ("http://example.com/" )
98+ req .timeout = None
99+
100+ with self .assertRaises (urllib .error .URLError ) as cm :
101+ h .do_open (FailingHTTPConnection (), req )
102+ err = cm .exception
103+ self .assertIs (err .reason , socket_error )
104+ self .assertEqual (err .errno , - 2 )
105+ self .assertEqual (err .strerror , 'Name or service not known' )
85106
86107
87108class RequestHdrsTests (unittest .TestCase ):
@@ -1789,6 +1810,31 @@ def test_proxy_digest_auth(self):
17891810 http_handler .requests [1 ]
17901811 .get_header ("Proxy-authorization" ).startswith ("Digest " ))
17911812
1813+ def test_proxy_digest_auth_uri (self ):
1814+ # The digest-uri of a request sent through an HTTP proxy is the
1815+ # absolute URL, matching the absolute-form request line
1816+ opener = OpenerDirector ()
1817+ ph = urllib .request .ProxyHandler (dict (http = "proxy.example.com:3128" ))
1818+ opener .add_handler (ph )
1819+ password_manager = urllib .request .HTTPPasswordMgr ()
1820+ digest_handler = urllib .request .ProxyDigestAuthHandler (password_manager )
1821+ realm = "ACME Networks"
1822+ http_handler = MockHTTPHandlerRedirect (
1823+ 407 ,
1824+ 'Proxy-Authenticate: Digest realm="%s", nonce="abcdef0123456789", '
1825+ 'algorithm="SHA-256"\r \n \r \n ' % realm )
1826+ opener .add_handler (digest_handler )
1827+ opener .add_handler (http_handler )
1828+
1829+ request_url = "http://acme.example.com/protected"
1830+ digest_handler .add_password (realm , "proxy.example.com:3128" ,
1831+ "wile" , "coyote" )
1832+ opener .open (request_url )
1833+
1834+ self .assertEqual (len (http_handler .requests ), 2 )
1835+ auth = http_handler .requests [1 ].get_header ("Proxy-authorization" )
1836+ self .assertIn ('uri="%s"' % request_url , auth )
1837+
17921838 def test_digest_auth_uses_full_url (self ):
17931839 # The non-proxy case must keep using the full request URL (not
17941840 # just the host) for the password lookup, so that sub-URI realm
@@ -1811,6 +1857,26 @@ def test_digest_auth_uses_full_url(self):
18111857 self .assertEqual (password_manager .target_realm , realm )
18121858 self .assertEqual (password_manager .target_url , request_url )
18131859
1860+ def test_digest_auth_empty_path (self ):
1861+ # gh-53089: the digest-uri must match the Request-URI, which is
1862+ # '/' for a URL with an empty path
1863+ password_manager = urllib .request .HTTPPasswordMgr ()
1864+ digest_handler = urllib .request .HTTPDigestAuthHandler (password_manager )
1865+ realm = "ACME Networks"
1866+ digest_handler .add_password (realm , "http://acme.example.com" ,
1867+ "wile" , "coyote" )
1868+ req = Request ("http://acme.example.com" )
1869+ chal = {"realm" : realm , "nonce" : "abcdef0123456789" ,
1870+ "algorithm" : "SHA-256" }
1871+
1872+ auth = digest_handler .get_authorization (req , chal )
1873+
1874+ H , KD = digest_handler .get_algorithm_impls ("SHA-256" )
1875+ respdig = KD (H ("wile:%s:coyote" % realm ),
1876+ "abcdef0123456789:%s" % H ("GET:/" ))
1877+ self .assertIn ('uri="/"' , auth )
1878+ self .assertIn ('response="%s"' % respdig , auth )
1879+
18141880 def test_basic_and_digest_auth_handlers (self ):
18151881 # HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
18161882 # response (https://bugs.python.org/issue1479302), where it should instead
0 commit comments