xitca_http/
builder.rs

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