|
3 | 3 | from urllib3.connection import HTTPSConnection |
4 | 4 | from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool |
5 | 5 | from urllib3.poolmanager import ProxyManager |
| 6 | +from urllib3.util.request import make_headers |
| 7 | +from urllib3.util.url import parse_url |
6 | 8 |
|
7 | 9 | if sys.version_info < (3, 12, 0): |
8 | 10 | ##################################### |
@@ -128,6 +130,22 @@ def urlopen(self, *args, **kwargs): |
128 | 130 |
|
129 | 131 | class ProxyHeaderManager(ProxyManager): |
130 | 132 | def __init__(self, *args, **kwargs): |
| 133 | + # urllib3.ProxyManager does not add Proxy-Authorization from user:pass in the |
| 134 | + # proxy URL; requests does via HTTPAdapter.proxy_headers(). Merge URL auth here |
| 135 | + # so direct proxy_from_url() matches requests and authenticates CONNECT. |
| 136 | + proxy_url = kwargs.get("proxy_url") |
| 137 | + if proxy_url is None and args: |
| 138 | + proxy_url = args[0] |
| 139 | + proxy_headers = kwargs.get("proxy_headers") |
| 140 | + merged = dict(proxy_headers or {}) |
| 141 | + if isinstance(proxy_url, str): |
| 142 | + parsed = parse_url(proxy_url) |
| 143 | + if parsed.auth and not any( |
| 144 | + k.lower() == "proxy-authorization" for k in merged |
| 145 | + ): |
| 146 | + merged.update(make_headers(proxy_basic_auth=parsed.auth)) |
| 147 | + if merged != dict(proxy_headers or {}): |
| 148 | + kwargs["proxy_headers"] = merged |
131 | 149 | super().__init__(*args, **kwargs) |
132 | 150 | self.pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSProxyConnectionPool} |
133 | 151 |
|
|
0 commit comments