Skip to content

Commit

Permalink
construct proxy-authorization from url
Browse files Browse the repository at this point in the history
  • Loading branch information
CCLDArjun committed Jan 15, 2024
1 parent 745b002 commit ecf64ef
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog/1999.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ability to specify ``Proxy-Authorization`` headers from the url
1 change: 1 addition & 0 deletions dummyserver/testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class HypercornDummyProxyTestCase:
proxy_url: typing.ClassVar[str]
https_proxy_port: typing.ClassVar[int]
https_proxy_url: typing.ClassVar[str]
proxy_url_with_auth: typing.ClassVar[str]

certs_dir: typing.ClassVar[str] = ""
bad_ca_path: typing.ClassVar[str] = ""
Expand Down
22 changes: 21 additions & 1 deletion src/urllib3/poolmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
from types import TracebackType
from urllib.parse import urljoin

from urllib3.util.request import make_headers

from ._collections import HTTPHeaderDict, RecentlyUsedContainer
from ._request_methods import RequestMethods
from .connection import ProxyConfig
from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme
from .exceptions import (
InvalidHeader,
LocationValueError,
MaxRetryError,
ProxySchemeUnknown,
Expand Down Expand Up @@ -550,7 +553,7 @@ def __init__(
proxy_url: str,
num_pools: int = 10,
headers: typing.Mapping[str, str] | None = None,
proxy_headers: typing.Mapping[str, str] | None = None,
proxy_headers: typing.MutableMapping[str, str] | None = None,
proxy_ssl_context: ssl.SSLContext | None = None,
use_forwarding_for_https: bool = False,
proxy_assert_hostname: None | str | Literal[False] = None,
Expand Down Expand Up @@ -580,6 +583,23 @@ def __init__(
proxy_assert_fingerprint,
)

if proxy.auth is not None:
split = proxy.auth.split(":")
if len(split) == 2:
auth = make_headers(proxy_basic_auth=proxy.auth)["proxy-authorization"]
if (
"proxy-authorization" in self.proxy_headers
and self.proxy_headers["proxy-authorization"] != auth
) or (
headers
and "proxy-authorization" in headers
and headers["proxy-authorization"] != auth
):
raise InvalidHeader(
"Proxy-Authorization given in headers or proxy_headers and URL don't match"
)
self.proxy_headers["proxy-authorization"] = auth

connection_pool_kw["_proxy"] = self.proxy
connection_pool_kw["_proxy_headers"] = self.proxy_headers
connection_pool_kw["_proxy_config"] = self.proxy_config
Expand Down
28 changes: 28 additions & 0 deletions test/with_dummyserver/test_proxy_poolmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from urllib3.exceptions import (
ConnectTimeoutError,
InsecureRequestWarning,
InvalidHeader,
MaxRetryError,
ProxyError,
ProxySchemeUnknown,
Expand All @@ -37,6 +38,7 @@
SSLError,
)
from urllib3.poolmanager import ProxyManager, proxy_from_url
from urllib3.util.request import make_headers
from urllib3.util.ssl_ import create_urllib3_context
from urllib3.util.timeout import Timeout

Expand Down Expand Up @@ -64,6 +66,9 @@ def setup_class(cls) -> None:
cls.https_url_fqdn = f"https://{cls.https_host}.:{int(cls.https_port)}"
cls.proxy_url = f"http://{cls.proxy_host}:{int(cls.proxy_port)}"
cls.https_proxy_url = f"https://{cls.proxy_host}:{int(cls.https_proxy_port)}"
cls.proxy_url_with_auth = (
f"http://user:password@{cls.proxy_host}:{int(cls.proxy_port)}"
)

# Generate another CA to test verification failure
cls.certs_dir = tempfile.mkdtemp()
Expand Down Expand Up @@ -321,6 +326,29 @@ def test_cross_protocol_redirect(self) -> None:
assert r._pool is not None
assert r._pool.host == self.https_host

def test_proxy_url_auth(self) -> None:
with proxy_from_url(self.proxy_url_with_auth) as http:
r = http.request_encode_url("GET", f"{self.http_url}/headers")
returned_headers = r.json()
assert (
returned_headers.get("Proxy-Authorization")
== "Basic dXNlcjpwYXNzd29yZA==" # base64 encoded "user:password"
)
with pytest.raises(InvalidHeader):
proxy_from_url(
self.proxy_url_with_auth,
proxy_headers=make_headers(
proxy_basic_auth="differentuser:differentpassword"
),
)
with pytest.raises(InvalidHeader):
proxy_from_url(
self.proxy_url_with_auth,
headers=make_headers(
proxy_basic_auth="differentuser:differentpassword"
),
)

def test_headers(self) -> None:
with proxy_from_url(
self.proxy_url,
Expand Down

0 comments on commit ecf64ef

Please sign in to comment.