Skip to main content

ntex_server/net/
config.rs

1use std::{cell::RefCell, fmt, io, marker::PhantomData, mem, net, rc::Rc, sync::Arc};
2
3use ntex_io::Io;
4use ntex_service::{IntoService, Pipeline, Service, cfg::SharedCfg, state::State};
5use ntex_util::{HashMap, future::BoxFuture};
6
7use super::factory::{FactoryService, FactoryServiceType, NetService, ServerService};
8use super::{Token, builder::bind_addr, socket::Listener};
9
10#[derive(Clone, Debug)]
11pub struct ServiceConfig<St>(pub(super) Rc<RefCell<ServiceConfigInner<St>>>);
12
13#[derive(Debug)]
14struct Socket {
15    name: String,
16    sockets: Vec<(Token, Listener, SharedCfg)>,
17}
18
19pub(super) struct ServiceConfigInner<St> {
20    token: Token,
21    on_start_set: bool,
22    on_start: Vec<Box<dyn OnWorkerStart<St>>>,
23    sockets: Vec<Socket>,
24    backlog: i32,
25}
26
27impl<St> fmt::Debug for ServiceConfigInner<St> {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.debug_struct("ServiceConfigInner")
30            .field("token", &self.token)
31            .field("backlog", &self.backlog)
32            .field("sockets", &self.sockets)
33            .finish()
34    }
35}
36
37impl<St> ServiceConfig<St>
38where
39    St: State<St, Io> + Clone + 'static,
40{
41    pub(super) fn new(token: Token, backlog: i32) -> Self {
42        ServiceConfig(Rc::new(RefCell::new(ServiceConfigInner {
43            token,
44            backlog,
45            sockets: Vec::new(),
46            on_start_set: false,
47            on_start: vec![on_worker_start(async |_| {
48                not_configured();
49                Ok::<_, &str>(())
50            })],
51        })))
52    }
53
54    /// Add new service to the server.
55    pub fn bind(
56        &self,
57        name: impl AsRef<str>,
58        addr: impl net::ToSocketAddrs,
59        cfg: impl Into<SharedCfg>,
60    ) -> io::Result<&Self> {
61        let mut inner = self.0.borrow_mut();
62
63        let cfg = cfg.into();
64        let sockets = bind_addr(addr, inner.backlog)?;
65        let socket = Socket {
66            name: name.as_ref().to_string(),
67            sockets: sockets
68                .into_iter()
69                .map(|lst| (inner.token.next(), Listener::from_tcp(lst), cfg.clone()))
70                .collect(),
71        };
72        inner.sockets.push(socket);
73
74        Ok(self)
75    }
76
77    /// Add new service to the server.
78    pub fn listen(
79        &self,
80        name: impl AsRef<str>,
81        lst: net::TcpListener,
82        cfg: impl Into<SharedCfg>,
83    ) -> &Self {
84        let mut inner = self.0.borrow_mut();
85        let socket = Socket {
86            name: name.as_ref().to_string(),
87            sockets: vec![(inner.token.next(), Listener::from_tcp(lst), cfg.into())],
88        };
89        inner.sockets.push(socket);
90
91        self
92    }
93
94    /// Register async service configuration function.
95    ///
96    /// This function get called during worker runtime configuration stage.
97    /// It get executed in the worker thread.
98    pub fn on_worker_start<F>(&self, f: F) -> &Self
99    where
100        F: AsyncFn(ServiceRuntime<St>) -> Result<(), &'static str> + Send + Clone + 'static,
101    {
102        let mut inner = self.0.borrow_mut();
103        if !inner.on_start_set {
104            inner.on_start.clear();
105            inner.on_start_set = true;
106        }
107        inner.on_start.push(on_worker_start(f));
108        self
109    }
110
111    pub(super) fn into_factory(
112        self,
113    ) -> (
114        Token,
115        Vec<(Token, String, Listener)>,
116        FactoryServiceType<St>,
117    ) {
118        let mut inner = self.0.borrow_mut();
119
120        let mut sockets = Vec::new();
121        let mut names = HashMap::default();
122        for (idx, s) in mem::take(&mut inner.sockets).into_iter().enumerate() {
123            names.insert(
124                s.name.clone(),
125                Entry {
126                    idx,
127                    name: s.name.clone(),
128                    tokens: s
129                        .sockets
130                        .iter()
131                        .map(|(token, _, cfg)| (*token, cfg.clone()))
132                        .collect(),
133                },
134            );
135
136            sockets.extend(
137                s.sockets
138                    .into_iter()
139                    .map(|(token, lst, _)| (token, s.name.clone(), lst)),
140            );
141        }
142
143        (
144            inner.token,
145            sockets,
146            Box::new(ConfiguredService {
147                names,
148                on_start: mem::take(&mut inner.on_start),
149            }),
150        )
151    }
152}
153
154struct ConfiguredService<St> {
155    names: HashMap<String, Entry>,
156    on_start: Vec<Box<dyn OnWorkerStart<St>>>,
157}
158
159impl<St> FactoryService<St> for ConfiguredService<St>
160where
161    St: State<St, Io> + Clone + 'static,
162{
163    fn clo(&self) -> FactoryServiceType<St> {
164        Box::new(Self {
165            names: self.names.clone(),
166            on_start: self.on_start.iter().map(|cb| (*cb).clo()).collect(),
167        })
168    }
169
170    fn create(
171        &self,
172        st: St,
173    ) -> BoxFuture<
174        'static,
175        Result<Vec<(Box<dyn NetService>, Arc<str>, Vec<(Token, SharedCfg)>)>, &'static str>,
176    > {
177        // configure services
178        let rt = ServiceRuntime::new(st, self.names.clone());
179        let on_start: Vec<_> = self
180            .on_start
181            .iter()
182            .map(|cb| cb.run(ServiceRuntime(rt.0.clone())))
183            .collect();
184
185        // construct services
186        Box::pin(async move {
187            for fut in on_start {
188                fut.await?;
189            }
190            rt.validate();
191
192            let names = mem::take(&mut rt.0.borrow_mut().names);
193            let mut services = mem::take(&mut rt.0.borrow_mut().services);
194
195            let mut res = Vec::new();
196            while let Some(svc) = services.pop() {
197                if let Some(svc) = svc {
198                    for entry in names.values() {
199                        if entry.idx == services.len() {
200                            res.push((
201                                svc,
202                                std::sync::Arc::from(entry.name.clone()),
203                                entry.tokens.clone(),
204                            ));
205                            break;
206                        }
207                    }
208                }
209            }
210            Ok(res)
211        })
212    }
213}
214
215fn not_configured() {
216    log::error!("Service is not configured");
217}
218
219pub struct ServiceRuntime<St>(Rc<RefCell<ServiceRuntimeInner<St>>>);
220
221#[derive(Debug, Clone)]
222struct Entry {
223    idx: usize,
224    name: String,
225    tokens: Vec<(Token, SharedCfg)>,
226}
227
228struct ServiceRuntimeInner<St> {
229    st: St,
230    names: HashMap<String, Entry>,
231    services: Vec<Option<Box<dyn NetService>>>,
232}
233
234impl<St> fmt::Debug for ServiceRuntime<St> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        let inner = self.0.borrow();
237        f.debug_struct("ServiceRuntimer")
238            .field("names", &inner.names)
239            .finish()
240    }
241}
242
243impl<St: State<St, Io> + Clone + 'static> ServiceRuntime<St> {
244    fn new(st: St, names: HashMap<String, Entry>) -> Self {
245        let services = (0..names.len()).map(|_| None).collect();
246        ServiceRuntime(Rc::new(RefCell::new(ServiceRuntimeInner {
247            st,
248            names,
249            services,
250        })))
251    }
252
253    fn validate(&self) {
254        let inner = self.0.as_ref().borrow();
255        for (name, item) in &inner.names {
256            if inner.services[item.idx].is_none() {
257                log::error!("Service {name:?} is not configured");
258            }
259        }
260    }
261
262    /// Register service.
263    ///
264    /// Name of the service must be registered during configuration stage with
265    /// `ServiceConfig::bind()` or `ServiceConfig::listen()` methods.
266    ///
267    /// # Panics
268    ///
269    /// Panics if service with specified name is registered already
270    pub fn service<S>(&self, name: &str, svc: impl IntoService<S, St, Io>) -> &Self
271    where
272        S: Service<St, Io> + 'static,
273    {
274        let mut inner = self.0.borrow_mut();
275        if let Some(entry) = inner.names.get_mut(name) {
276            let idx = entry.idx;
277            let pipeline = Pipeline::with_ctl(
278                inner.st.clone(),
279                inner.st.clone(),
280                svc.into_service().map(|_| ()).map_err(|_| ()),
281            );
282            let svc: Box<dyn NetService> = Box::new(ServerService { pipeline });
283            inner.services[idx] = Some(svc);
284        } else {
285            panic!("Unknown service: {name:?}");
286        }
287        self
288    }
289}
290
291trait OnWorkerStart<St>: Send {
292    fn clo(&self) -> Box<dyn OnWorkerStart<St>>;
293
294    fn run(&self, rt: ServiceRuntime<St>) -> BoxFuture<'static, Result<(), &'static str>>;
295}
296
297struct OnWorkerStartImpl<F, St> {
298    f: F,
299    st: PhantomData<St>,
300}
301
302fn on_worker_start<F, St>(f: F) -> Box<dyn OnWorkerStart<St> + Send>
303where
304    F: AsyncFn(ServiceRuntime<St>) -> Result<(), &'static str> + Send + Clone + 'static,
305    St: 'static,
306{
307    Box::new(OnWorkerStartImpl { f, st: PhantomData })
308}
309
310impl<F, St> OnWorkerStart<St> for OnWorkerStartImpl<F, St>
311where
312    F: AsyncFn(ServiceRuntime<St>) -> Result<(), &'static str> + Send + Clone + 'static,
313    St: 'static,
314{
315    fn clo(&self) -> Box<dyn OnWorkerStart<St>> {
316        Box::new(Self {
317            f: self.f.clone(),
318            st: PhantomData,
319        })
320    }
321
322    fn run(&self, rt: ServiceRuntime<St>) -> BoxFuture<'static, Result<(), &'static str>> {
323        let f = self.f.clone();
324        Box::pin(async move { (f)(rt).await })
325    }
326}
327
328// SAFETY: Send cannot be provided authomatically because of R param
329// but R always get executed in one thread and never leave it
330unsafe impl<F, St> Send for OnWorkerStartImpl<F, St> where F: Send {}