mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Fix httpx client creating a new ssl context with each client (memory leak) (#90191)
* Fix httpx client creating a new ssl context with each client While working on https://github.com/home-assistant/core/issues/83524 it was discovered that each new httpx client creates a new ssl contextf1157dbc41/httpx/_transports/default.py (L261)
If an ssl context is passed in creating a new one is avoided heref1157dbc41/httpx/_config.py (L110)
This change makes httpx ssl no-verify behavior match aiohttp ssl no-verify behavior6da04694fd/aiohttp/connector.py (L892)
aiohttp solved this by wrapping the code that generates the ssl context in an lru_cache * compact
This commit is contained in:
parent
ca157f4d19
commit
1f2268a878
@ -273,7 +273,7 @@ def _async_get_connector(
|
|||||||
if verify_ssl:
|
if verify_ssl:
|
||||||
ssl_context: bool | SSLContext = ssl_util.get_default_context()
|
ssl_context: bool | SSLContext = ssl_util.get_default_context()
|
||||||
else:
|
else:
|
||||||
ssl_context = False
|
ssl_context = ssl_util.get_default_no_verify_context()
|
||||||
|
|
||||||
connector = aiohttp.TCPConnector(
|
connector = aiohttp.TCPConnector(
|
||||||
enable_cleanup_closed=True,
|
enable_cleanup_closed=True,
|
||||||
|
@ -11,7 +11,7 @@ from typing_extensions import Self
|
|||||||
from homeassistant.const import APPLICATION_NAME, EVENT_HOMEASSISTANT_CLOSE, __version__
|
from homeassistant.const import APPLICATION_NAME, EVENT_HOMEASSISTANT_CLOSE, __version__
|
||||||
from homeassistant.core import Event, HomeAssistant, callback
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util import ssl as ssl_util
|
from homeassistant.util.ssl import get_default_context, get_default_no_verify_context
|
||||||
|
|
||||||
from .frame import warn_use
|
from .frame import warn_use
|
||||||
|
|
||||||
@ -65,8 +65,11 @@ def create_async_httpx_client(
|
|||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
"""
|
"""
|
||||||
|
ssl_context = (
|
||||||
|
get_default_context() if verify_ssl else get_default_no_verify_context()
|
||||||
|
)
|
||||||
client = HassHttpXAsyncClient(
|
client = HassHttpXAsyncClient(
|
||||||
verify=ssl_util.get_default_context() if verify_ssl else False,
|
verify=ssl_context,
|
||||||
headers={USER_AGENT: SERVER_SOFTWARE},
|
headers={USER_AGENT: SERVER_SOFTWARE},
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
"""Helper to create SSL contexts."""
|
"""Helper to create SSL contexts."""
|
||||||
|
import contextlib
|
||||||
from os import environ
|
from os import environ
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
import certifi
|
import certifi
|
||||||
|
|
||||||
|
|
||||||
|
def create_no_verify_ssl_context() -> ssl.SSLContext:
|
||||||
|
"""Return an SSL context that does not verify the server certificate.
|
||||||
|
|
||||||
|
This is a copy of aiohttp's create_default_context() function, with the
|
||||||
|
ssl verify turned off.
|
||||||
|
|
||||||
|
https://github.com/aio-libs/aiohttp/blob/33953f110e97eecc707e1402daa8d543f38a189b/aiohttp/connector.py#L911
|
||||||
|
"""
|
||||||
|
sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||||
|
sslcontext.options |= ssl.OP_NO_SSLv2
|
||||||
|
sslcontext.options |= ssl.OP_NO_SSLv3
|
||||||
|
sslcontext.check_hostname = False
|
||||||
|
sslcontext.verify_mode = ssl.CERT_NONE
|
||||||
|
with contextlib.suppress(AttributeError):
|
||||||
|
# This only works for OpenSSL >= 1.0.0
|
||||||
|
sslcontext.options |= ssl.OP_NO_COMPRESSION
|
||||||
|
sslcontext.set_default_verify_paths()
|
||||||
|
return sslcontext
|
||||||
|
|
||||||
|
|
||||||
def client_context() -> ssl.SSLContext:
|
def client_context() -> ssl.SSLContext:
|
||||||
"""Return an SSL context for making requests."""
|
"""Return an SSL context for making requests."""
|
||||||
|
|
||||||
@ -18,6 +39,7 @@ def client_context() -> ssl.SSLContext:
|
|||||||
|
|
||||||
# Create this only once and reuse it
|
# Create this only once and reuse it
|
||||||
_DEFAULT_SSL_CONTEXT = client_context()
|
_DEFAULT_SSL_CONTEXT = client_context()
|
||||||
|
_DEFAULT_NO_VERIFY_SSL_CONTEXT = create_no_verify_ssl_context()
|
||||||
|
|
||||||
|
|
||||||
def get_default_context() -> ssl.SSLContext:
|
def get_default_context() -> ssl.SSLContext:
|
||||||
@ -25,6 +47,11 @@ def get_default_context() -> ssl.SSLContext:
|
|||||||
return _DEFAULT_SSL_CONTEXT
|
return _DEFAULT_SSL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_no_verify_context() -> ssl.SSLContext:
|
||||||
|
"""Return the default SSL context that does not verify the server certificate."""
|
||||||
|
return _DEFAULT_NO_VERIFY_SSL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
def server_context_modern() -> ssl.SSLContext:
|
def server_context_modern() -> ssl.SSLContext:
|
||||||
"""Return an SSL context following the Mozilla recommendations.
|
"""Return an SSL context following the Mozilla recommendations.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user