ntex_server/net/
service.rs1use 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, affinity, metrics::WorkerSlot};
8
9use super::accept::{AcceptNotify, AcceptorCommand};
10use super::factory::{FactoryServiceType, NetService};
11use super::{MAX_CONNS_COUNTER, Token, socket::Connection, socket::Stream};
12
13pub 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
46impl<Cfg: ServerAppConfig> ServerConfiguration for StreamServer<Cfg> {
48 type Item = Connection;
49 type Service = StreamService;
50
51 async fn create(&self) -> io::Result<Self::Service> {
53 let cfg = self.state.create().await?;
55
56 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 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 fn pause(&self) {
83 self.accept.send(AcceptorCommand::Pause);
84 }
85
86 fn resume(&self) {
88 self.accept.send(AcceptorCommand::Resume);
89 }
90
91 fn terminate(&self) {
93 self.accept.send(AcceptorCommand::Terminate);
94 }
95
96 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 match con.io {
105 Stream::Tcp(ref stream) => affinity::incoming_cpu(stream),
106 #[cfg(unix)]
107 Stream::Uds(_) => None,
108 }
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 }
119 }
120}
121
122pub struct StreamService {
124 tokens: HashMap<Token, (usize, SharedCfg)>,
125 services: Vec<Box<dyn NetService>>,
126 conns: Counter,
127 metrics: Option<Arc<WorkerSlot>>,
128}
129
130pub(crate) struct ConnGuard {
132 _guard: CounterGuard,
133 metrics: Option<Arc<WorkerSlot>>,
134}
135
136impl ConnGuard {
137 fn new(guard: CounterGuard, metrics: Option<Arc<WorkerSlot>>) -> Self {
138 if let Some(ref m) = metrics {
139 m.conn_opened();
140 }
141 Self {
142 _guard: guard,
143 metrics,
144 }
145 }
146}
147
148impl Drop for ConnGuard {
149 fn drop(&mut self) {
150 if let Some(ref m) = self.metrics {
151 m.conn_closed();
152 }
153 }
154}
155
156impl fmt::Debug for StreamService {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 f.debug_struct("StreamService")
159 .field("tokens", &self.tokens)
160 .field("conns", &self.conns)
161 .finish()
162 }
163}
164
165impl Service<(), Connection> for StreamService {
166 type Res = ();
167 type Error = ();
168
169 fn bind(&self, ctx: BindCtx<'_>) {
170 self.conns.bind(&ctx);
171 for service in &self.services {
172 service.bind(&ctx);
173 }
174 }
175
176 async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
177 if !self.conns.is_available() {
178 self.conns.available().await;
179 }
180 for (idx, svc) in self.services.iter().enumerate() {
181 if svc.ready().await.is_err() {
182 for (idx_, cfg) in self.tokens.values() {
183 if idx == *idx_ {
184 log::error!("{}: Service readiness has failed", cfg.tag());
185 break;
186 }
187 }
188 return Err(());
189 }
190 }
191
192 Ok(())
193 }
194
195 async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
196 let _ = join_all(self.services.iter().map(|s| s.shutdown())).await;
197 log::info!(
198 "Worker service shutdown, {} connections",
199 super::num_connections()
200 );
201 }
202
203 #[allow(clippy::unused_async_trait_impl)]
204 async fn call(&self, con: Connection, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
205 if let Some((idx, cfg)) = self.tokens.get(&con.token) {
206 let stream = con.io.convert(cfg.clone()).map_err(|e| {
207 log::error!("Cannot convert to an async io stream: {e}");
208 })?;
209
210 let guard = ConnGuard::new(self.conns.get(), self.metrics.clone());
211 self.services[*idx].call(stream, guard);
212 Ok(())
213 } else {
214 log::error!("Cannot get handler service for connection: {con:?}");
215 Err(())
216 }
217 }
218}