1use std::{fmt, future::Future, sync::Arc, time::Duration};
2
3use futures_core::stream::Stream;
4use xitca_http::{
5 HttpServiceBuilder,
6 body::RequestBody,
7 config::{DEFAULT_HEADER_LIMIT, DEFAULT_READ_BUF_LIMIT, DEFAULT_WRITE_BUF_LIMIT, HttpServiceConfig},
8};
9use xitca_server::{Builder, ServerFuture, net::IntoListener};
10use xitca_service::ServiceExt;
11
12use crate::{
13 bytes::Bytes,
14 http::{Request, RequestExt, Response},
15 service::{Service, ready::ReadyService},
16};
17
18pub struct HttpServer<
20 S,
21 const HEADER_LIMIT: usize = DEFAULT_HEADER_LIMIT,
22 const READ_BUF_LIMIT: usize = DEFAULT_READ_BUF_LIMIT,
23 const WRITE_BUF_LIMIT: usize = DEFAULT_WRITE_BUF_LIMIT,
24> {
25 service: Arc<S>,
26 builder: Builder,
27 config: HttpServiceConfig<HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>,
28}
29
30impl<S> HttpServer<S>
31where
32 S: Send + Sync + 'static,
33{
34 pub fn serve(service: S) -> Self {
35 Self {
36 service: Arc::new(service),
37 builder: Builder::new(),
38 config: HttpServiceConfig::default(),
39 }
40 }
41}
42
43impl<S, const HEADER_LIMIT: usize, const READ_BUF_LIMIT: usize, const WRITE_BUF_LIMIT: usize>
44 HttpServer<S, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT>
45where
46 S: Send + Sync + 'static,
47{
48 pub fn server_threads(mut self, num: usize) -> Self {
55 self.builder = self.builder.server_threads(num);
56 self
57 }
58
59 pub fn worker_threads(mut self, num: usize) -> Self {
66 self.builder = self.builder.worker_threads(num);
67 self
68 }
69
70 pub fn worker_max_blocking_threads(mut self, num: usize) -> Self {
74 self.builder = self.builder.worker_max_blocking_threads(num);
75 self
76 }
77
78 pub fn disable_signal(mut self) -> Self {
83 self.builder = self.builder.disable_signal();
84 self
85 }
86
87 pub fn backlog(mut self, num: u32) -> Self {
88 self.builder = self.builder.backlog(num);
89 self
90 }
91
92 pub fn disable_vectored_write(mut self) -> Self {
96 self.config = self.config.disable_vectored_write();
97 self
98 }
99
100 pub fn keep_alive_timeout(mut self, dur: Duration) -> Self {
104 self.config = self.config.keep_alive_timeout(dur);
105 self
106 }
107
108 pub fn request_head_timeout(mut self, dur: Duration) -> Self {
114 self.config = self.config.request_head_timeout(dur);
115 self
116 }
117
118 pub fn tls_accept_timeout(mut self, dur: Duration) -> Self {
122 self.config = self.config.tls_accept_timeout(dur);
123 self
124 }
125
126 pub fn max_read_buf_size<const READ_BUF_LIMIT_2: usize>(
133 self,
134 ) -> HttpServer<S, HEADER_LIMIT, READ_BUF_LIMIT_2, WRITE_BUF_LIMIT> {
135 self.mutate_const_generic::<HEADER_LIMIT, READ_BUF_LIMIT_2, WRITE_BUF_LIMIT>()
136 }
137
138 pub fn max_write_buf_size<const WRITE_BUF_LIMIT_2: usize>(
145 self,
146 ) -> HttpServer<S, HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT_2> {
147 self.mutate_const_generic::<HEADER_LIMIT, READ_BUF_LIMIT, WRITE_BUF_LIMIT_2>()
148 }
149
150 pub fn max_request_headers<const HEADER_LIMIT_2: usize>(
154 self,
155 ) -> HttpServer<S, HEADER_LIMIT_2, READ_BUF_LIMIT, WRITE_BUF_LIMIT> {
156 self.mutate_const_generic::<HEADER_LIMIT_2, READ_BUF_LIMIT, WRITE_BUF_LIMIT>()
157 }
158
159 #[doc(hidden)]
160 pub fn on_worker_start<FS, Fut>(mut self, on_start: FS) -> Self
161 where
162 FS: Fn() -> Fut + Send + Sync + 'static,
163 Fut: Future + Send + 'static,
164 {
165 self.builder = self.builder.on_worker_start(on_start);
166 self
167 }
168
169 #[cfg(not(target_family = "wasm"))]
170 pub fn bind<A, ResB, BE>(mut self, addr: A) -> std::io::Result<Self>
171 where
172 A: std::net::ToSocketAddrs,
173 S: Service + 'static,
174 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
175 S::Error: fmt::Debug,
176 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
177 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
178 BE: fmt::Debug + 'static,
179 {
180 let config = self.config;
181 let service = self.service.clone().enclosed(HttpServiceBuilder::with_config(config));
182 self.builder = self.builder.bind("xitca-web", addr, service)?;
183 Ok(self)
184 }
185
186 pub fn listen<ResB, BE, L>(mut self, listener: L) -> std::io::Result<Self>
187 where
188 S: Service + 'static,
189 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
190 S::Error: fmt::Debug,
191 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
192 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
193 BE: fmt::Debug + 'static,
194 L: IntoListener + 'static,
195 {
196 let config = self.config;
197 let service = self.service.clone().enclosed(HttpServiceBuilder::with_config(config));
198 self.builder = self.builder.listen("xitca-web", listener, service);
199 Ok(self)
200 }
201
202 #[cfg(feature = "openssl")]
203 pub fn bind_openssl<A: std::net::ToSocketAddrs, ResB, BE>(
204 mut self,
205 addr: A,
206 mut builder: xitca_tls::openssl::ssl::SslAcceptorBuilder,
207 ) -> std::io::Result<Self>
208 where
209 S: Service + 'static,
210 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
211 S::Error: fmt::Debug,
212 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
213 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
214 BE: fmt::Debug + 'static,
215 {
216 let config = self.config;
217
218 const H11: &[u8] = b"\x08http/1.1";
219
220 const H2: &[u8] = b"\x02h2";
221
222 builder.set_alpn_select_callback(|_, protocols| {
223 if protocols.windows(3).any(|window| window == H2) {
224 #[cfg(feature = "http2")]
225 {
226 Ok(b"h2")
227 }
228 #[cfg(not(feature = "http2"))]
229 Err(xitca_tls::openssl::ssl::AlpnError::ALERT_FATAL)
230 } else if protocols.windows(9).any(|window| window == H11) {
231 Ok(b"http/1.1")
232 } else {
233 Err(xitca_tls::openssl::ssl::AlpnError::NOACK)
234 }
235 });
236
237 #[cfg(not(feature = "http2"))]
238 let protos = H11.iter().cloned().collect::<Vec<_>>();
239
240 #[cfg(feature = "http2")]
241 let protos = H11.iter().chain(H2).cloned().collect::<Vec<_>>();
242
243 builder.set_alpn_protos(&protos)?;
244
245 let acceptor = builder.build();
246
247 let service = self
248 .service
249 .clone()
250 .enclosed(HttpServiceBuilder::with_config(config).openssl(acceptor));
251
252 self.builder = self.builder.bind("xitca-web-openssl", addr, service)?;
253
254 Ok(self)
255 }
256
257 #[cfg(feature = "rustls")]
258 pub fn bind_rustls<A: std::net::ToSocketAddrs, ResB, BE>(
259 mut self,
260 addr: A,
261 #[cfg_attr(not(all(feature = "http1", feature = "http2")), allow(unused_mut))]
262 mut config: xitca_tls::rustls::ServerConfig,
263 ) -> std::io::Result<Self>
264 where
265 S: Service + 'static,
266 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
267 S::Error: fmt::Debug,
268 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
269 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
270 BE: fmt::Debug + 'static,
271 {
272 let service_config = self.config;
273
274 #[cfg(feature = "http2")]
275 config.alpn_protocols.push("h2".into());
276
277 #[cfg(feature = "http1")]
278 config.alpn_protocols.push("http/1.1".into());
279
280 let config = std::sync::Arc::new(config);
281
282 let service = self
283 .service
284 .clone()
285 .enclosed(HttpServiceBuilder::with_config(service_config).rustls(config));
286
287 self.builder = self.builder.bind("xitca-web-rustls", addr, service)?;
288
289 Ok(self)
290 }
291
292 #[cfg(unix)]
293 pub fn bind_unix<P: AsRef<std::path::Path>, ResB, BE>(mut self, path: P) -> std::io::Result<Self>
294 where
295 S: Service + 'static,
296 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
297 S::Error: fmt::Debug,
298 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
299 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
300 BE: fmt::Debug + 'static,
301 {
302 let config = self.config;
303 let service = self.service.clone().enclosed(HttpServiceBuilder::with_config(config));
304 self.builder = self.builder.bind_unix("xitca-web", path, service)?;
305 Ok(self)
306 }
307
308 #[cfg(feature = "http3")]
309 pub fn bind_h3<A: std::net::ToSocketAddrs, ResB, BE>(
310 mut self,
311 addr: A,
312 config: xitca_io::net::QuicConfig,
313 ) -> std::io::Result<Self>
314 where
315 S: Service + 'static,
316 S::Response: ReadyService + Service<Request<RequestExt<RequestBody>>, Response = Response<ResB>> + 'static,
317 S::Error: fmt::Debug,
318 <S::Response as Service<Request<RequestExt<RequestBody>>>>::Error: fmt::Debug,
319 ResB: Stream<Item = Result<Bytes, BE>> + 'static,
320 BE: fmt::Debug + 'static,
321 {
322 let service = self
323 .service
324 .clone()
325 .enclosed(HttpServiceBuilder::with_config(self.config));
326
327 self.builder = self.builder.bind_h3("xitca-web", addr, config, service)?;
328 Ok(self)
329 }
330
331 pub fn run(self) -> ServerFuture {
332 self.builder.build()
333 }
334
335 fn mutate_const_generic<const HEADER_LIMIT2: usize, const READ_BUF_LIMIT2: usize, const WRITE_BUF_LIMIT2: usize>(
336 self,
337 ) -> HttpServer<S, HEADER_LIMIT2, READ_BUF_LIMIT2, WRITE_BUF_LIMIT2> {
338 HttpServer {
339 service: self.service,
340 builder: self.builder,
341 config: self
342 .config
343 .mutate_const_generic::<HEADER_LIMIT2, READ_BUF_LIMIT2, WRITE_BUF_LIMIT2>(),
344 }
345 }
346}