Happy to announce v0.7 release of flare with Mojo 1.0.0b1.
Flare is a full networking stack written in Mojo. HTTP/1.1, HTTP/2, WebSocket (RFC 6455), TLS 1.2/1.3 with ALPN, TCP, UDP, Unix sockets, and DNS, all on top of one non-blocking reactor (kqueue, epoll, opt-in io_uring). When HTTP is the wrong shape for what you’re building, you can drop to raw sockets without leaving the library.
The runtime model is borrowed where it works. Sync handlers on a thread-per-core reactor, nginx-style: one in-flight request per worker, no async runtime, no per-connection task to schedule. Multi-worker mode uses per-worker SO_REUSEPORT listeners, the same pattern actix_web ships. The kernel does the load balancing across workers; each worker drives its own event loop on non-blocking sockets and only blocks at the kqueue / epoll_wait / io_uring_enter boundary. (When Mojo grows async/await, the Handler trait is already shaped to compose with both, but the sync model is what you target today.)
The handler surface composes by types. Router, App[S], middleware, and typed extractors (PathInt, Json[T], Form[T], Cookies) nest as structs, and the compiler folds the chain into one direct call sequence per request type. No virtual dispatch, no per-request allocation in the hot path.
from flare.prelude import *
def hello(req: Request) raises -> Response:
return ok("hello")
def main() raises:
var r = Router()
r.get("/", hello)
var srv = HttpServer.bind(SocketAddr.localhost(8080))
srv.serve(r^, num_workers=2)
What landed in v0.7
One server for every wire. HttpServer.serve(handler) peeks each accepted connection for the RFC 9113 preface and dispatches h2c without an Upgrade negotiation; over TLS it’s plain ALPN. The same Router, middleware, and extractors run on h1 and h2.
Router is now Copyable. Boxed handlers are shared across worker copies via an Arc-style refcount, so srv.serve(router^, num_workers=N) reuses the same routes per worker without re-allocating.
HTTP/2:
- Per-stream
Cancelpropagation. PeerRST_STREAMandGOAWAYflipcancel.cancelled()in the matching handler. - HPACK Huffman codec (RFC 7541, scalar correct, with a SIMD shim as parity fallback).
- CONTINUATION-flood and RAPID-RESET (CVE-2023-44487) state-machine fuzz harnesses.
WebSocket:
- WS-over-h2 (RFC 8441 Extended CONNECT) on both client and server.
- permessage-deflate (RFC 7692) with
no_context_takeoverand a 16 MiB per-message decompressed cap.
HTTP/1.1:
- Trailer fields (RFC 7230 4.1.2 / 4.4):
Response.trailers, automaticTrailer:header, smuggling guard, client-side parse off the chunked decoder. - Client connection pool:
HttpClient.with_pool(...), idle reuse, per-origin caps, stale-conn retry.
TLS: session resumption via tickets (RFC 5077 / RFC 8446 4.6.1), server-side cache plus client-side reconnect.
Check out the full v0.7 release note.
Performance
TFB plaintext (GET /plaintext, 13 bytes), wrk2 -t8 -c256 -d30s --latency with coordinated-omission correction, 5×30s rounds, AOT-built with -D ASSERT=none:
| Config | Req/s | p99 | p99.99 |
|---|---|---|---|
flare_mc_static (4w fast path) |
274,514 | 98.4 ± 406 ms | 148 ± 430 ms |
flare_mc (4w handler) |
212,246 | 2.61 ± 0.02 ms | 3.25 ± 0.10 ms |
flare (1w handler) |
71,619 | 3.01 ± 0.18 ms | 3.43 ± 5.67 ms |
The handler row is the one I care about most. At 4 workers, its p99.9 and p99.99 land with sub-100 µs σ across 5 runs, against σ in the 125–305 ms range for actix_web, hyper, and axum at the same percentiles. The static fast path posts the highest req/s of the pack but sits closer to its saturation cliff, which is why its tail σ blows out. Single-worker flare lands at 89% of nginx 1w throughput with a tighter p99 (3.01 vs 3.45 ms). Full Rust / Go / nginx side-by-sides and the σ “honesty meter” methodology live in docs/benchmark.md.
Try it
[dependencies]
flare = { git = "https://github.com/ehsanmok/flare.git", tag = "v0.7.0" }
- Repo: GitHub - ehsanmok/flare: Full Networking Stack for Mojo🔥 · GitHub
- Docs: Redirecting...
- Cookbook: docs/cookbook.md
- Feature inventory: docs/features.md
Issues and PRs welcome on the repo.