Repository navigation
CWIST v3.7.1
Emergency patch for v3.7, cut from main at 07d042f8.
This release is not bug-fix-only. It carries everything merged to main since v3.7: the serious bug fixes below, and also new, still-experimental features. Treat the features as alpha: their APIs may change before v4.0.
Included experimental features (alpha)
- Rust bindings (issue #36):
cwist-sys, which holds raw FFI bindings generated by bindgen, with a struct-layout contract test. Also a safecwistcrate on top of it. These are not published to crates.io. - FFI out-of-line wrappers: exported wrappers for public
static inlinehelpers, so bindings can call them. - Per-route user context: the
cwist_app_*_ex()registration functions pass a context pointer to the handler. cwist_err.hlegacy enum kept in sync with the canonical header, needed by the bindings.
Fixes
1. HTTP/3 server crash (SIGSEGV) on the second connection and at shutdown
fix(build): rebuild liblsquic when the lsquic submodule pin moves
A build tree that had built liblsquic.a before the 2026-09-21 lsquic pin change kept linking that old archive against the new headers. struct lsquic_stream_if differs between the two revisions, so lsquic called callbacks through the wrong offsets and crashed when a client closed a connection. The archive is now built through a stamp keyed on the submodule commit. Fresh checkouts and release tarballs were not affected.
2. TLS connections starved: only a few dozen served at once
perf(https): park idle TLS connections instead of holding a pool thread, fix(https): use cwist_alloc/cwist_free in https_park
A pool thread waited in poll() on each idle TLS connection (30 s for HTTP/1.1 keep-alive, up to the HTTP/2 idle timeout). With the default C1M settings this capped concurrently served TLS connections at the number of HTTPS pool threads: 25 out of 395,729 held in a 1M-connection run. Idle TLS connections are now parked in a per-process epoll set and handed back to the pool when bytes arrive. HTTP/2 session state moves into the connection while it is parked. Measured through fly.board (12 workers): 1,000,000 / 1,000,000 held and served over TLS HTTP/1.1 and over TLS HTTP/2.
3. HTTP/2 deferred responses (cwist_async_defer) lost, stuck or freed twice
Browsers showed a blank page when the handler deferred its response over HTTP/2.
fix(h2): bind https_conn and client_fd to req and avoid double-free in async drain: HTTP/2 requests did not carry their connection, so the deferred-completion path could not route the response.fix(http2): do not prematurely destroy deferred req/res in h2_async_drain: the request and response were freed before the queued completion was sent.fix(http2): prioritize async queue wake event in h2_wait_readable: pending completions are sent before the connection blocks on the socket again.fix(http2): flush buffered frames at end of h2_send_response_hc: a response could sit in the batch buffer until the next frame arrived.
4. HTTP/2 content-length could disagree with the DATA payload (RFC 9113 §8.1.1)
fix(http2): enforce RFC 9113 content-length alignment with actual frame payload
A Content-Length set by the handler was forwarded as is, even when the body sent differed (for example after compression middleware ran). Strict clients treat that as a malformed response. content-length is now always derived from the body actually sent.
5. HTTP client kept intermediate redirect headers
fix(http_client): keep only the final response's headers when following redirects
New knobs (fix 2)
CWIST_HTTPS_IDLE_TIMEOUT_MS: idle budget for parked HTTP/1.1 TLS connections (default 30000, the previous blocking-read timeout).CWIST_HTTPS_PARK=0: disable parking and fall back to the previous behaviour.- Parking is off automatically when full GC is enabled. Rebuild code that includes
<cwist/net/http/https.h>.
Other changes
- README: the reactor and classic C10K–C1M tables were re-measured. Every held reactor connection costs about 24 KB; the older "a million connections in a quarter gigabyte" claim was removed. A TLS-at-C1M section was added.
compile_commands.jsonwas regenerated with repository-relative paths.- CI on
mainaligned withdev: workflows, the browser/WASM component harness, thetutorials-checkandcomponent-browser-testtargets, and strict durable-queue backend checks. - Release rules in
CONTRIBUTING.md: patch releases are cut frommain, and the notes list everything they carry. test_inline_exportslinks with-rdynamic, so the wrapper-symbol check also passes under GCC 14 LTO.