Skip to main content

ntex/http/
service.rs

1use std::{cell::Cell, cell::RefCell, error, fmt, marker, rc::Rc, task::Context};
2
3use crate::io::{Filter, Io, IoRef, types};
4use crate::service::{IntoServiceFactory, Service, ServiceCtx, ServiceFactory};
5use crate::{SharedCfg, channel::oneshot, util::HashSet, util::join};
6
7use super::body::MessageBody;
8use super::config::DispatcherConfig;
9use super::error::{DispatchError, H2Error, ResponseError};
10use super::request::Request;
11use super::response::Response;
12use super::{h1, h2};
13
14/// `ServiceFactory` HTTP1.1/HTTP2 transport implementation
15#[derive(derive_more::Debug)]
16#[debug("HttpService")]
17pub struct HttpService<
18    F,
19    S,
20    B,
21    C1 = h1::DefaultControlService,
22    C2 = h2::DefaultControlService,
23> {
24    srv: S,
25    h1_control: C1,
26    h2_control: Rc<C2>,
27    _t: marker::PhantomData<(F, B)>,
28}
29
30impl<F, S, B> HttpService<F, S, B>
31where
32    F: Filter,
33    S: ServiceFactory<Request, SharedCfg> + 'static,
34    S::Error: ResponseError,
35    S::InitError: fmt::Debug,
36    S::Response: Into<Response<B>>,
37    B: MessageBody,
38{
39    /// Create new `HttpService` instance.
40    pub fn new<U>(service: U) -> Self
41    where
42        U: IntoServiceFactory<S, Request, SharedCfg>,
43    {
44        HttpService {
45            srv: service.into_factory(),
46            h1_control: h1::DefaultControlService,
47            h2_control: Rc::new(h2::DefaultControlService),
48            _t: marker::PhantomData,
49        }
50    }
51}
52
53impl<F, S, B> HttpService<F, S, B>
54where
55    F: Filter,
56    S: ServiceFactory<Request, SharedCfg> + 'static,
57    S::Error: ResponseError,
58    S::InitError: fmt::Debug,
59    S::Response: Into<Response<B>>,
60    B: MessageBody,
61{
62    /// Create *http service* for HTTP/1 protocol.
63    pub fn h1<U: IntoServiceFactory<S, Request, SharedCfg>>(
64        service: U,
65    ) -> h1::H1Service<F, S, B, h1::DefaultControlService> {
66        h1::H1Service::new(service)
67    }
68
69    /// Create *http service* for HTTP/2 protocol.
70    pub fn h2<U: IntoServiceFactory<S, Request, SharedCfg>>(
71        service: U,
72    ) -> h2::H2Service<F, S, B, h2::DefaultControlService> {
73        h2::H2Service::new(service)
74    }
75}
76
77impl<F, S, B, C1, C2> HttpService<F, S, B, C1, C2>
78where
79    F: Filter,
80    S: ServiceFactory<Request, SharedCfg> + 'static,
81    S::Error: ResponseError,
82    S::InitError: fmt::Debug,
83    S::Response: Into<Response<B>>,
84    B: MessageBody,
85    C1: ServiceFactory<h1::Control<F, S::Error>, SharedCfg, Response = h1::ControlAck<F>>,
86    C1::Error: error::Error,
87    C1::InitError: fmt::Debug,
88    C2: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>,
89    C2::Error: error::Error,
90    C2::InitError: fmt::Debug,
91{
92    /// Provide http/1 control service.
93    pub fn h1_control<CT, U>(self, control: U) -> HttpService<F, S, B, CT, C2>
94    where
95        U: IntoServiceFactory<CT, h1::Control<F, S::Error>, SharedCfg>,
96        CT: ServiceFactory<
97                h1::Control<F, S::Error>,
98                SharedCfg,
99                Response = h1::ControlAck<F>,
100            >,
101        CT::Error: error::Error,
102        CT::InitError: fmt::Debug,
103    {
104        HttpService {
105            h1_control: control.into_factory(),
106            h2_control: self.h2_control,
107            srv: self.srv,
108            _t: marker::PhantomData,
109        }
110    }
111
112    /// Provide http/1 control service.
113    pub fn h2_control<CT, U>(self, control: U) -> HttpService<F, S, B, C1, CT>
114    where
115        U: IntoServiceFactory<CT, h2::Control<H2Error>, SharedCfg>,
116        CT: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>,
117        CT::Error: error::Error,
118        CT::InitError: fmt::Debug,
119    {
120        HttpService {
121            h1_control: self.h1_control,
122            h2_control: Rc::new(control.into_factory()),
123            srv: self.srv,
124            _t: marker::PhantomData,
125        }
126    }
127}
128
129#[cfg(feature = "openssl")]
130#[allow(clippy::wildcard_imports)]
131mod openssl {
132    use ntex_tls::openssl::{SslAcceptor, SslFilter};
133    use tls_openssl::ssl;
134
135    use super::*;
136    use crate::{io::Layer, server::SslError};
137
138    impl<F, S, B, C1, C2> HttpService<Layer<SslFilter, F>, S, B, C1, C2>
139    where
140        F: Filter,
141        S: ServiceFactory<Request, SharedCfg> + 'static,
142        S::Error: ResponseError,
143        S::InitError: fmt::Debug,
144        S::Response: Into<Response<B>>,
145        B: MessageBody,
146        C1: ServiceFactory<
147                h1::Control<Layer<SslFilter, F>, S::Error>,
148                SharedCfg,
149                Response = h1::ControlAck<Layer<SslFilter, F>>,
150            > + 'static,
151        C1::Error: error::Error,
152        C1::InitError: fmt::Debug,
153        C2: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>
154            + 'static,
155        C2::Error: error::Error,
156        C2::InitError: fmt::Debug,
157    {
158        /// Create openssl based service
159        pub fn openssl(
160            self,
161            acceptor: ssl::SslAcceptor,
162        ) -> impl ServiceFactory<
163            Io<F>,
164            SharedCfg,
165            Response = (),
166            Error = SslError<DispatchError>,
167            InitError = (),
168        > {
169            SslAcceptor::new(acceptor)
170                .map_err(SslError::Ssl)
171                .map_init_err(|()| unreachable!())
172                .and_then(self.map_err(SslError::Service))
173        }
174    }
175}
176
177#[cfg(feature = "rustls")]
178#[allow(clippy::wildcard_imports)]
179mod rustls {
180    use ntex_tls::rustls::{TlsAcceptor, TlsServerFilter};
181    use tls_rustls::ServerConfig;
182
183    use super::*;
184    use crate::{io::Layer, server::SslError};
185
186    impl<F, S, B, C1, C2> HttpService<Layer<TlsServerFilter, F>, S, B, C1, C2>
187    where
188        F: Filter,
189        S: ServiceFactory<Request, SharedCfg> + 'static,
190        S::Error: ResponseError,
191        S::InitError: fmt::Debug,
192        S::Response: Into<Response<B>>,
193        B: MessageBody,
194        C1: ServiceFactory<
195                h1::Control<Layer<TlsServerFilter, F>, S::Error>,
196                SharedCfg,
197                Response = h1::ControlAck<Layer<TlsServerFilter, F>>,
198            > + 'static,
199        C1::Error: error::Error,
200        C1::InitError: fmt::Debug,
201        C2: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>
202            + 'static,
203        C2::Error: error::Error,
204        C2::InitError: fmt::Debug,
205    {
206        /// Create openssl based service
207        pub fn rustls(
208            self,
209            mut config: ServerConfig,
210        ) -> impl ServiceFactory<
211            Io<F>,
212            SharedCfg,
213            Response = (),
214            Error = SslError<DispatchError>,
215            InitError = (),
216        > {
217            let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
218            config.alpn_protocols = protos;
219
220            TlsAcceptor::from(config)
221                .map_err(|e| SslError::Ssl(Box::new(e)))
222                .map_init_err(|()| unreachable!())
223                .and_then(self.map_err(SslError::Service))
224        }
225    }
226}
227
228impl<F, S, B, C1, C2> ServiceFactory<Io<F>, SharedCfg> for HttpService<F, S, B, C1, C2>
229where
230    F: Filter,
231    S: ServiceFactory<Request, SharedCfg> + 'static,
232    S::Error: ResponseError,
233    S::InitError: fmt::Debug,
234    S::Response: Into<Response<B>>,
235    B: MessageBody,
236    C1: ServiceFactory<h1::Control<F, S::Error>, SharedCfg, Response = h1::ControlAck<F>>
237        + 'static,
238    C1::Error: error::Error,
239    C1::InitError: fmt::Debug,
240    C2: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>
241        + 'static,
242    C2::Error: error::Error,
243    C2::InitError: fmt::Debug,
244{
245    type Response = ();
246    type Error = DispatchError;
247    type InitError = ();
248    type Service = HttpServiceHandler<F, S::Service, B, C1::Service, C2>;
249
250    async fn create(&self, cfg: SharedCfg) -> Result<Self::Service, Self::InitError> {
251        let service = self
252            .srv
253            .create(cfg.clone())
254            .await
255            .map_err(|e| log::error!("Cannot construct publish service: {e:?}"))?;
256        let control = self
257            .h1_control
258            .create(cfg.clone())
259            .await
260            .map_err(|e| log::error!("Cannot construct control service: {e:?}"))?;
261
262        let (tx, rx) = oneshot::channel();
263        let config = DispatcherConfig::new(cfg.get(), service, control);
264
265        Ok(HttpServiceHandler {
266            cfg,
267            config: Rc::new(config),
268            h2_control: self.h2_control.clone(),
269            inflight: RefCell::new(HashSet::default()),
270            rx: Cell::new(Some(rx)),
271            tx: Cell::new(Some(tx)),
272            _t: marker::PhantomData,
273        })
274    }
275}
276
277/// `Service` implementation for http transport
278#[derive(derive_more::Debug)]
279#[debug("HttpServiceHandler")]
280pub struct HttpServiceHandler<F, S, B, C1, C2> {
281    cfg: SharedCfg,
282    config: Rc<DispatcherConfig<S, C1>>,
283    h2_control: Rc<C2>,
284    inflight: RefCell<HashSet<IoRef>>,
285    rx: Cell<Option<oneshot::Receiver<()>>>,
286    tx: Cell<Option<oneshot::Sender<()>>>,
287    _t: marker::PhantomData<(F, B)>,
288}
289
290impl<F, S, B, C1, C2> Service<Io<F>> for HttpServiceHandler<F, S, B, C1, C2>
291where
292    F: Filter,
293    S: Service<Request> + 'static,
294    S::Error: ResponseError,
295    S::Response: Into<Response<B>>,
296    B: MessageBody,
297    C1: Service<h1::Control<F, S::Error>, Response = h1::ControlAck<F>> + 'static,
298    C1::Error: error::Error,
299    C2: ServiceFactory<h2::Control<H2Error>, SharedCfg, Response = h2::ControlAck>
300        + 'static,
301    C2::Error: error::Error,
302    C2::InitError: fmt::Debug,
303{
304    type Response = ();
305    type Error = DispatchError;
306
307    async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
308        let cfg = self.config.as_ref();
309
310        let (ready1, ready2) = join(cfg.control.ready(), cfg.service.ready()).await;
311        ready1.map_err(|e| {
312            log::error!("Http control service readiness error: {e:?}");
313            DispatchError::Control(Rc::new(e))
314        })?;
315        ready2.map_err(|e| {
316            log::error!("Http service readiness error: {e:?}");
317            DispatchError::Service(Rc::new(e))
318        })
319    }
320
321    #[inline]
322    fn poll(&self, cx: &mut Context<'_>) -> Result<(), Self::Error> {
323        let cfg = self.config.as_ref();
324        cfg.control
325            .poll(cx)
326            .map_err(|e| DispatchError::Control(Rc::new(e)))?;
327        cfg.service
328            .poll(cx)
329            .map_err(|e| DispatchError::Service(Rc::new(e)))
330    }
331
332    async fn shutdown(&self) {
333        self.config.shutdown();
334
335        // check inflight connections
336        let inflight = {
337            let inflight = self.inflight.borrow();
338            for io in inflight.iter() {
339                io.notify_dispatcher();
340            }
341            inflight.len()
342        };
343        if inflight != 0 {
344            log::trace!("Shutting down service, in-flight connections: {inflight}");
345
346            if let Some(rx) = self.rx.take() {
347                let _ = rx.await;
348            }
349
350            log::trace!("Shutting down is complected");
351        }
352
353        join(
354            self.config.control.shutdown(),
355            self.config.service.shutdown(),
356        )
357        .await;
358    }
359
360    async fn call(
361        &self,
362        io: Io<F>,
363        _: ServiceCtx<'_, Self>,
364    ) -> Result<Self::Response, Self::Error> {
365        let id = self.config.next_id();
366        let ioref = io.get_ref();
367
368        let result = if io.query::<types::HttpProtocol>().get()
369            == Some(types::HttpProtocol::Http2)
370        {
371            let control = self
372                .h2_control
373                .create(self.cfg.clone())
374                .await
375                .map_err(|e| {
376                    DispatchError::Control(crate::util::str_rc_error(format!(
377                        "Cannot construct control service: {e:?}"
378                    )))
379                })?;
380            let inflight = {
381                let mut inflight = self.inflight.borrow_mut();
382                inflight.insert(io.get_ref());
383                inflight.len()
384            };
385
386            log::trace!(
387                "{}: New http2 connection {id}, peer address {:?}, in-flight: {inflight}",
388                io.tag(),
389                io.query::<types::PeerAddr>().get(),
390            );
391
392            h2::handle(id, io.into(), control, self.config.clone()).await
393        } else {
394            let inflight = {
395                let mut inflight = self.inflight.borrow_mut();
396                inflight.insert(io.get_ref());
397                inflight.len()
398            };
399
400            log::trace!(
401                "{}: New http1 connection {id}, peer address {:?}, in-flight: {inflight}",
402                io.tag(),
403                io.query::<types::PeerAddr>().get(),
404            );
405            h1::handle_io(id, io, self.config.clone()).await
406        };
407
408        {
409            let mut inflight = self.inflight.borrow_mut();
410            inflight.remove(&ioref);
411
412            if inflight.is_empty()
413                && let Some(tx) = self.tx.take()
414            {
415                let _ = tx.send(());
416            }
417        }
418
419        result
420    }
421}