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