scuffle_http/
server.rs

1use std::fmt::Debug;
2use std::net::SocketAddr;
3
4use crate::error::HttpError;
5use crate::service::{HttpService, HttpServiceFactory};
6
7/// The HTTP server.
8///
9/// This struct is the main entry point for creating and running an HTTP server.
10///
11/// Start creating a new server by calling [`HttpServer::builder`].
12#[derive(Debug, Clone, bon::Builder)]
13#[builder(state_mod(vis = "pub(crate)"))]
14#[allow(dead_code)]
15pub struct HttpServer<F> {
16    /// The [`scuffle_context::Context`] this server will live by.
17    #[builder(default = scuffle_context::Context::global())]
18    ctx: scuffle_context::Context,
19    /// The number of worker tasks to spawn for each server backend.
20    #[builder(default = 1)]
21    worker_tasks: usize,
22    /// The service factory that will be used to create new services.
23    service_factory: F,
24    /// The address to bind to.
25    ///
26    /// Use `[::]` for a dual-stack listener.
27    /// For example, use `[::]:80` to bind to port 80 on both IPv4 and IPv6.
28    bind: SocketAddr,
29    /// Enable HTTP/1.1.
30    #[builder(default = true)]
31    #[cfg(feature = "http1")]
32    #[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
33    enable_http1: bool,
34    /// Enable HTTP/2.
35    #[builder(default = true)]
36    #[cfg(feature = "http2")]
37    #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
38    enable_http2: bool,
39    #[builder(default = false, setters(vis = "", name = enable_http3_internal))]
40    #[cfg(feature = "http3")]
41    #[cfg_attr(docsrs, doc(cfg(feature = "http3")))]
42    enable_http3: bool,
43    /// rustls config.
44    ///
45    /// Use this field to set the server into TLS mode.
46    /// It will only accept TLS connections when this is set.
47    #[cfg(feature = "tls-rustls")]
48    #[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
49    rustls_config: Option<rustls::ServerConfig>,
50}
51
52#[cfg(feature = "http3")]
53#[cfg_attr(docsrs, doc(cfg(feature = "http3")))]
54impl<F, S> HttpServerBuilder<F, S>
55where
56    S: http_server_builder::State,
57    S::EnableHttp3: http_server_builder::IsUnset,
58    S::RustlsConfig: http_server_builder::IsSet,
59{
60    /// Enable HTTP/3 support.
61    ///
62    /// First enable TLS by calling [`rustls_config`](HttpServerBuilder::rustls_config) to enable HTTP/3.
63    pub fn enable_http3(self, enable_http3: bool) -> HttpServerBuilder<F, http_server_builder::SetEnableHttp3<S>> {
64        self.enable_http3_internal(enable_http3)
65    }
66}
67
68#[cfg(feature = "tower")]
69#[cfg_attr(docsrs, doc(cfg(feature = "tower")))]
70impl<M, S> HttpServerBuilder<crate::service::TowerMakeServiceFactory<M, ()>, S>
71where
72    M: tower::MakeService<(), crate::IncomingRequest> + Send,
73    M::Future: Send,
74    M::Service: crate::service::HttpService,
75    S: http_server_builder::State,
76    S::ServiceFactory: http_server_builder::IsUnset,
77{
78    /// Same as calling `service_factory(tower_make_service_factory(tower_make_service))`.
79    ///
80    /// # See Also
81    ///
82    /// - [`service_factory`](HttpServerBuilder::service_factory)
83    /// - [`tower_make_service_factory`](crate::service::tower_make_service_factory)
84    pub fn tower_make_service_factory(
85        self,
86        tower_make_service: M,
87    ) -> HttpServerBuilder<crate::service::TowerMakeServiceFactory<M, ()>, http_server_builder::SetServiceFactory<S>> {
88        self.service_factory(crate::service::tower_make_service_factory(tower_make_service))
89    }
90}
91
92#[cfg(feature = "tower")]
93#[cfg_attr(docsrs, doc(cfg(feature = "tower")))]
94impl<M, S> HttpServerBuilder<crate::service::TowerMakeServiceWithAddrFactory<M>, S>
95where
96    M: tower::MakeService<SocketAddr, crate::IncomingRequest> + Send,
97    M::Future: Send,
98    M::Service: crate::service::HttpService,
99    S: http_server_builder::State,
100    S::ServiceFactory: http_server_builder::IsUnset,
101{
102    /// Same as calling `service_factory(tower_make_service_with_addr_factory(tower_make_service))`.
103    ///
104    /// # See Also
105    ///
106    /// - [`service_factory`](HttpServerBuilder::service_factory)
107    /// - [`tower_make_service_with_addr_factory`](crate::service::tower_make_service_with_addr_factory)
108    pub fn tower_make_service_with_addr(
109        self,
110        tower_make_service: M,
111    ) -> HttpServerBuilder<crate::service::TowerMakeServiceWithAddrFactory<M>, http_server_builder::SetServiceFactory<S>>
112    {
113        self.service_factory(crate::service::tower_make_service_with_addr_factory(tower_make_service))
114    }
115}
116
117#[cfg(feature = "tower")]
118#[cfg_attr(docsrs, doc(cfg(feature = "tower")))]
119impl<M, T, S> HttpServerBuilder<crate::service::TowerMakeServiceFactory<M, T>, S>
120where
121    M: tower::MakeService<T, crate::IncomingRequest> + Send,
122    M::Future: Send,
123    M::Service: crate::service::HttpService,
124    T: Clone + Send,
125    S: http_server_builder::State,
126    S::ServiceFactory: http_server_builder::IsUnset,
127{
128    /// Same as calling `service_factory(custom_tower_make_service_factory(tower_make_service, target))`.
129    ///
130    /// # See Also
131    ///
132    /// - [`service_factory`](HttpServerBuilder::service_factory)
133    /// - [`custom_tower_make_service_factory`](crate::service::custom_tower_make_service_factory)
134    pub fn custom_tower_make_service_factory(
135        self,
136        tower_make_service: M,
137        target: T,
138    ) -> HttpServerBuilder<crate::service::TowerMakeServiceFactory<M, T>, http_server_builder::SetServiceFactory<S>> {
139        self.service_factory(crate::service::custom_tower_make_service_factory(tower_make_service, target))
140    }
141}
142
143impl<F> HttpServer<F>
144where
145    F: HttpServiceFactory + Clone + Send + 'static,
146    F::Error: std::error::Error + Send,
147    F::Service: Clone + Send + 'static,
148    <F::Service as HttpService>::Error: std::error::Error + Send + Sync,
149    <F::Service as HttpService>::ResBody: Send,
150    <<F::Service as HttpService>::ResBody as http_body::Body>::Data: Send,
151    <<F::Service as HttpService>::ResBody as http_body::Body>::Error: std::error::Error + Send + Sync,
152{
153    #[cfg(feature = "tls-rustls")]
154    fn set_alpn_protocols(&mut self) {
155        let Some(rustls_config) = &mut self.rustls_config else {
156            return;
157        };
158
159        // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
160        if rustls_config.alpn_protocols.is_empty() {
161            #[cfg(feature = "http1")]
162            if self.enable_http1 {
163                rustls_config.alpn_protocols.push(b"http/1.0".to_vec());
164                rustls_config.alpn_protocols.push(b"http/1.1".to_vec());
165            }
166
167            #[cfg(feature = "http2")]
168            if self.enable_http2 {
169                rustls_config.alpn_protocols.push(b"h2".to_vec());
170                rustls_config.alpn_protocols.push(b"h2c".to_vec());
171            }
172
173            #[cfg(feature = "http3")]
174            if self.enable_http3 {
175                rustls_config.alpn_protocols.push(b"h3".to_vec());
176            }
177        }
178    }
179
180    /// Run the server.
181    ///
182    /// This will:
183    ///
184    /// - Start listening on all configured interfaces for incoming connections.
185    /// - Accept all incoming connections.
186    /// - Handle incoming requests by passing them to the configured service factory.
187    pub async fn run(#[allow(unused_mut)] mut self) -> Result<(), HttpError<F>> {
188        #[cfg(feature = "tls-rustls")]
189        self.set_alpn_protocols();
190
191        #[cfg(all(not(any(feature = "http1", feature = "http2")), feature = "tls-rustls"))]
192        let start_tcp_backend = false;
193        #[cfg(all(feature = "http1", not(feature = "http2")))]
194        let start_tcp_backend = self.enable_http1;
195        #[cfg(all(not(feature = "http1"), feature = "http2"))]
196        let start_tcp_backend = self.enable_http2;
197        #[cfg(all(feature = "http1", feature = "http2"))]
198        let start_tcp_backend = self.enable_http1 || self.enable_http2;
199
200        #[cfg(feature = "tls-rustls")]
201        if let Some(_rustls_config) = self.rustls_config {
202            #[cfg(not(feature = "http3"))]
203            let enable_http3 = false;
204            #[cfg(feature = "http3")]
205            let enable_http3 = self.enable_http3;
206
207            match (start_tcp_backend, enable_http3) {
208                #[cfg(feature = "http3")]
209                (false, true) => {
210                    let backend = crate::backend::h3::Http3Backend::builder()
211                        .ctx(self.ctx)
212                        .worker_tasks(self.worker_tasks)
213                        .service_factory(self.service_factory)
214                        .bind(self.bind)
215                        .rustls_config(_rustls_config)
216                        .build();
217
218                    return backend.run().await;
219                }
220                #[cfg(any(feature = "http1", feature = "http2"))]
221                (true, false) => {
222                    let builder = crate::backend::hyper::HyperBackend::builder()
223                        .ctx(self.ctx)
224                        .worker_tasks(self.worker_tasks)
225                        .service_factory(self.service_factory)
226                        .bind(self.bind)
227                        .rustls_config(_rustls_config);
228
229                    #[cfg(feature = "http1")]
230                    let builder = builder.http1_enabled(self.enable_http1);
231
232                    #[cfg(feature = "http2")]
233                    let builder = builder.http2_enabled(self.enable_http2);
234
235                    return builder.build().run().await;
236                }
237                #[cfg(all(any(feature = "http1", feature = "http2"), feature = "http3"))]
238                (true, true) => {
239                    let builder = crate::backend::hyper::HyperBackend::builder()
240                        .ctx(self.ctx.clone())
241                        .worker_tasks(self.worker_tasks)
242                        .service_factory(self.service_factory.clone())
243                        .bind(self.bind)
244                        .rustls_config(_rustls_config.clone());
245
246                    #[cfg(feature = "http1")]
247                    let builder = builder.http1_enabled(self.enable_http1);
248
249                    #[cfg(feature = "http2")]
250                    let builder = builder.http2_enabled(self.enable_http2);
251
252                    let hyper = std::pin::pin!(builder.build().run());
253
254                    let http3 = crate::backend::h3::Http3Backend::builder()
255                        .ctx(self.ctx)
256                        .worker_tasks(self.worker_tasks)
257                        .service_factory(self.service_factory)
258                        .bind(self.bind)
259                        .rustls_config(_rustls_config)
260                        .build()
261                        .run();
262                    let http3 = std::pin::pin!(http3);
263
264                    let res = futures::future::select(hyper, http3).await;
265                    match res {
266                        futures::future::Either::Left((res, _)) => return res,
267                        futures::future::Either::Right((res, _)) => return res,
268                    }
269                }
270                _ => return Ok(()),
271            }
272
273            // This line must be unreachable
274        }
275
276        // At this point we know that we are not using TLS either
277        // - because the feature is disabled
278        // - or because it's enabled but the config is None.
279
280        #[cfg(any(feature = "http1", feature = "http2"))]
281        if start_tcp_backend {
282            let builder = crate::backend::hyper::HyperBackend::builder()
283                .ctx(self.ctx)
284                .worker_tasks(self.worker_tasks)
285                .service_factory(self.service_factory)
286                .bind(self.bind);
287
288            #[cfg(feature = "http1")]
289            let builder = builder.http1_enabled(self.enable_http1);
290
291            #[cfg(feature = "http2")]
292            let builder = builder.http2_enabled(self.enable_http2);
293
294            return builder.build().run().await;
295        }
296
297        Ok(())
298    }
299}