From 0d63e2f9b5262d139a9768618e8d6699083c11e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Nov 2023 12:37:54 -0600 Subject: [PATCH] Ensure large payloads are compressed in the executor with aiohttp 3.9.0 (#103592) --- homeassistant/components/api/__init__.py | 3 ++- homeassistant/components/hassio/ingress.py | 3 ++- homeassistant/components/http/view.py | 3 ++- homeassistant/helpers/aiohttp_compat.py | 18 +++++++++++++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index a9efda90482..6bb3cc34050 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -41,6 +41,7 @@ from homeassistant.exceptions import ( Unauthorized, ) from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers.aiohttp_compat import enable_compression from homeassistant.helpers.event import EventStateChangedData from homeassistant.helpers.json import json_dumps from homeassistant.helpers.service import async_get_all_descriptions @@ -219,7 +220,7 @@ class APIStatesView(HomeAssistantView): response = web.Response( body=f'[{",".join(states)}]', content_type=CONTENT_TYPE_JSON ) - response.enable_compression() + enable_compression(response) return response diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 345c14163f5..b29f80ff2b3 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -17,6 +17,7 @@ from yarl import URL from homeassistant.components.http import HomeAssistantView from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.aiohttp_compat import enable_compression from homeassistant.helpers.typing import UNDEFINED from .const import X_HASS_SOURCE, X_INGRESS_PATH @@ -191,7 +192,7 @@ class HassIOIngress(HomeAssistantView): if content_length_int > MIN_COMPRESSED_SIZE and should_compress( content_type or simple_response.content_type ): - simple_response.enable_compression() + enable_compression(simple_response) await simple_response.prepare(request) return simple_response diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index abdcfe466c1..7481381bbc8 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -20,6 +20,7 @@ import voluptuous as vol from homeassistant import exceptions from homeassistant.const import CONTENT_TYPE_JSON from homeassistant.core import Context, HomeAssistant, is_callback +from homeassistant.helpers.aiohttp_compat import enable_compression from homeassistant.helpers.json import ( find_paths_unserializable_data, json_bytes, @@ -72,7 +73,7 @@ class HomeAssistantView: status=int(status_code), headers=headers, ) - response.enable_compression() + enable_compression(response) return response def json_message( diff --git a/homeassistant/helpers/aiohttp_compat.py b/homeassistant/helpers/aiohttp_compat.py index 78aad44fa66..6e281b659fe 100644 --- a/homeassistant/helpers/aiohttp_compat.py +++ b/homeassistant/helpers/aiohttp_compat.py @@ -1,7 +1,7 @@ """Helper to restore old aiohttp behavior.""" from __future__ import annotations -from aiohttp import web_protocol, web_server +from aiohttp import web, web_protocol, web_server class CancelOnDisconnectRequestHandler(web_protocol.RequestHandler): @@ -23,3 +23,19 @@ def restore_original_aiohttp_cancel_behavior() -> None: """ web_protocol.RequestHandler = CancelOnDisconnectRequestHandler # type: ignore[misc] web_server.RequestHandler = CancelOnDisconnectRequestHandler # type: ignore[misc] + + +def enable_compression(response: web.Response) -> None: + """Enable compression on the response.""" + # + # Set _zlib_executor_size in the constructor once support for + # aiohttp < 3.9.0 is dropped + # + # We want large zlib payloads to be compressed in the executor + # to avoid blocking the event loop. + # + # 32KiB was chosen based on testing in production. + # aiohttp will generate a warning for payloads larger than 1MiB + # + response._zlib_executor_size = 32768 # pylint: disable=protected-access + response.enable_compression()