Skip to main content

ntex_server/net/
builder.rs

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