AtlatestRepositorysigil-websocket

sigil-websocket / tree / test / integrationnowait-pump-client.sgl

1;;; Regression: a nowait-only pump with NO scheduler must still hear the peer.
2;;;
3;;; This is the shape `slate node init` uses, and the shape that stalled: no
4;;; `with-async`, a bounded `upgrade-timeout-ms`, and a poll loop whose only
5;;; read primitive is `ws-receive-nowait`. `ws-connect` puts the transport in
6;;; non-blocking mode whenever a scheduler OR an upgrade timeout is present,
7;;; so nowait's single socket read is safe here -- but it used to be gated on
8;;; `(current-scheduler)` alone, which denied it, and the pump went deaf while
9;;; the server's frame sat unread in the kernel receive queue.
10;;;
11;;; The server sends its frame AFTER the 101, deliberately not coalesced with
12;;; it: a coalesced frame lands in `ws-connection-buffer` during the handshake
13;;; and would be decodable without any socket read at all, which is exactly
14;;; the case that hid this defect.
15;;;
16;;; Nothing here is TLS-specific. The guard was never about TLS, and this runs
17;;; over plain `ws://` so it needs no certificate and no `openssl` on PATH
18;;; (openssl is absent from the bare PATH on this estate's hosts, which is on
19;;; its own enough to keep a TLS fixture out of a gate).
20;;;
21;;; WHAT THIS TEST DOES NOT COVER, stated so nobody reads it as broader than it
22;;; is: the `wss://` arm. The guard expression is transport-independent, so the
23;;; logic under test is the same either way -- but the CONSEQUENCES are not
24;;; symmetric. On plain TCP, step 2 carries a second no-park gate
25;;; (`socket-ready?`); on TLS it does not, because `socket-ready?` raises on a
26;;; TLS connection, so the recorded non-blocking flag is the only thing keeping
27;;; that read from blocking. The TLS arm is therefore the one with the larger
28;;; blast radius if the flag is ever wrong, and it is the arm this test does
29;;; not exercise. It was verified by hand instead, against a local TLS server
30;;; and against the live Enclave, on 2026-09-04.
31(import (sigil core)
32 (sigil time)
33 (sigil websocket))
35(define port
36 (or (and (getenv "SIGIL_WS_TEST_PORT")
37 (string->number (getenv "SIGIL_WS_TEST_PORT")))
38 (error "nowait pump client requires SIGIL_WS_TEST_PORT")))
40(define conn
41 (ws-connect (string-append "ws://127.0.0.1:" (number->string port) "/")
42 connect-timeout-ms: 5000
43 handshake-timeout-ms: 5000
44 upgrade-timeout-ms: 5000))
46(unless conn
47 (error "nowait pump client: upgrade did not complete"))
49;; Deliberately NOT inside with-async: the absence of a scheduler is the
50;; condition under test, so wrapping this would silently retire the regression.
51(define deadline (+ (current-milliseconds) 5000))
53(let loop ()
54 (let ((msg (ws-receive-nowait conn)))
55 (cond
56 ((ws-message? msg)
57 (unless (string=? (ws-message-data msg) "HELLO-FROM-SERVER")
58 (error "nowait pump client: unexpected payload" (ws-message-data msg)))
59 (display "native-websocket-nowait-pump-ok")
60 (newline))
61 ((eq? msg 'closed)
62 (error "nowait pump client: connection closed before the frame arrived"))
63 ((> (current-milliseconds) deadline)
64 (error "nowait pump client: no frame after 5000ms (nowait pump went deaf)"))
65 (else
66 (sleep 0.05)
67 (loop)))))