Skip to main content

ntex_server/net/
builder.rs

1use std::{fmt, io, net, sync::Arc};
2
3use ntex_io::Io;
4use ntex_rt::System;
5use ntex_service::{IntoService, Service, cfg::SharedCfg};
6use ntex_util::time::Millis;
7use socket2::{Domain, SockAddr, Socket, Type};
8
9use crate::{NoConfig, Server, ServerAppConfig, WorkerPool};
10
11use super::accept::AcceptLoop;
12use super::config::ServiceConfig;
13use super::factory::{self, FactoryServiceType};
14use super::{Connection, ServerStatus, StreamServer, Token, socket::Listener};
15
16/// Streaming service builder
17///
18/// This type can be used to construct an instance of `net streaming server` through a
19/// builder-like pattern.
20pub struct ServerBuilder<Cfg = NoConfig> {
21    name: String,
22    token: Token,
23    backlog: i32,
24    state: Arc<Cfg>,
25    services: Vec<FactoryServiceType<Cfg>>,
26    sockets: Vec<(Token, String, Listener)>,
27    accept: AcceptLoop,
28    pool: WorkerPool,
29}
30
31impl Default for ServerBuilder {
32    fn default() -> Self {
33        Self::new(NoConfig)
34    }
35}
36
37impl<Cfg> ServerBuilder<Cfg>
38where
39    Cfg: ServerAppConfig,
40{
41    #[must_use]
42    /// Create new Server builder instance.
43    ///
44    /// Provided function get called during worker runtime configuration stage
45    /// and must construct server state.
46    pub fn new(cfg: Cfg) -> ServerBuilder<Cfg> {
47        let sys = System::current();
48        let mut accept = AcceptLoop::default();
49        accept.name(sys.name());
50        if sys.testing() {
51            accept.testing();
52        }
53
54        ServerBuilder {
55            accept,
56            name: sys.name().to_string(),
57            token: Token(0),
58            state: Arc::new(cfg),
59            services: Vec::new(),
60            sockets: Vec::new(),
61            backlog: 2048,
62            pool: WorkerPool::default().name(sys.name()),
63        }
64    }
65
66    #[must_use]
67    /// Set server name.
68    ///
69    /// Name is used for worker thread name
70    pub fn name<T: AsRef<str>>(mut self, name: T) -> Self {
71        self.name = name.as_ref().to_string();
72        self.accept.name(self.name.as_str());
73        self.pool = self.pool.name(self.name.as_str());
74        self
75    }
76
77    #[must_use]
78    /// Set number of workers to start.
79    ///
80    /// By default server uses number of available logical cpu as workers
81    /// count.
82    pub fn workers(mut self, num: usize) -> Self {
83        self.pool = self.pool.workers(num);
84        self
85    }
86
87    #[must_use]
88    /// Set the maximum number of pending connections.
89    ///
90    /// This refers to the number of clients that can be waiting to be served.
91    /// Exceeding this number results in the client getting an error when
92    /// attempting to connect. It should only affect servers under significant
93    /// load.
94    ///
95    /// Generally set in the 64-2048 range. Default value is 2048.
96    ///
97    /// This method should be called before `bind()` method call.
98    pub fn backlog(mut self, num: i32) -> Self {
99        self.backlog = num;
100        self
101    }
102
103    #[must_use]
104    /// Sets the maximum per-worker number of concurrent connections.
105    ///
106    /// All socket listeners will stop accepting connections when this limit is
107    /// reached for each worker.
108    ///
109    /// By default max connections is set to a 25k per worker.
110    pub fn maxconn(self, num: usize) -> Self {
111        super::max_concurrent_connections(num);
112        self
113    }
114
115    #[must_use]
116    /// Stop ntex runtime when server get dropped.
117    ///
118    /// By default "stop runtime" is disabled.
119    pub fn stop_runtime(mut self) -> Self {
120        self.pool = self.pool.stop_runtime();
121        self
122    }
123
124    #[must_use]
125    /// Stops the server when one of the workers panics.
126    ///
127    /// By default, "stop on panic" is disabled.
128    pub fn stop_on_panic(mut self) -> Self {
129        self.pool = self.pool.stop_on_panic();
130        self
131    }
132
133    #[must_use]
134    /// Disable signal handling.
135    ///
136    /// By default, signal handling is enabled.
137    pub fn disable_signals(mut self) -> Self {
138        self.pool = self.pool.disable_signals();
139        self
140    }
141
142    #[must_use]
143    /// Enable cpu affinity.
144    ///
145    /// By default, affinity is disabled.
146    pub fn enable_affinity(mut self) -> Self {
147        self.pool = self.pool.enable_affinity();
148        self
149    }
150
151    #[must_use]
152    /// Graceful shutdown.
153    ///
154    /// Gracefully shuts down on SIGSEGV or SIGQUIT and app panics.
155    /// Graceful shutdown is always enabled for SIGTERM.
156    /// By default, it is disabled for SIGSEGV and SIGQUIT and panics.
157    pub fn graceful_shutdown(mut self) -> Self {
158        self.pool = self.pool.graceful_shutdown();
159        self
160    }
161
162    #[must_use]
163    /// Timeout for graceful worker shutdown.
164    ///
165    /// After receiving a stop signal, workers have this much time to finish
166    /// serving requests. Workers that are still alive after the timeout are
167    /// forcefully dropped.
168    ///
169    /// By default, the shutdown timeout is set to 30 seconds.
170    pub fn shutdown_timeout<T: Into<Millis>>(mut self, timeout: T) -> Self {
171        self.pool = self.pool.shutdown_timeout(timeout);
172        self
173    }
174
175    #[must_use]
176    /// Sets the server status handler.
177    ///
178    /// The server calls this handler on every internal status update.
179    pub fn status_handler<F>(mut self, handler: F) -> Self
180    where
181        F: FnMut(ServerStatus) + Send + 'static,
182    {
183        self.accept.set_status_handler(handler);
184        self
185    }
186
187    /// Execute external async configuration as part of the server building
188    /// process.
189    ///
190    /// This function is useful for moving parts of configuration to a
191    /// different module or even library.
192    pub async fn configure<F>(mut self, f: F) -> io::Result<Self>
193    where
194        F: AsyncFnOnce(ServiceConfig<Cfg>) -> io::Result<()>,
195    {
196        let cfg = ServiceConfig::new(self.token, self.backlog);
197
198        f(cfg.clone()).await?;
199
200        let (token, sockets, factory) = cfg.into_factory();
201        self.token = token;
202        self.sockets.extend(sockets);
203        self.services.push(factory);
204
205        Ok(self)
206    }
207
208    #[allow(clippy::needless_pass_by_value)]
209    /// Add new service to the server.
210    pub fn bind<F, S, I>(
211        mut self,
212        name: impl AsRef<str>,
213        addr: impl net::ToSocketAddrs,
214        cfg: impl Into<SharedCfg>,
215        factory: F,
216    ) -> io::Result<Self>
217    where
218        F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
219        S: Service<Cfg::State, Io> + 'static,
220        I: IntoService<S, Cfg::State, Io> + 'static,
221    {
222        let cfg = cfg.into();
223        let sockets = bind_addr(addr, self.backlog)?;
224
225        let mut tokens = Vec::new();
226        for lst in sockets {
227            let token = self.token.next();
228            self.sockets
229                .push((token, name.as_ref().to_string(), Listener::from_tcp(lst)));
230            tokens.push((token, cfg.clone()));
231        }
232
233        self.services.push(factory::create_factory_service(
234            name.as_ref().to_string(),
235            tokens,
236            factory,
237        ));
238
239        Ok(self)
240    }
241
242    #[cfg(unix)]
243    /// Add new unix domain service to the server.
244    pub fn bind_uds<F, I, S>(
245        self,
246        name: impl AsRef<str>,
247        addr: impl AsRef<std::path::Path>,
248        cfg: impl Into<SharedCfg>,
249        factory: F,
250    ) -> io::Result<Self>
251    where
252        F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
253        I: IntoService<S, Cfg::State, Io> + 'static,
254        S: Service<Cfg::State, Io> + 'static,
255    {
256        use std::os::unix::net::UnixListener;
257
258        // The path must not exist when we try to bind.
259        // Try to remove it to avoid bind error.
260        if let Err(e) = std::fs::remove_file(addr.as_ref()) {
261            // NotFound is expected and not an issue. Anything else is.
262            if e.kind() != std::io::ErrorKind::NotFound {
263                return Err(e);
264            }
265        }
266
267        let lst = UnixListener::bind(addr)?;
268        self.listen_uds(name, lst, cfg.into(), factory)
269    }
270
271    #[cfg(unix)]
272    /// Add new unix domain service to the server.
273    /// Useful when running as a systemd service and
274    /// a socket FD can be acquired using the systemd crate.
275    pub fn listen_uds<F, I, S>(
276        mut self,
277        name: impl AsRef<str>,
278        lst: std::os::unix::net::UnixListener,
279        cfg: impl Into<SharedCfg>,
280        factory: F,
281    ) -> io::Result<Self>
282    where
283        F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
284        I: IntoService<S, Cfg::State, Io> + 'static,
285        S: Service<Cfg::State, Io> + 'static,
286    {
287        let token = self.token.next();
288        self.services.push(factory::create_factory_service(
289            name.as_ref().to_string(),
290            vec![(token, cfg.into())],
291            factory,
292        ));
293        self.sockets
294            .push((token, name.as_ref().to_string(), Listener::from_uds(lst)));
295        Ok(self)
296    }
297
298    /// Add new service to the server.
299    pub fn listen<F, S, I>(
300        mut self,
301        name: impl AsRef<str>,
302        lst: net::TcpListener,
303        cfg: impl Into<SharedCfg>,
304        factory: F,
305    ) -> io::Result<Self>
306    where
307        F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
308        S: Service<Cfg::State, Io> + 'static,
309        I: IntoService<S, Cfg::State, Io> + 'static,
310    {
311        let token = self.token.next();
312        self.services.push(factory::create_factory_service(
313            name.as_ref().to_string(),
314            vec![(token, cfg.into())],
315            factory,
316        ));
317        self.sockets
318            .push((token, name.as_ref().to_string(), Listener::from_tcp(lst)));
319        Ok(self)
320    }
321
322    /// Starts processing incoming connections and return server controller.
323    pub fn run(self) -> Server<Connection> {
324        assert!(
325            !self.sockets.is_empty(),
326            "Server should have at least one bound socket"
327        );
328        let srv = StreamServer::new(self.accept.notify(), self.state, self.services);
329        let svc = self.pool.run(srv);
330
331        let sockets = self
332            .sockets
333            .into_iter()
334            .map(|sock| {
335                log::info!("Starting \"{}\" service on {}", sock.1, sock.2);
336                (sock.0, sock.2)
337            })
338            .collect();
339        self.accept.start(sockets, svc.clone());
340
341        svc
342    }
343}
344
345impl<Cfg> fmt::Debug for ServerBuilder<Cfg> {
346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347        f.debug_struct("ServerBuilder")
348            .field("name", &self.name)
349            .field("token", &self.token)
350            .field("backlog", &self.backlog)
351            .field("sockets", &self.sockets)
352            .field("accept", &self.accept)
353            .field("worker-pool", &self.pool)
354            .finish()
355    }
356}
357
358pub fn bind_addr<S: net::ToSocketAddrs>(
359    addr: S,
360    backlog: i32,
361) -> io::Result<Vec<net::TcpListener>> {
362    let mut err = None;
363    let mut succ = false;
364    let mut sockets = Vec::new();
365    for addr in addr.to_socket_addrs()? {
366        match create_tcp_listener(addr, backlog) {
367            Ok(lst) => {
368                succ = true;
369                sockets.push(lst);
370            }
371            Err(e) => err = Some(e),
372        }
373    }
374
375    if succ {
376        Ok(sockets)
377    } else if let Some(e) = err.take() {
378        Err(e)
379    } else {
380        Err(io::Error::new(
381            io::ErrorKind::InvalidInput,
382            "Cannot bind to address.",
383        ))
384    }
385}
386
387pub fn create_tcp_listener(addr: net::SocketAddr, backlog: i32) -> io::Result<net::TcpListener> {
388    let builder = match addr {
389        net::SocketAddr::V4(_) => Socket::new(Domain::IPV4, Type::STREAM, None)?,
390        net::SocketAddr::V6(_) => Socket::new(Domain::IPV6, Type::STREAM, None)?,
391    };
392
393    // On Windows, this allows rebinding sockets which are actively in use,
394    // which allows “socket hijacking”, so we explicitly don't set it here.
395    // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
396    #[cfg(not(windows))]
397    builder.set_reuse_address(true)?;
398
399    builder.bind(&SockAddr::from(addr))?;
400    builder.listen(backlog)?;
401    Ok(net::TcpListener::from(builder))
402}
403
404#[cfg(test)]
405mod tests {
406    use super::*;
407
408    #[test]
409    fn test_bind_addr() {
410        let addrs: Vec<net::SocketAddr> = Vec::new();
411        assert!(bind_addr(&addrs[..], 10).is_err());
412    }
413
414    #[ntex::test]
415    async fn test_debug() {
416        let builder = ServerBuilder::default();
417        assert!(format!("{builder:?}").contains("ServerBuilder"));
418    }
419}