diff --git a/gvm/connections/_connection.py b/gvm/connections/_connection.py
index e4b3bba91..009fd8e21 100644
--- a/gvm/connections/_connection.py
+++ b/gvm/connections/_connection.py
@@ -82,11 +82,22 @@ def send(self, data: bytes) -> None:
Args:
data: Data to be send to the server as bytes.
+
+ Raises:
+ GvmError: If the socket is not connected or the remote server
+ has closed the connection. In the latter case the connection
+ needs to be re-established via :py:meth:`connect`.
"""
if self._socket is None:
raise GvmError("Socket is not connected")
- self._socket.sendall(data)
+ try:
+ self._socket.sendall(data)
+ except ConnectionError as e:
+ # Connection was closed by server, e.g. after an error response
+ raise GvmError(
+ f"Remote closed the connection. Error was {e}"
+ ) from e
def read(self) -> bytes:
"""Read data from the remote server
@@ -98,7 +109,13 @@ def read(self) -> bytes:
time() + self._timeout if self._timeout is not None else None
)
- data = self._read()
+ try:
+ data = self._read()
+ except ConnectionError as e:
+ # Connection was reset by server
+ raise GvmError(
+ f"Remote closed the connection. Error was {e}"
+ ) from e
if not data:
# Connection was closed by server
diff --git a/tests/connections/test_gvm_connection.py b/tests/connections/test_gvm_connection.py
index b3aecfe9d..92b1e67a4 100644
--- a/tests/connections/test_gvm_connection.py
+++ b/tests/connections/test_gvm_connection.py
@@ -43,6 +43,50 @@ def test_read_no_data(self):
with self.assertRaises(GvmError, msg="Remote closed the connection"):
connection.read()
+ def test_read_connection_reset(self):
+ read_mock = MagicMock()
+ read_mock.side_effect = ConnectionResetError("Connection reset")
+
+ connection = DummyConnection()
+ connection._read = read_mock
+
+ with self.assertRaisesRegex(
+ GvmError, "^Remote closed the connection. Error was "
+ ):
+ connection.read()
+
+ def test_send_not_connected(self):
+ connection = DummyConnection()
+
+ with self.assertRaisesRegex(GvmError, "^Socket is not connected$"):
+ connection.send(b"")
+
+ def test_send_remote_closed(self):
+ for error in (
+ BrokenPipeError("Broken pipe"),
+ ConnectionResetError("Connection reset"),
+ ConnectionAbortedError("Connection aborted"),
+ ):
+ with self.subTest(error=type(error).__name__):
+ connection = DummyConnection()
+ connection._socket = MagicMock()
+ connection._socket.sendall.side_effect = error
+
+ with self.assertRaisesRegex(
+ GvmError, "^Remote closed the connection. Error was "
+ ) as cm:
+ connection.send(b"")
+
+ self.assertIs(cm.exception.__cause__, error)
+
+ def test_send_other_os_error_is_not_wrapped(self):
+ connection = DummyConnection()
+ connection._socket = MagicMock()
+ connection._socket.sendall.side_effect = TimeoutError("timed out")
+
+ with self.assertRaises(TimeoutError):
+ connection.send(b"")
+
def test_read_trigger_timeout(self):
# mocking the response into two parts, so we run into the timeout
# check in the loop