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}