From 4a161170b8c351d86a89c3b85a03b9ea9736472c Mon Sep 17 00:00:00 2001 From: Vinay Kumar Date: Tue, 11 Aug 2026 21:48:14 +0530 Subject: [PATCH] Fixes #1632: fall back to Requests' CA bundle when OpenSSL default certs are empty `ensure_default_certs_loaded()` only called `SSLContext.load_default_certs()`, which relies on OpenSSL's default verify paths. Those are empty on some platforms (notably macOS python.org/pyenv builds), and because HTTPie always passes its own SSLContext, Requests skips its certifi-backed context and `load_verify_locations()` entirely -- leaving no trust store at all, so every HTTPS request failed with CERTIFICATE_VERIFY_FAILED even though plain `requests` worked fine in the same environment. Now, if the context still has no CA certs after `load_default_certs()`, load the bundle Requests itself would have used (certifi) via `DEFAULT_CA_BUNDLE_PATH`, handling both file and directory bundles and tolerating a missing path. --- httpie/compat.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/httpie/compat.py b/httpie/compat.py index d12abcff02..c73bfafd9e 100644 --- a/httpie/compat.py +++ b/httpie/compat.py @@ -1,7 +1,10 @@ +import os import sys from ssl import SSLContext from typing import Any, Optional, Iterable +from requests.utils import DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths + from httpie.cookies import HTTPieCookiePolicy from http import cookiejar # noqa @@ -103,11 +106,34 @@ def get_dist_name(entry_point: importlib_metadata.EntryPoint) -> Optional[str]: def ensure_default_certs_loaded(ssl_context: SSLContext) -> None: """ - Workaround for a bug in Requests 2.32.3 + Load the default CA certificates into the given SSL context. + Workaround for a bug in Requests 2.32.3 See + `SSLContext.load_default_certs()` relies on OpenSSL's default verify + paths, which are empty on some platforms (notably macOS with the + python.org/pyenv builds). Because HTTPie always passes its own + `SSLContext` to Requests, Requests skips both its own certifi-backed + context and `load_verify_locations()`, so an empty context means *no* + trust store at all and every HTTPS request fails to verify. + Fall back to the CA bundle Requests itself would have used (certifi). + See + """ - if hasattr(ssl_context, 'load_default_certs'): - if not ssl_context.get_ca_certs(): - ssl_context.load_default_certs() + if not hasattr(ssl_context, 'load_default_certs'): + return + + if ssl_context.get_ca_certs(): + return + + ssl_context.load_default_certs() + if ssl_context.get_ca_certs(): + return + + ca_bundle = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + if ca_bundle and os.path.exists(ca_bundle): + if os.path.isdir(ca_bundle): + ssl_context.load_verify_locations(capath=ca_bundle) + else: + ssl_context.load_verify_locations(cafile=ca_bundle)