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