diff --git a/CHANGES/4648.bugfix b/CHANGES/4648.bugfix new file mode 100644 index 0000000000..094eb9d492 --- /dev/null +++ b/CHANGES/4648.bugfix @@ -0,0 +1 @@ +Fix supporting WebSockets proxies configured via environment variables. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 9ea3d7acbd..dd73eb8da3 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -240,14 +240,20 @@ class ProxyInfo: def proxies_from_env() -> Dict[str, ProxyInfo]: - proxy_urls = {k: URL(v) for k, v in getproxies().items() if k in ("http", "https")} + proxy_urls = { + k: URL(v) + for k, v in getproxies().items() + if k in ("http", "https", "ws", "wss") + } netrc_obj = netrc_from_env() stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()} ret = {} for proto, val in stripped.items(): proxy, auth = val - if proxy.scheme == "https": - client_logger.warning("HTTPS proxies %s are not supported, ignoring", proxy) + if proxy.scheme in ("https", "wss"): + client_logger.warning( + "%s proxies %s are not supported, ignoring", proxy.scheme.upper(), proxy + ) continue if netrc_obj and auth is None: auth_from_netrc = None diff --git a/docs/client_advanced.rst b/docs/client_advanced.rst index 55a014d9d3..43d7dd251e 100644 --- a/docs/client_advanced.rst +++ b/docs/client_advanced.rst @@ -555,13 +555,17 @@ Contrary to the ``requests`` library, it won't read environment variables by default. But you can do so by passing ``trust_env=True`` into :class:`aiohttp.ClientSession` constructor for extracting proxy configuration from -*HTTP_PROXY* or *HTTPS_PROXY* *environment variables* (both are case -insensitive):: +*HTTP_PROXY*, *HTTPS_PROXY*, *WS_PROXY* or *WSS_PROXY* *environment +variables* (all are case insensitive):: async with aiohttp.ClientSession(trust_env=True) as session: async with session.get("http://python.org") as resp: print(resp.status) +.. versionadded:: 3.8 + + *WS_PROXY* and *WSS_PROXY* are supported since aiohttp v3.8. + Proxy credentials are given from ``~/.netrc`` file if present (see :class:`aiohttp.ClientSession` for more details). diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 7038fe9be6..d43e963789 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -462,32 +462,26 @@ def test_set_content_disposition_bad_param() -> None: # --------------------- proxies_from_env ------------------------------ -def test_proxies_from_env_http(mocker) -> None: +@pytest.mark.parametrize("protocol", ["http", "https", "ws", "wss"]) +def test_proxies_from_env(monkeypatch, protocol) -> None: url = URL("http://aiohttp.io/path") - mocker.patch.dict(os.environ, {"http_proxy": str(url)}) - ret = helpers.proxies_from_env() - assert ret.keys() == {"http"} - assert ret["http"].proxy == url - assert ret["http"].proxy_auth is None - - -def test_proxies_from_env_http_proxy_for_https_proto(mocker) -> None: - url = URL("http://aiohttp.io/path") - mocker.patch.dict(os.environ, {"https_proxy": str(url)}) + monkeypatch.setenv(protocol + "_proxy", str(url)) ret = helpers.proxies_from_env() - assert ret.keys() == {"https"} - assert ret["https"].proxy == url - assert ret["https"].proxy_auth is None + assert ret.keys() == {protocol} + assert ret[protocol].proxy == url + assert ret[protocol].proxy_auth is None -def test_proxies_from_env_https_proxy_skipped(mocker) -> None: - url = URL("https://aiohttp.io/path") - mocker.patch.dict(os.environ, {"https_proxy": str(url)}) - log = mocker.patch("aiohttp.log.client_logger.warning") +@pytest.mark.parametrize("protocol", ["https", "wss"]) +def test_proxies_from_env_skipped(monkeypatch, caplog, protocol) -> None: + url = URL(protocol + "://aiohttp.io/path") + monkeypatch.setenv(protocol + "_proxy", str(url)) assert helpers.proxies_from_env() == {} - log.assert_called_with( - "HTTPS proxies %s are not supported, ignoring", URL("https://aiohttp.io/path") + assert len(caplog.records) == 1 + log_message = "{proto!s} proxies {url!s} are not supported, ignoring".format( + proto=protocol.upper(), url=url ) + assert caplog.record_tuples == [("aiohttp.client", 30, log_message)] def test_proxies_from_env_http_with_auth(mocker) -> None: