Skip to main content

xitca_http/
builder.rs

1use core::{fmt, marker::PhantomData};
2
3use xitca_io::net;
4use xitca_service::Service;
5
6use super::{
7    config::{DEFAULT_HEADER_LIMIT, DEFAULT_READ_BUF_LIMIT, DEFAULT_WRITE_BUF_LIMIT, HttpServiceConfig},
8    service::HttpService,
9    tls,
10};
11
12// marker type for separate HttpServerBuilders' ServiceFactory implement with specialized trait
13// method.
14#[doc(hidden)]
15pub(crate) mod marker {
16    // protocol version markers
17    pub struct Http;
18    #[cfg(feature = "http1")]
19    pub struct Http1;
20    #[cfg(feature = "http2")]
21    pub struct Http2;
22
23    // I/O mode markers
24    pub struct Poll;
25    #[cfg(feature = "io-uring")]
26    pub struct Uring;
27}
28
29/// HttpService middleware.
30/// bridge TCP/UDP traffic and HTTP protocols for [`Service`] type.
31pub struct HttpServiceBuilder<
32    V,
33    Io,
34    St,
35    FA,
36    const HEADER_LIMIT: usize,
37    const READ_BUF_LIMIT: usize,
38    const WRITE_BUF_LIMIT: usize,
39> {
40    pub(crate) tls_factory: FA,
41    pub(crate) config: HttpServiceConfig<HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>,
42    pub(crate) _body: PhantomData<fn(V, Io, St)>,
43}
44
45impl
46    HttpServiceBuilder<
47        marker::Http,
48        marker::Poll,
49        net::Stream,
50        tls::NoOpTlsAcceptorBuilder,
51        DEFAULT_HEADER_LIMIT,
52        DEFAULT_READ_BUF_LIMIT,
53        DEFAULT_WRITE_BUF_LIMIT,
54    >
55{
56    /// construct a new service middleware.
57    pub const fn new() -> Self {
58        Self::with_config(HttpServiceConfig::new())
59    }
60
61    /// construct a new service middleware with given [HttpServiceConfig].
62    pub const fn with_config<const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const WRITE_BUF_LIMIT: usize>(
63        config: HttpServiceConfig<HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>,
64    ) -> HttpServiceBuilder<
65        marker::Http,
66        marker::Poll,
67        net::Stream,
68        tls::NoOpTlsAcceptorBuilder,
69        HEADER_LIMIT,
70        READ_BUF_LIMIT,
71        WRITE_BUF_LIMIT,
72    > {
73        HttpServiceBuilder {
74            tls_factory: tls::NoOpTlsAcceptorBuilder,
75            config,
76            _body: PhantomData,
77        }
78    }
79
80    #[cfg(feature = "http1")]
81    /// construct a new service middleware for Http/1 protocol only.
82    pub fn h1() -> HttpServiceBuilder<
83        marker::Http1,
84        marker::Poll,
85        net::TcpStream,
86        tls::NoOpTlsAcceptorBuilder,
87        DEFAULT_HEADER_LIMIT,
88        DEFAULT_READ_BUF_LIMIT,
89        DEFAULT_WRITE_BUF_LIMIT,
90    > {
91        HttpServiceBuilder {
92            tls_factory: tls::NoOpTlsAcceptorBuilder,
93            config: HttpServiceConfig::default(),
94            _body: PhantomData,
95        }
96    }
97
98    #[cfg(feature = "http2")]
99    /// construct a new service middleware for Http/2 protocol only.
100    pub fn h2() -> HttpServiceBuilder<
101        marker::Http2,
102        marker::Poll,
103        net::TcpStream,
104        tls::NoOpTlsAcceptorBuilder,
105        DEFAULT_HEADER_LIMIT,
106        DEFAULT_READ_BUF_LIMIT,
107        DEFAULT_WRITE_BUF_LIMIT,
108    > {
109        HttpServiceBuilder {
110            tls_factory: tls::NoOpTlsAcceptorBuilder,
111            config: HttpServiceConfig::default(),
112            _body: PhantomData,
113        }
114    }
115
116    #[cfg(feature = "http3")]
117    /// construct a new service middleware for Http/3 protocol only.
118    pub fn h3() -> super::h3::H3ServiceBuilder {
119        super::h3::H3ServiceBuilder::new()
120    }
121}
122
123#[cfg(feature = "io-uring")]
124impl<St, FA, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const WRITE_BUF_LIMIT: usize>
125    HttpServiceBuilder<marker::Http, marker::Poll, St, FA, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
126{
127    /// switch to io-uring I/O mode. The stream type is preserved (conversion happens at dispatch).
128    pub fn io_uring(
129        self,
130    ) -> HttpServiceBuilder<marker::Http, marker::Uring, St, FA, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT> {
131        HttpServiceBuilder {
132            tls_factory: self.tls_factory,
133            config: self.config,
134            _body: PhantomData,
135        }
136    }
137}
138
139impl<V, Io, St, FA, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const WRITE_BUF_LIMIT: usize>
140    HttpServiceBuilder<V, Io, St, FA, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
141{
142    /// replace service middleware's configuration.
143    pub fn config<const HEADER_LIMIT_2: usize, const READ_BUF_LIMIT_2: usize, const WRITE_BUF_LIMIT_2: usize>(
144        self,
145        config: HttpServiceConfig<HEADER_LIMIT_2, READ_BUF_LIMIT_2, WRITE_BUF_LIMIT_2>,
146    ) -> HttpServiceBuilder<V, Io, St, FA, HEADER_LIMIT_2, READ_BUF_LIMIT_2, WRITE_BUF_LIMIT_2> {
147        HttpServiceBuilder {
148            tls_factory: self.tls_factory,
149            config,
150            _body: PhantomData,
151        }
152    }
153
154    /// replace tls service. tls service is used for Http/1 and Http/2 protocols.
155    pub fn with_tls<TlsF>(
156        self,
157        tls_factory: TlsF,
158    ) -> HttpServiceBuilder<V, Io, St, TlsF, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT> {
159        HttpServiceBuilder {
160            tls_factory,
161            config: self.config,
162            _body: PhantomData,
163        }
164    }
165    #[cfg(feature = "openssl")]
166    /// use openssl as tls service. tls service is used for Http/1 and Http/2 protocols.
167    pub fn openssl(
168        self,
169        acceptor: tls::openssl::TlsAcceptor,
170    ) -> HttpServiceBuilder<V, Io, St, tls::openssl::TlsAcceptorBuilder, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
171    {
172        self.with_tls(tls::openssl::TlsAcceptorBuilder::new(acceptor))
173    }
174
175    #[cfg(feature = "rustls")]
176    /// use rustls as tls service. tls service is used for Http/1 and Http/2 protocols.
177    pub fn rustls(
178        self,
179        config: tls::rustls::RustlsConfig,
180    ) -> HttpServiceBuilder<V, Io, St, tls::rustls::TlsAcceptorBuilder, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
181    {
182        self.with_tls(tls::rustls::TlsAcceptorBuilder::new(config))
183    }
184
185    #[cfg(feature = "native-tls")]
186    /// use native-tls as tls service. tnative-tlsls service is used for Http/1 protocol only.
187    pub fn native_tls(
188        self,
189        acceptor: tls::native_tls::TlsAcceptor,
190    ) -> HttpServiceBuilder<V, Io, St, tls::native_tls::TlsAcceptorBuilder, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
191    {
192        self.with_tls(tls::native_tls::TlsAcceptorBuilder::new(acceptor))
193    }
194}
195
196type Error = Box<dyn fmt::Debug>;
197
198impl<FA, Io, S, E, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const WRITE_BUF_LIMIT: usize>
199    Service<Result<S, E>>
200    for HttpServiceBuilder<marker::Http, Io, net::Stream, FA, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
201where
202    FA: Service,
203    FA::Error: fmt::Debug + 'static,
204    E: fmt::Debug + 'static,
205{
206    type Response =
207        HttpService<marker::Http, Io, net::Stream, S, FA::Response, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>;
208    type Error = Error;
209
210    async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
211        let service = res.map_err(|e| Box::new(e) as Error)?;
212        let tls_acceptor = self.tls_factory.call(()).await.map_err(|e| Box::new(e) as Error)?;
213        Ok(HttpService::new(self.config, service, tls_acceptor))
214    }
215}