Skip to main content

ntex_server/net/
service.rs

1use std::{fmt, io, sync::Arc};
2
3use ntex_io::{IoConfig, IoMetricsGroup};
4use ntex_service::{BindCtx, Ctx, Service, cfg::SharedCfg};
5use ntex_util::{HashMap, future::join_all, services::Counter, services::counter::CounterGuard};
6
7use crate::{ServerAppConfig, ServerConfiguration, metrics::WorkerSlot};
8
9use super::accept::{AcceptNotify, AcceptorCommand};
10use super::factory::{FactoryServiceType, NetService};
11use super::{Token, socket::Connection};
12
13/// Worker configuration for network servers.
14///
15/// Created by [`ServerBuilder::run`](super::ServerBuilder::run). Each worker
16/// creates its application state and the services for all registered
17/// listeners.
18pub struct StreamServer<Cfg> {
19    accept: AcceptNotify,
20    state: Arc<Cfg>,
21    services: Vec<FactoryServiceType<Cfg>>,
22    max_conns: usize,
23}
24
25impl<Cfg: ServerAppConfig> StreamServer<Cfg> {
26    pub(crate) fn new(
27        accept: AcceptNotify,
28        state: Arc<Cfg>,
29        services: Vec<FactoryServiceType<Cfg>>,
30        max_conns: usize,
31    ) -> Self {
32        Self {
33            accept,
34            state,
35            services,
36            max_conns,
37        }
38    }
39}
40
41impl<Cfg> fmt::Debug for StreamServer<Cfg> {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        f.debug_struct("StreamServer")
44            .field("services", &self.services.len())
45            .field("max_conns", &self.max_conns)
46            .finish()
47    }
48}
49
50/// Worker service factory.
51impl<Cfg: ServerAppConfig> ServerConfiguration for StreamServer<Cfg> {
52    type Item = Connection;
53    type Service = StreamService;
54
55    /// Create service for handling connections
56    async fn create(&self) -> io::Result<Self::Service> {
57        // construct configuration
58        let cfg = self.state.create().await?;
59
60        // construct services
61        let mut tokens = HashMap::default();
62        let mut services = Vec::new();
63        let group = IoMetricsGroup::current();
64
65        for info in &self.services {
66            for (svc, _, svc_tokens) in info.create(cfg.clone()).await.map_err(io::Error::other)? {
67                services.push(svc);
68                let idx = services.len() - 1;
69                for (token, cfg) in &svc_tokens {
70                    // listener connections have their own metrics
71                    group.register(&cfg.get::<IoConfig>());
72                    tokens.insert(*token, (idx, cfg.clone()));
73                }
74            }
75        }
76
77        Ok(StreamService {
78            services,
79            tokens,
80            conns: Counter::new(self.max_conns),
81            metrics: WorkerSlot::current(),
82        })
83    }
84
85    /// Pause the server.
86    fn pause(&self) {
87        self.accept.send(AcceptorCommand::Pause);
88    }
89
90    /// Resume the server.
91    fn resume(&self) {
92        self.accept.send(AcceptorCommand::Resume);
93    }
94
95    /// Terminate the server.
96    fn terminate(&self) {
97        self.accept.send(AcceptorCommand::Terminate);
98    }
99
100    /// Stop the server.
101    async fn stop(&self) {
102        let (tx, rx) = oneshot::channel();
103        self.accept.send(AcceptorCommand::Stop(tx));
104        let _ = rx.await;
105    }
106
107    fn incoming_cpu(&self, con: &Connection) -> Option<usize> {
108        con.incoming_cpu()
109    }
110}
111
112impl<Cfg: ServerAppConfig> Clone for StreamServer<Cfg> {
113    fn clone(&self) -> Self {
114        Self {
115            state: self.state.clone(),
116            accept: self.accept.clone(),
117            services: self.services.iter().map(|s| s.clo()).collect(),
118            max_conns: self.max_conns,
119        }
120    }
121}
122
123/// Per-worker service that dispatches connections to listener services.
124pub struct StreamService {
125    tokens: HashMap<Token, (usize, SharedCfg)>,
126    services: Vec<Box<dyn NetService>>,
127    conns: Counter,
128    metrics: Option<Arc<WorkerSlot>>,
129}
130
131/// Open connection, released on drop.
132pub(crate) struct ConnGuard {
133    _guard: CounterGuard,
134    metrics: Option<Arc<WorkerSlot>>,
135}
136
137impl ConnGuard {
138    fn new(guard: CounterGuard, metrics: Option<Arc<WorkerSlot>>) -> Self {
139        if let Some(ref m) = metrics {
140            m.conn_opened();
141        }
142        Self {
143            _guard: guard,
144            metrics,
145        }
146    }
147}
148
149impl Drop for ConnGuard {
150    fn drop(&mut self) {
151        if let Some(ref m) = self.metrics {
152            m.conn_closed();
153        }
154    }
155}
156
157impl fmt::Debug for StreamService {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        f.debug_struct("StreamService")
160            .field("tokens", &self.tokens)
161            .field("conns", &self.conns)
162            .finish()
163    }
164}
165
166impl Service<(), Connection> for StreamService {
167    type Res = ();
168    type Error = ();
169
170    fn bind(&self, ctx: BindCtx<'_>) {
171        self.conns.bind(&ctx);
172        for service in &self.services {
173            service.bind(&ctx);
174        }
175    }
176
177    async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
178        if !self.conns.is_available() {
179            self.conns.available().await;
180        }
181        for (idx, svc) in self.services.iter().enumerate() {
182            if svc.ready().await.is_err() {
183                for (idx_, cfg) in self.tokens.values() {
184                    if idx == *idx_ {
185                        log::error!("{}: Service readiness has failed", cfg.tag());
186                        break;
187                    }
188                }
189                return Err(());
190            }
191        }
192
193        Ok(())
194    }
195
196    async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
197        let _ = join_all(self.services.iter().map(|s| s.shutdown())).await;
198        log::info!(
199            "Worker service shutdown, {} connections",
200            self.conns.total()
201        );
202    }
203
204    #[allow(clippy::unused_async_trait_impl)]
205    async fn call(&self, con: Connection, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
206        if let Some((idx, cfg)) = self.tokens.get(&con.token) {
207            let stream = con.io.convert(cfg.clone()).map_err(|e| {
208                log::error!("Cannot convert to an async io stream: {e}");
209            })?;
210
211            let guard = ConnGuard::new(self.conns.get(), self.metrics.clone());
212            self.services[*idx].call(stream, guard);
213            Ok(())
214        } else {
215            log::error!("Cannot get handler service for connection: {con:?}");
216            Err(())
217        }
218    }
219}