Expand description
simoom: a small, fast, secure embeddable HTTP/1.1, HTTP/2 and HTTP/3 server.
The API is synchronous at its core and needs no async runtime. A Service runs inline
on a loop thread (one loop per core, shared-nothing); slow or blocking work goes to a
bounded offload pool with CoDel admission.
use simoom::{Outcome, Reply, Request, Server, StaticReply};
use std::sync::Arc;
let hello = Arc::new(StaticReply::new(200, &[("content-type", "text/plain")], b"Hello, World!"));
let server = Server::builder()
.listen("127.0.0.1:8080")
.threads(0) // one loop per core
.service(move |_req: &mut Request<'_>, reply: Reply<'_>| -> Outcome { reply.fixed(&hello) })
.start()
.unwrap();
println!("listening on {}", server.local_addr());§Answering
A Reply ends in exactly one of: an immediate response (Reply::body,
Reply::fixed…), a Responder for later (Reply::defer), a streamed body
(Reply::stream, Reply::sse with a BodySink), offloaded work (Reply::offload,
blocking), or a WebSocket (Reply::websocket).
§Blocking work: which path to use
A service must never block a loop thread. Pick one of:
| path | when |
|---|---|
Reply::defer + your own threads | the host already has a worker pool, its own naming, limits and backpressure (an integration engine such as Cameleer): copy the request with Request::to_owned_request, answer through the Responder from any thread. Limits::defer_timeout bounds the wait, Responder::is_cancelled tells the worker the client left. simoom’s pool and CoDel are not involved. |
blocking, Reply::blocking, Reply::offload | blocking handlers without a pool of their own (database or HTTP calls of up to seconds). The offload pool starts up to Limits::offload_threads workers on demand (32 per CPU by default, 64 to 512) and answers a fast 503 only when its queue keeps growing past what the workers absorb (CoDel with a target of at least the jobs’ mean run time). |
| inline | microseconds of CPU, no I/O. The stall watchdog (feature watchdog) moves a route that turns out slow to the offload pool, as a safety net, not a plan: each call before the move holds the loop. |
§Request bodies
Bodies up to Limits::body_buffer are buffered whole (Request::body); longer ones
stream through a BodySource (Request::take_body) with pull-based backpressure.
§Protocols and features
| feature | what it adds |
|---|---|
http1 (default) | HTTP/1.1: keep-alive, pipelining, chunked, 100-continue |
http2 (default) | h2c prior knowledge, Upgrade: h2c, h2 over TLS |
websocket (default), alias ws | WebSocket upgrades |
tls | TLS 1.3 listeners ([Builder::listen_tls]), ALPN h2 / http/1.1 |
http3 | HTTP/3 over QUIC ([Builder::listen_quic]); announced with alt-svc |
full | all of them |
compression | gzip and deflate responses negotiated from Accept-Encoding (Builder::compression), request body decoding (Request::decoded_body) |
websocket-h2 | WebSocket over HTTP/2 (RFC 8441 extended CONNECT) with the same Reply::websocket |
h2-keepalive | HTTP/2 PING keepalive (Limits::h2_keepalive) |
The last three are not part of default or full, which keep their size budgets.
§Safety nets
- Every limit of
Limitsis enforced (head size, body size, timeouts, minimum body rate, write deadline, connection caps). - A handler panic becomes a 500 and the connection closes.
- The stall watchdog finds inline handlers that use more than
Limits::inline_budgetof CPU, or block, and moves just their route to the offload pool afterLimits::stalls_before_offloadstalls withinLimits::stall_window; a route comes back afterLimits::stall_recoveryfast calls there (cargo featurewatchdog, on by default). - Graceful stop (
ServerHandle::stop): GOAWAY on HTTP/2 and HTTP/3,Connection: closeon HTTP/1.1.
Structs§
- Blocking
- The service returned by
blocking. - Body
Sink - The body of a streamed response (
crate::Reply::stream,crate::Reply::sse): chunked on HTTP/1.1, DATA frames on HTTP/2 and HTTP/3.Send: write from any thread. - Body
Source - A request body that streams (
crate::Request::take_body): bodies larger thancrate::Limits::body_bufferor of unknown length.Send: read it from any thread. - Builder
- Configures and starts a server. See
Server::builder. - Completion
- One finished request, for access logs and metrics (
Builder::on_complete). - Frame
Budgets - HTTP/2 frame budgets (
Limits::h2_frame_budgets): how many of each frame kind a peer may send (or make us send) within a sliding window before the connection is closed withGOAWAY(ENHANCE_YOUR_CALM).u32::MAXturns one off. They defend against the HTTP/2 floods (CVE-2019-9511..9518, Rapid Reset CVE-2023-44487, MadeYouReset CVE-2025-8671); the defaults leave ordinary clients, browsers and gRPC far below them. - Limits
- Server limits. Every field is a hard bound; exceeding one gives a specific status code (or
closes the connection) and increments a counter in
crate::Stats. - Offload
Stats - Counters of an
OffloadPool. - Owned
Request - An owned request, for work on other threads (
crate::blocking,crate::Reply::offload). - Ready
- The future returned by
BodySink::ready. - Reply
- How a
crate::Serviceanswers one request. Build the response withReply::statusandReply::header, then finish it with exactly one of: - Request
- A request, borrowed from the connection for the duration of
crate::Service::call. - Responder
- Answers a request later, from any thread (
crate::Reply::defer). It completes exactly once:Responder::sendorResponder::streamconsume it, and dropping it unanswered sends a 500. - Response
- An owned response: what a
crate::Responderor acrate::blockinghandler sends. - Router
- Routes requests by method and path.
- Server
- Entry point:
Server::builder. - Server
Handle - A running server. Dropping it stops at once; use
ServerHandle::stopandServerHandle::joinfor a graceful stop. - Static
Reply - A reply serialized once, for the fast path of fixed answers (
crate::Reply::fixed). On HTTP/1.1 it is copied out with the currentDatespliced in: one copy, no formatting. - Stats
- Server counters.
- TlsInfo
- The TLS session of a connection (
Request::tls): what Camel exposes asCamelHttpSslSessionand theCamelHttpSslClientCertheaders. Captured once, when the handshake completes. - WebSocket
- The connection, inside a
WebSocketHandlercallback. - WsHandle
- Sends on a WebSocket from any thread. Messages to a closed connection are dropped.
Enums§
- Backend
- Which I/O back end a driver uses.
- Body
- A response body.
- Outcome
- What
Service::callreturns: informational. The server tracks what theReplydid; a call that neither answered nor deferred gets a 500. - Version
- The HTTP version a request arrived with.
- WsMessage
- A complete message.
Traits§
- Service
- Handles requests. It runs inline on a loop thread, so it must not block: answer at once
(microseconds), or defer (
Reply::defer), or move the work to the offload pool (Reply::offload,blocking). - WebSocket
Handler - Handles one WebSocket connection, on its loop thread. Must not block.
Functions§
- blocking
- A service that runs blocking code on the shared, bounded offload pool: the request is
copied (
OwnedRequest),fruns on a worker thread, and itsResponsecomes back to the connection. When the pool is full or CoDel reports overload (jobs waiting longer thancrate::Limits::offload_targetfor a whole interval) the request gets a fast503withRetry-Afterinstead of queueing. - percent_
decode - Decodes
%XXescapes;Nonefor a malformed escape.