## HTTP Proxying
-HTTPX supports setting up proxies the same way that Requests does via the `proxies` parameter.
+HTTPX supports setting up HTTP proxies the same way that Requests does via the `proxies` parameter.
For example to forward all HTTP traffic to `http://127.0.0.1:3080` and all HTTPS traffic
to `http://127.0.0.1:3081` your `proxies` config would look like this:
... ...
```
+Credentials may be passed in as part of the URL in the standard way, i.e.
+`http://username:password@127.0.0.1:3080`.
+
Proxies can be configured for a specific scheme and host, all schemes of a host,
all hosts for a scheme, or for all requests. When determining which proxy configuration
to use for a given request this same order is used.
To use proxies you must pass the proxy information at `Client` initialization,
rather than on the `.get(...)` call or other request methods.
+SOCKS proxies are *not* supported yet.
+
## Timeout Configuration
HTTPX is careful to enforce timeouts everywhere by default.
import os
import ssl
import typing
+from base64 import b64encode
from pathlib import Path
import certifi
if mode not in ("DEFAULT", "CONNECT_ONLY", "TUNNEL_ONLY"):
raise ValueError(f"Unknown proxy mode {mode!r}")
+ if url.username or url.password:
+ headers.setdefault(
+ "Proxy-Authorization",
+ self.build_auth_header(url.username, url.password),
+ )
+ # Remove userinfo from the URL authority, e.g.:
+ # 'username:password@proxy_host:proxy_port' -> 'proxy_host:proxy_port'
+ url = url.copy_with(username=None, password=None)
+
self.url = url
self.headers = headers
self.mode = mode
+ def build_auth_header(self, username: str, password: str) -> str:
+ userpass = (username.encode("utf-8"), password.encode("utf-8"))
+ token = b64encode(b":".join(userpass)).decode().strip()
+ return f"Basic {token}"
+
def __repr__(self) -> str:
return (
f"Proxy(url={str(self.url)!r}, "
assert ssl_config.ssl_context.keylog_filename is None
-def test_proxy_from_url():
- proxy = httpx.Proxy("https://example.com")
- assert repr(proxy) == "Proxy(url='https://example.com', headers={}, mode='DEFAULT')"
+@pytest.mark.parametrize(
+ "url,expected_url,expected_headers,expected_mode",
+ [
+ ("https://example.com", "https://example.com", {}, "DEFAULT"),
+ (
+ "https://user:pass@example.com",
+ "https://example.com:443",
+ {"proxy-authorization": "Basic dXNlcjpwYXNz"},
+ "DEFAULT",
+ ),
+ ],
+)
+def test_proxy_from_url(url, expected_url, expected_headers, expected_mode):
+ proxy = httpx.Proxy(url)
+
+ assert str(proxy.url) == expected_url
+ assert dict(proxy.headers) == expected_headers
+ assert proxy.mode == expected_mode
+ assert repr(proxy) == "Proxy(url='{}', headers={}, mode='{}')".format(
+ expected_url, str(expected_headers), expected_mode
+ )
def test_invalid_proxy_scheme():