Skip to main content

simoom/
lib.rs

1//! simoom: a small, fast, secure embeddable HTTP/1.1, HTTP/2 and HTTP/3 server.
2//!
3//! The API is synchronous at its core and needs no async runtime. A [`Service`] runs **inline
4//! on a loop thread** (one loop per core, shared-nothing); slow or blocking work goes to a
5//! bounded offload pool with CoDel admission.
6//!
7//! ```no_run
8//! use simoom::{Outcome, Reply, Request, Server, StaticReply};
9//! use std::sync::Arc;
10//!
11//! let hello = Arc::new(StaticReply::new(200, &[("content-type", "text/plain")], b"Hello, World!"));
12//! let server = Server::builder()
13//!     .listen("127.0.0.1:8080")
14//!     .threads(0) // one loop per core
15//!     .service(move |_req: &mut Request<'_>, reply: Reply<'_>| -> Outcome { reply.fixed(&hello) })
16//!     .start()
17//!     .unwrap();
18//! println!("listening on {}", server.local_addr());
19//! # server.stop(std::time::Duration::from_secs(1));
20//! # server.join();
21//! ```
22//!
23//! # Answering
24//!
25//! A [`Reply`] ends in exactly one of: an immediate response ([`Reply::body`],
26//! [`Reply::fixed`]...), a [`Responder`] for later ([`Reply::defer`]), a streamed body
27//! ([`Reply::stream`], [`Reply::sse`] with a [`BodySink`]), offloaded work ([`Reply::offload`],
28//! [`blocking`]), or a WebSocket ([`Reply::websocket`]).
29//!
30//! # Blocking work: which path to use
31//!
32//! A service must never block a loop thread. Pick one of:
33//!
34//! | path | when |
35//! |---|---|
36//! | [`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. |
37//! | [`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). |
38//! | 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. |
39//!
40//! # Request bodies
41//!
42//! Bodies up to [`Limits::body_buffer`] are buffered whole ([`Request::body`]); longer ones
43//! stream through a [`BodySource`] ([`Request::take_body`]) with pull-based backpressure.
44//!
45//! # Protocols and features
46//!
47//! | feature | what it adds |
48//! |---|---|
49//! | `http1` (default) | HTTP/1.1: keep-alive, pipelining, chunked, 100-continue |
50//! | `http2` (default) | h2c prior knowledge, `Upgrade: h2c`, h2 over TLS |
51//! | `websocket` (default), alias `ws` | WebSocket upgrades |
52//! | `tls` | TLS 1.3 listeners ([`Builder::listen_tls`]), ALPN h2 / http/1.1 |
53//! | `http3` | HTTP/3 over QUIC ([`Builder::listen_quic`]); announced with `alt-svc` |
54//! | `full` | all of them |
55//! | `compression` | gzip and deflate responses negotiated from `Accept-Encoding` (`Builder::compression`), request body decoding (`Request::decoded_body`) |
56//! | `websocket-h2` | WebSocket over HTTP/2 (RFC 8441 extended CONNECT) with the same [`Reply::websocket`] |
57//! | `h2-keepalive` | HTTP/2 PING keepalive ([`Limits::h2_keepalive`]) |
58//!
59//! The last three are not part of `default` or `full`, which keep their size budgets.
60//!
61//! # Safety nets
62//!
63//! - Every limit of [`Limits`] is enforced (head size, body size, timeouts, minimum body rate,
64//!   write deadline, connection caps).
65//! - A handler panic becomes a 500 and the connection closes.
66//! - The stall watchdog finds inline handlers that use more than [`Limits::inline_budget`] of
67//!   CPU, or block, and moves just their route to the offload pool after
68//!   [`Limits::stalls_before_offload`] stalls within [`Limits::stall_window`]; a route comes
69//!   back after [`Limits::stall_recovery`] fast calls there (cargo feature `watchdog`, on by
70//!   default).
71//! - Graceful stop ([`ServerHandle::stop`]): GOAWAY on HTTP/2 and HTTP/3, `Connection: close`
72//!   on HTTP/1.1.
73
74#![forbid(unsafe_code)]
75// A build without any protocol feature serves nothing; it still compiles, quietly.
76#![cfg_attr(not(any(feature = "http1", feature = "http2", feature = "http3")), allow(dead_code, unused_imports, unused_variables))]
77// Builds without HTTP/1.1 (h2c only, h2 over TLS only, HTTP/3 only) leave the HTTP/1.1 output
78// helpers and fields unused; the linker drops them.
79#![cfg_attr(not(feature = "http1"), allow(dead_code))]
80
81#[cfg(feature = "compression")]
82mod compress;
83#[cfg(feature = "conditional")]
84pub mod conditional;
85#[cfg(feature = "cookies")]
86pub mod cookie;
87#[cfg(feature = "auth")]
88pub mod auth;
89#[cfg(feature = "cors")]
90pub mod cors;
91#[cfg(any(feature = "cookies", feature = "conditional"))]
92mod httpdate;
93#[cfg(feature = "multipart")]
94pub mod multipart;
95mod conn;
96mod handle;
97#[cfg(feature = "http1")]
98mod h1;
99#[cfg(feature = "http2")]
100mod h2;
101#[cfg(feature = "http3")]
102mod h3;
103mod limits;
104mod reply;
105mod request;
106mod router;
107mod server;
108mod service;
109mod types;
110mod util;
111#[cfg(feature = "watchdog")]
112mod watchdog;
113#[cfg(not(feature = "watchdog"))]
114#[path = "watchdog_off.rs"]
115mod watchdog;
116#[cfg(feature = "websocket")]
117mod ws;
118
119#[cfg(feature = "compression")]
120pub use compress::{Compression, ContentCoding, DecodeBodyError, DecodedRead};
121pub use handle::{BodySink, BodySource, Ready, Responder};
122pub use limits::{FrameBudgets, Limits};
123pub use reply::Reply;
124pub use request::{OwnedRequest, Request, TlsInfo};
125pub use router::Router;
126pub use server::{Builder, Completion, Server, ServerHandle, Stats};
127pub use service::{blocking, Blocking, Outcome, Service};
128#[doc(hidden)]
129pub use service::WatchRef;
130pub use simoom_rt::offload::OffloadStats;
131pub use simoom_rt::Backend;
132pub use types::{Body, Response, StaticReply, Version};
133pub use util::percent_decode;
134#[cfg(feature = "websocket")]
135pub use ws::{WebSocket, WebSocketHandler, WsHandle, WsMessage};
136/// permessage-deflate settings ([`Builder::websocket_deflate`]).
137#[cfg(feature = "ws-deflate")]
138pub use simoom_http1::ws::deflate::DeflateConfig as WsDeflate;
139
140/// TLS configuration types (feature `tls`).
141#[cfg(feature = "tls")]
142pub mod tls {
143    pub use simoom_tls::certgen;
144    pub use simoom_tls::{CertifiedKey, ServerConfig};
145}