Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Allow providing credentials to http_proxy (#10360)
Browse files Browse the repository at this point in the history
  • Loading branch information
dklimpel authored Jul 15, 2021
1 parent 7695ca0 commit c7603af
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 14 deletions.
1 change: 1 addition & 0 deletions changelog.d/10360.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow providing credentials to `http_proxy`.
12 changes: 11 additions & 1 deletion synapse/http/proxyagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ def __init__(
https_proxy = proxies["https"].encode() if "https" in proxies else None
no_proxy = proxies["no"] if "no" in proxies else None

# Parse credentials from https proxy connection string if present
# Parse credentials from http and https proxy connection string if present
self.http_proxy_creds, http_proxy = parse_username_password(http_proxy)
self.https_proxy_creds, https_proxy = parse_username_password(https_proxy)

self.http_proxy_endpoint = _http_proxy_endpoint(
Expand Down Expand Up @@ -189,6 +190,15 @@ def request(self, method, uri, headers=None, bodyProducer=None):
and self.http_proxy_endpoint
and not should_skip_proxy
):
# Determine whether we need to set Proxy-Authorization headers
if self.http_proxy_creds:
# Set a Proxy-Authorization header
if headers is None:
headers = Headers()
headers.addRawHeader(
b"Proxy-Authorization",
self.http_proxy_creds.as_proxy_authorization_value(),
)
# Cache *all* connections under the same key, since we are only
# connecting to a single destination, the proxy:
pool_key = ("http-proxy", self.http_proxy_endpoint)
Expand Down
65 changes: 52 additions & 13 deletions tests/http/test_proxyagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,41 @@ def test_https_request_via_no_proxy_star(self):

@patch.dict(os.environ, {"http_proxy": "proxy.com:8888", "no_proxy": "unused.com"})
def test_http_request_via_proxy(self):
"""
Tests that requests can be made through a proxy.
"""
self._do_http_request_via_proxy(auth_credentials=None)

@patch.dict(
os.environ,
{"http_proxy": "bob:[email protected]:8888", "no_proxy": "unused.com"},
)
def test_http_request_via_proxy_with_auth(self):
"""
Tests that authenticated requests can be made through a proxy.
"""
self._do_http_request_via_proxy(auth_credentials="bob:pinkponies")

@patch.dict(os.environ, {"https_proxy": "proxy.com", "no_proxy": "unused.com"})
def test_https_request_via_proxy(self):
"""Tests that TLS-encrypted requests can be made through a proxy"""
self._do_https_request_via_proxy(auth_credentials=None)

@patch.dict(
os.environ,
{"https_proxy": "bob:[email protected]", "no_proxy": "unused.com"},
)
def test_https_request_via_proxy_with_auth(self):
"""Tests that authenticated, TLS-encrypted requests can be made through a proxy"""
self._do_https_request_via_proxy(auth_credentials="bob:pinkponies")

def _do_http_request_via_proxy(
self,
auth_credentials: Optional[str] = None,
):
"""
Tests that requests can be made through a proxy.
"""
agent = ProxyAgent(self.reactor, use_proxy=True)

self.reactor.lookups["proxy.com"] = "1.2.3.5"
Expand All @@ -229,6 +264,23 @@ def test_http_request_via_proxy(self):
self.assertEqual(len(http_server.requests), 1)

request = http_server.requests[0]

# Check whether auth credentials have been supplied to the proxy
proxy_auth_header_values = request.requestHeaders.getRawHeaders(
b"Proxy-Authorization"
)

if auth_credentials is not None:
# Compute the correct header value for Proxy-Authorization
encoded_credentials = base64.b64encode(b"bob:pinkponies")
expected_header_value = b"Basic " + encoded_credentials

# Validate the header's value
self.assertIn(expected_header_value, proxy_auth_header_values)
else:
# Check that the Proxy-Authorization header has not been supplied to the proxy
self.assertIsNone(proxy_auth_header_values)

self.assertEqual(request.method, b"GET")
self.assertEqual(request.path, b"http://test.com")
self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"test.com"])
Expand All @@ -241,19 +293,6 @@ def test_http_request_via_proxy(self):
body = self.successResultOf(treq.content(resp))
self.assertEqual(body, b"result")

@patch.dict(os.environ, {"https_proxy": "proxy.com", "no_proxy": "unused.com"})
def test_https_request_via_proxy(self):
"""Tests that TLS-encrypted requests can be made through a proxy"""
self._do_https_request_via_proxy(auth_credentials=None)

@patch.dict(
os.environ,
{"https_proxy": "bob:[email protected]", "no_proxy": "unused.com"},
)
def test_https_request_via_proxy_with_auth(self):
"""Tests that authenticated, TLS-encrypted requests can be made through a proxy"""
self._do_https_request_via_proxy(auth_credentials="bob:pinkponies")

def _do_https_request_via_proxy(
self,
auth_credentials: Optional[str] = None,
Expand Down

0 comments on commit c7603af

Please sign in to comment.