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#[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 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 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 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 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 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 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 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#[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 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}