Skip to main content

actix_server/
builder.rs

1use std::{future::Future, io, num::NonZeroUsize, time::Duration};
2
3use actix_rt::net::TcpStream;
4use futures_core::future::BoxFuture;
5use tokio::sync::{
6    mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
7    watch,
8};
9
10use crate::{
11    server::ServerCommand,
12    service::{InternalServiceFactory, ServerServiceFactory, StreamNewService},
13    socket::{create_mio_tcp_listener, MioListener, MioTcpListener, StdTcpListener, ToSocketAddrs},
14    worker::ServerWorkerConfig,
15    Server,
16};
17
18/// Multipath TCP (MPTCP) preference.
19///
20/// Currently only useful on Linux.
21///
22#[cfg_attr(target_os = "linux", doc = "Also see [`ServerBuilder::mptcp()`].")]
23#[derive(Debug, Clone)]
24pub enum MpTcp {
25    /// MPTCP will not be used when binding sockets.
26    Disabled,
27
28    /// MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be
29    /// attempted, too.
30    TcpFallback,
31
32    /// MPTCP will be used when binding sockets (with no fallback).
33    NoFallback,
34}
35
36/// [Server] builder.
37pub struct ServerBuilder {
38    pub(crate) threads: usize,
39    pub(crate) token: usize,
40    pub(crate) backlog: u32,
41    pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
42    pub(crate) sockets: Vec<(usize, String, MioListener)>,
43    pub(crate) mptcp: MpTcp,
44    pub(crate) exit: bool,
45    pub(crate) listen_os_signals: bool,
46    pub(crate) shutdown_signal: Option<BoxFuture<'static, ()>>,
47    pub(crate) graceful_shutdown_tx: watch::Sender<()>,
48    pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
49    pub(crate) cmd_rx: UnboundedReceiver<ServerCommand>,
50    pub(crate) worker_config: ServerWorkerConfig,
51}
52
53impl Default for ServerBuilder {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl ServerBuilder {
60    /// Create new Server builder instance
61    pub fn new() -> ServerBuilder {
62        let (cmd_tx, cmd_rx) = unbounded_channel();
63        let (graceful_shutdown_tx, _) = watch::channel(());
64
65        ServerBuilder {
66            threads: std::thread::available_parallelism().map_or(2, NonZeroUsize::get),
67            token: 0,
68            factories: Vec::new(),
69            sockets: Vec::new(),
70            backlog: 2048,
71            mptcp: MpTcp::Disabled,
72            exit: false,
73            listen_os_signals: true,
74            shutdown_signal: None,
75            graceful_shutdown_tx,
76            cmd_tx,
77            cmd_rx,
78            worker_config: ServerWorkerConfig::default(),
79        }
80    }
81
82    /// Returns a signal that resolves when this server starts a graceful shutdown.
83    ///
84    /// The signal cannot stop the server. It only propagates a shutdown that was started by an OS
85    /// signal, [`ServerHandle::stop`](crate::ServerHandle::stop), or [`Self::shutdown_signal`].
86    pub fn graceful_shutdown_signal(&self) -> crate::GracefulShutdownSignal {
87        crate::GracefulShutdownSignal::new(self.graceful_shutdown_tx.subscribe())
88    }
89
90    /// Sets number of workers to start.
91    ///
92    /// See [`bind()`](Self::bind()) for more details on how worker count affects the number of
93    /// server factory instantiations.
94    ///
95    /// The default worker count is the determined by [`std::thread::available_parallelism()`]. See
96    /// its documentation to determine what behavior you should expect when server is run.
97    ///
98    /// `num` must be greater than 0.
99    ///
100    /// # Panics
101    ///
102    /// Panics if `num` is 0.
103    pub fn workers(mut self, num: usize) -> Self {
104        assert_ne!(num, 0, "workers must be greater than 0");
105        self.threads = num;
106        self
107    }
108
109    /// Set max number of threads for each worker's blocking task thread pool.
110    ///
111    /// One thread pool is set up **per worker**; not shared across workers.
112    ///
113    /// # Examples:
114    /// ```
115    /// # use actix_server::ServerBuilder;
116    /// let builder = ServerBuilder::new()
117    ///     .workers(4) // server has 4 worker thread.
118    ///     .worker_max_blocking_threads(4); // every worker has 4 max blocking threads.
119    /// ```
120    ///
121    /// See [tokio::runtime::Builder::max_blocking_threads] for behavior reference.
122    pub fn worker_max_blocking_threads(mut self, num: usize) -> Self {
123        self.worker_config.max_blocking_threads(num);
124        self
125    }
126
127    /// Set the maximum number of pending connections.
128    ///
129    /// This refers to the number of clients that can be waiting to be served. Exceeding this number
130    /// results in the client getting an error when attempting to connect. It should only affect
131    /// servers under significant load.
132    ///
133    /// Generally set in the 64-2048 range. Default value is 2048.
134    ///
135    /// This method should be called before `bind()` method call.
136    pub fn backlog(mut self, num: u32) -> Self {
137        self.backlog = num;
138        self
139    }
140
141    /// Sets MultiPath TCP (MPTCP) preference on bound sockets.
142    ///
143    /// Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance
144    /// by sharing a network data stream across multiple underlying TCP sessions. See [mptcp.dev]
145    /// for more info about MPTCP itself.
146    ///
147    /// MPTCP is available on Linux kernel version 5.6 and higher. In addition, you'll also need to
148    /// ensure the kernel option is enabled using `sysctl net.mptcp.enabled=1`.
149    ///
150    /// This method will have no effect if called after a `bind()`.
151    ///
152    /// [mptcp.dev]: https://www.mptcp.dev
153    #[cfg(target_os = "linux")]
154    pub fn mptcp(mut self, mptcp_enabled: MpTcp) -> Self {
155        self.mptcp = mptcp_enabled;
156        self
157    }
158
159    /// Sets the maximum per-worker number of concurrent connections.
160    ///
161    /// All socket listeners will stop accepting connections when this limit is reached for
162    /// each worker.
163    ///
164    /// By default max connections is set to a 25k per worker.
165    pub fn max_concurrent_connections(mut self, num: usize) -> Self {
166        self.worker_config.max_concurrent_connections(num);
167        self
168    }
169
170    #[doc(hidden)]
171    #[deprecated(since = "2.0.0", note = "Renamed to `max_concurrent_connections`.")]
172    pub fn maxconn(self, num: usize) -> Self {
173        self.max_concurrent_connections(num)
174    }
175
176    /// Sets flag to stop Actix `System` after server shutdown.
177    ///
178    /// This has no effect when server is running in a Tokio-only runtime.
179    pub fn system_exit(mut self) -> Self {
180        self.exit = true;
181        self
182    }
183
184    /// Disables OS signal handling.
185    pub fn disable_signals(mut self) -> Self {
186        self.listen_os_signals = false;
187        self
188    }
189
190    /// Specify shutdown signal from a future.
191    ///
192    /// Using this method will prevent OS signal handlers being set up.
193    ///
194    /// Typically, a `CancellationToken` will be used, but any future _can_ be.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// # use std::io;
200    /// # use tokio::net::TcpStream;
201    /// # use actix_server::Server;
202    /// # async fn run() -> io::Result<()> {
203    /// use actix_service::fn_service;
204    /// use tokio_util::sync::CancellationToken;
205    ///
206    /// let stop_signal = CancellationToken::new();
207    ///
208    /// Server::build()
209    ///     .bind("shutdown-signal", "127.0.0.1:12345", || {
210    ///         fn_service(|_stream: TcpStream| async { Ok::<_, io::Error>(()) })
211    ///     })?
212    ///     .shutdown_signal(stop_signal.cancelled_owned())
213    ///     .run()
214    ///     .await
215    /// # }
216    /// ```
217    pub fn shutdown_signal<Fut>(mut self, shutdown_signal: Fut) -> Self
218    where
219        Fut: Future<Output = ()> + Send + 'static,
220    {
221        self.shutdown_signal = Some(Box::pin(shutdown_signal));
222        self
223    }
224
225    /// Timeout for graceful workers shutdown in seconds.
226    ///
227    /// After receiving a stop signal, workers have this much time to finish serving requests.
228    /// Workers still alive after the timeout are force dropped.
229    ///
230    /// By default shutdown timeout sets to 30 seconds.
231    pub fn shutdown_timeout(mut self, sec: u64) -> Self {
232        self.worker_config
233            .shutdown_timeout(Duration::from_secs(sec));
234        self
235    }
236
237    /// Adds new service to the server.
238    ///
239    /// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
240    ///
241    /// # Worker Count
242    ///
243    /// The `factory` will be instantiated multiple times in most scenarios. The number of
244    /// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
245    /// `addrs`.
246    ///
247    /// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
248    /// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
249    /// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
250    /// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
251    ///
252    /// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
253    /// the number of instantiations in a similar way.
254    ///
255    /// # Errors
256    ///
257    /// Returns an `io::Error` if:
258    /// - `addrs` cannot be resolved into one or more socket addresses;
259    /// - all the resolved socket addresses are already bound.
260    pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
261    where
262        F: ServerServiceFactory<TcpStream>,
263        U: ToSocketAddrs,
264        N: AsRef<str>,
265    {
266        let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
267
268        tracing::trace!("binding server to: {sockets:?}");
269
270        for lst in sockets {
271            let token = self.next_token();
272
273            self.factories.push(StreamNewService::create(
274                name.as_ref().to_string(),
275                token,
276                factory.clone(),
277                lst.local_addr()?,
278            ));
279
280            self.sockets
281                .push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
282        }
283
284        Ok(self)
285    }
286
287    /// Adds service to the server using a socket listener already bound.
288    ///
289    /// # Worker Count
290    ///
291    /// The `factory` will be instantiated multiple times in most scenarios. The number of
292    /// instantiations is: number of [`workers`](Self::workers()).
293    pub fn listen<F, N: AsRef<str>>(
294        mut self,
295        name: N,
296        lst: StdTcpListener,
297        factory: F,
298    ) -> io::Result<Self>
299    where
300        F: ServerServiceFactory<TcpStream>,
301    {
302        lst.set_nonblocking(true)?;
303        let addr = lst.local_addr()?;
304
305        let token = self.next_token();
306        self.factories.push(StreamNewService::create(
307            name.as_ref().to_string(),
308            token,
309            factory,
310            addr,
311        ));
312
313        self.sockets
314            .push((token, name.as_ref().to_string(), MioListener::from(lst)));
315
316        Ok(self)
317    }
318
319    /// Starts processing incoming connections and return server controller.
320    pub fn run(self) -> Server {
321        if self.sockets.is_empty() {
322            panic!("Server should have at least one bound socket");
323        } else {
324            tracing::info!("starting {} workers", self.threads);
325            Server::new(self)
326        }
327    }
328
329    fn next_token(&mut self) -> usize {
330        let token = self.token;
331        self.token += 1;
332        token
333    }
334}
335
336#[cfg(unix)]
337impl ServerBuilder {
338    /// Adds new service to the server using a UDS (unix domain socket) address.
339    ///
340    /// # Worker Count
341    ///
342    /// The `factory` will be instantiated multiple times in most scenarios. The number of
343    /// instantiations is: number of [`workers`](Self::workers()).
344    pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
345    where
346        F: ServerServiceFactory<actix_rt::net::UnixStream>,
347        N: AsRef<str>,
348        U: AsRef<std::path::Path>,
349    {
350        // The path must not exist when we try to bind.
351        // Try to remove it to avoid bind error.
352        if let Err(err) = std::fs::remove_file(addr.as_ref()) {
353            // NotFound is expected and not an issue. Anything else is.
354            if err.kind() != std::io::ErrorKind::NotFound {
355                return Err(err);
356            }
357        }
358
359        let lst = crate::socket::StdUnixListener::bind(addr)?;
360        self.listen_uds(name, lst, factory)
361    }
362
363    /// Adds new service to the server using a UDS (unix domain socket) listener already bound.
364    ///
365    /// Useful when running as a systemd service and a socket FD is acquired externally.
366    ///
367    /// # Worker Count
368    ///
369    /// The `factory` will be instantiated multiple times in most scenarios. The number of
370    /// instantiations is: number of [`workers`](Self::workers()).
371    pub fn listen_uds<F, N: AsRef<str>>(
372        mut self,
373        name: N,
374        lst: crate::socket::StdUnixListener,
375        factory: F,
376    ) -> io::Result<Self>
377    where
378        F: ServerServiceFactory<actix_rt::net::UnixStream>,
379    {
380        use std::net::{IpAddr, Ipv4Addr};
381
382        lst.set_nonblocking(true)?;
383
384        let token = self.next_token();
385        let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
386
387        self.factories.push(StreamNewService::create(
388            name.as_ref().to_string(),
389            token,
390            factory,
391            addr,
392        ));
393
394        self.sockets
395            .push((token, name.as_ref().to_string(), MioListener::from(lst)));
396
397        Ok(self)
398    }
399}
400
401pub(super) fn bind_addr<S: ToSocketAddrs>(
402    addr: S,
403    backlog: u32,
404    mptcp: &MpTcp,
405) -> io::Result<Vec<MioTcpListener>> {
406    let mut opt_err = None;
407    let mut success = false;
408    let mut sockets = Vec::new();
409
410    for addr in addr.to_socket_addrs()? {
411        match create_mio_tcp_listener(addr, backlog, mptcp) {
412            Ok(lst) => {
413                success = true;
414                sockets.push(lst);
415            }
416            Err(err) => opt_err = Some(err),
417        }
418    }
419
420    if success {
421        Ok(sockets)
422    } else if let Some(err) = opt_err.take() {
423        Err(err)
424    } else {
425        Err(io::Error::other("Can not bind to address."))
426    }
427}