Releases: ehsanmok/flare
Release list
v0.10.0
flare v0.10.0 moves the toolchain onto Mojo 1.0.0 and MAX 26.5.0 (both stable, out of nightly), closes the remaining h2spec conformance gaps, and lands a breaking cut of the prelude. 70 commits since v0.9.0.
Toolchain
- Mojo pin widened to
>=1.0.0,<1.1.0; MAX pin to>=26.5.0. Both now resolve off the stablemaxconda channel instead ofmax-nightly. json,mojodoc, andmozzare pinned to their own new tagged releases (v0.3.0,v0.1.0,v0.2.0) instead of trackingmain, so aflarecheckout resolves deterministically.- The API renames that came with 1.0.0 are done throughout: pointer arithmetic and
.load/.free/memcpyon theirunsafe_names,.bitcast[]to.unsafe_bitcast[],__del__to__deinit__,ImplicitlyDestructibletoDeinitable, and thereadargument convention toimm.
Breaking: prelude cut from 454 symbols to 125
flare.prelude re-exported everything, including wire-format internals (encode_varint, qpack_encode_field_section, huffman_encode, ...). It now exports exactly what the root flare package exports, generated from that list rather than hand-maintained. WithCancel, ok_json, Body, and ChunkSource moved into the root surface, since their absence there was the actual bug. Migration: import from the owning module -- from flare.http2 import Http2Connection, from flare.quic import encode_varint, from flare.runtime import Reactor.
HTTP/2 conformance
h2spec now runs for real in the conformance CI job instead of being advertised-only. The gaps it found are closed: connection errors are reported with GOAWAY and frame shape is validated, header blocks decode atomically, DATA/WINDOW_UPDATE enforce stream state and flow control, and buffered responses respect the peer's send window.
HTTP client and streaming
HttpClient.get_streaming_tls()-- streaming download over HTTPS;get_streaming()previously raised there and pointed callers at the bufferedget().- Chunked request bodies decode on the
Handlerpath. - Streaming responses coalesce chunks per pump / per writable edge instead of one write per chunk, with a bulk-copy on the framing path -- a latency and syscall-count win on the streaming benchmark.
Fixes
- WebSocket: RSV1 without a negotiated extension is now rejected (RFC 6455).
- SSE: the idle keep-alive heartbeat is rate-limited.
- ASan:
test_h3_0rtt_e2e,test_tls_acceptor, andtest_tls_server_ffiretry up to three times on the forked-loopback timing flake instead of trusting a single run.
Server
bind_many() (multiple bind addresses) and num_workers>=2 can now be combined -- previously raised. Each (address, worker) pair gets its own SO_REUSEPORT listener.
Docs and testing
docs/features.md and docs/architecture.md corrected against what the code actually does (streaming chunk-coalescing contract, the retired io_uring bufring 64-conn crash claim, the prelude surface). tests/test_prelude_surface.mojo is a new ratchet asserting the prelude and root package export the same entities. A new interop test exercises flare's client against a server it did not write (and vice versa). Comments and docstrings across the codebase, tests, and README also got a pass: stale pre-1.0 nightly/1.0.0b1/1.0.0b2/rc0 version tags are gone, and where the underlying limitation is still real the note stays, just without the dated label.
Known limits (tracked for v0.10.x)
- QUIC server-side loss-driven retransmit and send pacing are still not wired -- design landed (
docs/superpowers/specs/2026-08-11-quic-server-loss-recovery-design.md), implementation pending. get_streaming_tls()(download) andsend_chunked()(upload) still pin ALPN tohttp/1.1-- design for h2/h3 negotiation landed (docs/superpowers/specs/2026-08-11-http-client-streaming-h2-h3-design.md, tracked as #5), implementation pending.- The HTTP/3 client still rejects request bodies larger than one packet.
- The io_uring bufring handler stays opt-in (
FLARE_BUFRING_HANDLER=1).
Full inventory in docs/features.md.
Full Changelog: v0.9.0...v0.10.0
v0.9.0
flare v0.9.0 is the release where a single Handler serves HTTP/1.1, h2c, HTTP/2, and HTTP/3 at once, and HttpClient speaks all four back. 149 commits since v0.8.1. The headline is the HTTP/3 client and the QUIC hardening that makes it hold up on a real network, but the cycle also closed the ergonomics gap the wire work had opened: streaming responses through the normal handler path, full gRPC, WebSocket over HTTP/2, sessions, OpenAPI, and reliability middleware.
This is a minor bump. Existing serve / Handler / ChunkSource call sites compile unchanged; everything below is additive.
HTTP/3 and QUIC
HttpClient(prefer_http3=True)/.with_prefer_h3()dials HTTP/3 over QUIC, discovers origins viaAlt-Svc(RFC 7838), and races h3 against h2/h1 with happy-eyeballs so a dead QUIC path never costs a timeout. Connections pool and multiplex per origin; idempotent requests can ride 0-RTT and replay at 1-RTT if the server rejects early data.- QUIC server: Retry issuance plus client-side Retry handling (RFC 9000 sec 8.1), connection migration with a strict egress hold, RFC 9221 datagrams, stateless reset on unknown DCIDs, and structural PTO / ack-delay timers.
flare.quic.ccships NewReno, CUBIC, and HyStart++; the 1-RTT path runs an RTT estimator and ACK-based loss detection (RFC 9002).- Batched UDP I/O:
recvmmsgingress on by default,sendmmsg/ GSO egress available, behind an ENOSYS fallback. - On the gate workload flare HTTP/3 runs at 74,653 req/s, about 2.9% ahead of quiche 0.22.
Streaming-proxy surface
The shape an inference or reverse-proxy front needs, without a raw reactor loop or pointer smuggling. StreamHandler + StreamConn give you a typed lifecycle and framework-owned per-connection state; UpstreamChunkSource streams a body off a reactor-registered fd; FrameMux multiplexes logical streams over one Unix socket; watermark backpressure couples upstream reads to downstream writability. serve_streaming runs it single- or multi-worker. A complete single-upstream relay is on_open (attach a source) plus on_upstream (conn.relay_upstream()). ByteReader / ByteWriter replace raw-pointer framing.
gRPC
All four server shapes mount on HttpServer: unary, server-streaming (incrementally flushed, one DATA frame per message), client-streaming, and bidi. grpc-timeout deadlines are enforced and gzip message compression is negotiated. tools/proto_gen.py emits proto3 messages and service-block code (server trait, per-RPC adapters, typed client stub, serialized FileDescriptorProto). Server reflection answers list_services, file_by_filename, and file_containing_symbol; grpc.health.v1.Health ships Check and Watch. The client covers unary plus server-, client-, and bidi-streaming. Maps and oneof in the message codegen are the one remaining gap.
WebSocket
- WebSocket over HTTP/2 (RFC 8441 Extended CONNECT), client and server, including reactor sidecar dispatch (
WsH2Handler+HttpServer.serve[H, W]). - Stateful server handlers via the
WsHandlertrait andWsServer.serve[H]. permessage-deflatecontext-takeover (RFC 7692 §7.1) with a persistent compressor pair.WsAutoClientpicks the carrier wire (h1 or h2) from the negotiated ALPN and drives the handshake.
HTTP client
Connection pooling keyed on (scheme, host, port), with a TLS pool for HTTPS keep-alive. RequestBuilder for per-request method/headers/query/body, MultipartFormBuilder for multipart/form-data, chunked streaming upload (send_chunked), and streaming download (get_streaming) that reads the body in bounded memory. Redirect policies, a cookie jar, retry with backoff, and transparent gzip/deflate/brotli decompression bounded by a 16 MiB zip-bomb cap. HTTP proxy support via CONNECT tunnels and the HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars.
Routing, state, sessions, OpenAPI, caching
Routeris nowDefaultableand composes with stock middleware;Router.mount(prefix, sub)mounts sub-routers.State[T]carries registration-time state (a DB pool, config) beside request extractors, the analogue of axum'sState(db). TypedJson[T]/Form[T]/JsonAs[T]/Sessionextractors landed.- Sessions gained a pluggable
SessionBackendwith TTL expiry, CSPRNG session ids, and signed-cookie carriers. spec_from_routerderives an OpenAPI 3.1 spec by walking a runtimeRouter.- RFC 9111 HTTP caching:
Cache[Inner, S]middleware, a directive parser, and an in-memory store.
Reliability and errors
RateLimit, CircuitBreaker, and PostHocDeadline middleware. DeadlineWatchdog flips a Cancel cell from a background thread, so cooperative handlers get real mid-flight deadline enforcement. Typed errors map to status now: ValidationError to 400, AuthError to 401/403, plus unauthorized() and forbidden() builders.
Security
The H2 DoS mitigations are enforced in code now, not advertised-only: max_header_list_size on HEADERS+CONTINUATION returns RST with ENHANCE_YOUR_CALM, a CONTINUATION frame cap (CVE-2024-27316), and an RST_STREAM flood answered with GOAWAY (CVE-2023-44487). The client decompression path is bounded by a 16 MiB cap.
Testing and fuzzing
62 fuzz harnesses (58 fuzz + 4 property), 9M+ runs combined, zero known crashes, an ASan CI gate, and h1/ws conformance corpora. H2cTestClient[H] exercises the HTTP/2 handler path in-process without TLS.
Known limits (tracked for v0.9.x)
- QUIC server-side loss-driven retransmit and send pacing are not wired yet; the bench gate is met without them.
HttpClient.get_streaming()raises on HTTPS; use bufferedget()there.- The HTTP/3 client rejects request bodies larger than one packet.
- The io_uring bufring handler stays opt-in (
FLARE_BUFRING_HANDLER=1).
Full inventory in docs/features.md.
Full Changelog: v0.8.1...v0.9.0
v0.8.1
Patch release on top of v0.8.0.
Fixes
- deps:
recipe.yamlnow pulls thejsonv0.2.1 source tag for Mojo 1.0.0b2 compatibility (#4 — thanks @bowyern).
Build
- Bump package version to
0.8.1inpixi.tomlandrecipe.yaml.
No flare source or API changes — v0.8.0 consumers can move to v0.8.1 without code changes.
Full Changelog: v0.8.0...v0.8.1
v0.8.0
flare v0.8.0
A protocol-surface release: flare gains a sans-I/O QUIC v1 + HTTP/3
stack, a gRPC unary server, HTTP caching and reliability
middleware, and a full migration to the Mojo 1.0.0b2 toolchain. 214
commits since v0.7.0.
QUIC v1 (sans-I/O core + reactor)
- Wire codecs: variable-length integers (RFC 9000 §16), long/short
packet headers (§17), transport-frame codec (§19), and transport
parameters (§18). - State machines: per-stream (§3) and per-connection (§10) lifecycle in
flare.quic.state. - Crypto: pluggable
QuicCryptotrait with RFC 9001 initial-secret
math and anOpenSslQuicCryptoproduction backend — AEAD
(RFC 9001 §5.3) and header-protection mask (§5.4) via dedicated
flare_quic_aead_*/flare_quic_hp_maskFFI thunks. - Server I/O: UDP-listener bind with per-datagram dispatch,
QuicConnection.handle_packetwired end-to-end, and a PTO / idle /
ack-delayTimerWheel. - Optional rustls QUIC backend: a
rustls_wrapperRust crate +
build_rustls.sh, FFI binding, and live-fire handshake fixtures
(incl. miri). - Coverage: loopback integration plus handshake/CID and crypto fuzz
harnesses; RFC 9001 Appendix A conformance vectors.
HTTP/3 + QPACK
- Frame codec + SETTINGS payload (RFC 9114 §7).
- Sans-I/O drivers:
H3Connection, a request-stream reader state
machine, and a response-stream writer. - Server path: peer uni-stream dispatch, SETTINGS/GOAWAY consumption,
and thefeed_stream_chunk -> take_request -> emit_response -> take_response_framesloop. - QPACK static-table encoder/decoder (RFC 9204).
- Conformance round-trip suite +
fuzz_h3_server(200K execs).
ALPN protocol routing
- Wire-protocol routing decision layer;
http.server.bind_with_h3
opens the UDP listener and routes by ALPN; ALPN-driven WebSocket
dispatch (WsClient.connect_prefer_h2, auto-client wire dispatcher).
gRPC (unary)
- LPM framing codec + canonical status codes;
GrpcServerAdapter
bridges unary RPCs over HTTP/2. - Typed surface:
GrpcRequestHeaders,GrpcUnaryReplywith ok/err
factories;grpc-status-details-binbase64 trailers; binary/text
metadata key discipline. - Hardening:
run_unary_callnever raises (errors map to typed
outcomes), case-insensitivete: trailers,fuzz_grpc_lpm_decoder.
HTTP caching & middleware
- RFC 9111
Cache-Controldirective parser + bounded store; a
Cache[Inner, S]middleware with two-levelVarylookup and
is_freshfreshness checks. Retry+Timeoutreliability middleware (RFC 9110-aligned backoff).H1LeniencyConfignamed relaxation flags wired into the H1 parser.
WebSocket
- permessage-deflate context-takeover (persistent LZ77 across
messages). - RFC 6455 §5 conformance fixture corpus + runner; forked-server I/O
round-trip suite.
Developer surface
- Template inheritance:
{% block %}/{% extends %}. - OpenAPI 3.1 spec model + deterministic JSON emitter.
TestClient[H]for in-process handler exercises.
Performance
- HPACK Huffman: table-driven decoder, 3.6x-4.2x over the scalar
path. - One-pass exact-length response serialization; per-header
to_lowercaseallocation removed; UTF-8 validation bypassed on the
H1 parse hot path. - No regression vs Go
net/httpand nginx (single-worker) or
hyper/axum/actix-web (multi-worker); per-percentile sigma published in
docs/benchmark.md.
Correctness & hardening
- HTTP client: reject 16-digit chunk-size to stop a signed-
Int
overflow driving an out-of-bounds read in the chunked decoder. - HTTP parser: sanitize unvalidated request-line bytes before echoing
them in error messages. - TLS FFI: keep owned path/SNI buffers live across OpenSSL calls;
NUL-terminate cert/key/CA paths; install the rustls QUIC cdylib as
.dylibon macOS. - Auth: forward caller-supplied
Authorizationon H1 and h2c-upgrade
paths (needed for S3 / AWS SigV4). - io_uring: size SQE/CQE mmaps from the entry count, not the
ring-entries offset. - HTTP/2: stop
take_completed_streamsre-dispatching already-served
streams; stream slab keyed onstream_idwith a fast-path table. - New fuzz harnesses across quic (varint, long-header,
transport_params), qpack, h3 (frame), grpc, and cache-control; h1
wire-grammar conformance corpus.
Structure (v0.8 decomposition)
- A 1000-line-per-module bar enforced by
tools/check_reactor_size.sh. Split the server reactor into epoll +
uring modules, relocated unified-reactor dispatch, extracted client
parse/send, the io_uring ABI codec, quic/http2 value types, and the
extractor parsers. - Lifecycle: migrated
__copyinit__/__moveinit__to__init__
overloads; removed stub-only public surfaces and a stale
prelude.mojoshadow.
Mojo 1.0.0b2 migration
reflect[T]alias form, non-nullableUnsafePointer,
MutExternalOrigin->MutUntrackedOrigin, andStringSlice
construction viaCStringSlice.- Dependencies pinned to json v0.2.1 and mozz v0.1.4; quiche
baseline bumped 0.22.0 -> 0.24.5.
v0.7
First stable release on Mojo 1.0.0b1. v0.7 closes the v0.6 deferred backlog and ships the production-shape v0.7 line items end-to-end: HTTP/2 is now a first-class wire on the same HttpServer / HttpClient, the reactor gains an opt-in io_uring backend, the application layer fills in (sessions, signed cookies, structured logging, Prometheus metrics, SSE, PROXY-protocol, mTLS, UDS), the bench harness now self-detects the saturation cliff before publishing numbers, and the per-percentile σ over 5 runs is the canonical honesty meter for the tail.
151 commits since v0.6.0. Compatible with Mojo 1.0.0b1, json 0.1.6, mozz 0.1.3.
Highlights
HTTP/2 — same handler, version-aware
- Unified
HttpServer.serve(handler)auto-dispatches HTTP/1.1 + HTTP/2 per connection. Preface peek for cleartext, ALPNh2for TLS. The sameRouter, middleware, and extractors run on both wires. - h2c via Upgrade (RFC 7540 §3.2): mid-stream switch from h1 to h2 on the same connection.
HttpClient(h2c_upgrade=True)on the client side. - h2c via prior knowledge:
HttpClient(prefer_h2c=True)sends the h2 preface immediately. Http2ClientConnectiondriver (RFC 9113 client side) feeds the sameHttpClient; no separateHttp2Clientto learn.- HPACK Huffman codec (RFC 7541): scalar-correct decoder + encoder for H=1 literals, with a SIMD shim shipped as parity fallback.
- RFC 8441 Extended CONNECT — both server and client side. WebSocket can now ride a single h2 stream via
WsOverH2Stream+bootstrap_ws_over_h2. - Per-stream
Cancelpropagation: peerRST_STREAMflips the per-stream cancel cell so handlers observecancel.cancelled().GOAWAYand drain do the same. Http2Configwith non-default SETTINGS emission on the initial frame.- Fuzz coverage for CONTINUATION-flood and RAPID-RESET (CVE-2023-44487) state machines.
Reactor — opt-in io_uring
Backend chosen at compile time per OS / kernel; epoll/kqueue remains the default everywhere.
io_uringsubstrate: direct-syscall FFI, SQE encoder + CQE decoder, mmap'd SQ / CQ rings with atomic head/tail. Fuzzed (encoder + decoder harness, 200k runs).UringReactorwith comptime backend selector and epoll-shaped API.FLARE_DISABLE_IO_URING=1for one-off opt-out.- Buffer-ring path (
IORING_REGISTER_PBUF_RING+IOSQE_BUFFER_SELECT, multishot recv) withIORING_SETUP_DEFER_TASKRUN/COOP_TASKRUN/SUBMIT_ALL. Closes the 1-worker throughput regression vs epoll; opt-in viaFLARE_BUFRING_HANDLER=1while the multi-worker wiring stabilises. prep_multishot_accept, liveIORING_ACCEPT_MULTISHOTround-trip.run_uring_recv_reactor_loop[H]+_shared[H]dispatch loops.
Application layer (framework parity)
RouterisCopyablevia Arc-style refcounted boxed handlers — safe forsrv.serve(router^, num_workers=N)without re-allocating per worker.HttpServer.bind_many— multi-listener accept demux onto a single handler.- HTTP/1.1 trailer fields (RFC 7230 §4.1.2 / §4.4):
Response.trailers, autoTrailer:header, smuggling guard, client-side parse off the chunked decoder. - HTTP/1.1 client connection pool:
HttpClient.with_pool(...)keyed on(scheme, host, port), idle reuse, per-origin caps, stale-conn retry. - Server-Sent Events — first-class surface with backpressure-aware emit + last-event-id support.
Conditional[Inner]middleware (304 / 412 with ETag / Last-Modified).RedirectPolicywith cross-origin gating.StructuredLogger[Inner]JSON-per-line emitter.Metrics[Inner]+MetricsRegistry— Prometheus exposition middleware.- Server-side auth extractors + CSRF helper.
- askama-shape template engine.
RequestChunkSourcefor streaming inbound bodies.- PROXY protocol v1 + v2 parser (HAProxy upstream).
HandlerInfallibletrait +WithRaisesadapter —def home(req: Request) -> Response(noraises) is now a first-class shape; theRouter.get(...)call site accepts both shapes at the same type.HandlerExtractorconvenience trait drops the turbofish onr.get[H](path, h).ok_json_valuetyped-JSON response builder.
Transports
- TLS session resumption (RFC 5077 / RFC 8446 §4.6.1) — server-side ticket cache + client-side reconnect.
flare.uds:UnixListener+UnixStream(AF_UNIX sidecar IPC).- WebSocket over HTTP/2 (RFC 8441) end-to-end.
- WebSocket
permessage-deflate(RFC 7692): codec,Sec-WebSocket-Extensionsparser + emitter,negotiate_permessage_deflate,no_context_takeoverenforced on both sides, 16 MiB per-message decompressed cap. - Multi-worker
WsServer+WsClientALPN locking.
Developer experience
flare.prelude—from flare.prelude import *gets you the everyday handler surface in one line.flare.testing.fork_serverhelper for cookbook examples + integration tests.Request.test_get/Request.test_postfactories.- Examples regrouped into
basic/(14) /intermediate/(20) /advanced/(14); every example runs underpixi run tests. - Mojo typed errors adopted as the v0.7+ default error style across the public surface.
Hot-path performance work
- Comptime PHF dispatch for ~70 standard HTTP headers.
- Interned HTTP method names + common header values.
- SIMD-friendly parser primitives.
- Skip-header-decode fast-path for short requests.
- Byte fast-path for
Connection: keep-alive/close. memcpycompaction forread_bufprefix-drop on pipelined keep-alive.- Tagged-pointer single-dict dispatch in the unified reactor.
serve_static_multicore— N-worker static fast path.- Per-worker
BufferPoolwith 4 size classes,DateCache,ResponsePool(withResponse.reset). writev(2)vectored I/O primitive in the runtime.FLARE_REUSEPORT_WORKERS=1default fornum_workers >= 2— matches actix_web's listener strategy. Set=0to opt into the shared-listenerEPOLLEXCLUSIVEshape (uniformly tighter p99.99 σ under sustained load, for 7–22 % less req/s depending on path).
Safety + harness
debug_assert[assert_mode="safe"]coverage on every FFI / unsafe-pointer boundary.mojo build -D ASSERT=nonefor production builds.OwnedDLHandlelifetime hardened across every FFI shim (post-Mojo 1.0.0b1 destructor-ordering tightening).- ASan +
asserts-allsanitizer harness across the unified HTTP / WS-h2 FFI surface. - 24 fuzz harnesses, 5.4M+ runs combined, zero known crashes. New harnesses:
fuzz-io-uring-sqe(encoder + decoder),fuzz-h2-continuation,fuzz-h2-rapid-reset,fuzz-huffman-simd(differential parity),fuzz-ws-deflate(codec round-trip + cap honesty). - Bench harness stabilisation: 20 s probes, cliff-fanout gate on
p99.9 / p99.99, transient-blip retry, absolute-p99 growth gate, post-search validation pass with 8 % back-off on failure. - Per-percentile σ over 5 runs in
summary.md— σ is the honesty meter for the tail.
Performance
TFB plaintext, wrk2 -t8 -c256 -d30s --latency (coordinated-omission corrected), Linux x86_64, AOT-built with no debug asserts on both sides (mojo build -D ASSERT=none for flare, cargo build --release --locked for the Rust baselines). Latency cells are median ± σ over 5 × 30 s measurement rounds at the calibrated sustainable rate.
4-worker comparison:
| Server | Req/s | p99 (ms) | p99.9 (ms) | p99.99 (ms) |
|---|---|---|---|---|
| flare_mc_static | 274,514 | 98.43 ± 406.17 | 133.63 ± 425.84 | 148.35 ± 430.22 |
| actix_web | 223,847 | 2.72 ± 0.08 | 3.18 ± 269.54 | 7.51 ± 305.02 |
| hyper | 215,508 | 2.83 ± 0.07 | 3.30 ± 125.23 | 10.85 ± 147.50 |
| flare_mc | 212,246 | 2.61 ± 0.02 | 2.93 ± 0.02 | 3.25 ± 0.10 |
| axum | 199,380 | 2.80 ± 0.14 | 3.23 ± 5.50 | 3.58 ± 7.55 |
flare_mc has the cleanest tail of the entire 4-worker pack — sub-100 µs σ at p99 / p99.9 / p99.99 against σ of 125–305 ms for the Rust baselines at p99.9 / p99.99. flare_mc_static takes the throughput crown at 274k (~23 % over actix_web), with σ surfacing that this rate is sitting at the saturation cliff.
Single-worker comparison:
| Server | Req/s | p99 (ms) | p99.9 (ms) | p99.99 (ms) |
|---|---|---|---|---|
| nginx | 80,239 | 3.45 ± 0.07 | 4.13 ± 0.11 | 4.80 ± 0.11 |
| flare | 71,619 | 3.01 ± 0.18 | 3.30 ± 1.49 | 3.43 ± 5.67 |
Go net/http |
40,173 | 3.21 ± 0.01 | 3.74 ± 0.09 | 4.62 ± 0.32 |
flare 1w posts 89 % of nginx throughput with a tighter median p99, and 1.78× Go net/http at the same worker count.
Full methodology, calibration gates, and the listener-mode A/B (SO_REUSEPORT per-worker vs shared-listener EPOLLEXCLUSIVE) live in docs/benchmark.md.
Breaking changes from v0.6.0
Http2ClientandHttp2Serverare removed. UseHttpClient(withprefer_h2c=True/h2c_upgrade=True/ ALPN-aware over TLS) andHttpServer.serve(handler)(which dispatches both wires on the same accept loop). The application surface —Router,Handler, middleware, extractors — is unchanged.HandlerExtractorconvenience trait lets you drop the turbofish onr.get[H](path, h). Old turbofish call sites still compile.- Mojo 1.0.0b1 baseline. A handful of internal sites that previously held
Span[UInt8, _]views over short-livedLists have been migrated to explicitSpan(ptr=..., length=...)so the lifetime tracks the named owner. - Conda channels: the
pixi.tomlworkspace channels listhttps://conda.modular.com/max-nightlyfirst (Mojo nightly). Downstream consumers pinning earlier flare versions should make sure their channel order matches. - Linux glibc floor: CI runs on
ubuntu-latestwith a glibc 2.34 sysroot floor (Ubuntu 22.04 baseline). Older distros may need a newer libc.
##...
v0.6.0
flare v0.6.0 is the five-track HTTP feature pass: HTTP/2, sessions + signed cookies, middleware + CORS + FileServer + brotli, request body parsing (cookies / urlencoded / multipart), and an EPOLLEXCLUSIVE shared-listener multi-worker scheduler with optional cross-worker handoff. Plus a perf pass on the response hot path and a real macOS arm64 FFI lifetime fix.
HTTP/2
- RFC 9113 frame codec (
flare/http2/frame.mojo): DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION; flag + length validation at parse time. - RFC 7541 HPACK encoder + decoder; static + dynamic table; static-table happy path inlined.
- Connection + stream state machines (
flare/http2/state.mojo); h2c upgrade detection on the HTTP/1.1 read path; ALPN dispatch. H2Connectiondriver wired intoHttpServer; new public surface:H2Connection,HpackEncoder, frame codec types underflare.http2.
Request parsing
- Request/response cookie ergonomics + RFC 6265 cookie jars;
Cookiesextractor. application/x-www-form-urlencodedparser +Form[T]extractor (parses, validates, and binds to a struct's fields at extraction time).multipart/form-data(RFC 7578) streaming parser +Multipartextractor; bounded buffer, per-part disposition + content-type.
Sessions + signed cookies
flare.crypto.hmac— HMAC-SHA256 FFI, RFC 4231 vectors. Uses the same OpenSSL we already link for TLS.signed_cookie_encode/signed_cookie_decodelower-level codec with constant-time tag compare.- Typed
Session[T]overCookieSessionStore(stateless; signed cookie carries the payload) andInMemorySessionStore(server-side; signed cookie carries the id).
Middleware + content negotiation
Logger,RequestId,Compress,CatchPanic— each is aHandlerthat wraps anotherHandler, no callback chain. Compiler monomorphises the chain into one direct call sequence per request type.Corsmiddleware: allowlist + preflight;Access-Control-*header writer.FileServerwith HEAD + Range (RFC 9110) support; safe path resolution via_file_exists(now FFI-lifetime-correct on macOS arm64).- gzip + brotli content-encoding (RFC 9110 §12.5.3 q-value parser). Brotli encode/decode through
libbrotlienc/libbrotlidec.
Multi-worker scheduler
HttpServer.serve(handler, num_workers=N)over anEPOLLEXCLUSIVEshared listener on Linux (kernel-balanced accept, no thundering herd).flare.runtime.HandoffQueue— bounded MPSC FIFO of opaque Int tokens guarded bypthread_mutex_t.WorkerHandoffPool+HandoffPolicy.from_env(FLARE_SOAK_WORKERS=on) for application-level work-stealing on skewed-keepalive workloads. Default off; production remains opt-in until v0.7 reactor-side wiring.
Performance
Response.__init__no longer copies the body (wasbody.copy()per request). New_string_to_bytesdoes one bulkresize+memcpyfor the response builders._serialize_responsewrites status +Content-Lengthvia stackitoa(noString(int)heap allocs).- TFB plaintext, EPYC 7R32,
wrk2 -t8 -c256 -d30s, calibrated peak with--latency(CO-corrected):
| Server | Workers | Req/s | p99 ms | p99.99 ms |
|---|---|---|---|---|
| flare_mc (shared listener) | 4 | 170,305 | 2.38 | 3.11 |
| flare (reactor) | 1 | 56,086 | 2.70 | 3.54 |
flare_mc holds the best p99 / p99.9 / p99.99 of the four 4-worker frameworks (vs. hyper / axum / actix_web). flare 1w is 88 % of nginx 1w throughput. Apple M-series 1w: ~157K req/s, ~1.10x Go net/http. Full tables in docs/benchmark.md.
Fixed
- macOS arm64 FFI lifetime bug in
flare/http/{middleware,encoding}.mojo. A function-localOwnedDLHandlewas reclaimed by Mojo's ASAP-destruction rule before the cached function pointer was invoked;dlcloseran first and the pointer dangled, segfaulting the runtime undercompress_brotli/decompress_brotli/_file_exists. Both call sites now route through borrow-the-handle helpers (read lib: OwnedDLHandle), the same idiom the zlib path already uses. - CI macOS leg bumped from
macos-14tomacos-15(Sequoia, arm64); mandatory again, no advisory mode, no retry loop. - 45
mojowarnings across tests / examples / lib audited to zero (deprecatedalias→comptime,from os import *→from std.os import *, implicitInt → UInt8, etc.).
Breaking
Response.__init__now takesvar body / reason / versionand moves them in. Direct callers passingbody=fooneedbody=foo^. Theok/ok_json/bad_request/not_found/internal_errorhelpers are unaffected.
Public API reorganisation
- Top-level
flare/__init__.mojore-exports the common surface sofrom flare import HttpServer, Router, Request, Response, Handler, ok, ok_json, SocketAddr, IpAddr, HttpClient, get, post, num_cpus, default_worker_countresolves without reaching into sub-packages. Sub-package__init__.mojofiles (flare.http,flare.http2,flare.runtime) aligned with the same versionless tone. - All internal version refs (
v0.X.Y, track / phase / step identifiers, design-doc cross-references) stripped from public docstrings;mojo docoutput now describes behaviour, not roadmap.
Compatibility & infra
mojo == 1.0.0b1.dev2026042717pinned acrosspixi.toml+recipe.yaml.- Inlined
jsonsource bumped tov0.1.5(same Mojo pin, verified againstehsanmok/json@v0.1.5/recipe.yaml). mozz(fuzz harness runtime) bumped tov0.1.2.- CI:
ubuntu-22.04+macos-15, both mandatory. - Fuzz: 22 harnesses, 5M+ cumulative runs, zero known crashes (new this release:
fuzz-form,fuzz-multipart,fuzz-session-decode,fuzz-fs-range,fuzz-h2-frame,fuzz-hpack-decoder). - Tests: 961 across 60 files + 36 runnable examples (every example part of
pixi run tests).
Docs
- README rewritten: feature list refreshed for v0.6, Quick start now exposes complexity gradually (Beginner: router + path params; Intermediate: typed extractors + middleware shape; Advanced:
Cancel,ComptimeRouter,App[S]+ middleware composition). docs/cookbook.md: 10 new "I want to..." rows for examples 27–36 (request cookies, urlencoded forms, multipart uploads, signed-cookie sessions, middleware stack, CORS, FileServer, brotli, HTTP/2 driver, work-stealing).docs/architecture.md: layer diagram now listsflare.http2andflare.cryptoas first-class modules; the v0.5 placeholderflare.h2 (planned)line is gone.
Install
[dependencies]
flare = { git = "https://github.com/ehsanmok/flare.git", tag = "v0.6.0" }v0.5.0
Operational core
Request.peer: SocketAddr+Peerextractor; the kernel's view of the connecting peer threaded onto every parsed request.- Sanitised 4xx / 5xx response bodies (parse-error messages logged with request id, never echoed). Local-dev opt-out via
ServerConfig.expose_error_messages = True. Canceltoken (peer FIN, timeout, drain unified through one cell).CancelHandlertrait +WithCancel[H]adapter for handlers that opt into cooperative polling.- Per-request deadlines:
read_body_timeout_msenforced end-to-end on the cancel-aware reactor read path;handler_timeout_msandrequest_timeout_msconfigured + asserted at construction. HttpServer.drain(timeout_ms) -> ShutdownReporton the single-threaded reactor; multi-workerScheduler.drainreturns oneShutdownReportper worker.
Buffer ownership + streaming
RouteracceptsHandlerstructs (not justdeffunctions).- Concrete extractors (
PathInt/PathStr/PathFloat/PathBool+ Query / Header / Optional variants) —.valueis the parsed primitive directly. HeaderMapView[origin]zero-copy header storage on the read path; RFC 7230 § 3.2.4 / § 3.2.6 token + field-value validation at parse time.Pool[T]typed allocator (replaces the_Boxed[H]pad after publicsize_of[T]()).RequestView[origin]zero-copy request reads on the cancel-aware reactor;ViewHandlertrait +HttpServer.serve_view[VH]entry point.- Streaming bodies:
Body/ChunkSourcetraits,InlineBody,ChunkedBody[Source],StreamingResponse[B: Body]sibling type, RFC 7230 chunked serializer with lowercase hex chunk-size lines. - Cross-thread
Cancel.SHUTDOWNflip via worker-self-walks-conns (no shared-state mutex).
Server-side TLS
TlsAcceptor+TlsServerConfig+TlsInfo. ALPN selection callback, mTLS opt-in with construction-time validation, cert reload without restart (acceptor.reload()), blocking-pollhandshake_fd(fd)entry point.Request.tls_info: Optional[TlsInfo]populated from the live handshake.- Server-side OpenSSL FFI surface: nine
flare_ssl_*C exports coveringSSL_CTX_new_server,SSL_accept, peer-cert / cipher / version / ALPN getters,ctx_reload,set_verify_client_cert.
Blocking-escape hatch
block_in_pool[T](work, cancel) raises -> Truns blocking syscalls on a fresh kernel thread (per-callpthread_create+pthread_join). Cancel contract: pre-flight + post-flight checks; user code pollscancelinsidework()for mid-flight short-circuit. Crash isolation: a segfault inwork()kills its own pthread, not the reactor's.
Bench + soak
- Bench harness fully on
wrk2(CO-corrected), two-phase (find-peak then sustain at 90 % of peak). Per-run--latencydistribution: p50 / p75 / p90 / p99 / p99.9 / p99.99 / p99.999. - Five workloads ship:
micro-static,mixed_keepalive,uploads,downloads,slow_clients,churn. - Soak harness (
bench-soak-*): three-tier slow-client / churn / mixed-load. Smoke (60 s/workload), extended (5 min/workload), 24 h release-gate viaSOAK_DURATION_SECS=86400. Per-workload gates: zero non-2xx,rss_end <= 2 * rss_start,fd_end <= fd_start + 16.
Numbers (Linux EPYC 7R32, single-worker, plaintext)
- ~80K req/s, on par with
nginx (worker_processes 1), ~1.96×Go net/http (GOMAXPROCS=1). flare_mc4 pinned workers: 4.38× the single-thread reactor (near-linearSO_REUSEPORTscaling).- Tail at sustained 90 %-of-peak load: p50 ~ 1.2 ms, p99 ~ 3.1 ms, p99.99 ~ 3.8 ms.
Compatibility & infra
mojo == 0.26.3.0.dev2026042005pinned acrosspixi.toml+recipe.yaml.jsondep bumped tov0.1.4; OpenSSL pinned at major 3.- CI Linux runners pinned to
ubuntu-22.04(Mojo runtime crash insidelibKGENCompilerRTShared.soonubuntu-latest/ 24.04 — works around a documented glibc-2.39 ABI break). - CI generates the self-signed cert before
pixi run tests(TLS test cert paths are repo-relativebuild/tls-bench-certs/).
Docs
- README leads with
## Features+## Numbers(full-library framing, not v0.5-changelog). docs/benchmark.mdcarries methodology + single-worker / multi-worker discipline (no apples-to-oranges multi-vs-single ratios) + soak harness as## Soak: long-running operational gates.docs/cookbook.md,docs/architecture.md,docs/security.mdupdated.- Public docs are version-agnostic across the board.
Mojo-blocked, deferred to the v0.5.x patch line (do NOT block this tag)
install_drain_on_sigterm/install_sigterm_handler— Mojo "global variables are not supported"; production deployments wire their ownsignal(2)handler today (seeexamples/23_drain.mojo).- Reactor-state-machine TLS handshake (
STATE_TLS_HANDSHAKEadvanced viaon_readable/on_writable) — gated on parametric trait method specialisation cost in a future Mojo nightly. Blocking-pollhandshake_fdships today. - Reactor pull-edge for
StreamingResponse[B]— same parametric-trait specialisation block. Streaming serializer + handler-driven chunked body ship today; reactor-driven pull-loop integration is the follow-up.
v0.4.1
Comptime + extractor track
- Typed extractors with reflective auto-injection (
Extracted[H]works with anyHandler;HandlerStructtrait removed). - Comptime route trie via
ComptimeRouter; handlers carried inline (no moreset_handler). - Pre-encoded literal responses via
serve_static. - SIMD-width-parametric header scanner.
API breaks (within 0.x line)
QueryOpt/HeaderOptrenamed toOptionalQuery/OptionalHeader.HandlerStructtrait removed; existing handlers continue to work viaExtracted[H].
Compatibility & infra
- Mojo 1.0.0b1 compatibility fix in
_server_reactor_impl. - Native allocator pair for heap cells (unblocks
mozzfuzz environment). - Source-only conda distribution (drops
mojo package); pinnedmojo == 0.26.3.0.dev2026042005.
Docs
- README selling-points humanized; Quick Start reordered for gradual disclosure; three example bugs fixed; extractor / comptime-trie / static / SIMD highlights added.
v0.4.0
Composable HTTP release. Same v0.3.0 reactor underneath, one unified entry point (srv.serve(handler, num_workers=N)), and thread-per-core scaling that hits a measured 257,461 req/s at 4 workers on Linux EPYC: 4.4x linear, 3.6x nginx (1 worker), 7x Go net/http.
Headline: multicore reactor
var srv = HttpServer.bind(SocketAddr.localhost(8080))
srv.serve(router^, num_workers=default_worker_count())One call. Same function as the single-threaded reactor, one extra argument. num_workers=1 (the default) keeps the v0.3.x hot path; num_workers >= 2 binds N SO_REUSEPORT listeners on N pthread workers via flare.runtime.scheduler.Scheduler, with optional per-core pinning on Linux (pin_cores=True by default, no-op on macOS). Shared-nothing per-connection ownership, no locks on the hot path.
Linux multicore benchmark (AWS EPYC 7R32, wrk -t8 -c256 -d30s, 5-run median, stdev <= 3%)
| Server | Req/s (median) | p50 | p99 | vs Go net/http |
vs 1-thread flare |
|---|---|---|---|---|---|
flare_mc (4 workers, pinned) |
257,461 | 0.97 ms | 1.58 ms | 7.03x | 4.38x |
| nginx (1 worker) | 70,592 | 3.53 ms | 4.23 ms | 1.93x | 1.20x |
| flare (single-threaded) | 58,731 | — | 4.64 ms | 1.60x | 1.00x |
Go net/http (GOMAXPROCS=1) |
36,617 | — | — | 1.00x | 0.62x |
Near-linear scaling: each of the four pinned workers runs its own un-contended reactor. p99 tail latency collapses from 4.64 ms (single-thread flare under 256 concurrent connections) to 1.58 ms (multicore), and the flare-vs-Go gap widens from ~2x (single-thread) to 7x (4 workers) because Go net/http + netpoll overhead grows faster with concurrency on a slower EPYC core than flare's SO_REUSEPORT sharding does.
macOS loopback saturates at ~140K req/s regardless of worker count because wrk and the server share one client-side CPU; use Linux for multicore numbers.
Single-thread performance (carried over from v0.3.0)
- Linux (AWS EPYC 7R32): on par with single-worker nginx, about 1.96x Go
net/http(GOMAXPROCS=1). - macOS (Apple M-series): about 1.10x Go
net/http(GOMAXPROCS=1).
Same reactor, same kqueue/epoll code path, no throughput regression. Intra-platform ratios are the invariant across Mojo nightlies.
Handlers, routing, state
Handlertrait with a blanket impl, so every existingdef(Request) raises -> Responsehandler is already aHandler.Routerwithget/post/put/patch/delete, path params (/users/:id), wildcard tails (/files/*), auto 405 withAllow:+ 404.App[S: Copyable, H: Handler]+State[T]view for handing application state to middleware layers.- Middleware is a
Handlerthat wraps anotherHandler.[Inner: Handler]generic structs, so composition monomorphises into one direct call chain at compile time. Seeexamples/18_middleware.mojofor a five-layer production pipeline (RequestID->Logger->Timing->Recover->RequireAuth->Router). req.param(name),req.has_param(name),req.has_params(). Path params live on a lazily-allocated pointer so handlers that never route pay zeroDictallocation per request.
Compile-time server wiring
HttpServer.serve[H: Handler & Copyable](handler, num_workers, pin_cores): unified entry point, monomorphised against the concrete handler type.HttpServer.serve_comptime[handler, config]takes both the handler andServerConfigas comptime parameters and runs every invariant (max_body_size >= max_header_size,max_keepalive_requests >= 1,idle_timeout_ms >= 0, etc.) throughconstrained[...]. Misconfigured servers fail the build, not the first request.FnHandlerCT[F]zero-size trait adapter for binding a function into the handler surface at compile time.
What's in
- 460 tests, 18 runnable examples, 16 fuzz harnesses, over 1M aggregate fuzz runs, zero known crashes
- New examples:
15_router,16_state,17_multicore,18_middleware - New test tasks:
test-handler,test-router,test-server-handler,test-app-state,test-serve-comptime,test-thread-ffi,test-reuseport,test-scheduler,test-server-multicore - New fuzz harnesses:
fuzz_router_paths(200K runs),fuzz_scheduler_shutdown(10K runs)
What's deferred
- Typed extractors (
Path[T],Json[T],Query[T],Header[T]), comptime route trie, pre-encoded literal responses, SIMD-width-parametric header scanner: v0.4.1 - HTTP/2 (
h2over TLS, RFC 9113 + 7541): v0.4.2 - Streaming bodies (
Body+ChunkSourcetraits): v0.4.3+
Install
[dependencies]
flare = { git = "https://github.com/ehsanmok/flare.git", tag = "v0.4.0" }Pinned to Mojo 0.26.3.0.dev2026042005 + json v0.1.3.
Full changelog
v0.3.0
flare v0.3.0 cuts over to a single-threaded reactor architecture. The HTTP
server goes from ~50K req/s blocking to ~140-157K req/s on an event loop,
and the new flare.runtime subpackage exposes the reactor primitives
(Reactor, Event, TimerWheel) so you can build other protocols on the
same foundation.
Public API is byte-for-byte compatible with v0.2.0. HttpServer.bind() +
srv.serve(handler) just works, only now under a kqueue/epoll loop.
Headline
- Single-threaded reactor HTTP server (kqueue on macOS, epoll on Linux).
On Linux AWS EPYC: on par with single-worker nginx and ~2x Go's
net/http. On Apple M-series: ~1.1x Go'snet/http. Measured with
the standard TFB plaintext test,GOMAXPROCS=1andworker_processes 1. - 375 tests and 15 fuzz harnesses (over a million fuzz runs, zero
known crashes). - Four new examples for UDP, TLS, cookies, and direct reactor usage.
- Pinned toolchain for reproducibility: Mojo 0.26.3.0.dev2026041805 +
json v0.1.2 (both built by the same compiler).
Benchmarks
macOS, Apple M-series, single-threaded, wrk -t1 -c64 -d30s, 5-run
median of middle 3 with stdev ≤ 3%.
| Server | Req/s (median) | p50 | p99 | vs Go net/http |
|---|---|---|---|---|
| flare (reactor) | 157,459 | 0.39 ms | 0.80 ms | 1.10x |
Go net/http (1 thread) |
143,500 | 0.44 ms | 0.86 ms | 1.00x |
Linux, AWS EPYC 7R32 (64 vCPU).
| Server | Req/s (median) | p50 | p99 | vs Go net/http |
|---|---|---|---|---|
| nginx (1 worker) | 81,612 | 0.40 ms | 0.79 ms | 2.00x |
| flare (reactor) | 79,965 | 0.78 ms | 1.53 ms | 1.96x |
Go net/http (1 thread) |
40,739 | 1.59 ms | 3.10 ms | 1.00x |
Reproduce locally:
pixi run --environment bench bench-vs-baseline-quick # flare vs Go, ~7 min
pixi run --environment bench bench-vs-baseline # + nginx, + latency floorSee the README benchmarks section
for the full methodology (integrity check, pinned toolchains, warmup
protocol, per-run provenance dir).
What's new since v0.2.0
flare.runtime (new subpackage)
Reactor— uniform API over kqueue (macOS) and epoll (Linux),
dispatched at compile time. Register fds with a token and interest
bits, poll for readiness, wake from another thread.TimerWheel— hashed timing wheel (512 slots + overflow) with O(1)
schedule / cancel. Used for per-connection idle timeouts.Event+INTEREST_READ/INTEREST_WRITE/EVENT_READABLE/
EVENT_WRITABLE/EVENT_ERROR/EVENT_HUP/WAKEUP_TOKEN.
flare.http.HttpServer (rewritten)
- Event loop replaces the blocking
_handle_connection_bufferedpath.
Old code fully removed. Public API unchanged: every existing
test_server.mojotest passes without modification. - Per-connection state machine in
flare/http/_server_reactor_impl.mojo:
READING → WRITING → keep-alive loop → CLOSING. - Configurable
idle_timeout_ms,write_timeout_ms,
shutdown_timeout_msper connection; hashed timing wheel enforces
them without extra syscalls per request. - Graceful shutdown:
HttpServer.close()stops accepting new
connections and drains in-flight ones up toshutdown_timeout_ms.
Parser and serialiser cleanup
_read_line_buf: single-scan plus one-shotString(unsafe_from_utf8=...)
construction instead of the per-byteline += chr(...)loop.
Biggest single throughput win._is_content_length/_is_connection: inline case-insensitive
byte compare, avoiding the per-header_ascii_lowerallocation._ascii_lower/_lower/_append_str: bulk write through
pre-sized buffers,memcpyreplaces per-byte loops everywhere._ascii_strip_slice: single-allocation strip replacing the
String(String(...)).strip()triple per header value.
New examples (all runnable via pixi run example-<name>)
11_udp.mojo—UdpSocket.bind+send_to+recv_fromround
trip, plusDatagramTooLarge.12_tls.mojo—TlsConfig,TlsStream.connectto a real HTTPS
endpoint, raw TLS handshake + minimal HTTP/1.0 GET, plus the
TlsVerify.NONEescape hatch.13_cookies.mojo—Cookie,CookieJar,parse_cookie_header
(client side),parse_set_cookie_header(server side) with
Max-Age,Path,Secureattributes.14_reactor.mojo— directflare.runtime.Reactorusage so you
can build non-HTTP protocols on the same event loop.
Rigorous benchmark harness
pixi run --environment bench bench-vs-baselineorchestrates
integrity check + 5-run measurement across flare, Gonet/http,
nginx. 3% stdev gate, full env capture per run, TFB plaintext
workload.- Go and nginx versions pinned via
[feature.bench.dependencies]in
pixi.tomlso the comparison cannot drift silently.
Fuzz surface
- New harnesses:
fuzz-reactor-churn(reactor register/modify/
unregister/poll churn, 200K runs),fuzz-server-reactor-chunks
(random HTTP-ish bytes fed to the state machine, 30K runs),
prop-timer-wheel(property test for the timing wheel, 100K runs). - Existing harnesses unchanged, all still at 0 crashes.
Packaging and tooling
- Pixi environments layered:
default(lean, runtime only),
dev(+ mojodoc, pre-commit),fuzz(+ mozz),bench
(+ Go, nginx, wrk). pixi run formatmoved behind-e devso the lean default env
stays small for users and CI.jsondep pinned totag = "v0.1.2", both sides built against
Mojo 0.26.3.0.dev2026041805. Same pin inrecipe.yamlfor
rattler-build consumers.
Upgrading from v0.2.0
Your code does not need to change. The public API is the same. If you
had tag = "v0.2.0" in pixi.toml, bump to tag = "v0.3.0":
[dependencies]
flare = { git = "https://github.com/ehsanmok/flare.git", tag = "v0.3.0" }Then pixi install. You're on the reactor.
Known caveats
nginxbaseline on macOS is still flaky. The pinned nginx config
occasionally fails to come up under macOS Pixi; the harness reports
the row as broken when it happens. The Go baseline is reliable on
both platforms and is the authoritative comparison. Linux nginx is
stable.- Linux numbers in the README footnote are un-pinned
wrk/ server
processes on a 64-vCPU EPYC instance, so absolute req/s is
microarchitecture-dependent. Intra-platform ratios (flare vs Go,
flare vs nginx) are the apples-to-apples comparison, not the cross-
platform req/s. - HTTP/2 is not here yet. Scheduled for v0.4.0 alongside
thread-per-core (SO_REUSEPORT). See the development notes for the
v0.4.0 scope.
Commit range
v0.2.0...v0.3.0
is about 30 atomic commits covering the whole reactor migration:
epoll / kqueue / eventfd / pipe FFI, the flare.runtime.Reactor and
TimerWheel abstractions, the per-connection state machine, the
hard replacement of HttpServer, the rigorous benchmark harness, and
the parser / serialiser allocation cleanup.
Thanks for reading. If you hit anything weird, open an issue with
your pixi info output and the exact command that broke.