Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

std.actor.http.server

HTTP/1.1 with one supervised actor per connection, over plain TCP or TLS. The server uses the actor networking, actor TLS and resumable HTTP decoder modules. Application callbacks run on one thread. A contained trap discards the connection’s heap and closes its tracked sockets; admission and unrelated connections continue.

The qualification record separates tested behavior from outstanding production-readiness gates. Qualification currently targets x86-64 GNU/Linux. HTTP/2 and HTTP/3 are outside this implementation.

Choosing a server

Both HTTP servers remain supported, with different execution contracts.

Choicestd.http.serverstd.actor.http.server
Ordinary handlersSequential Exchange operations, shared handlerComplete bounded Request, mutable handler per connection
StreamingSequential reads and writes on workersEvents, partial writes and explicit resumable state
SchedulingBounded worker pool; idle heads wait off workersOne nonblocking stage; callbacks run to completion
Contained failuresReturned errors; runtime traps end the processReturned errors, handler traps and actor heap exhaustion
Existing helpersRouter, uploads, files, WebSockets and SSEHTTP framing and response primitives; application dispatch

Choose using the application’s failure model, I/O needs and measured workload. The comparison harness does not assume that either implementation is faster. The actor server does not make blocking database or HTTP-client operations nonblocking.

Complete requests

Start with the runnable actor HTTP lesson. The simple entry points are:

Entry pointContract
serve(listener, factory, &config, &stop)Serve plain TCP until shutdown; returns Result[Report, ServerError]
serve_tls(listener, tls, factory, &config, &stop)The same lifecycle using an owned std.tls.ServerConfig
config()Default configuration, without starting a server

The server takes ownership of the listener and factory, and of a supplied TLS configuration. A failed setup releases these resources; they are not returned for retry. This differs from a queue’s failed submission: serve owns the server lifecycle, including cleanup after an error. Configuration and the stop token are borrowed for the call.

factory is a named function returning Result[H, FE]. The server calls it inside each connection’s heap. H implements RequestHandler[E]:

def handle(self: &mut H, request: Request, response: &mut Response) -> Result[(), E]

The handler persists across requests on that connection. It is not shared across connections. Factory and handler error types may differ. Its stored fields must obey the actor heap boundary: use owned Bytes for persistent text, not str, shared owners or locks. Closures with captured state do not cross that boundary. Build connection state in the factory instead of allocating it outside the actor heap.

Request owns head: RequestHead, target: RequestTarget, body: Bytes and trailers: list[Header]. These values are temporary callback inputs. The complete request body is buffered up to config.limits.max_body, subject also to the actor’s heap limit. Route using the validated target and method; the worker server’s Exchange and stored Router are not actor state.

Use Response.send_text, send, or respond for a whole bounded response. It must fit in pending_output together with its encoded head and framing reserve. Larger bodies use the streaming interface. Returning without a response produces 500; an unfinished response is finished before the callback completes, which can fail on a length mismatch.

Configuration and bounds

FieldDefaultMeaning
capacity256At most this many connection actors; accepted range 1–1000
heap_limit4 MiBBudget per connection; accepted range 256 KiB–64 MiB
input_buffer, output_buffer4096 eachTCP ciphertext or plaintext transport buffers; at least 64 bytes
pending_output65536Application response queue, including its head; at least 512 bytes
limitsstd.http.limits()Maximum line, head, header count and decoded request body
idle_timeout5 sWaiting for a request
head_timeout10 sReceiving a request head
body_timeout30 sReceiving its body
write_timeout10 sStalled transport output
response_timeout60 sCompleting a response
handshake_timeout10 sEstablishing TLS
drain_timeout5 sGrace period before forced stage shutdown
max_requests1000Maximum requests per connection, a Positive[i64]

Zero phase durations disable those phase timers. A zero drain duration instead requests immediate teardown. The independent heap, buffer and protocol limits all apply: increasing max_body alone does not increase available memory. The runtime has at most 1024 live actor heaps per process, including the server’s acceptor and any other stages. Capacity is not a promise that admission can always reserve another heap or descriptor.

At capacity or when admission cannot reserve a child, the server closes a newly accepted connection. It does not promise an HTTP 503 response for overload. HTTP parse refusals use bounded lingering close so unread request bytes do not immediately turn a completed response into a TCP reset. Its private lingering state records only whether the 16 MiB drain threshold has been reached, not an unbounded byte total; transport consumption still uses the full available input.

Errors, reports and shutdown

ServerError distinguishes invalid configuration and loss of the acceptor, and preserves allocation, byte-buffer, HTTP, network, TLS, timer, selector, actor-start and time-range causes in named variants. Application failures are classified as Handler; application error values are not sent to the peer. Report application-specific diagnostics before returning them when needed, using an operation that respects the nonblocking callback contract.

A handler Err produces a generic 500 when a response has not started. After output has started, an error closes the connection rather than adding a second response. A trap closes the connection without a recoverable HTTP answer. Isolated construction covers failures before the handler starts too. It does not retry a partially handled request.

Report.actors is the underlying actor Stats; it includes the acceptor and admitted construction attempts. requests counts completed responses, rejected counts admission refusals, and protocol_errors counts reported parse failures. These observation-only counters saturate at u64.MAX; completion credit, admission and shutdown still progress normally at that limit. A selector or setup failure returns Err instead of a report.

A connection submits one completion at a time and waits for admission to acknowledge it before starting another request. A full admission mailbox uses a private 1 ms retry timer instead of runnable retries on every scheduling round. Live application messages and timers continue to dispatch while it waits; retirement retains pending completion and shutdown control. If the shared timer pool is exhausted, runnable retries preserve control delivery. Admission retains at most one pending acknowledgement per connection and retries it when the child’s mailbox is full. This bounds the added queue by capacity; the stage reserves one additional timer record per connection. Phase deadlines and the external forced-drain deadline continue to apply.

Notice.CompletedBy(Address) submits completion credit and Notice.Completed returns it. These variants, Resume, Rejected and Drain are reserved for server control; applications send Notice.Message(value). The additional enum variant requires updating exhaustive matches over Notice.

Cancel the external stop token, or call Response.shutdown_server() from an authorized application route, to start draining. Admission stops, idle connections close, and active responses get the configured grace period. The external driver then calls stage shutdown, even if application mailboxes or timers are full. Early error returns also drop the stage and reclaim its live heaps. Use std.signal.cancel_on outside the stage to turn process signals into cancellation.

Reliability boundary

Callbacks and destructors are not preempted. Blocking I/O or an infinite loop can stall the whole stage, including its shutdown checks. Stack overflow and internal runtime panics remain process-fatal. Traps do not roll back external effects or run user destructors; foreign-library allocations and process-wide locks have the actor runtime’s limits.

TLS sessions reconstruct their configuration inside the connection actor’s heap; traps reclaim it without retaining retired configurations. Configuration rotation and repeated fresh server lifecycles can therefore release old TLS configurations. Size connection heaps for the local certificate, key, trust-anchor and CRL graph; see TLS memory ownership. WebSocket upgrades, dedicated actor SSE helpers, blocking-work offload and multithreaded stages are not provided by this module.

Scheduler adapter

Node[F, H] is an opaque actor-dispatch adapter with private state. Its public started, receive and spawn methods implement the lifecycle expected by std.actor, using Context[Notice[M], ServerError] and returning ServerError. spawn returns an optional child node. These methods advance the server’s admission or connection state; applications use the serve entry points instead of constructing or driving nodes directly. The type and lifecycle methods are public because the existing generic actor dispatcher checks their visibility across module boundaries. This is a visibility exception, not a new raw runtime hook.