diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index e962c141bef..f07e88d811a 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -171,13 +171,13 @@ class Scanner: session = self.hass.helpers.aiohttp_client.async_get_clientsession() try: resp = await session.get(xml_location, timeout=5) - xml = await resp.text() + xml = await resp.text(errors="replace") # Samsung Smart TV sometimes returns an empty document the # first time. Retry once. if not xml: resp = await session.get(xml_location, timeout=5) - xml = await resp.text() + xml = await resp.text(errors="replace") except (aiohttp.ClientError, asyncio.TimeoutError) as err: _LOGGER.debug("Error fetching %s: %s", xml_location, err) return {} diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index 008995cd78d..bba809aedbb 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -170,3 +170,46 @@ async def test_scan_description_parse_fail(hass, aioclient_mock): return_value=[Mock(st="mock-st", location="http://1.1.1.1", values={})], ): await scanner.async_scan(None) + + +async def test_invalid_characters(hass, aioclient_mock): + """Test that we replace bad characters with placeholders.""" + aioclient_mock.get( + "http://1.1.1.1", + text=""" + + + ABC + \xff\xff\xff\xff + + + """, + ) + scanner = ssdp.Scanner( + hass, + { + "mock-domain": [ + { + ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC", + } + ] + }, + ) + + with patch( + "netdisco.ssdp.scan", + return_value=[Mock(st="mock-st", location="http://1.1.1.1", values={})], + ), patch.object( + hass.config_entries.flow, "async_init", return_value=mock_coro() + ) as mock_init: + await scanner.async_scan(None) + + assert len(mock_init.mock_calls) == 1 + assert mock_init.mock_calls[0][1][0] == "mock-domain" + assert mock_init.mock_calls[0][2]["context"] == {"source": "ssdp"} + assert mock_init.mock_calls[0][2]["data"] == { + "ssdp_location": "http://1.1.1.1", + "ssdp_st": "mock-st", + "deviceType": "ABC", + "serialNumber": "ÿÿÿÿ", + } diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index 53949c20b06..5219212f1cf 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -245,9 +245,9 @@ class AiohttpClientMockResponse: """Return mock response.""" return self.response - async def text(self, encoding="utf-8"): + async def text(self, encoding="utf-8", errors="strict"): """Return mock response as a string.""" - return self.response.decode(encoding) + return self.response.decode(encoding, errors=errors) async def json(self, encoding="utf-8", content_type=None): """Return mock response as a json."""