trillium_server_common/
config.rs

1use crate::{
2    server_handle::CompletionFuture, Acceptor, CloneCounterObserver, Server, ServerHandle, Stopper,
3};
4use async_cell::sync::AsyncCell;
5use std::{
6    marker::PhantomData,
7    net::SocketAddr,
8    sync::{Arc, RwLock},
9};
10use trillium::{Handler, HttpConfig, Info};
11
12/**
13# Primary entrypoint for configuring and running a trillium server
14
15The associated methods on this struct are intended to be chained.
16
17## Example
18```rust,no_run
19trillium_smol::config() // or trillium_async_std, trillium_tokio
20    .with_port(8080) // the default
21    .with_host("localhost") // the default
22    .with_nodelay()
23    .with_max_connections(Some(10000))
24    .without_signals()
25    .run(|conn: trillium::Conn| async move { conn.ok("hello") });
26```
27
28# Socket binding
29
30The socket binding logic is as follows:
31
32* If a LISTEN_FD environment variable is available on `cfg(unix)`
33  systems, that will be used, overriding host and port settings
34* Otherwise:
35  * Host will be selected from explicit configuration using
36    [`Config::with_host`] or else the `HOST` environment variable,
37    or else a default of "localhost".
38    * On `cfg(unix)` systems only: If the host string (as set by env var
39      or direct config) begins with `.`, `/`, or `~`, it is
40      interpreted to be a path, and trillium will bind to it as a unix
41      domain socket. Port will be ignored. The socket will be deleted
42      on clean shutdown.
43  * Port will be selected from explicit configuration using
44    [`Config::with_port`] or else the `PORT` environment variable,
45    or else a default of 8080.
46
47## Signals
48
49On `cfg(unix)` systems, `SIGTERM`, `SIGINT`, and `SIGQUIT` are all
50registered to perform a graceful shutdown on the first signal and an
51immediate shutdown on a subsequent signal. This behavior may change as
52trillium matures. To disable this behavior, use
53[`Config::without_signals`].
54
55## For runtime adapter authors
56
57In order to use this to _implement_ a trillium server, see
58[`trillium_server_common::ConfigExt`](crate::ConfigExt)
59*/
60
61#[derive(Debug)]
62pub struct Config<ServerType, AcceptorType> {
63    pub(crate) acceptor: AcceptorType,
64    pub(crate) port: Option<u16>,
65    pub(crate) host: Option<String>,
66    pub(crate) nodelay: bool,
67    pub(crate) stopper: Stopper,
68    pub(crate) observer: CloneCounterObserver,
69    pub(crate) register_signals: bool,
70    pub(crate) max_connections: Option<usize>,
71    pub(crate) info: Arc<AsyncCell<Info>>,
72    pub(crate) completion_future: CompletionFuture,
73    pub(crate) binding: RwLock<Option<ServerType>>,
74    pub(crate) server: PhantomData<ServerType>,
75    pub(crate) http_config: HttpConfig,
76}
77
78impl<ServerType, AcceptorType> Config<ServerType, AcceptorType>
79where
80    ServerType: Server,
81    AcceptorType: Acceptor<ServerType::Transport>,
82{
83    /// Starts an async runtime and runs the provided handler with
84    /// this config in that runtime. This is the appropriate
85    /// entrypoint for applications that do not need to spawn tasks
86    /// outside of trillium's web server. For applications that embed a
87    /// trillium server inside of an already-running async runtime, use
88    /// [`Config::run_async`]
89    pub fn run<H: Handler>(self, h: H) {
90        ServerType::run(self, h)
91    }
92
93    /// Runs the provided handler with this config, in an
94    /// already-running runtime. This is the appropriate entrypoint
95    /// for an application that needs to spawn async tasks that are
96    /// unrelated to the trillium application. If you do not need to spawn
97    /// other tasks, [`Config::run`] is the preferred entrypoint
98    pub async fn run_async(self, handler: impl Handler) {
99        let completion_future = self.completion_future.clone();
100        ServerType::run_async(self, handler).await;
101        completion_future.notify()
102    }
103
104    /// Spawns the server onto the async runtime, returning a
105    /// ServerHandle that can be awaited directly to return an
106    /// [`Info`] or used with [`ServerHandle::info`] and
107    /// [`ServerHandle::stop`]
108    pub fn spawn(self, handler: impl Handler) -> ServerHandle {
109        let server_handle = self.handle();
110        ServerType::spawn(self.run_async(handler));
111        server_handle
112    }
113
114    /// Returns a [`ServerHandle`] for this Config. This is useful
115    /// when spawning the server onto a runtime.
116    pub fn handle(&self) -> ServerHandle {
117        ServerHandle {
118            stopper: self.stopper.clone(),
119            info: self.info.clone(),
120            completion: self.completion_future.clone(),
121            observer: self.observer.clone(),
122        }
123    }
124
125    /// Configures the server to listen on this port. The default is
126    /// the PORT environment variable or 8080
127    pub fn with_port(mut self, port: u16) -> Self {
128        if self.has_binding() {
129            eprintln!("constructing a config with both a port and a pre-bound listener will ignore the port. this may be a panic in the future");
130        }
131        self.port = Some(port);
132        self
133    }
134
135    /// Configures the server to listen on this host or ip
136    /// address. The default is the HOST environment variable or
137    /// "localhost"
138    pub fn with_host(mut self, host: &str) -> Self {
139        if self.has_binding() {
140            eprintln!("constructing a config with both a host and a pre-bound listener will ignore the host. this may be a panic in the future");
141        }
142        self.host = Some(host.into());
143        self
144    }
145
146    /// Configures the server to NOT register for graceful-shutdown
147    /// signals with the operating system. Default behavior is for the
148    /// server to listen for SIGINT and SIGTERM and perform a graceful
149    /// shutdown.
150    pub fn without_signals(mut self) -> Self {
151        self.register_signals = false;
152        self
153    }
154
155    /// Configures the tcp listener to use TCP_NODELAY. See
156    /// <https://en.wikipedia.org/wiki/Nagle%27s_algorithm> for more
157    /// information on this setting.
158    pub fn with_nodelay(mut self) -> Self {
159        self.nodelay = true;
160        self
161    }
162
163    /// Configures the server to listen on the ip and port specified
164    /// by the provided socketaddr. This is identical to
165    /// `self.with_host(&socketaddr.ip().to_string()).with_port(socketaddr.port())`
166    pub fn with_socketaddr(self, socketaddr: SocketAddr) -> Self {
167        self.with_host(&socketaddr.ip().to_string())
168            .with_port(socketaddr.port())
169    }
170
171    /// Configures the tls acceptor for this server
172    pub fn with_acceptor<A: Acceptor<ServerType::Transport>>(
173        self,
174        acceptor: A,
175    ) -> Config<ServerType, A> {
176        Config {
177            acceptor,
178            host: self.host,
179            port: self.port,
180            nodelay: self.nodelay,
181            server: PhantomData,
182            stopper: self.stopper,
183            observer: self.observer,
184            register_signals: self.register_signals,
185            max_connections: self.max_connections,
186            info: self.info,
187            completion_future: self.completion_future,
188            binding: self.binding,
189            http_config: self.http_config,
190        }
191    }
192
193    /// use the specific [`Stopper`] provided
194    pub fn with_stopper(mut self, stopper: Stopper) -> Self {
195        self.stopper = stopper;
196        self
197    }
198
199    /// use the specified [`CloneCounterObserver`] to monitor or
200    /// modify the outstanding connection count for graceful shutdown
201    pub fn with_observer(mut self, observer: CloneCounterObserver) -> Self {
202        self.observer = observer;
203        self
204    }
205
206    /**
207    Configures the maximum number of connections to accept. The
208    default is 75% of the soft rlimit_nofile (`ulimit -n`) on unix
209    systems, and None on other sytems.
210    */
211    pub fn with_max_connections(mut self, max_connections: Option<usize>) -> Self {
212        self.max_connections = max_connections;
213        self
214    }
215
216    /// configures trillium-http performance and security tuning parameters.
217    ///
218    /// See [`HttpConfig`] for documentation
219    pub fn with_http_config(mut self, http_config: HttpConfig) -> Self {
220        self.http_config = http_config;
221        self
222    }
223
224    /// Use a pre-bound transport stream as server.
225    ///
226    /// The argument to this varies for different servers, but usually
227    /// accepts the runtime's TcpListener and, on unix platforms, the UnixListener.
228    ///
229    /// ## Note well
230    ///
231    /// Many of the other options on this config will be ignored if you provide a listener. In
232    /// particular, `host` and `port` will be ignored. All of the other options will be used.
233    ///
234    /// Additionally, cloning this config will not clone the listener.
235    pub fn with_prebound_server(mut self, server: impl Into<ServerType>) -> Self {
236        if self.host.is_some() {
237            eprintln!("constructing a config with both a host and a pre-bound listener will ignore the host. this may be a panic in the future");
238        }
239
240        if self.port.is_some() {
241            eprintln!("constructing a config with both a port and a pre-bound listener will ignore the port. this may be a panic in the future");
242        }
243
244        self.binding = RwLock::new(Some(server.into()));
245        self
246    }
247
248    fn has_binding(&self) -> bool {
249        self.binding
250            .read()
251            .as_deref()
252            .map_or(false, Option::is_some)
253    }
254}
255
256impl<ServerType: Server> Config<ServerType, ()> {
257    /// build a new config with default acceptor
258    pub fn new() -> Self {
259        Self::default()
260    }
261}
262
263impl<ServerType, AcceptorType> Clone for Config<ServerType, AcceptorType>
264where
265    ServerType: Server,
266    AcceptorType: Acceptor<ServerType::Transport> + Clone,
267{
268    fn clone(&self) -> Self {
269        if self.has_binding() {
270            eprintln!("cloning a Config with a pre-bound listener will not clone the listener. this may be a panic in the future.");
271        }
272
273        Self {
274            acceptor: self.acceptor.clone(),
275            port: self.port,
276            host: self.host.clone(),
277            server: PhantomData,
278            nodelay: self.nodelay,
279            stopper: self.stopper.clone(),
280            observer: self.observer.clone(),
281            register_signals: self.register_signals,
282            max_connections: self.max_connections,
283            info: AsyncCell::shared(),
284            completion_future: CompletionFuture::new(),
285            binding: RwLock::new(None),
286            http_config: self.http_config,
287        }
288    }
289}
290
291impl<ServerType: Server> Default for Config<ServerType, ()> {
292    fn default() -> Self {
293        #[cfg(unix)]
294        let max_connections = {
295            rlimit::getrlimit(rlimit::Resource::NOFILE)
296                .ok()
297                .and_then(|(soft, _hard)| soft.try_into().ok())
298                .map(|limit: usize| 3 * limit / 4)
299        };
300
301        #[cfg(not(unix))]
302        let max_connections = None;
303
304        log::debug!("using max connections of {:?}", max_connections);
305
306        Self {
307            acceptor: (),
308            port: None,
309            host: None,
310            server: PhantomData,
311            nodelay: false,
312            stopper: Stopper::new(),
313            observer: CloneCounterObserver::new(),
314            register_signals: cfg!(unix),
315            max_connections,
316            info: AsyncCell::shared(),
317            completion_future: CompletionFuture::new(),
318            binding: RwLock::new(None),
319            http_config: HttpConfig::default(),
320        }
321    }
322}