mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Skip compressing HTTP JSON responses below 1 KiB (#175560)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
2293f51828
commit
631179dffe
@@ -46,6 +46,7 @@ from homeassistant.exceptions import (
|
||||
Unauthorized,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv, recorder, template
|
||||
from homeassistant.helpers.http import MIN_COMPRESSED_RESPONSE_SIZE
|
||||
from homeassistant.helpers.json import json_dumps, json_fragment
|
||||
from homeassistant.helpers.service import async_get_all_descriptions
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
@@ -223,12 +224,14 @@ class APIStatesView(HomeAssistantView):
|
||||
for state in hass.states.async_all()
|
||||
if entity_perm(state.entity_id, POLICY_READ)
|
||||
)
|
||||
body = b"".join((b"[", b",".join(states), b"]"))
|
||||
response = web.Response(
|
||||
body=b"".join((b"[", b",".join(states), b"]")),
|
||||
body=body,
|
||||
content_type=CONTENT_TYPE_JSON,
|
||||
zlib_executor_size=32768,
|
||||
)
|
||||
response.enable_compression()
|
||||
if len(body) > MIN_COMPRESSED_RESPONSE_SIZE:
|
||||
response.enable_compression()
|
||||
return response
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ from .json import find_paths_unserializable_data, json_bytes, json_dumps
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Responses smaller than this fit within a single network packet, so
|
||||
# compressing them wastes event-loop CPU without reducing round-trips.
|
||||
MIN_COMPRESSED_RESPONSE_SIZE: Final = 1024
|
||||
|
||||
|
||||
type AllowCorsType = Callable[[AbstractRoute | AbstractResource], None]
|
||||
KEY_AUTHENTICATED: Final = "ha_authenticated"
|
||||
@@ -160,7 +164,8 @@ class HomeAssistantView:
|
||||
headers=headers,
|
||||
zlib_executor_size=32768,
|
||||
)
|
||||
response.enable_compression()
|
||||
if len(msg) > MIN_COMPRESSED_RESPONSE_SIZE:
|
||||
response.enable_compression()
|
||||
return response
|
||||
|
||||
def json_message(
|
||||
|
||||
@@ -51,6 +51,33 @@ async def test_api_list_state_entities(
|
||||
assert remote_data == local_data
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_count", "expect_compression"),
|
||||
[
|
||||
pytest.param(1, False, id="small-body-not-compressed"),
|
||||
pytest.param(50, True, id="large-body-compressed"),
|
||||
],
|
||||
)
|
||||
async def test_api_states_compression_threshold(
|
||||
hass: HomeAssistant,
|
||||
mock_api_client: TestClient,
|
||||
entity_count: int,
|
||||
expect_compression: bool,
|
||||
) -> None:
|
||||
"""Test that only state list responses above the size threshold are compressed."""
|
||||
for i in range(entity_count):
|
||||
hass.states.async_set(
|
||||
f"test.entity_{i}", "on", {"friendly_name": f"Entity {i}"}
|
||||
)
|
||||
|
||||
resp = await mock_api_client.get(
|
||||
const.URL_API_STATES, headers={"Accept-Encoding": "gzip, deflate"}
|
||||
)
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert ("Content-Encoding" in resp.headers) is expect_compression
|
||||
|
||||
|
||||
async def test_api_get_state(hass: HomeAssistant, mock_api_client: TestClient) -> None:
|
||||
"""Test if the debug interface allows us to get a state."""
|
||||
hass.states.async_set("hello.world", "nice", {"attr": 1})
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Tests for the HTTP helpers."""
|
||||
|
||||
from http import HTTPStatus
|
||||
|
||||
from aiohttp import web
|
||||
import pytest
|
||||
|
||||
from homeassistant.helpers.http import MIN_COMPRESSED_RESPONSE_SIZE, HomeAssistantView
|
||||
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("body_size", "expect_compression"),
|
||||
[
|
||||
pytest.param(8, False, id="small-body-not-compressed"),
|
||||
pytest.param(
|
||||
MIN_COMPRESSED_RESPONSE_SIZE * 2, True, id="large-body-compressed"
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("socket_enabled")
|
||||
async def test_json_response_compression_threshold(
|
||||
aiohttp_client: ClientSessionGenerator,
|
||||
body_size: int,
|
||||
expect_compression: bool,
|
||||
) -> None:
|
||||
"""Test HomeAssistantView.json only compresses bodies above the threshold."""
|
||||
|
||||
async def handler(request: web.Request) -> web.Response:
|
||||
return HomeAssistantView.json({"data": "x" * body_size})
|
||||
|
||||
app = web.Application()
|
||||
app.router.add_get("/", handler)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.get("/", headers={"Accept-Encoding": "gzip, deflate"})
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert ("Content-Encoding" in resp.headers) is expect_compression
|
||||
Reference in New Issue
Block a user