Avoid compressing binary images on ingress (#96581)
parent
a27e126c86
commit
62c5194bc8
homeassistant/components/hassio
tests/components/hassio
|
@ -177,7 +177,8 @@ class HassIOView(HomeAssistantView):
|
||||||
)
|
)
|
||||||
response.content_type = client.content_type
|
response.content_type = client.content_type
|
||||||
|
|
||||||
response.enable_compression()
|
if should_compress(response.content_type):
|
||||||
|
response.enable_compression()
|
||||||
await response.prepare(request)
|
await response.prepare(request)
|
||||||
async for data in client.content.iter_chunked(8192):
|
async for data in client.content.iter_chunked(8192):
|
||||||
await response.write(data)
|
await response.write(data)
|
||||||
|
@ -213,3 +214,10 @@ def _get_timeout(path: str) -> ClientTimeout:
|
||||||
if NO_TIMEOUT.match(path):
|
if NO_TIMEOUT.match(path):
|
||||||
return ClientTimeout(connect=10, total=None)
|
return ClientTimeout(connect=10, total=None)
|
||||||
return ClientTimeout(connect=10, total=300)
|
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/"))
|
||||||
|
|
|
@ -20,6 +20,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.typing import UNDEFINED
|
from homeassistant.helpers.typing import UNDEFINED
|
||||||
|
|
||||||
from .const import X_HASS_SOURCE, X_INGRESS_PATH
|
from .const import X_HASS_SOURCE, X_INGRESS_PATH
|
||||||
|
from .http import should_compress
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -182,7 +183,9 @@ class HassIOIngress(HomeAssistantView):
|
||||||
content_type=result.content_type,
|
content_type=result.content_type,
|
||||||
body=body,
|
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()
|
simple_response.enable_compression()
|
||||||
await simple_response.prepare(request)
|
await simple_response.prepare(request)
|
||||||
return simple_response
|
return simple_response
|
||||||
|
@ -192,7 +195,8 @@ class HassIOIngress(HomeAssistantView):
|
||||||
response.content_type = result.content_type
|
response.content_type = result.content_type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response.enable_compression()
|
if should_compress(response.content_type):
|
||||||
|
response.enable_compression()
|
||||||
await response.prepare(request)
|
await response.prepare(request)
|
||||||
async for data in result.content.iter_chunked(8192):
|
async for data in result.content.iter_chunked(8192):
|
||||||
await response.write(data)
|
await response.write(data)
|
||||||
|
|
|
@ -396,6 +396,68 @@ async def test_ingress_request_get_compressed(
|
||||||
assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO]
|
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(
|
@pytest.mark.parametrize(
|
||||||
"build_type",
|
"build_type",
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in New Issue