#!/usr/bin/env python3 """Read-only candidate checks. No login, database operation or task publication.""" import argparse import base64 import hashlib import os import socket import threading import time import uvicorn import wsproto # noqa: F401 - required, pinned WebSocket transport async def probe_app(scope, receive, send): if scope["type"] != "websocket": raise RuntimeError("The probe must be handled as a WebSocket") if (await receive())["type"] != "websocket.connect": raise RuntimeError("Missing WebSocket connection event") await send({"type": "websocket.accept"}) await send({"type": "websocket.send", "text": "eusrr-ready"}) await send({"type": "websocket.close", "code": 1000}) def handshake(port, path, expected_status): key = base64.b64encode(os.urandom(16)).decode("ascii") with socket.create_connection(("127.0.0.1", port), timeout=5) as client: client.settimeout(5) client.sendall(( f"GET {path} HTTP/1.1\r\nHost: 127.0.0.1:{port}\r\n" "Connection: Upgrade\r\nUpgrade: websocket\r\n" "Sec-WebSocket-Version: 13\r\n" f"Sec-WebSocket-Key: {key}\r\n\r\n" ).encode("ascii")) response = b"" while b"\r\n\r\n" not in response: chunk = client.recv(4096) if not chunk or len(response) > 16384: raise RuntimeError("Incomplete WebSocket handshake") response += chunk headers, payload = response.split(b"\r\n\r\n", 1) if int(headers.split(b" ", 2)[1]) != expected_status: raise RuntimeError(f"Unexpected WebSocket HTTP status (expected {expected_status})") if expected_status == 101: accept = base64.b64encode(hashlib.sha1( (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode("ascii") ).digest()) fields = dict(line.split(b":", 1) for line in headers.split(b"\r\n")[1:] if b":" in line) fields = {name.lower(): value.strip() for name, value in fields.items()} if fields.get(b"sec-websocket-accept") != accept: raise RuntimeError("Invalid WebSocket upgrade response") expected_frame = b"\x81\x0beusrr-ready" while len(payload) < len(expected_frame): chunk = client.recv(4096) if not chunk: raise RuntimeError("Missing WebSocket probe frame") payload += chunk if not payload.startswith(expected_frame): raise RuntimeError("Invalid WebSocket probe frame") def check_transport(): # Exercise Uvicorn's actual HTTP-to-WebSocket upgrade, not just an import. listener = socket.socket() listener.bind(("127.0.0.1", 0)) listener.listen(4) server = uvicorn.Server(uvicorn.Config( probe_app, ws="wsproto", lifespan="off", log_level="error", access_log=False, )) thread = threading.Thread(target=server.run, kwargs={"sockets": [listener]}, daemon=True) thread.start() try: deadline = time.monotonic() + 5 while not server.started and thread.is_alive() and time.monotonic() < deadline: time.sleep(0.05) if not server.started: raise RuntimeError("Uvicorn WebSocket probe did not start") handshake(listener.getsockname()[1], "/", 101) finally: server.should_exit = True thread.join(timeout=5) listener.close() if thread.is_alive(): raise RuntimeError("Uvicorn WebSocket probe did not stop") def main(): parser = argparse.ArgumentParser() parser.add_argument("--app-port", type=int, choices=(9001, 9002)) args = parser.parse_args() check_transport() if args.app_port: # Anonymous access must be rejected. No ticket is minted or consumed; # authentication is never bypassed for a deployment health check. handshake(args.app_port, "/ws/", 403) print("WebSocket transport and requested candidate checks passed.") if __name__ == "__main__": main()