Skip to content

v0.9.0

Choose a tag to compare

@ehsanmok ehsanmok released this 21 Jul 00:52
· 70 commits to main since this release

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 via Alt-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.cc ships NewReno, CUBIC, and HyStart++; the 1-RTT path runs an RTT estimator and ACK-based loss detection (RFC 9002).
  • Batched UDP I/O: recvmmsg ingress 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 WsHandler trait and WsServer.serve[H].
  • permessage-deflate context-takeover (RFC 7692 §7.1) with a persistent compressor pair.
  • WsAutoClient picks 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

  • Router is now Defaultable and 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's State(db). Typed Json[T] / Form[T] / JsonAs[T] / Session extractors landed.
  • Sessions gained a pluggable SessionBackend with TTL expiry, CSPRNG session ids, and signed-cookie carriers.
  • spec_from_router derives an OpenAPI 3.1 spec by walking a runtime Router.
  • 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 buffered get() 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