You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
urllib2 failing with squid proxy and digest authentication #60299
an "HTTP Error 407: Proxy Authentication Required" is raised, and under the hood here's what's happening:
the request is made without authentication
the server replies it must be made with digest authentication, and gives the nonce
the error is raised.
Instead, urllib2 should now try to connect with the username and password, and do so up to 5 times (as hardcoded in urllib2.http_error_auth_reqed), and then raise a "HTTP Error 401: digest auth failed". And it's indeed what it does if you replace the line "MyHTTPPasswordMgr = urllib2.HTTPPasswordMgr" with
So the problem is in HTTPPasswordMgr, which is apparently unable to match the authentication data with the realm. Some tests¹ suggest that this can vary according to the proxy engine and to the proxy address format (works with apache, but doesn't if then you add "http://" in front of the proxy address).
This reminds a bit bug 680577, and in particular I noticed that (possibly unrelated) the behaviour reported in the following message: http://bugs.python.org/msg14444
has not changed:
In [1]: import urllib2
In [2]: urllib2.HTTPPasswordMgr().is_suburi("/foo/spam", "/foo/eggs")Out[2]: True
This affects also python 3.2, you can try the following:
Finally, notice the wrong behaviour of "is_suburi()" mentioned in http://bugs.python.org/msg14444 is still present (and I still suspect it has something to do with this).
alexwe commented on Sep 15, 2014
alexwemannequin
Mannequin
More actions
So, I analyzed the error and I think I found the problem. (urllib.request - Python 3.5.0)
It all starts with l. 1079, as the 407 error gets handled, everything ok here, in l. 1081 http_error_auth_reqed(...) gets executed.
So next, we are in l. 939, also everything correct here, retry_http_digest_auth gets executed in l. 953. (woops, "http_digest_auth"?!, nah, let's see what comes next)
So we are in l. 953 and follow the code to come to the get_authorization(...) call.
Now we are in l. 981, and in that part of the code lies the problem.
To get the username and password for the proxy, the function find_user_password(realm, req.full_url) gets executed.
An example, if my proxy has the address abc.com:8080 and my request is for xyz.com, the function tries to find a password for the xyz.com url, instead of the abc.com:8080 url. So it can't find a password and the whole auth process stops with the 407 error.
But if you just change the line, to use the host, normal http digest auth doesn't work anymore, I would suggest?
So it's also obvious why the workaround of toobaz works.
To solve the Problem, two auth handler would be needed, one for the proxy and one for normal http auth.
Two different handlers were used in the basic auth, so I think it would be an consistent solution?
There are two problems with the test case, and one bug in Python:
HTTPPasswordMgr doesn’t handle realm=None; it has to be a string. You can use HTTPPasswordMgrWithDefaultRealm though.
The password managers won’t match a proxy with a non-standard port number against a hostname without a port. So you have to include the port in the add_password() call.
AbstractDigestAuthHandler.get_authorization() is using the wrong URL, as Alexander already discovered. I made proxy-digest.patch which should fix this.
The wrong URL (final URL rather than proxy) was actually tested for in test_urllib2_localnet.ProxyAuthTests. So I fixed those tests.
I should point out my patch also adds add_password() methods for the authentication handlers to the documentation. These were only documented by example, but everyone seems to prefer using them rather than the equivalent password manager method.
Activity
toobaz commentedon Sep 30, 2012
If you run the following code:
an "HTTP Error 407: Proxy Authentication Required" is raised, and under the hood here's what's happening:
Instead, urllib2 should now try to connect with the username and password, and do so up to 5 times (as hardcoded in urllib2.http_error_auth_reqed), and then raise a "HTTP Error 401: digest auth failed". And it's indeed what it does if you replace the line "MyHTTPPasswordMgr = urllib2.HTTPPasswordMgr" with
So the problem is in HTTPPasswordMgr, which is apparently unable to match the authentication data with the realm. Some tests¹ suggest that this can vary according to the proxy engine and to the proxy address format (works with apache, but doesn't if then you add "http://" in front of the proxy address).
This reminds a bit bug 680577, and in particular I noticed that (possibly unrelated) the behaviour reported in the following message:
http://bugs.python.org/msg14444
has not changed:
In [1]: import urllib2
In [2]: urllib2.HTTPPasswordMgr().is_suburi("/foo/spam", "/foo/eggs")Out[2]: True
This affects also python 3.2, you can try the following:
¹ http://lists.python.it/pipermail/python/2012-September/013309.html (in Italian)
BreamoreBoy commentedon Jul 2, 2014
Can we have an update on this please.
toobaz commentedon Jul 2, 2014
The bug is still present in 2.7.7 and 3.4.1.
By the way, under python 3 the workaround takes the form
Finally, notice the wrong behaviour of "is_suburi()" mentioned in http://bugs.python.org/msg14444 is still present (and I still suspect it has something to do with this).
alexwe commentedon Sep 15, 2014
So, I analyzed the error and I think I found the problem. (urllib.request - Python 3.5.0)
It all starts with l. 1079, as the 407 error gets handled, everything ok here, in l. 1081 http_error_auth_reqed(...) gets executed.
So next, we are in l. 939, also everything correct here, retry_http_digest_auth gets executed in l. 953. (woops, "http_digest_auth"?!, nah, let's see what comes next)
So we are in l. 953 and follow the code to come to the get_authorization(...) call.
Now we are in l. 981, and in that part of the code lies the problem.
To get the username and password for the proxy, the function find_user_password(realm, req.full_url) gets executed.
An example, if my proxy has the address abc.com:8080 and my request is for xyz.com, the function tries to find a password for the xyz.com url, instead of the abc.com:8080 url. So it can't find a password and the whole auth process stops with the 407 error.
But if you just change the line, to use the host, normal http digest auth doesn't work anymore, I would suggest?
So it's also obvious why the workaround of toobaz works.
--------------------------------------------------------
To solve the Problem, two auth handler would be needed, one for the proxy and one for normal http auth.
Two different handlers were used in the basic auth, so I think it would be an consistent solution?
vadmium commentedon Jun 21, 2015
There are two problems with the test case, and one bug in Python:
HTTPPasswordMgr doesn’t handle realm=None; it has to be a string. You can use HTTPPasswordMgrWithDefaultRealm though.
The password managers won’t match a proxy with a non-standard port number against a hostname without a port. So you have to include the port in the add_password() call.
AbstractDigestAuthHandler.get_authorization() is using the wrong URL, as Alexander already discovered. I made proxy-digest.patch which should fix this.
The wrong URL (final URL rather than proxy) was actually tested for in test_urllib2_localnet.ProxyAuthTests. So I fixed those tests.
vadmium commentedon Jun 21, 2015
I should point out my patch also adds add_password() methods for the authentication handlers to the documentation. These were only documented by example, but everyone seems to prefer using them rather than the equivalent password manager method.
pythongh-60299: Fix ProxyDigestAuthHandler credential lookup to use p…