From: Can Sarıgöl Date: Wed, 16 Oct 2019 13:31:47 +0000 (+0300) Subject: Cache netrc authentication per-client (#400) X-Git-Tag: 0.7.6~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84731c8be5ba682b04f9456a177430b8315f3188;p=thirdparty%2Fhttpx.git Cache netrc authentication per-client (#400) --- diff --git a/docs/advanced.md b/docs/advanced.md index 88195e67..368682fe 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -91,6 +91,11 @@ h2_response = h2_client.get("https://myserver.com") HTTPX supports .netrc file. In `trust_env=True` cases, if auth parameter is not defined, HTTPX tries to add auth into request's header from .netrc file. +!!! note + The NETRC file is cached across requests made by a client. + If you need to refresh the cache (e.g. because the NETRC file has changed), + you should create a new client or restart the interpreter. + As default `trust_env` is true. To set false: ```python >>> httpx.get('https://example.org/', trust_env=False) diff --git a/httpx/client.py b/httpx/client.py index bfee2de5..bdf00d4a 100644 --- a/httpx/client.py +++ b/httpx/client.py @@ -1,5 +1,6 @@ import functools import inspect +import netrc import typing from types import TracebackType @@ -48,7 +49,7 @@ from .models import ( ResponseContent, URLTypes, ) -from .utils import ElapsedTimer, get_environment_proxies, get_netrc_login +from .utils import ElapsedTimer, get_environment_proxies, get_netrc class BaseClient: @@ -277,13 +278,20 @@ class BaseClient: ) if trust_env: - netrc_login = get_netrc_login(request.url.authority) - if netrc_login: - username, _, password = netrc_login - return BasicAuthMiddleware(username=username, password=password) + netrc_info = self._get_netrc() + if netrc_info: + netrc_login = netrc_info.authenticators(request.url.authority) + if netrc_login: + username, _, password = netrc_login + assert password is not None + return BasicAuthMiddleware(username=username, password=password) return None + @functools.lru_cache(1) + def _get_netrc(self) -> typing.Optional[netrc.netrc]: + return get_netrc() + def _dispatcher_for_request( self, request: AsyncRequest, proxies: typing.Dict[str, AsyncDispatcher] ) -> AsyncDispatcher: diff --git a/httpx/utils.py b/httpx/utils.py index c16341c5..a3aff358 100644 --- a/httpx/utils.py +++ b/httpx/utils.py @@ -98,7 +98,7 @@ def guess_json_utf(data: bytes) -> typing.Optional[str]: NETRC_STATIC_FILES = (Path("~/.netrc"), Path("~/_netrc")) -def get_netrc_login(host: str) -> typing.Optional[typing.Tuple[str, str, str]]: +def get_netrc() -> typing.Optional[netrc.netrc]: NETRC_FILES = (Path(os.getenv("NETRC", "")),) + NETRC_STATIC_FILES netrc_path = None @@ -110,9 +110,7 @@ def get_netrc_login(host: str) -> typing.Optional[typing.Tuple[str, str, str]]: if netrc_path is None: return None - - netrc_info = netrc.netrc(str(netrc_path)) - return netrc_info.authenticators(host) # type: ignore + return netrc.netrc(str(netrc_path)) def get_ca_bundle_from_env() -> typing.Optional[str]: diff --git a/tests/test_utils.py b/tests/test_utils.py index 800928c9..f8295b29 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -10,7 +10,7 @@ from httpx.utils import ( ElapsedTimer, get_ca_bundle_from_env, get_environment_proxies, - get_netrc_login, + get_netrc, guess_json_utf, obfuscate_sensitive_headers, parse_header_links, @@ -54,24 +54,24 @@ def test_guess_by_bom(encoding, expected): def test_bad_get_netrc_login(): - assert get_netrc_login("url") is None - os.environ["NETRC"] = "tests/.netrc" - assert get_netrc_login("url") is None - - os.environ["NETRC"] = "wrongpath" - assert get_netrc_login("url") is None + assert str(get_netrc()) is not None from httpx import utils utils.NETRC_STATIC_FILES = () + + os.environ["NETRC"] = "wrongpath" + assert utils.get_netrc() is None + os.environ["NETRC"] = "" - assert utils.get_netrc_login("url") is None + assert utils.get_netrc() is None def test_get_netrc_login(): os.environ["NETRC"] = "tests/.netrc" - assert get_netrc_login("netrcexample.org") == ( + netrc = get_netrc() + assert netrc.authenticators("netrcexample.org") == ( "example-username", None, "example-password",