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: no4
;;; `with-async`, a bounded `upgrade-timeout-ms`, and a poll loop whose only5
;;; read primitive is `ws-receive-nowait`. `ws-connect` puts the transport in6
;;; 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 on8
;;; `(current-scheduler)` alone, which denied it, and the pump went deaf while9
;;; the server's frame sat unread in the kernel receive queue.10
;;;11
;;; The server sends its frame AFTER the 101, deliberately not coalesced with12
;;; it: a coalesced frame lands in `ws-connection-buffer` during the handshake13
;;; and would be decodable without any socket read at all, which is exactly14
;;; the case that hid this defect.15
;;;16
;;; Nothing here is TLS-specific. The guard was never about TLS, and this runs17
;;; over plain `ws://` so it needs no certificate and no `openssl` on PATH18
;;; (openssl is absent from the bare PATH on this estate's hosts, which is on19
;;; 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 it22
;;; is: the `wss://` arm. The guard expression is transport-independent, so the23
;;; logic under test is the same either way -- but the CONSEQUENCES are not24
;;; symmetric. On plain TCP, step 2 carries a second no-park gate25
;;; (`socket-ready?`); on TLS it does not, because `socket-ready?` raises on a26
;;; TLS connection, so the recorded non-blocking flag is the only thing keeping27
;;; that read from blocking. The TLS arm is therefore the one with the larger28
;;; blast radius if the flag is ever wrong, and it is the arm this test does29
;;; not exercise. It was verified by hand instead, against a local TLS server30
;;; and against the live Enclave, on 2026-09-04.31
(import (sigil core)32
(sigil time)33
(sigil websocket))35
(define port36
(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 conn41
(ws-connect (string-append "ws://127.0.0.1:" (number->string port) "/")42
connect-timeout-ms: 500043
handshake-timeout-ms: 500044
upgrade-timeout-ms: 5000))46
(unless conn47
(error "nowait pump client: upgrade did not complete"))49
;; Deliberately NOT inside with-async: the absence of a scheduler is the50
;; 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
(cond56
((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
(else66
(sleep 0.05)67
(loop)))))