mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 14:29:21 -04:00
Fix Supervisor connection leak when leaving an app's log page (#183772)
This commit is contained in:
@@ -178,29 +178,28 @@ class HassIOView(HomeAssistantView):
|
||||
headers[RANGE] = request.headers[RANGE]
|
||||
|
||||
try:
|
||||
client = await self._websession.request(
|
||||
async with self._websession.request(
|
||||
method=request.method,
|
||||
url=f"http://{self._host}/{quote(path)}",
|
||||
params=request.query,
|
||||
data=request.content if request.method != "GET" else None,
|
||||
headers=headers,
|
||||
timeout=_get_timeout(path),
|
||||
)
|
||||
) as client:
|
||||
# Stream response
|
||||
response = web.StreamResponse(
|
||||
status=client.status, headers=_response_header(client)
|
||||
)
|
||||
response.content_type = client.content_type
|
||||
|
||||
# Stream response
|
||||
response = web.StreamResponse(
|
||||
status=client.status, headers=_response_header(client)
|
||||
)
|
||||
response.content_type = client.content_type
|
||||
|
||||
if should_compress(response.content_type, path):
|
||||
response.enable_compression()
|
||||
await response.prepare(request)
|
||||
# In testing iter_chunked, iter_any, and iter_chunks:
|
||||
# iter_chunks was the best performing option since
|
||||
# it does not have to do as much re-assembly
|
||||
async for data, _ in client.content.iter_chunks():
|
||||
await response.write(data)
|
||||
if should_compress(response.content_type, path):
|
||||
response.enable_compression()
|
||||
await response.prepare(request)
|
||||
# In testing iter_chunked, iter_any, and iter_chunks:
|
||||
# iter_chunks was the best performing option since
|
||||
# it does not have to do as much re-assembly
|
||||
async for data, _ in client.content.iter_chunks():
|
||||
await response.write(data)
|
||||
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("Client error on api %s request %s", path, err)
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
"""The tests for the hassio component."""
|
||||
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock, PropertyMock, patch
|
||||
|
||||
from aiohttp import StreamReader
|
||||
from aiohttp import ClientError, StreamReader
|
||||
from aiohttp.test_utils import TestClient
|
||||
import pytest
|
||||
|
||||
from tests.common import MockUser
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -478,3 +481,28 @@ async def test_forward_range_header_for_logs(
|
||||
assert aioclient_mock.mock_calls[3][-1].get("Range") == test_range
|
||||
assert aioclient_mock.mock_calls[4][-1].get("Range") == test_range
|
||||
assert aioclient_mock.mock_calls[5][-1].get("Range") is None
|
||||
|
||||
|
||||
async def test_supervisor_response_released_when_stream_cancelled(
|
||||
hassio_client: TestClient, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test the response from the Supervisor is released when the stream is cancelled."""
|
||||
# A frontend disconnect cancels the handler, simulated here from the stream.
|
||||
stream = StreamReader(Mock(), limit=2**16)
|
||||
stream.feed_data(b"line 1\n")
|
||||
stream.set_exception(asyncio.CancelledError())
|
||||
|
||||
aioclient_mock.get("http://127.0.0.1/addons/bl_b392/logs/follow")
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
AiohttpClientMockResponse, "content", new_callable=PropertyMock
|
||||
) as mock_content,
|
||||
patch.object(AiohttpClientMockResponse, "release") as mock_release,
|
||||
):
|
||||
mock_content.return_value = stream
|
||||
resp = await hassio_client.get("/api/hassio/addons/bl_b392/logs/follow")
|
||||
with suppress(ClientError):
|
||||
await resp.read()
|
||||
|
||||
mock_release.assert_called_once()
|
||||
|
||||
@@ -336,6 +336,7 @@ class AiohttpClientMockResponse:
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
"""Exit the context manager."""
|
||||
self.release()
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
||||
Reference in New Issue
Block a user