Skip to content

Latest commit

 

History

History
210 lines (189 loc) · 23.7 KB

cookbook.md

File metadata and controls

210 lines (189 loc) · 23.7 KB

Cookbook

Examples are organised under examples/ into three tiers — basic, intermediate, and advanced. Each example is a single self-contained file: clone, run, see what changes when you tweak it. Every example is part of pixi run tests and runs on CI, so they stay green with the code. Run any single example with pixi run example-<name> (see pixi.toml for the full task list).

Imports and handler shapes

flare.prelude and the root flare package export the same list: Request, Response, Router, HttpServer, the ok / ok_json / not_found / ... builders, Method / Status, the Handler family, SocketAddr, the extractors, the stock middleware, sessions, and the TCP / TLS / Unix / WebSocket types. A symbol is on it if another exported symbol's signature names it, or if the examples need it. Wire-format codecs, runtime primitives and anything in a _-prefixed module keep their own import, for example from flare.http2 import Http2Connection. Code meant to last is better off naming the module it takes things from.

srv.serve(r^) accepts any Handler. Multi-worker (num_workers=N) needs a Copyable handler, since each worker gets its own H.copy(); Router qualifies because its routes sit behind an Arc-style refcount. raises on a handler is optional and tracks the body: drop it when the handler cannot fail, keep it when it parses input or does I/O (the server converts a raise to a sanitized 500). For stateful infallible handlers see HandlerInfallible.

Free with every server: 404 on unknown paths, 405 with Allow, sanitized 4xx/5xx, peer-FIN cancellation, RFC 7230 size limits, per-worker kqueue / epoll (opt-in io_uring).

Basic — networking primitives, hello-world HTTP, simple WS

The "first ten minutes with flare" surface. Plain functions, no typed extractors, no middleware composition. If you're skimming to see whether the framework feels right, start here.

File What it shows
addresses.mojo IpAddr, SocketAddr, v4 / v6 classification
dns_resolution.mojo resolve(), resolve_v4(), resolve_v6(), numeric-IP passthrough
error_handling.mojo typed error hierarchy and the context each error carries
tcp_echo.mojo TcpListener + TcpStream round-trip, TCP options
udp.mojo UdpSocket.bind, send_to, recv_from, DatagramTooLarge
encoding.mojo gzip / deflate compress and decompress
tls.mojo TlsConfig, TlsStream.connect, raw TLS handshake + GET
http_get.mojo HttpClient GET / POST / PUT / PATCH / DELETE / HEAD
websocket_echo.mojo WsClient connect, send, receive
ergonomics.mojo high-level requests-style API (BufReader, WsMessage, Auth)
http_server.mojo HttpServer with routing, JSON responses, response helpers
router.mojo Router with path parameters, method dispatch, 404 / 405
ws_server.mojo WsServer handshake + frame loop
cookies.mojo Cookie, CookieJar, parse_cookie_header, parse_set_cookie_header

Intermediate — building a real app

Typed extractors, shared state, middleware composition, sessions, forms, multipart uploads, content-encoding negotiation, CORS, static files, Server-Sent Events. The next 30 minutes after the basics.

File What it shows
extractors.mojo Typed extractors (PathInt, QueryStr, HeaderStr, ...), reflective Extracted[H] auto-injection, and State[T] registration-time injection alongside request extractors
typed_extractors.mojo OptionalPath{Int,Str,Float,Bool} optional path params + JsonAs[T: FromJson] typed-body deserialization
state.mojo Shared application state via a captured wrapping Handler (a Counters snapshot tagged onto responses) -- the no-injection pattern; for injected state beside extractors see State[T] in extractors.mojo
middleware.mojo Middleware composition (outside-in): RequestID → Logger → Timing → Recover → RequireAuth → Router
middleware_stack.mojo Logger + RequestId + Compress + CatchPanic chain
multicore.mojo HttpServer.serve(..., num_workers=default_worker_count())
static_response.mojo Pre-encoded StaticResponse + HttpServer.serve_static fast path
cancel.mojo CancelHandler polling cancel.cancelled() between expensive steps
drain.mojo HttpServer.drain(timeout_ms) per-worker (caller wires SIGTERM today)
sse.mojo Streaming response body via ChunkSource (Server-Sent Events shape)
response_from_body.mojo Opt-in Response[B: Body] ergonomics via response_from_body (buffered InlineBody + chunk-streamed ChunkedBody over a forked server)
request_cookies.mojo Reading inbound Cookie: headers + the Cookies extractor
forms.mojo application/x-www-form-urlencoded parsing + the Form extractor
multipart_upload.mojo multipart/form-data (file uploads) + the Multipart extractor
sessions.mojo Typed Session[T] over CookieSessionStore (HMAC-SHA256 signed)
http_ws_one_port.mojo HTTP and WebSocket served by one HttpServer on one port, via ServerConfig.ws
cors.mojo Cors permissive vs allowlist + preflight + credentials
static_files.mojo FileServer with HEAD + Range + path safety
openapi.mojo Derive an OpenAPI 3.1 document from a live Router with spec_from_router
brotli.mojo compress_brotli / decompress_brotli + Compress middleware emitting br
ok_json_typed.mojo Typed JSON request → typed JSON response via ok_json_value
infallible_handler.mojo HandlerInfallible + WithRaises adapter for provably no-raises paths
trailers.mojo HTTP/1.1 trailer fields (gRPC-style status trailer): Response.trailers, Trailer: header, smuggling guard
multi_listener.mojo HttpServer.bind(List[SocketAddr]) over multiple distinct addresses, single accept loop
reliability.mojo Retry[Inner] + PostHocDeadline[Inner] + RetryPolicy -- RFC 9110 §9.2.2 idempotent-method gate (GET / HEAD / PUT / DELETE / OPTIONS), opt-in exponential backoff with jitter (backoff_base_ms / backoff_max_ms / backoff_jitter_ms), post-hoc 504 wall-clock guard
http_cache.mojo Cache[Inner, S] middleware over an InMemoryCacheStore -- RFC 9111 freshness check on CacheEntry.is_fresh, Vary-aware secondary key, conditional revalidation (If-None-Match / If-Modified-Since + 304 folding back into the cached entry)
template_inheritance.mojo Single-level template inheritance: parent layout + child overrides via {% block %} / {% extends %} / Template.render_extending

Advanced — comptime, low-level reactor, HTTP/2, mTLS, work-stealing

The Mojo-unique surfaces (compile-time route tables, direct reactor primitives, io_uring), HTTP/2-specific dispatch, mTLS, ACME-style cert reload, AF_UNIX sidecar IPC, multi-worker handoff. Don't reach for these until the intermediate tier feels natural.

File What it shows
comptime_router.mojo ComptimeRouter[routes] with comptime segment parsing and per-route dispatch unroll
reactor.mojo direct flare.runtime.Reactor usage for custom protocols
work_stealing.mojo HandoffQueue + WorkerHandoffPool + FLARE_SOAK_WORKERS knob
uds_sidecar.mojo UnixListener / UnixStream AF_UNIX sidecar IPC
streaming_proxy.mojo streaming-proxy surface: StreamHandler + attach_upstream(UpstreamChunkSource) + relay_upstream() over serve_streaming, relaying a FrameMux backend with watermark backpressure, no fd, no Span, no per-connection table in front code
cert_reload.mojo TlsAcceptor.reload() for ACME / Let's Encrypt cert rotation without restart
mtls.mojo mTLS configuration + construction-time validation
https_server.mojo In-process HTTPS server: HttpServer.bind_tls + serve (reactor-multiplexed, ALPN-dispatched h1 or h2) with a buffered handler and a stream_response chunked body over SSL_write
http2.mojo Http2Connection driver, ALPN dispatch, h2c upgrade detection
http2_config.mojo Http2Config SETTINGS knobs + validation
http2_client.mojo HttpClient(prefer_h2c=True) GET + POST over h2c (cleartext HTTP/2 via prior knowledge)
http2_server_router.mojo Path-dispatching handler served over HTTP/2 via the unified HttpServer.serve(handler) (auto-dispatches HTTP/1.1 + HTTP/2 on the same port)
h2c_client.mojo HTTP/2 cleartext client via the Upgrade: h2c + HTTP2-Settings dance (RFC 7540 §3.2): first request flows over h1 then carries forward to h2
client_pool.mojo HttpClient.with_pool — keyed idle reuse, per-origin caps, stale-conn retry
ws_over_h2.mojo RFC 8441 WebSockets-over-HTTP/2 (Extended CONNECT + :protocol=websocket)
ws_over_h3.mojo RFC 9220 WebSockets-over-HTTP/3: attach_ws_h3 server and WsOverH3Stream client over live QUIC
ws_permessage_deflate.mojo RFC 7692 permessage-deflate extension: offer / negotiate / compress / decompress
alpn_dispatch_demo.mojo ALPN -> wire-protocol dispatcher decisions: h1 / h2c / h2 / h3, including RFC 7301 §3.2 server-preference negotiation
http3_server.mojo Serve the same Handler over HTTP/1.1 + HTTP/2 + HTTP/3 simultaneously: TCP listener for h1 / h2c / h2 alongside a UDP listener for h3
http3_client.mojo HttpClient(prefer_http3=True) live HTTP/3 GET over QUIC: Alt-Svc discovery, happy-eyeballs race vs h2 / h1, transparent fallback
http3_server_walkthrough.mojo Http3Connection driver lifecycle walkthrough (pure state-machine surface; pairs with http3_server.mojo)
quic_codec_demo.mojo QUIC varint / frame codec / transport params / state machine / congestion controller round-trips
h3_codec_demo.mojo HTTP/3 codec round-trip without a QUIC stream: QPACK field sections + RFC 9114 §7 frame codec + request reader
quic_handler_dispatch.mojo RFC 9000 §19 transport-frame dispatch via FrameHandler + parse_frame_into (per-type callbacks, no union carrier)
grpc_unary_server.mojo Reactor-mounted gRPC unary server: a GrpcService served over the unified HttpServer H2 reactor, grpc-status in H2 trailers
grpc_unary_demo.mojo Sans-I/O gRPC unary adapter (no socket): LPM framing + metadata + status + run_unary_call orchestration
grpc_client_demo.mojo GrpcClient unary RPC over the HttpClient HTTP/2 path (h2c), against a forked gRPC-speaking server
openai_sse_front.mojo OpenAI-shaped chat.completion.chunk SSE front relaying an upstream token stream over UpstreamChunkSource / FrameMux with watermark backpressure
unified_middleware.mojo One Logger(RequestId(Router)) middleware stack shared across both a Handler control plane and a StreamHandler streaming endpoint
production_setup.mojo Production-shaped server: RequestId + StructuredLogger + CatchPanic + graceful shutdown + healthz

"I want to..." quick links

Goal Start here
Serve hello-world http_server.mojo
Add a route with a parameter router.mojo
Make HTTP requests http_get.mojo
Download a large body without buffering it HttpClient.get_streaming(url) for http://, get_streaming_tls(url) for https://, then loop read_chunk(n)
Accept a chunked upload Nothing to do -- Transfer-Encoding: chunked request bodies are decoded before Handler.serve sees them
Talk WebSocket websocket_echo.mojo
Use TLS as a client tls.mojo
Terminate HTTPS in-process (h1 or h2 by ALPN, multi-worker) https_server.mojo
Manage cookies cookies.mojo
Pass typed inputs to a handler extractors.mojo
Share state across handlers state.mojo
Stack middleware middleware.mojo
Stack Logger / RequestId / Compress / CatchPanic middleware_stack.mojo
Scale to all cores multicore.mojo
Skip the parser entirely static_response.mojo
Detect mid-handler client disconnect cancel.mojo
Drain on SIGTERM drain.mojo
Stream a response body / SSE sse.mojo
Read inbound cookies request_cookies.mojo
Parse a form POST forms.mojo
Accept file uploads multipart_upload.mojo
Use signed-cookie sessions sessions.mojo
Configure CORS cors.mojo
Serve static files (with Range) static_files.mojo
Publish an OpenAPI spec for my routes openapi.mojo -- spec_from_router(router, title, version) then emit_openapi_json; body schemas are hand-written, the router cannot supply them
Send Content-Encoding: br brotli.mojo
Return a typed JSON response ok_json_typed.mojo
Use a no-raises handler infallible_handler.mojo
Compile-time route table comptime_router.mojo
Drive the reactor directly reactor.mojo
Reload a TLS cert without restart cert_reload.mojo
Configure mTLS mtls.mojo
Drive HTTP/2 directly http2.mojo
Tune HTTP/2 SETTINGS http2_config.mojo
Make HTTP/2 client requests (h2c via prior knowledge; https:// auto-negotiates h2 vs h1.1 via ALPN) http2_client.mojo
Serve HTTP/1.1 + HTTP/2 from one port http2_server_router.mojo
Serve HTTP and WebSocket from one port http_ws_one_port.mojo -- set ServerConfig.ws = WsUpgrade(ws_fn), or WsUpgrade(ws_fn, offload=True) to give each socket its own thread
Bound a slow or silent peer on the client HttpClient.with_read_timeout(ms) arms SO_RCVTIMEO for each read; the timeout_ms constructor argument bounds the connect and, on https://, the TLS handshake. There is no whole-request deadline yet
Stream a response too large to buffer http_stream_client.mojo -- get_streaming(url) on either scheme, then loop read_chunk(n) until it returns empty
Upload a body too large to buffer streaming_upload.mojo -- send_chunked from a ChunkSource; pass body_size when the length is known
AF_UNIX sidecar IPC uds_sidecar.mojo
Proxy an external producer's stream with end-to-end backpressure streaming_proxy.mojo
Even out skewed-keepalive load work_stealing.mojo
Emit gRPC-style HTTP/1.1 trailers trailers.mojo
Bind a single worker on multiple addresses multi_listener.mojo
Speak h2c via the Upgrade dance from a client h2c_client.mojo
Reuse h1.1 connections via HttpClient.with_pool client_pool.mojo
Tunnel WebSockets over HTTP/2 (RFC 8441) ws_over_h2.mojo
Tunnel WebSockets over HTTP/3 (RFC 9220) ws_over_h3.mojo
Compress WebSocket payloads with permessage-deflate ws_permessage_deflate.mojo
Stand up a production-shaped server (RequestId + structured logs + graceful shutdown + healthz) production_setup.mojo
Reuse markup via template inheritance ({% block %} / {% extends %}) template_inheritance.mojo
Retry idempotent requests + bound handler latency reliability.mojo
Cache GET responses with RFC 9111 freshness + revalidation http_cache.mojo
Route inbound TLS connections to h1 / h2 / h3 by ALPN alpn_dispatch_demo.mojo
Serve HTTP/3 alongside HTTP/1.1 + HTTP/2 on the same Handler http3_server.mojo
Pick the WebSocket carrier wire by negotiated ALPN flare.ws.WsAutoClient + flare.ws.decide_wire (tests/ws/test_ws_autoclient.mojo)

Reading data from a Request

flare exposes inbound request data through a layered surface — the right shape depends on whether you want a plain field, a parsed primitive, or a typed struct populated by the request as a whole. The table below is the canonical reference; pick the cheapest shape that gets you the value you need.

Shape What you get Example When to reach for it
Plain field access The raw string / bytes / list / HeaderMap directly off Request req.method, req.url, req.body, req.headers, req.peer, req.version You want the wire-level value as the parser saw it. No copying, no extraction.
Path params Value matched by a Router path segment (:name) req.param("id") raises -> String, req.has_param, req.params_mut()["id"] = ... The URL path itself carries the value. param raises if the segment didn't match (use has_param to peek).
Query params Single value from the URL's query string req.query_param("k") -> String (returns "" if missing), req.has_query_param Querystring ?k=v style data; return-value-on-miss makes one-line reads natural.
Cookies Inbound Cookie header parsed into name/value pairs req.cookies() -> CookieJar, req.cookie("name") -> String (returns "" if missing), req.has_cookie Inspecting an inbound Cookie header directly. For typed extraction prefer the Cookies extractor.
Body decoding Body interpreted as text / JSON / raw bytes req.text() -> String, req.json() raises -> json.Value, req.body: List[UInt8] Reading the body opportunistically inside a handler without an extractor.
*.extract(req) extractors Typed value from the body or a header set Form.extract(req) -> Form, Multipart.extract(req) -> MultipartForm, Cookies.extract(req) -> Cookies Hand-written extractor pipeline; reach for this when the auto-injection adapter is overkill but you still want typed parsing.
Comptime-keyed extractors Single typed primitive keyed at compile time PathInt["id"], QueryStr["q"], OptionalQueryInt["page"], HeaderStr["Authorization"], Json[T] Building blocks for the auto-injection shape (next row); each one is a Defaultable struct with a value field.
Extracted[H] auto-injection A handler struct whose fields are the extractor set; the adapter walks the field list per request and populates each r.get("/users/:id", Extracted[GetUser]()) where GetUser(HandlerExtractor) declares id: PathInt["id"] etc. Production handler shape: declarative, typed, monomorphised. The adapter raises 400 with the parser error on extractor failure; serve raises propagate to 500.

Examples that exercise each shape, in order: the router.mojo example covers plain field + path param shapes; request_cookies.mojo walks the cookie surface; forms.mojo and multipart_upload.mojo use the *.extract(req) extractors; extractors.mojo and ok_json_typed.mojo show comptime-keyed extractors and the Extracted[H] auto-injection adapter.