diff --git a/src/httpx2/httpx2/_config.py b/src/httpx2/httpx2/_config.py index 46a6e6ec..350c1588 100644 --- a/src/httpx2/httpx2/_config.py +++ b/src/httpx2/httpx2/_config.py @@ -28,7 +28,7 @@ def create_ssl_context( import ssl import warnings - import certifi + import truststore if verify is True: if trust_env and os.environ.get("SSL_CERT_FILE"): # pragma: nocover @@ -36,8 +36,8 @@ def create_ssl_context( elif trust_env and os.environ.get("SSL_CERT_DIR"): # pragma: nocover ctx = ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"]) else: - # Default case... - ctx = ssl.create_default_context(cafile=certifi.where()) + # Default case: rely on the system trust store via `truststore`. + ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) elif verify is False: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.check_hostname = False diff --git a/src/httpx2/pyproject.toml b/src/httpx2/pyproject.toml index 6cb5a96a..3aeebf57 100644 --- a/src/httpx2/pyproject.toml +++ b/src/httpx2/pyproject.toml @@ -42,7 +42,7 @@ dynamic = ["readme", "version", "dependencies"] [tool.hatch.metadata.hooks.uv-dynamic-versioning] dependencies = [ - "certifi", + "truststore>=0.10", "httpcore2=={{ version }}", "anyio", "idna", diff --git a/tests/httpx2/test_config.py b/tests/httpx2/test_config.py index ef47844e..529369b9 100644 --- a/tests/httpx2/test_config.py +++ b/tests/httpx2/test_config.py @@ -2,9 +2,7 @@ import ssl import typing -from pathlib import Path -import certifi import pytest import httpx2 @@ -25,27 +23,6 @@ def test_load_ssl_config_verify_non_existing_file() -> None: context.load_verify_locations(cafile="/path/to/nowhere") -def test_load_ssl_with_keylog(monkeypatch: typing.Any, tmp_path: Path) -> None: - keylog_file = tmp_path / "sslkeylog" - monkeypatch.setenv("SSLKEYLOGFILE", str(keylog_file)) - context = httpx2.create_ssl_context() - assert context.keylog_filename == str(keylog_file) - - -def test_load_ssl_config_verify_existing_file() -> None: - context = httpx2.create_ssl_context() - context.load_verify_locations(capath=certifi.where()) - assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED - assert context.check_hostname is True - - -def test_load_ssl_config_verify_directory() -> None: - context = httpx2.create_ssl_context() - context.load_verify_locations(capath=Path(certifi.where()).parent) - assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED - assert context.check_hostname is True - - def test_load_ssl_config_cert_and_key(cert_pem_file: str, cert_private_key_file: str) -> None: context = httpx2.create_ssl_context() context.load_cert_chain(cert_pem_file, cert_private_key_file) diff --git a/uv.lock b/uv.lock index 9d3efcac..3bbb1181 100644 --- a/uv.lock +++ b/uv.lock @@ -1351,9 +1351,9 @@ name = "httpx2" source = { editable = "src/httpx2" } dependencies = [ { name = "anyio" }, - { name = "certifi" }, { name = "httpcore2" }, { name = "idna" }, + { name = "truststore" }, ] [package.optional-dependencies] @@ -1381,7 +1381,6 @@ requires-dist = [ { name = "anyio" }, { name = "brotli", marker = "platform_python_implementation == 'CPython' and extra == 'brotli'" }, { name = "brotlicffi", marker = "platform_python_implementation != 'CPython' and extra == 'brotli'" }, - { name = "certifi" }, { name = "click", marker = "extra == 'cli'", specifier = "==8.*" }, { name = "h2", marker = "extra == 'http2'", specifier = ">=3,<5" }, { name = "httpcore2", editable = "src/httpcore2" }, @@ -1389,6 +1388,7 @@ requires-dist = [ { name = "pygments", marker = "extra == 'cli'", specifier = "==2.*" }, { name = "rich", marker = "extra == 'cli'", specifier = ">=10,<15" }, { name = "socksio", marker = "extra == 'socks'", specifier = "==1.*" }, + { name = "truststore", specifier = ">=0.10" }, { name = "zstandard", marker = "python_full_version < '3.14' and extra == 'zstd'", specifier = ">=0.18.0" }, ] provides-extras = ["brotli", "cli", "http2", "socks", "zstd"] @@ -3276,6 +3276,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/f3/c34dbabf6da5eda56fe923226769d40e11806952cd7f46655dd06e10f018/trustme-1.2.1-py3-none-any.whl", hash = "sha256:d768e5fc57c86dfc5ec9365102e9b092541cd6954b35d8c1eea01a84f35a762a", size = 16530, upload-time = "2025-01-02T01:55:30.181Z" }, ] +[[package]] +name = "truststore" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, +] + [[package]] name = "twine" version = "6.1.0"