Skip to main content

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#[cfg(unix)]
104pub use client::UnixClientConfig;
105
106mod server;
107use server::Config;
108
109mod transport;
110pub use async_global_executor;
111pub use async_io;
112pub use async_net;
113pub use transport::SmolTransport;
114
115mod runtime;
116pub use runtime::SmolRuntime;
117
118#[cfg(all(
119    feature = "reuseport",
120    unix,
121    not(target_os = "solaris"),
122    not(target_os = "illumos"),
123    not(target_os = "cygwin"),
124    not(target_vendor = "apple")
125))]
126mod reuseport;
127
128mod udp;
129pub use udp::SmolUdpSocket;
130
131/// # Runs a trillium handler in a sync context with default config
132///
133/// Runs a trillium handler on the async-global-executor runtime with
134/// default configuration. See [`crate::config`] for what the defaults are
135/// and how to override them
136///
137///
138/// This function will block the current thread until the server shuts
139/// down
140pub fn run(handler: impl Handler) {
141    config().run(handler)
142}
143
144/// # Runs a trillium handler in an async context with default config
145///
146/// Run the provided trillium handler on an already-running async-executor
147/// with default settings. The defaults are the same as [`crate::run`]. To
148/// customize these settings, see [`crate::config`].
149///
150/// This function will poll pending until the server shuts down.
151pub async fn run_async(handler: impl Handler) {
152    config().run_async(handler).await
153}
154
155/// # Configures a server before running it
156///
157/// ## Defaults
158///
159/// The default configuration is as follows:
160///
161/// port: the contents of the `PORT` env var or else 8080
162/// host: the contents of the `HOST` env var or else "localhost"
163/// signals handling and graceful shutdown: enabled on cfg(unix) systems
164/// tcp nodelay: disabled
165/// tls acceptor: none
166///
167/// ## Usage
168///
169/// ```rust
170/// let swansong = trillium_smol::Swansong::new();
171/// # swansong.shut_down(); // stoppping the server immediately for the test
172/// trillium_smol::config()
173///     .with_port(0)
174///     .with_host("127.0.0.1")
175///     .without_signals()
176///     .with_nodelay()
177///     .with_acceptor(()) // see [`trillium_rustls`], [`trillium_native_tls`], and [`trillium_openssl`]
178///     .with_swansong(swansong)
179///     .run(|conn: trillium::Conn| async move { conn.ok("hello smol") });
180/// ```
181///
182/// See [`trillium_server_common::Config`] for more details
183pub fn config() -> Config<()> {
184    Config::new()
185}