AtlatestRepositorysigil-http

sigil-http / tree / test / integrationstreaming-server-main.sgl

1;;; (t1-streaming-server main) - integration test server for the T1 fix.
2;;;
3;;; Exercises the streaming response paths (`http-response/file` and
4;;; `http-response/sse`), which spawn a `go` goroutine and therefore require a
5;;; live async scheduler. The point of T1 is that these now work from a BARE
6;;; `http-serve` (no surrounding `with-async`).
7;;;
8;;; Usage: t1-streaming-server <port> <asset-path> [bare|wrapped]
9;;;
10;;; bare -> (http-serve handler ...) with NO with-async (default). This is
11;;; the case the T1 fix makes work: http-serve self-installs a
12;;; scheduler so the streaming `go` succeeds.
13;;; wrapped -> (with-async (go (http-server-start server))) — the existing
14;;; consumer pattern (mirrors live-crafter). Must keep working
15;;; unchanged (no nested/duplicate scheduler).
17(define-library (t1-streaming-server main)
18 (import (sigil core)
19 (sigil io)
20 (sigil process)
21 (sigil async)
22 (sigil http server)
23 (sigil http request)
24 (sigil http response))
26 (export main)
28 (begin
30 ;; Asset path captured from argv at startup.
31 (define *asset-path* (make-parameter "/dev/null"))
33 ;; A compressible text body comfortably over the gzip size floor (1 KB).
34 (define big-text
35 (let loop ((n 0) (acc ""))
36 (if (>= n 200)
37 acc
38 (loop (+ n 1)
39 (string-append acc "The quick brown fox jumps over the lazy dog. ")))))
41 (define (handler request)
42 (let ((path (http-request-path request)))
43 (cond
44 ((string=? path "/hello")
45 (http-response/text HTTP-OK "Hello, World!"))
46 ;; Large compressible text — exercises opt-in gzip (T11).
47 ((string=? path "/big-text")
48 (http-response/text HTTP-OK big-text))
49 ;; Streaming file — the "image sometimes doesn't download" path.
50 ((string=? path "/file")
51 (http-response/file (*asset-path*)))
52 ;; Server-Sent Events — five events then close.
53 ((string=? path "/sse")
54 (http-response/sse
55 (lambda (send close)
56 (let loop ((i 1))
57 (if (<= i 5)
58 (begin
59 (send "tick" (number->string i))
60 (loop (+ i 1)))
61 (close))))))
62 (else (http-response/not-found)))))
64 ;;; Modes:
65 ;;; bare -> (http-serve ...) no with-async (T1 fix case; default)
66 ;;; wrapped -> (with-async (go (http-server-start ...))) consumer pattern
67 ;;; gzip -> bare + gzip: #t (opt-in gzip; T11)
68 (define (main)
69 (let* ((args (cdr (command-line)))
70 (port (string->number (list-ref args 0)))
71 (asset (list-ref args 1))
72 (mode (if (>= (length args) 3) (list-ref args 2) "bare")))
73 (*asset-path* asset)
74 (cond
75 ((string=? mode "wrapped")
76 ;; Existing-consumer regression path: the server runs as a
77 ;; goroutine inside a caller-owned scheduler. http-server-start
78 ;; must reuse that scheduler rather than nest a second one.
79 (let ((server (make-http-server handler port: port host: "127.0.0.1")))
80 (with-async (go (http-server-start server)))))
81 ((string=? mode "gzip")
82 ;; Opt-in gzip enabled (T11). Still a bare call (T1 fix applies).
83 (http-serve handler port: port host: "127.0.0.1" gzip: #t))
84 (else
85 ;; Bare path: no with-async at the call site (the T1 fix case).
86 (http-serve handler port: port host: "127.0.0.1")))))))