mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Only create a single resolver object if there are multiple aiohttp sessions (#144090)
This commit is contained in:
parent
97be2c4ac9
commit
2890fc7dd2
@ -28,6 +28,7 @@ from homeassistant.util.json import json_loads
|
|||||||
|
|
||||||
from .frame import warn_use
|
from .frame import warn_use
|
||||||
from .json import json_dumps
|
from .json import json_dumps
|
||||||
|
from .singleton import singleton
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from aiohttp.typedefs import JSONDecoder
|
from aiohttp.typedefs import JSONDecoder
|
||||||
@ -39,6 +40,7 @@ DATA_CONNECTOR: HassKey[dict[tuple[bool, int, str], aiohttp.BaseConnector]] = Ha
|
|||||||
DATA_CLIENTSESSION: HassKey[dict[tuple[bool, int, str], aiohttp.ClientSession]] = (
|
DATA_CLIENTSESSION: HassKey[dict[tuple[bool, int, str], aiohttp.ClientSession]] = (
|
||||||
HassKey("aiohttp_clientsession")
|
HassKey("aiohttp_clientsession")
|
||||||
)
|
)
|
||||||
|
DATA_RESOLVER: HassKey[HassAsyncDNSResolver] = HassKey("aiohttp_resolver")
|
||||||
|
|
||||||
SERVER_SOFTWARE = (
|
SERVER_SOFTWARE = (
|
||||||
f"{APPLICATION_NAME}/{__version__} "
|
f"{APPLICATION_NAME}/{__version__} "
|
||||||
@ -70,6 +72,21 @@ MAXIMUM_CONNECTIONS = 4096
|
|||||||
MAXIMUM_CONNECTIONS_PER_HOST = 100
|
MAXIMUM_CONNECTIONS_PER_HOST = 100
|
||||||
|
|
||||||
|
|
||||||
|
class HassAsyncDNSResolver(AsyncDualMDNSResolver):
|
||||||
|
"""Home Assistant AsyncDNSResolver.
|
||||||
|
|
||||||
|
This is a wrapper around the AsyncDualMDNSResolver to only
|
||||||
|
close the resolver when the Home Assistant instance is closed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def real_close(self) -> None:
|
||||||
|
"""Close the resolver."""
|
||||||
|
await super().close()
|
||||||
|
|
||||||
|
async def close(self) -> None:
|
||||||
|
"""Close the resolver."""
|
||||||
|
|
||||||
|
|
||||||
class HassClientResponse(aiohttp.ClientResponse):
|
class HassClientResponse(aiohttp.ClientResponse):
|
||||||
"""aiohttp.ClientResponse with a json method that uses json_loads by default."""
|
"""aiohttp.ClientResponse with a json method that uses json_loads by default."""
|
||||||
|
|
||||||
@ -363,7 +380,7 @@ def _async_get_connector(
|
|||||||
ssl=ssl_context,
|
ssl=ssl_context,
|
||||||
limit=MAXIMUM_CONNECTIONS,
|
limit=MAXIMUM_CONNECTIONS,
|
||||||
limit_per_host=MAXIMUM_CONNECTIONS_PER_HOST,
|
limit_per_host=MAXIMUM_CONNECTIONS_PER_HOST,
|
||||||
resolver=_async_make_resolver(hass),
|
resolver=_async_get_or_create_resolver(hass),
|
||||||
)
|
)
|
||||||
connectors[connector_key] = connector
|
connectors[connector_key] = connector
|
||||||
|
|
||||||
@ -376,6 +393,19 @@ def _async_get_connector(
|
|||||||
return connector
|
return connector
|
||||||
|
|
||||||
|
|
||||||
|
@singleton(DATA_RESOLVER)
|
||||||
@callback
|
@callback
|
||||||
def _async_make_resolver(hass: HomeAssistant) -> AsyncDualMDNSResolver:
|
def _async_get_or_create_resolver(hass: HomeAssistant) -> HassAsyncDNSResolver:
|
||||||
return AsyncDualMDNSResolver(async_zeroconf=zeroconf.async_get_async_zeroconf(hass))
|
"""Return the HassAsyncDNSResolver."""
|
||||||
|
resolver = _async_make_resolver(hass)
|
||||||
|
|
||||||
|
async def _async_close_resolver(event: Event) -> None:
|
||||||
|
await resolver.real_close()
|
||||||
|
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, _async_close_resolver)
|
||||||
|
return resolver
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_make_resolver(hass: HomeAssistant) -> HassAsyncDNSResolver:
|
||||||
|
return HassAsyncDNSResolver(async_zeroconf=zeroconf.async_get_async_zeroconf(hass))
|
||||||
|
@ -1319,9 +1319,11 @@ def disable_translations_once(
|
|||||||
@pytest_asyncio.fixture(autouse=True, scope="session", loop_scope="session")
|
@pytest_asyncio.fixture(autouse=True, scope="session", loop_scope="session")
|
||||||
async def mock_zeroconf_resolver() -> AsyncGenerator[_patch]:
|
async def mock_zeroconf_resolver() -> AsyncGenerator[_patch]:
|
||||||
"""Mock out the zeroconf resolver."""
|
"""Mock out the zeroconf resolver."""
|
||||||
|
resolver = AsyncResolver()
|
||||||
|
resolver.real_close = resolver.close
|
||||||
patcher = patch(
|
patcher = patch(
|
||||||
"homeassistant.helpers.aiohttp_client._async_make_resolver",
|
"homeassistant.helpers.aiohttp_client._async_make_resolver",
|
||||||
return_value=AsyncResolver(),
|
return_value=resolver,
|
||||||
)
|
)
|
||||||
patcher.start()
|
patcher.start()
|
||||||
try:
|
try:
|
||||||
|
@ -401,3 +401,15 @@ async def test_async_mdnsresolver(
|
|||||||
resp = await session.post("http://localhost/xyz", json={"x": 1})
|
resp = await session.post("http://localhost/xyz", json={"x": 1})
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
assert await resp.json() == {"x": 1}
|
assert await resp.json() == {"x": 1}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_resolver_is_singleton(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that the resolver is a singleton."""
|
||||||
|
session = client.async_get_clientsession(hass)
|
||||||
|
session2 = client.async_get_clientsession(hass)
|
||||||
|
session3 = client.async_create_clientsession(hass)
|
||||||
|
assert isinstance(session._connector, aiohttp.TCPConnector)
|
||||||
|
assert isinstance(session2._connector, aiohttp.TCPConnector)
|
||||||
|
assert isinstance(session3._connector, aiohttp.TCPConnector)
|
||||||
|
assert session._connector._resolver is session2._connector._resolver
|
||||||
|
assert session._connector._resolver is session3._connector._resolver
|
||||||
|
Loading…
x
Reference in New Issue
Block a user