Keep the server running if a reply fails

If a client disconnected before its reply, send_response raised OSError and the server ended. The request loop now logs the error and waits for the next client. An error from accept() still ends the server.
This commit is contained in:
tim
2026-10-10 10:59:34 +02:00
parent 0abc8536f0
commit 6ee9bbaca8
2 changed files with 54 additions and 0 deletions
+46
View File
@@ -1006,6 +1006,52 @@ class TestServerMain:
assert "invalid request from" in capsys.readouterr().out
def test_client_that_disconnects_before_the_reply_does_not_end_the_server(self, tmp_path, capsys):
"""The reply to a closed connection fails. The next client must still get an answer."""
sock_path = str(tmp_path / "server_gone.sock")
ready, client_closed = threading.Event(), threading.Event()
def send_after_close(*args, **kwargs):
# Hold the reply until the client is gone, so that the send fails each time.
client_closed.wait(timeout=5)
return send_response(*args, **kwargs)
t = self._make_server_thread(sock_path, ready, threading.Event())
with patch("bmspy.server.send_response", send_after_close):
t.start()
ready.wait(timeout=2)
try:
sock = socket_module.socket(socket_module.AF_UNIX, socket_module.SOCK_STREAM)
sock.connect(sock_path)
payload = json.dumps({"command": "GET", "client": "gone"}).encode()
sock.sendall(struct.pack("!I", len(payload)) + payload)
sock.close()
client_closed.set()
response = self._send_command(sock_path, {"command": "REGISTER", "client": "test"})
assert response == {"status": "REGISTERED", "client": "test"}
assert t.is_alive()
finally:
client_closed.set()
self._stop_server(sock_path, t)
assert "socket ERROR: unable to encode response" in capsys.readouterr().out
def test_failed_accept_still_ends_the_server(self, tmp_path):
"""A listening socket that fails must not become an endless loop."""
sock_path = str(tmp_path / "server_accept.sock")
with patch("sys.argv", ["bmspy-server", "--socket", sock_path, "--device", "/dev/ttyUSB0"]), \
patch("bmspy.server.signal.signal"), \
patch("bmspy.server.atexit.register"), \
patch("bmspy.server.initialise_serial"), \
patch("bmspy.server.start_polling"), \
patch("bmspy.server.os.path.isdir", return_value=True), \
patch.object(socket_module.socket, "accept", side_effect=OSError("accept failed")) as accept:
from bmspy.server import main as server_main
with pytest.raises(OSError, match="accept failed"):
server_main()
assert accept.call_count == 1
class TestSendResponseDebug:
def test_debug_3_logs_sending(self, capsys):
srv, cli = socket_module.socketpair()