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, metrics::WorkerSlot};
8
9use super::accept::{AcceptNotify, AcceptorCommand};
10use super::factory::{FactoryServiceType, NetService};
11use super::{MAX_CONNS_COUNTER, Token, socket::Connection};
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 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
118pub struct StreamService {
120 tokens: HashMap<Token, (usize, SharedCfg)>,
121 services: Vec<Box<dyn NetService>>,
122 conns: Counter,
123 metrics: Option<Arc<WorkerSlot>>,
124}
125
126pub(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}