AtlatestRenderedmarkdown
Readme

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 like Set-Cookie, and it does not follow redirects (a 3xx is returned as-is, which is what a faithful proxy needs). It handles Content-Length, chunked, and connection-close framing, with a single-allocation body assembly. http-request stays the string-oriented convenience path; reach for http-fetch-bytes when the bytes must survive exactly.

[0.18.1] - 2026-07-11

Fixed

  • http-server-stop now actually stops a running serve loop. The server record is immutable — http-server-start and the serve loop derive updated copies — so a caller that held the record it passed to http-server-start never 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 per make-http-server and carried by reference through every derived record; http-server-stop sets it and the loop — whose waits are all deadline-bounded — sees it within the sweep interval, closes the sockets it owns, and http-server-start returns. Stopping an idle server completes in ~100ms (probe-verified; hung forever before). Covered by phase 3 of test/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 via await-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: close after every response. The server honors an explicit Connection: close and 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: chunked and a terminating 0\r\n\r\n, instead of relying on connection close for framing. http-response/file keeps its Content-Length framing (byte-exact).
  • Range: requests and 206 Partial Content. http-response/file accepts a range: keyword (the raw request Range header) and honors byte ranges: bytes=A-B, open-ended bytes=A-, and suffix bytes=-N. Satisfiable ranges yield 206 with Content-Range; unsatisfiable ranges yield 416 Range Not Satisfiable with Content-Range: bytes */<total>; an absent or malformed header falls back to a full 200. Adds the HTTP-RANGE-NOT-SATISFIABLE constant and exports resolve-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 bare http-serve / http-server-start without wrapping the call in with-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 throwing go: not running in an async context on a default server, which caused large assets to intermittently fail to download.
  • HEAD requests now return identical status and headers (including a correct Content-Length) with zero body bytes, instead of sending the full body.

Added

  • Opt-in automatic gzip via a gzip: option on make-http-server / http-serve (default #f, so the default server is unchanged). When enabled, non-streaming responses are gzip-encoded per the request's Accept-Encoding (compressible content types over a size floor), adding Content-Encoding: gzip and Vary: 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/json now accepts either a dict or an already-encoded string.
  • http-response/sse-broadcast defaults 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-gzip response 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