diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index 34e1d89b8b4..0e18a009323 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -177,7 +177,8 @@ class HassIOView(HomeAssistantView): ) response.content_type = client.content_type - response.enable_compression() + if should_compress(response.content_type): + response.enable_compression() await response.prepare(request) async for data in client.content.iter_chunked(8192): await response.write(data) @@ -213,3 +214,10 @@ def _get_timeout(path: str) -> ClientTimeout: if NO_TIMEOUT.match(path): return ClientTimeout(connect=10, total=None) return ClientTimeout(connect=10, total=300) + + +def should_compress(content_type: str) -> bool: + """Return if we should compress a response.""" + if content_type.startswith("image/"): + return "svg" in content_type + return not content_type.startswith(("video/", "audio/", "font/")) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 2a9d9b73978..4a612de7f87 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -20,6 +20,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import UNDEFINED from .const import X_HASS_SOURCE, X_INGRESS_PATH +from .http import should_compress _LOGGER = logging.getLogger(__name__) @@ -182,7 +183,9 @@ class HassIOIngress(HomeAssistantView): content_type=result.content_type, body=body, ) - if content_length_int > MIN_COMPRESSED_SIZE: + if content_length_int > MIN_COMPRESSED_SIZE and should_compress( + simple_response.content_type + ): simple_response.enable_compression() await simple_response.prepare(request) return simple_response @@ -192,7 +195,8 @@ class HassIOIngress(HomeAssistantView): response.content_type = result.content_type try: - response.enable_compression() + if should_compress(response.content_type): + response.enable_compression() await response.prepare(request) async for data in result.content.iter_chunked(8192): await response.write(data) diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 6df946ad2cf..3eda10b1514 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -396,6 +396,68 @@ async def test_ingress_request_get_compressed( assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] +@pytest.mark.parametrize( + "content_type", + [ + "image/png", + "image/jpeg", + "font/woff2", + "video/mp4", + ], +) +async def test_ingress_request_not_compressed( + hassio_noauth_client, content_type: str, aioclient_mock: AiohttpClientMocker +) -> None: + """Test ingress does not compress images.""" + body = b"this_is_long_enough_to_be_compressed" * 100 + aioclient_mock.get( + "http://127.0.0.1/ingress/core/x.any", + data=body, + headers={"Content-Length": len(body), "Content-Type": content_type}, + ) + + resp = await hassio_noauth_client.get( + "/api/hassio_ingress/core/x.any", + headers={"X-Test-Header": "beer", "Accept-Encoding": "gzip, deflate"}, + ) + + # Check we got right response + assert resp.status == HTTPStatus.OK + assert resp.headers["Content-Type"] == content_type + assert "Content-Encoding" not in resp.headers + + +@pytest.mark.parametrize( + "content_type", + [ + "image/svg+xml", + "text/html", + "application/javascript", + "text/plain", + ], +) +async def test_ingress_request_compressed( + hassio_noauth_client, content_type: str, aioclient_mock: AiohttpClientMocker +) -> None: + """Test ingress compresses text.""" + body = b"this_is_long_enough_to_be_compressed" * 100 + aioclient_mock.get( + "http://127.0.0.1/ingress/core/x.any", + data=body, + headers={"Content-Length": len(body), "Content-Type": content_type}, + ) + + resp = await hassio_noauth_client.get( + "/api/hassio_ingress/core/x.any", + headers={"X-Test-Header": "beer", "Accept-Encoding": "gzip, deflate"}, + ) + + # Check we got right response + assert resp.status == HTTPStatus.OK + assert resp.headers["Content-Type"] == content_type + assert resp.headers["Content-Encoding"] == "deflate" + + @pytest.mark.parametrize( "build_type", [