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