"""rustdesk-server — real websocket handshakes against hbbs/hbbr's ws listeners. 21118 (hbbs) and 21119 (hbbr) are the servers' websocket endpoints — the surface a real RustDesk web client uses. A completed `101 Switching Protocols` upgrade is the app's own protocol answering (verified live against 1.1.12 and 1.1.16 during enrollment); a bare GET without upgrade headers gets NO response, so this can only pass against a genuinely serving listener, never a proxy or placeholder. """ from __future__ import annotations import socket import time WS_PORTS = {21118: "hbbs", 21119: "hbbr"} _REQ = ( b"GET / HTTP/1.1\r\n" b"Host: rustdesk\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" b"Sec-WebSocket-Version: 13\r\n" b"\r\n" ) def _handshake(port: int, timeout: float = 5.0) -> bytes: with socket.create_connection(("127.0.0.1", port), timeout=timeout) as s: s.sendall(_REQ) s.settimeout(timeout) return s.recv(200) def test_websocket_upgrades(meta): """Both ws endpoints complete an HTTP->websocket upgrade with 101 (poll per §5).""" deadline = time.time() + meta.HTTP_TIMEOUT pending = dict(WS_PORTS) while pending and time.time() < deadline: for port in list(pending): try: data = _handshake(port) except OSError: data = b"" if data.startswith(b"HTTP/1.1 101"): del pending[port] if pending: time.sleep(3) assert not pending, ( f"websocket endpoints did not complete 101 upgrades: {pending} " f"(waited {meta.HTTP_TIMEOUT}s)" )