Skip to content

Commit 671c4e1

Browse files
authored
Reduce log spam from unauthenticated websocket connections (#151388)
1 parent 8aae2a9 commit 671c4e1

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

homeassistant/components/websocket_api/http.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from .util import describe_request
3838

3939
CLOSE_MSG_TYPES = {WSMsgType.CLOSE, WSMsgType.CLOSED, WSMsgType.CLOSING}
40+
AUTH_MESSAGE_TIMEOUT = 10 # seconds
4041

4142
if TYPE_CHECKING:
4243
from .connection import ActiveConnection
@@ -389,9 +390,11 @@ async def _async_handle_auth_phase(
389390

390391
# Auth Phase
391392
try:
392-
msg = await self._wsock.receive(10)
393+
msg = await self._wsock.receive(AUTH_MESSAGE_TIMEOUT)
393394
except TimeoutError as err:
394-
raise Disconnect("Did not receive auth message within 10 seconds") from err
395+
raise Disconnect(
396+
f"Did not receive auth message within {AUTH_MESSAGE_TIMEOUT} seconds"
397+
) from err
395398

396399
if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSED, WSMsgType.CLOSING):
397400
raise Disconnect("Received close message during auth phase")
@@ -538,6 +541,14 @@ async def _async_cleanup_writer_and_close(
538541
finally:
539542
if disconnect_warn is None:
540543
logger.debug("%s: Disconnected", self.description)
544+
elif connection is None:
545+
# Auth phase disconnects (connection is None) should be logged at debug level
546+
# as they can be from random port scanners or non-legitimate connections
547+
logger.debug(
548+
"%s: Disconnected during auth phase: %s",
549+
self.description,
550+
disconnect_warn,
551+
)
541552
else:
542553
logger.warning(
543554
"%s: Disconnected: %s", self.description, disconnect_warn

tests/components/websocket_api/test_http.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
from datetime import timedelta
5+
import logging
56
from typing import Any, cast
67
from unittest.mock import patch
78

@@ -20,7 +21,11 @@
2021
from homeassistant.util.dt import utcnow
2122

2223
from tests.common import async_call_logger_set_level, async_fire_time_changed
23-
from tests.typing import MockHAClientWebSocket, WebSocketGenerator
24+
from tests.typing import (
25+
ClientSessionGenerator,
26+
MockHAClientWebSocket,
27+
WebSocketGenerator,
28+
)
2429

2530

2631
@pytest.fixture
@@ -400,6 +405,48 @@ async def test_prepare_fail_connection_reset(
400405
assert "Connection reset by peer while preparing WebSocket" in caplog.text
401406

402407

408+
async def test_auth_timeout_logs_at_debug(
409+
hass: HomeAssistant,
410+
hass_client: ClientSessionGenerator,
411+
caplog: pytest.LogCaptureFixture,
412+
) -> None:
413+
"""Test auth timeout is logged at debug level not warning."""
414+
# Setup websocket API
415+
assert await async_setup_component(hass, "websocket_api", {})
416+
417+
client = await hass_client()
418+
419+
# Patch the auth timeout to be very short (0.001 seconds)
420+
with (
421+
caplog.at_level(logging.DEBUG, "homeassistant.components.websocket_api"),
422+
patch(
423+
"homeassistant.components.websocket_api.http.AUTH_MESSAGE_TIMEOUT", 0.001
424+
),
425+
):
426+
# Try to connect - will timeout quickly since we don't send auth
427+
ws = await client.ws_connect("/api/websocket")
428+
# Wait a bit for the timeout to trigger and cleanup to complete
429+
await asyncio.sleep(0.1)
430+
await ws.close()
431+
await asyncio.sleep(0.1)
432+
433+
# Check that "Did not receive auth message" is logged at debug, not warning
434+
debug_messages = [
435+
r.message for r in caplog.records if r.levelno == logging.DEBUG
436+
]
437+
assert any(
438+
"Disconnected during auth phase: Did not receive auth message" in msg
439+
for msg in debug_messages
440+
)
441+
442+
# Check it's NOT logged at warning level
443+
warning_messages = [
444+
r.message for r in caplog.records if r.levelno >= logging.WARNING
445+
]
446+
for msg in warning_messages:
447+
assert "Did not receive auth message" not in msg
448+
449+
403450
async def test_enable_coalesce(
404451
hass: HomeAssistant,
405452
hass_ws_client: WebSocketGenerator,

0 commit comments

Comments
 (0)