trillium_smol/lib.rs
1#![deny(
2 clippy::dbg_macro,
3 missing_copy_implementations,
4 rustdoc::missing_crate_level_docs,
5 missing_debug_implementations,
6 missing_docs,
7 nonstandard_style,
8 unused_qualifications
9)]
10
11//! # Trillium adapter using smol and async-global-executor
12//!
13//! ## Default / 12-factor applications
14//!
15//! ```rust,no_run
16//! trillium_smol::run(|conn: trillium::Conn| async move { conn.ok("hello smol") });
17//! ```
18//!
19//! ## Server configuration
20//!
21//! For more details, see [trillium_smol::config](crate::config).
22//!
23//! ```rust
24//! let swansong = trillium_smol::Swansong::new();
25//! # swansong.shut_down(); // stoppping the server immediately for the test
26//! trillium_smol::config()
27//! .with_port(0)
28//! .with_host("127.0.0.1")
29//! .without_signals()
30//! .with_nodelay()
31//! .with_acceptor(()) // see [`trillium_rustls`], [`trillium_native_tls`], and [`trillium_openssl`]
32//! .with_swansong(swansong)
33//! .run(|conn: trillium::Conn| async move { conn.ok("hello smol") });
34//! ```
35//!
36//! For advanced binding — several listeners on one server, fallible binds you
37//! can recover from, or adopting an already-bound socket — call `.listeners()`
38//! to get a [`ListenerConfig`].
39//!
40//! ## Thread-per-core with `SO_REUSEPORT` on Linux
41//!
42//! `SO_REUSEPORT` is a socket option that lets several sockets bind the same
43//! address and port at once, with the kernel distributing incoming connections
44//! across them. Enable the `reuseport` cargo feature on Linux to use it for
45//! thread-per-core fan-out:
46//!
47//! ```rust,ignore
48//! use trillium::Conn;
49//!
50//! fn main() -> std::io::Result<()> {
51//! trillium_smol::config()
52//! .listeners()
53//! .bind_reuseport_tcp(8080)?
54//! .run(|conn: Conn| async move { conn.ok("hello") });
55//! Ok(())
56//! }
57//! ```
58//!
59//! Each worker thread runs its own single-threaded executor, pinned to a core,
60//! driving that worker's accept loop — one `SO_REUSEPORT` listener per worker,
61//! and every connection it accepts is handled on that same executor. The shared
62//! multi-threaded global executor is still present alongside them, hosting
63//! HTTP/3, signal handling, and the application tasks you spawn, so QUIC is
64//! never fanned out this way. Set the worker count with
65//! `.with_reuseport_workers(n)`; it defaults to the `WORKERS` environment
66//! variable, or if that's not set, to available parallelism.
67//!
68//! This trades the global executor's load balancing for per-core locality,
69//! which can improve throughput for short, CPU-cheap requests served over many
70//! connections. It is gated off on platforms where plain `SO_REUSEPORT` does
71//! not distribute connections (including macOS), where it would offer no
72//! benefit.
73//!
74//! ## Client
75//!
76//! ```rust
77//! # #[cfg(feature = "smol")]
78//! trillium_testing::with_server("ok", |url| async move {
79//! use trillium_client::{Client, Conn};
80//! use trillium_smol::TcpConnector;
81//! let mut conn = Conn::<TcpConnector>::get(url.clone()).execute().await?;
82//! assert_eq!(conn.response_body().read_string().await?, "ok");
83//!
84//! let client = Client::<TcpConnector>::new();
85//! let mut conn = client.get(url);
86//! conn.send().await?;
87//! assert_eq!(conn.response_body().read_string().await?, "ok");
88//! Ok(())
89//! });
90//! ```
91
92#[cfg(test)]
93#[doc = include_str!("../README.md")]
94mod readme {}
95
96use trillium::Handler;
97pub use trillium_server_common::{
98 Binding, Connector, IntoListenAddr, ListenerConfig, Runtime, RuntimeTrait, Swansong,
99};
100
101mod client;
102pub use client::ClientConfig;
103
104mod server;
105use server::Config;
106
107mod transport;
108pub use async_global_executor;
109pub use async_io;
110pub use async_net;
111pub use transport::SmolTransport;
112
113mod runtime;
114pub use runtime::SmolRuntime;
115
116#[cfg(all(
117 feature = "reuseport",
118 unix,
119 not(target_os = "solaris"),
120 not(target_os = "illumos"),
121 not(target_os = "cygwin"),
122 not(target_vendor = "apple")
123))]
124mod reuseport;
125
126mod udp;
127pub use udp::SmolUdpSocket;
128
129/// # Runs a trillium handler in a sync context with default config
130///
131/// Runs a trillium handler on the async-global-executor runtime with
132/// default configuration. See [`crate::config`] for what the defaults are
133/// and how to override them
134///
135///
136/// This function will block the current thread until the server shuts
137/// down
138pub fn run(handler: impl Handler) {
139 config().run(handler)
140}
141
142/// # Runs a trillium handler in an async context with default config
143///
144/// Run the provided trillium handler on an already-running async-executor
145/// with default settings. The defaults are the same as [`crate::run`]. To
146/// customize these settings, see [`crate::config`].
147///
148/// This function will poll pending until the server shuts down.
149pub async fn run_async(handler: impl Handler) {
150 config().run_async(handler).await
151}
152
153/// # Configures a server before running it
154///
155/// ## Defaults
156///
157/// The default configuration is as follows:
158///
159/// port: the contents of the `PORT` env var or else 8080
160/// host: the contents of the `HOST` env var or else "localhost"
161/// signals handling and graceful shutdown: enabled on cfg(unix) systems
162/// tcp nodelay: disabled
163/// tls acceptor: none
164///
165/// ## Usage
166///
167/// ```rust
168/// let swansong = trillium_smol::Swansong::new();
169/// # swansong.shut_down(); // stoppping the server immediately for the test
170/// trillium_smol::config()
171/// .with_port(0)
172/// .with_host("127.0.0.1")
173/// .without_signals()
174/// .with_nodelay()
175/// .with_acceptor(()) // see [`trillium_rustls`], [`trillium_native_tls`], and [`trillium_openssl`]
176/// .with_swansong(swansong)
177/// .run(|conn: trillium::Conn| async move { conn.ok("hello smol") });
178/// ```
179///
180/// See [`trillium_server_common::Config`] for more details
181pub fn config() -> Config<()> {
182 Config::new()
183}