From 79fb21ae695d81e15948bcc2033df3a9c0e6607c Mon Sep 17 00:00:00 2001 From: Jarkko Jaakola Date: Mon, 6 May 2024 12:13:58 +0300 Subject: [PATCH] fix: handle ConnectionError in rapu._handle_messages If client connection is broken there is nothing the server can do. Handle the specific case and silence stats and reporting. The aiohttp expects a response object, although this is not and cannot be written to client as the connection is already broken. --- karapace/rapu.py | 5 +++++ tests/unit/test_rapu.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/karapace/rapu.py b/karapace/rapu.py index 5ba90b3d5..7108b7631 100644 --- a/karapace/rapu.py +++ b/karapace/rapu.py @@ -393,6 +393,11 @@ async def _handle_request( ) headers = {"Content-Type": "application/json"} resp = aiohttp.web.Response(body=body, status=status.value, headers=headers) + except ConnectionError as connection_error: + # TCP level connection errors, e.g. TCP reset, client closes connection. + self.log.debug("Connection error.", exc_info=connection_error) + # No response can be returned and written to client, aiohttp expects some response here. + resp = aiohttp.web.Response(text="Connection error", status=HTTPStatus.INTERNAL_SERVER_ERROR.value) except asyncio.CancelledError: self.log.debug("Client closed connection") raise diff --git a/tests/unit/test_rapu.py b/tests/unit/test_rapu.py index 7205fc8d2..bb3e5df4e 100644 --- a/tests/unit/test_rapu.py +++ b/tests/unit/test_rapu.py @@ -2,7 +2,11 @@ Copyright (c) 2023 Aiven Ltd See LICENSE for details """ +from aiohttp.web import Request +from karapace.config import DEFAULTS +from karapace.karapace import KarapaceBase from karapace.rapu import HTTPRequest, REST_ACCEPT_RE, REST_CONTENT_TYPE_RE +from unittest.mock import Mock async def test_header_get(): @@ -154,3 +158,21 @@ def test_content_type_re(): "serialization_format": "json", "general_format": None, } + + +async def test_raise_connection_error_handling() -> None: + request_mock = Mock(spec=Request) + request_mock.read.side_effect = ConnectionError("Connection error test.") + callback_mock = Mock() + + app = KarapaceBase(config=DEFAULTS) + + response = await app._handle_request( # pylint: disable=protected-access + request=request_mock, + path_for_stats="/", + callback=callback_mock, + ) + + assert response.status == 500 + request_mock.read.assert_has_calls([]) + callback_mock.assert_not_called()