Improve handling decode errors in rest (#150699)

This commit is contained in:
G Johansson
2025-08-15 15:51:48 +02:00
committed by Franck Nijhof
parent 22e19e768e
commit c551a133c1
2 changed files with 70 additions and 3 deletions

View File

@@ -45,6 +45,7 @@ class RestData:
self._method = method
self._resource = resource
self._encoding = encoding
self._force_use_set_encoding = False
# Convert auth tuple to aiohttp.BasicAuth if needed
if isinstance(auth, tuple) and len(auth) == 2:
@@ -152,10 +153,19 @@ class RestData:
# Read the response
# Only use configured encoding if no charset in Content-Type header
# If charset is present in Content-Type, let aiohttp use it
if response.charset:
if self._force_use_set_encoding is False and response.charset:
# Let aiohttp use the charset from Content-Type header
self.data = await response.text()
else:
try:
self.data = await response.text()
except UnicodeDecodeError as ex:
self._force_use_set_encoding = True
_LOGGER.debug(
"Response charset came back as %s but could not be decoded, continue with configured encoding %s. %s",
response.charset,
self._encoding,
ex,
)
if self._force_use_set_encoding or not response.charset:
# Use configured encoding as fallback
self.data = await response.text(encoding=self._encoding)
self.headers = response.headers

View File

@@ -1,13 +1,17 @@
"""Test REST data module logging improvements."""
from datetime import timedelta
import logging
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components.rest import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
@@ -89,6 +93,59 @@ async def test_rest_data_no_warning_on_200_with_wrong_content_type(
)
async def test_rest_data_with_incorrect_charset_in_header(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
caplog: pytest.LogCaptureFixture,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that we can handle sites which provides an incorrect charset."""
aioclient_mock.get(
"http://example.com/api",
status=200,
text="<p>Some html</p>",
headers={"Content-Type": "text/html; charset=utf-8"},
)
assert await async_setup_component(
hass,
DOMAIN,
{
DOMAIN: {
"resource": "http://example.com/api",
"method": "GET",
"encoding": "windows-1250",
"sensor": [
{
"name": "test_sensor",
"value_template": "{{ value }}",
}
],
}
},
)
await hass.async_block_till_done()
with patch(
"tests.test_util.aiohttp.AiohttpClientMockResponse.text",
side_effect=UnicodeDecodeError("utf-8", b"", 1, 0, ""),
):
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
log_text = "Response charset came back as utf-8 but could not be decoded, continue with configured encoding windows-1250."
assert log_text in caplog.text
caplog.clear()
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Only log once as we only try once with automatic decoding
assert log_text not in caplog.text
async def test_rest_data_no_warning_on_success_json(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,