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::{Token, socket::Connection};
12
13pub 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
50impl<Cfg: ServerAppConfig> ServerConfiguration for StreamServer<Cfg> {
52 type Item = Connection;
53 type Service = StreamService;
54
55 async fn create(&self) -> io::Result<Self::Service> {
57 let cfg = self.state.create().await?;
59
60 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 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 fn pause(&self) {
87 self.accept.send(AcceptorCommand::Pause);
88 }
89
90 fn resume(&self) {
92 self.accept.send(AcceptorCommand::Resume);
93 }
94
95 fn terminate(&self) {
97 self.accept.send(AcceptorCommand::Terminate);
98 }
99
100 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
123pub struct StreamService {
125 tokens: HashMap<Token, (usize, SharedCfg)>,
126 services: Vec<Box<dyn NetService>>,
127 conns: Counter,
128 metrics: Option<Arc<WorkerSlot>>,
129}
130
131pub(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}