sigil-http / treeCHANGELOG.md
Changelog
All notable changes to sigil-http are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.18.2] - 2026-07-23
Added
http-fetch-bytes: a byte-faithful, in-memory HTTP fetch. It returns a response as{ status, headers, body }where the body is a raw bytevector and is never decoded to a string, so binary payloads (wasm modules, tarballs, images) round-trip uncorrupted. It preserves header order and duplicates (an ordered alist of lowercased-name/value pairs), so a relay can faithfully forward repeated headers likeSet-Cookie, and it does not follow redirects (a 3xx is returned as-is, which is what a faithful proxy needs). It handlesContent-Length, chunked, and connection-close framing, with a single-allocation body assembly.http-requeststays the string-oriented convenience path; reach forhttp-fetch-byteswhen the bytes must survive exactly.
[0.18.1] - 2026-07-11
Fixed
http-server-stopnow actually stops a running serve loop. The server record is immutable —http-server-startand the serve loop derive updated copies — so a caller that held the record it passed tohttp-server-startnever shared state with the loop, and stopping from another task silently did nothing (the loop kept running forever; with the loop now suspending on the scheduler, it would have kept a periodic wait alive indefinitely). The stop signal now lives in a one-slot vector created permake-http-serverand carried by reference through every derived record;http-server-stopsets it and the loop — whose waits are all deadline-bounded — sees it within the sweep interval, closes the sockets it owns, andhttp-server-startreturns. Stopping an idle server completes in ~100ms (probe-verified; hung forever before). Covered by phase 3 oftest/integration/run-starvation-test.sh.- Server loop no longer starves other async tasks' socket I/O. While at least one client was connected (e.g. a browser holding an SSE stream), the server loop's drain phase busy-spun a short-timeout native
socket-select. Because the async scheduler only polls socket io-waiters when its run queue is empty, that busy loop kept the queue perpetually non-empty and every OTHER task's socket read starved — first reads on outbound connections (IRC-style clients, websocket clients, IPC sockets) were delayed for as long as any HTTP client stayed connected, while timer- and channel-driven work kept running. The drain now suspends on its whole socket set (listen + clients) through the scheduler viaawait-readable-any, waking immediately on readable data and sweeping connection timeouts on a bounded deadline (1s), so sibling io-waiters are serviced within milliseconds. Regression test:test/integration/run-starvation-test.sh(compiled probe; measures a sibling socket's first-read latency with a client parked on the server).
Requires sigil 0.17.10 or newer (the first release providing (sigil async) await-readable-any); the package's sigil: requirement is bumped to ^0.17.10 accordingly.
[0.18.0] - 2026-07-10
Added
- Persistent connections (keep-alive). Non-streaming HTTP/1.1 responses now keep the TCP connection open and serve subsequent requests on the same socket instead of forcing
Connection: closeafter every response. The server honors an explicitConnection: closeand defaults HTTP/1.0 to close; the age-based timeout is refreshed per request so it acts as an idle timeout for kept-alive connections. This removes the connection-churn that amplified image-heavy page loads. - Chunked response transfer-encoding. Streaming responses whose length is unknown (SSE and bare procedure bodies) are now framed with
Transfer-Encoding: chunkedand a terminating0\r\n\r\n, instead of relying on connection close for framing.http-response/filekeeps itsContent-Lengthframing (byte-exact). Range:requests and206 Partial Content.http-response/fileaccepts arange:keyword (the raw requestRangeheader) and honors byte ranges:bytes=A-B, open-endedbytes=A-, and suffixbytes=-N. Satisfiable ranges yield206withContent-Range; unsatisfiable ranges yield416 Range Not SatisfiablewithContent-Range: bytes */<total>; an absent or malformed header falls back to a full200. Adds theHTTP-RANGE-NOT-SATISFIABLEconstant and exportsresolve-range.
Notes
- Reusing a streamed connection for keep-alive is not yet supported — a streaming response still closes when the stream ends (the select loop would need to re-adopt the goroutine-owned socket). HTTP request pipelining is also unsupported. Both are tracked follow-ups.
[0.17.0] - 2026-07-10
Changed
- Streaming responses (
http-response/file, SSE) now work from a barehttp-serve/http-server-startwithout wrapping the call inwith-async. The server establishes its own async scheduler when none is active, and reuses the caller's when one is present. This fixes streaming and SSE routes throwinggo: not running in an async contexton a default server, which caused large assets to intermittently fail to download. HEADrequests now return identical status and headers (including a correctContent-Length) with zero body bytes, instead of sending the full body.
Added
- Opt-in automatic gzip via a
gzip:option onmake-http-server/http-serve(default#f, so the default server is unchanged). When enabled, non-streaming responses are gzip-encoded per the request'sAccept-Encoding(compressible content types over a size floor), addingContent-Encoding: gzipandVary: Accept-Encoding. Streaming and incompressible bodies are left untouched.
[0.16.7] - 2026-07-07
Changed
- Realigned the sigil toolchain pin to
^0.17. http-response/jsonnow accepts either a dict or an already-encoded string.http-response/sse-broadcastdefaults its message handler to identity.
[0.16.6] - 2026-06-30
Fixed
- HTTP client
timeout:callers can now distinguish a request that was never sent from one that was delivered but whose ack is unconfirmed.
[0.16.5] - 2026-06-30
Changed
- Threaded a
connect-timeout:option through the HTTP client and relocked onto sigil-tls 0.16.2.
[0.16.4] - 2026-06-30
Fixed
- Response reads now terminate on HTTP framing rather than on connection close.
[0.16.3] - 2026-06-29
Added
- Opt-in
timeout:keyword for HTTP client reads.
[0.16.2] - 2026-06-24
Changed
- Repointed sigil-test dev-dependencies at the reintegrated monorepo.
[0.16.1] - 2026-05-31
Added
http-response-gzipresponse helper (gzip content negotiation).
Changed
- Response send loop rewritten to be O(n) via offset-based partial writes, avoiding O(n²) recopying of large bodies over slow links.
[0.16.0] - 2026-05-06
Initial tagged release of the standalone sigil-http package, extracted from the sigil monorepo (requires sigil ^0.16).
Fixed
- Non-blocking HTTP response writes.
- HTTP request body byte handling (Content-Length counted in bytes).
Added
- Server crash diagnostics and guard blocks around the handler, streaming goroutine, and server loop; an outer server-loop guard that restarts on an otherwise-escaping exception.
- SSE heartbeat utility for dead-client detection.
[0.17.0]: https://codeberg.org/sigil/sigil-http/compare/v0.16.7...v0.17.0 [0.16.7]: https://codeberg.org/sigil/sigil-http/compare/v0.16.6...v0.16.7 [0.16.6]: https://codeberg.org/sigil/sigil-http/compare/v0.16.5...v0.16.6 [0.16.5]: https://codeberg.org/sigil/sigil-http/compare/v0.16.4...v0.16.5 [0.16.4]: https://codeberg.org/sigil/sigil-http/compare/v0.16.3...v0.16.4 [0.16.3]: https://codeberg.org/sigil/sigil-http/compare/v0.16.2...v0.16.3 [0.16.2]: https://codeberg.org/sigil/sigil-http/compare/v0.16.1...v0.16.2 [0.16.1]: https://codeberg.org/sigil/sigil-http/compare/v0.16.0...v0.16.1 [0.16.0]: https://codeberg.org/sigil/sigil-http/releases/tag/v0.16.0