pub struct Server { /* private fields */ }Expand description
The main Actus server.
Implementations§
Source§impl Server
impl Server
Sourcepub fn new(router: Router) -> Self
pub fn new(router: Router) -> Self
Create a server for router with default settings: no middleware, no
CORS, the default body cap, and no DoS limits. Configure it with the
with_* builder methods, then call run.
Sourcepub fn with_middleware(self, middleware: impl Middleware + 'static) -> Self
pub fn with_middleware(self, middleware: impl Middleware + 'static) -> Self
Adds a middleware to the server’s request processing chain.
Sourcepub fn with_cors(self, cors: CorsLayer) -> Self
pub fn with_cors(self, cors: CorsLayer) -> Self
Enables CORS with the given policy. The server then answers preflight
(OPTIONS) requests itself and adds the Access-Control-* headers to
every cross-origin response (including error responses). See
CorsLayer.
Sourcepub fn with_compression(self, layer: CompressionLayer) -> Self
pub fn with_compression(self, layer: CompressionLayer) -> Self
Enables response compression (gzip / brotli). For each response Actus
picks an encoding from the request’s Accept-Encoding and compresses
buffered, compressible bodies above the layer’s size threshold. See
CompressionLayer. (Requires the compression feature.)
Sourcepub fn with_max_body_bytes(self, max: usize) -> Self
pub fn with_max_body_bytes(self, max: usize) -> Self
Caps the request body Actus will buffer (default
DEFAULT_MAX_BODY_BYTES = 2 MiB). A larger body is rejected with
413 Payload Too Large before it can exhaust memory — the limit
bounds buffered bytes, so it also covers chunked bodies that lie about
(or omit) Content-Length.
0 is accepted and means “reject every non-empty body” — typically
only useful on a strictly-GET surface that should never see a body.
Sourcepub fn with_request_timeout(self, d: Duration) -> Self
pub fn with_request_timeout(self, d: Duration) -> Self
Cap the total time any single request may take — body parse,
middleware before, handler, middleware after, and finalization
combined. An over-budget request is aborted (the handler’s future
is dropped) and the client gets 504 Gateway Timeout. No timeout
is set by default.
Scope. The timer covers the request/response exchange. A
WebSocket upgrade succeeds inside the timer (the 101 is the
response); the post-upgrade conversation runs in its own task and
is not bound by this timeout.
Effect of an over-budget request. When the timer elapses the in-flight future is dropped, which cancels whatever the handler was awaiting (DB query, channel recv, etc.). The 504 reply is one-shot — the after-chain doesn’t run on it (by definition, some component upstream was unresponsive; running more risks hanging again).
Sourcepub fn with_drain_deadline(self, d: Duration) -> Self
pub fn with_drain_deadline(self, d: Duration) -> Self
Override the grace period for in-flight connections after a
shutdown signal (default DEFAULT_DRAIN_DEADLINE = 30 s).
Anything still running at the deadline is hard-aborted via
JoinSet::shutdown. Use a longer value for surfaces that hold
long-lived connections (large file downloads, WebSockets);
a shorter value for fast-iteration dev workflows. Duration::ZERO
aborts every in-flight task immediately.
Sourcepub fn with_max_connections(self, n: usize) -> Self
pub fn with_max_connections(self, n: usize) -> Self
Cap concurrent connection tasks at n. While the cap is held, the
accept loop pauses on permit acquisition; new SYNs queue in the
kernel’s accept backlog and (once that fills, governed by
SOMAXCONN) get dropped at the OS level. No userland reject /
no-503-per-conn cost — the kernel handles the spillover.
Each connection task holds its permit until it ends, including the
post-handshake WebSocket conversation. Size accordingly: a
with_max_connections(N) server can hold N long-lived WebSockets
before it stops accepting new connections of any kind.
Unbounded by default (no semaphore installed).
Sourcepub fn with_max_inflight_body_bytes(self, n: usize) -> Self
pub fn with_max_inflight_body_bytes(self, n: usize) -> Self
Cap the total bytes being buffered across all in-flight body reads
at n. Each request reserves its per-request cap (see
with_max_body_bytes) from this global budget upfront; if the
budget is exhausted, the request is refused with 503 Service Unavailable (via WebError::Busy) and a short Retry-After.
Together with Self::with_max_connections this puts a hard
ceiling on the framework’s memory under adversarial load:
with_max_connections(C) * with_max_body_bytes(B) is the worst
case absent this knob; with it, the ceiling is min(C * B, this value).
Pre-reserving the per-request cap over-counts (a 1 KB request reserves up to its full cap); the alternative — incremental per-chunk byte accounting — is more code for the same effective ceiling, and a request that has already started buffering can’t be sensibly aborted partway through anyway.
n is clamped to u32::MAX internally (Tokio’s Semaphore
permit count uses u32); for practical deployments this is no
limit (4 GiB).
Unbounded by default.
Sourcepub fn with_header_read_timeout(self, d: Duration) -> Self
pub fn with_header_read_timeout(self, d: Duration) -> Self
Bound how long after starting to read request headers we’ll wait
before dropping the connection. Forwards to hyper’s
http1::Builder::header_read_timeout. Catches slowloris (sending
headers one byte at a time) and clients that TCP-connect-and-send-
nothing — the most common file-descriptor-exhaustion attack on a
keep-alive HTTP server.
Note: hyper 1.x doesn’t have a separate “idle between requests” timeout (after a complete request, an idle keep-alive connection stays open until either side closes or the OS-level TCP keep-alive fires). If that matters for your deployment, either disable keep-alive entirely upstream of Actus or rely on the OS knobs.
No timeout by default (hyper’s default).
Sourcepub async fn run(self, port: u16) -> Result<(), ServerError>
pub async fn run(self, port: u16) -> Result<(), ServerError>
Runs the server on 127.0.0.1:port (loopback only). For a different
bind address — e.g. 0.0.0.0:port to accept connections from other
hosts in a container — use Server::run_on.
Listens for SIGTERM/SIGINT (Unix) or Ctrl-C (cross-platform) and shuts down gracefully: stops accepting new connections, signals in-flight connections to finish, and waits up to 30 seconds for them to drain before returning.
Sourcepub async fn run_on(self, addr: SocketAddr) -> Result<(), ServerError>
pub async fn run_on(self, addr: SocketAddr) -> Result<(), ServerError>
Like Server::run but binds an arbitrary address. Pass
0.0.0.0:port (or [::]:port) to accept connections from other hosts.
Sourcepub async fn run_with_shutdown(
self,
port: u16,
shutdown: impl Future<Output = ()> + Send + 'static,
) -> Result<(), ServerError>
pub async fn run_with_shutdown( self, port: u16, shutdown: impl Future<Output = ()> + Send + 'static, ) -> Result<(), ServerError>
Like Server::run but with a custom shutdown trigger (a future that,
when it resolves, starts the graceful drain). Binds 127.0.0.1:port;
see Server::run_with_shutdown_on for a custom bind address. Useful
for tests or for embedding the server in a larger supervision tree.
Sourcepub async fn run_with_shutdown_on(
self,
addr: SocketAddr,
shutdown: impl Future<Output = ()> + Send + 'static,
) -> Result<(), ServerError>
pub async fn run_with_shutdown_on( self, addr: SocketAddr, shutdown: impl Future<Output = ()> + Send + 'static, ) -> Result<(), ServerError>
The general form: bind addr, serve until shutdown resolves, then
drain. Server::run, Server::run_on, and
Server::run_with_shutdown are thin wrappers over this.
Drain bound. Once shutdown resolves the server stops accepting
and signals every in-flight connection to wind down. The drain
deadline defaults to DEFAULT_DRAIN_DEADLINE (30 s); override
with Server::with_drain_deadline. Anything still running at
the deadline is hard-aborted via JoinSet::shutdown. In particular,
long-lived connections (WebSockets, slow downloads, kept-alive idle
clients) and any connection task that raced the shutdown notification
and missed it both get aborted at the deadline rather than draining
gracefully.