From c1fd0fea0bd3df8fae8f5f61831e79f7feb9f384 Mon Sep 17 00:00:00 2001 From: JeffreyChen Date: Fri, 19 Jun 2026 01:54:46 +0800 Subject: [PATCH] Honor the caller's connect timeout for the viewer handshake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RemoteDesktopViewer.connect floored the auth-handshake socket timeout at max(60s, timeout), so an explicit short timeout (e.g. 2s) was ignored. When a plain viewer hit a TLS host (or a plain viewer hit a WS host) the two sides each blocked for 60s on a handshake that never completed — racing the 60s test timeout and intermittently hanging the suite. Bound the handshake by the caller's timeout instead (the handshake is a tiny HMAC exchange, so the connect budget is ample; callers needing longer pass a larger timeout). Drop the now-unused auth-timeout constant and tighten the WebSocket rejection test to a 2s budget so both mismatch tests fail fast and deterministically. --- je_auto_control/utils/remote_desktop/viewer.py | 13 +++++++------ .../headless/test_remote_desktop_websocket.py | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/je_auto_control/utils/remote_desktop/viewer.py b/je_auto_control/utils/remote_desktop/viewer.py index 64158322..6d9fbd25 100644 --- a/je_auto_control/utils/remote_desktop/viewer.py +++ b/je_auto_control/utils/remote_desktop/viewer.py @@ -31,7 +31,6 @@ ChatCallback = Callable[[str, str], None] ErrorCallback = Callable[[Exception], None] -_DEFAULT_AUTH_TIMEOUT_S = 60.0 _DEFAULT_CONNECT_TIMEOUT_S = 5.0 _NOT_CONNECTED_MESSAGE = "viewer is not connected" @@ -190,11 +189,13 @@ def connect(self, timeout: float = _DEFAULT_CONNECT_TIMEOUT_S) -> None: raw_sock = socket.create_connection( (self._host, self._port), timeout=timeout, ) - # If the caller explicitly asked for a longer connect budget, - # honor it for the handshake too — otherwise a slow remote (CI - # runners, high-latency links) trips the 5 s default before the - # caller's window expires. - raw_sock.settimeout(max(_DEFAULT_AUTH_TIMEOUT_S, float(timeout))) + # Bound the auth handshake by the caller's timeout. A short, explicit + # timeout must be honored — e.g. a plain viewer hitting a TLS host has + # to fail fast instead of blocking on a handshake that never + # completes. The handshake is a tiny HMAC exchange, so the connect + # budget is ample; callers needing longer simply pass a larger + # timeout. + raw_sock.settimeout(float(timeout)) try: sock = self._maybe_wrap_tls(raw_sock) channel = self._build_channel(sock) diff --git a/test/unit_test/headless/test_remote_desktop_websocket.py b/test/unit_test/headless/test_remote_desktop_websocket.py index bbaffb42..935d6ad6 100644 --- a/test/unit_test/headless/test_remote_desktop_websocket.py +++ b/test/unit_test/headless/test_remote_desktop_websocket.py @@ -242,7 +242,7 @@ def test_plain_tcp_viewer_against_ws_host_is_rejected(): host="127.0.0.1", port=host.port, token="tok", ) with pytest.raises((OSError, AuthenticationError)): - viewer.connect(timeout=30.0) + viewer.connect(timeout=2.0) assert _wait_until(lambda: host.connected_clients == 0) finally: host.stop(timeout=1.0)