Compare commits

...

2 Commits

Author SHA1 Message Date
Harshit Chaudhary
57a462ead9
Merge a4342e9aa1 into b7ba504e06 2026-07-04 13:38:00 -07:00
Harshit Chaudhary
a4342e9aa1 fix: catch OSError in websocket send to prevent server crash send_socket_catch_exception only caught ConnectionError and its
subclasses (ConnectionResetError, BrokenPipeError). When a client
  becomes unreachable mid-generation (laptop sleeps, drops off WiFi),
  the OS raises OSError: [Errno 113] No route to host (EHOSTUNREACH),
  which is a sibling of ConnectionError, not a subclass. It therefore
  escaped the handler, propagated through publish_loop, and could crash
  the entire server over a single dead client.
2026-06-15 01:55:17 +05:30
2 changed files with 39 additions and 1 deletions

View File

@ -72,7 +72,7 @@ def _remove_sensitive_from_queue(queue: list) -> list:
async def send_socket_catch_exception(function, message):
try:
await function(message)
except (aiohttp.ClientError, aiohttp.ClientPayloadError, ConnectionResetError, BrokenPipeError, ConnectionError) as err:
except (aiohttp.ClientError, aiohttp.ClientPayloadError, OSError) as err:
logging.warning("send error: {}".format(err))
# Track deprecated paths that have been warned about to only warn once per file

View File

@ -0,0 +1,38 @@
"""Tests for server.send_socket_catch_exception.
"""
import pytest
import utils.install_util # noqa: F401
from server import send_socket_catch_exception
pytestmark = pytest.mark.asyncio
class TestSendSocketCatchException:
"""The send helper must swallow client-disconnect errors, never propagate them."""
async def test_swallows_no_route_to_host(self, caplog):
async def failing_send(_message):
raise OSError(113, "No route to host")
await send_socket_catch_exception(failing_send, b"payload")
assert "send error" in caplog.text
async def test_swallows_connection_reset(self):
"""Existing behaviour preserved: ConnectionResetError is still caught."""
async def failing_send(_message):
raise ConnectionResetError("peer reset the connection")
await send_socket_catch_exception(failing_send, b"payload")
async def test_successful_send_delivers_message(self):
"""When the send succeeds the message is passed through and nothing is logged."""
received = []
async def ok_send(message):
received.append(message)
await send_socket_catch_exception(ok_send, b"hello")
assert received == [b"hello"]