Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions gvm/connections/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
44 changes: 44 additions & 0 deletions tests/connections/test_gvm_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"<gmp/>")

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"<gmp/>")

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"<gmp/>")

def test_read_trigger_timeout(self):
# mocking the response into two parts, so we run into the timeout
# check in the loop
Expand Down
Loading