diff --git a/custom_components/multiscrape/http_session.py b/custom_components/multiscrape/http_session.py index a4d9961..c2ff6ab 100644 --- a/custom_components/multiscrape/http_session.py +++ b/custom_components/multiscrape/http_session.py @@ -12,6 +12,7 @@ CONF_TIMEOUT, CONF_USERNAME, CONF_VERIFY_SSL, HTTP_DIGEST_AUTHENTICATION) from homeassistant.core import HomeAssistant +from homeassistant.util.ssl import client_context, create_no_verify_ssl_context from .const import (CONF_FORM_INPUT, CONF_FORM_INPUT_FILTER, CONF_FORM_RESUBMIT_ERROR, CONF_FORM_SELECT, @@ -68,9 +69,13 @@ def __init__( self._file_manager = file_manager self._form_authenticator = form_authenticator - # Create dedicated httpx client with its own cookie jar + # Create dedicated httpx client with its own cookie jar. + # Use HA's pre-warmed cached SSL context so CA certs are never loaded + # on the event loop (homeassistant.util.ssl pre-warms the cache at + # import time precisely to avoid blocking I/O here). + ssl_ctx = client_context() if http_config.verify_ssl else create_no_verify_ssl_context() self._client = httpx.AsyncClient( - verify=http_config.verify_ssl, + verify=ssl_ctx, timeout=http_config.timeout, follow_redirects=True, ) diff --git a/tests/test_http_session.py b/tests/test_http_session.py index 30d412c..678caba 100644 --- a/tests/test_http_session.py +++ b/tests/test_http_session.py @@ -1,6 +1,9 @@ """Tests for the unified HttpSession class.""" +import ssl +from unittest.mock import patch + import httpx import pytest import respx @@ -120,6 +123,32 @@ def session_with_file_manager(hass: HomeAssistant, http_config, mock_file_manage """ +# ============================================================================ +# Regression: issue #578 — blocking SSL cert load on event loop +# ============================================================================ + + +@pytest.mark.parametrize("verify_ssl", [True, False]) +def test_init_does_not_load_verify_locations(hass: HomeAssistant, verify_ssl): + """HttpSession must not call ssl.SSLContext.load_verify_locations during init. + + Regression guard for issue #578: constructing httpx.AsyncClient(verify=bool) + triggers a synchronous CA-bundle load on the event loop. We avoid this by + passing a pre-built (and pre-warmed) ssl.SSLContext from HA's util.ssl. + """ + with patch.object( + ssl.SSLContext, "load_verify_locations" + ) as mock_load: + HttpSession( + config_name="test_ssl", + hass=hass, + http_config=make_http_config(verify_ssl=verify_ssl), + file_manager=None, + ) + + mock_load.assert_not_called() + + # ============================================================================ # Basic Request Tests # ============================================================================