1use std::{io, marker::PhantomData, net, sync::Arc, sync::Mutex};
2
3#[cfg(feature = "openssl")]
4use tls_openssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
5#[cfg(feature = "rustls")]
6use tls_rustls::ServerConfig as RustlsServerConfig;
7
8use crate::error::IntoFailure;
9use crate::http::{self, Request, Response, ResponseError};
10use crate::server::{NoConfig, Server, ServerAppConfig, ServerBuilder};
11use crate::service::{IntoServiceFactory, Service, ServiceFactory, State, fn_service};
12use crate::{SharedCfg, time::Seconds};
13
14struct Config {
15 host: Option<String>,
16}
17
18#[derive(derive_more::Debug)]
36#[debug("HttpServer")]
37pub struct HttpServer<Cfg, F, I, Sf>
38where
39 Cfg: ServerAppConfig,
40 Cfg::State: Clone,
41 F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
42 I: IntoServiceFactory<Sf, Cfg::State, Request>,
43 Sf: ServiceFactory<Cfg::State, Request>,
44 Sf::Res: Into<Response>,
45 Sf::Error: ResponseError,
46 Sf::InitError: IntoFailure,
47{
48 factory: F,
49 config: Arc<Mutex<Config>>,
50 backlog: i32,
51 builder: ServerBuilder<Cfg>,
52 _t: PhantomData<Sf>,
53}
54
55impl<F, I, Sf> HttpServer<NoConfig, F, I, Sf>
56where
57 F: AsyncFn(&()) -> I + Send + Clone + 'static,
58 I: IntoServiceFactory<Sf, (), Request>,
59 Sf: ServiceFactory<(), Request> + 'static,
60 Sf::Res: Into<Response>,
61 Sf::Error: ResponseError,
62 Sf::InitError: IntoFailure,
63{
64 #[must_use]
65 pub fn new(factory: F) -> Self {
67 HttpServer {
68 factory,
69 config: Arc::new(Mutex::new(Config { host: None })),
70 backlog: 1024,
71 builder: ServerBuilder::default(),
72 _t: PhantomData,
73 }
74 }
75}
76
77impl<Cfg, F, I, Sf> HttpServer<Cfg, F, I, Sf>
78where
79 Cfg: ServerAppConfig,
80 Cfg::State: Clone,
81 F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
82 I: IntoServiceFactory<Sf, Cfg::State, Request>,
83 Sf: ServiceFactory<Cfg::State, Request> + 'static,
84 Sf::Res: Into<Response>,
85 Sf::Error: ResponseError,
86 Sf::InitError: IntoFailure,
87{
88 #[must_use]
89 pub fn with_config(cfg: Cfg, factory: F) -> Self
91 where
92 Cfg: ServerAppConfig,
93 {
94 HttpServer {
95 factory,
96 config: Arc::new(Mutex::new(Config { host: None })),
97 backlog: 1024,
98 builder: ServerBuilder::new(cfg),
99 _t: PhantomData,
100 }
101 }
102
103 #[must_use]
104 pub fn workers(mut self, num: usize) -> Self {
109 self.builder = self.builder.workers(num);
110 self
111 }
112
113 #[must_use]
114 pub fn backlog(mut self, backlog: i32) -> Self {
125 self.backlog = backlog;
126 self.builder = self.builder.backlog(backlog);
127 self
128 }
129
130 #[must_use]
131 pub fn max_connections(mut self, num: usize) -> Self {
138 self.builder = self.builder.max_connections(num);
139 self
140 }
141
142 #[must_use]
143 pub fn max_tls_handshakes(self, num: usize) -> Self {
152 ntex_tls::max_concurrent_ssl_accept(num);
153 self
154 }
155
156 #[must_use]
157 pub fn server_hostname<T: AsRef<str>>(self, val: T) -> Self {
165 self.config.lock().unwrap().host = Some(val.as_ref().to_owned());
166 self
167 }
168
169 #[must_use]
170 pub fn stop_runtime(mut self) -> Self {
174 self.builder = self.builder.stop_runtime();
175 self
176 }
177
178 #[must_use]
179 pub fn stop_on_panic(mut self) -> Self {
183 self.builder = self.builder.stop_on_panic();
184 self
185 }
186
187 #[must_use]
188 pub fn disable_signals(mut self) -> Self {
192 self.builder = self.builder.disable_signals();
193 self
194 }
195
196 #[must_use]
197 pub fn graceful_shutdown_timeout(mut self, sec: Seconds) -> Self {
210 self.builder = self.builder.graceful_shutdown_timeout(sec);
211 self
212 }
213
214 #[must_use]
215 pub fn enable_affinity(mut self) -> Self {
219 self.builder = self.builder.enable_affinity();
220 self
221 }
222
223 #[must_use]
224 pub fn graceful_shutdown(mut self) -> Self {
232 self.builder = self.builder.graceful_shutdown();
233 self
234 }
235
236 pub fn listen(mut self, lst: net::TcpListener, cfg: impl Into<SharedCfg>) -> io::Result<Self> {
241 let factory = self.factory.clone();
242 let addr = lst.local_addr().unwrap();
243
244 self.builder = self.builder.listen(
245 format!("ntex-web-service-{addr}"),
246 lst,
247 cfg.into(),
248 async move |st| {
249 let state = st.clone();
250 fn_service(async move |req| {
251 Ok(State {
252 req,
253 state: state.clone(),
254 })
255 })
256 .and_then(http::HttpService::new(factory(st).await))
257 },
258 )?;
259 Ok(self)
260 }
261
262 #[cfg(feature = "openssl")]
263 pub fn listen_openssl(
267 self,
268 lst: net::TcpListener,
269 cfg: impl Into<SharedCfg>,
270 builder: SslAcceptorBuilder,
271 ) -> io::Result<Self> {
272 self.listen_openssl_inner(lst, cfg.into(), openssl_acceptor(builder)?)
273 }
274
275 #[cfg(feature = "openssl")]
276 fn listen_openssl_inner(
277 mut self,
278 lst: net::TcpListener,
279 cfg: SharedCfg,
280 acceptor: SslAcceptor,
281 ) -> io::Result<Self> {
282 let factory = self.factory.clone();
283 let addr = lst.local_addr().unwrap();
284
285 self.builder = self.builder.listen(
286 format!("ntex-web-service-{addr}"),
287 lst,
288 cfg,
289 async move |st| {
290 let state = st.clone();
291
292 http::openssl(
293 acceptor.clone(),
294 fn_service(async move |req| {
295 Ok(State {
296 req,
297 state: state.clone(),
298 })
299 })
300 .and_then(http::HttpService::new(factory(st).await)),
301 )
302 },
303 )?;
304 Ok(self)
305 }
306
307 #[cfg(feature = "rustls")]
308 pub fn listen_rustls(
312 self,
313 lst: net::TcpListener,
314 cfg: impl Into<SharedCfg>,
315 config: RustlsServerConfig,
316 ) -> io::Result<Self> {
317 self.listen_rustls_inner(lst, cfg.into(), config)
318 }
319
320 #[cfg(feature = "rustls")]
321 fn listen_rustls_inner(
322 mut self,
323 lst: net::TcpListener,
324 cfg: SharedCfg,
325 config: RustlsServerConfig,
326 ) -> io::Result<Self> {
327 let factory = self.factory.clone();
328 let addr = lst.local_addr().unwrap();
329
330 self.builder = self.builder.listen(
331 format!("ntex-web-rustls-service-{addr}"),
332 lst,
333 cfg,
334 async move |st| {
335 let state = st.clone();
336 http::rustls(
337 config.clone(),
338 http::ALPN_PROTOS,
339 fn_service(async move |req| {
340 Ok(State {
341 req,
342 state: state.clone(),
343 })
344 })
345 .and_then(http::HttpService::new(factory(st).await)),
346 )
347 },
348 )?;
349 Ok(self)
350 }
351
352 pub fn bind<A: net::ToSocketAddrs>(
356 mut self,
357 addr: A,
358 cfg: impl Into<SharedCfg>,
359 ) -> io::Result<Self> {
360 let cfg = cfg.into();
361 for lst in self.bind2(addr)? {
362 self = self.listen(lst, cfg.clone())?;
363 }
364
365 Ok(self)
366 }
367
368 fn bind2<A: net::ToSocketAddrs>(&self, addr: A) -> io::Result<Vec<net::TcpListener>> {
369 let mut err = None;
370 let mut succ = false;
371 let mut sockets = Vec::new();
372 for addr in addr.to_socket_addrs()? {
373 match crate::server::create_tcp_listener(addr, self.backlog) {
374 Ok(lst) => {
375 succ = true;
376 sockets.push(lst);
377 }
378 Err(e) => err = Some(e),
379 }
380 }
381
382 if succ {
383 Ok(sockets)
384 } else if let Some(e) = err.take() {
385 Err(e)
386 } else {
387 Err(io::Error::new(
388 io::ErrorKind::InvalidInput,
389 "Cannot bind to address.",
390 ))
391 }
392 }
393
394 #[cfg(feature = "openssl")]
395 pub fn bind_openssl<A>(
399 mut self,
400 addr: A,
401 builder: SslAcceptorBuilder,
402 cfg: impl Into<SharedCfg>,
403 ) -> io::Result<Self>
404 where
405 A: net::ToSocketAddrs,
406 {
407 let cfg = cfg.into();
408 let sockets = self.bind2(addr)?;
409 let acceptor = openssl_acceptor(builder)?;
410
411 for lst in sockets {
412 self = self.listen_openssl_inner(lst, cfg.clone(), acceptor.clone())?;
413 }
414
415 Ok(self)
416 }
417
418 #[cfg(feature = "rustls")]
419 pub fn bind_rustls<A: net::ToSocketAddrs>(
423 mut self,
424 addr: A,
425 config: &RustlsServerConfig,
426 cfg: impl Into<SharedCfg>,
427 ) -> io::Result<Self> {
428 let cfg = cfg.into();
429 let sockets = self.bind2(addr)?;
430 for lst in sockets {
431 self = self.listen_rustls_inner(lst, cfg.clone(), config.clone())?;
432 }
433 Ok(self)
434 }
435
436 #[cfg(unix)]
437 pub fn listen_uds(
441 mut self,
442 lst: std::os::unix::net::UnixListener,
443 cfg: impl Into<SharedCfg>,
444 ) -> io::Result<Self> {
445 let factory = self.factory.clone();
446 let addr = format!("ntex-web-service-{:?}", lst.local_addr()?);
447
448 self.builder = self
449 .builder
450 .listen_uds(addr, lst, cfg.into(), async move |st| {
451 let state = st.clone();
452 fn_service(async move |req| {
453 Ok(State {
454 req,
455 state: state.clone(),
456 })
457 })
458 .and_then(http::HttpService::new(factory(st).await))
459 })?;
460 Ok(self)
461 }
462
463 #[cfg(unix)]
464 pub fn bind_uds<A>(mut self, addr: A, cfg: impl Into<SharedCfg>) -> io::Result<Self>
468 where
469 A: AsRef<std::path::Path>,
470 {
471 let factory = self.factory.clone();
472
473 self.builder = self.builder.bind_uds(
474 format!("ntex-web-service-{:?}", addr.as_ref().display()),
475 addr,
476 cfg.into(),
477 async move |st| {
478 let state = st.clone();
479 fn_service(async move |req| {
480 Ok(State {
481 req,
482 state: state.clone(),
483 })
484 })
485 .and_then(http::HttpService::new(factory(st).await))
486 },
487 )?;
488 Ok(self)
489 }
490}
491
492impl<Cfg, F, I, Sf> HttpServer<Cfg, F, I, Sf>
493where
494 Cfg: ServerAppConfig,
495 Cfg::State: Clone,
496 F: AsyncFn(&Cfg::State) -> I + Send + Clone + 'static,
497 I: IntoServiceFactory<Sf, Cfg::State, Request>,
498 Sf: ServiceFactory<Cfg::State, Request> + 'static,
499 Sf::Res: Into<Response>,
500 Sf::Error: ResponseError,
501 Sf::InitError: IntoFailure,
502{
503 pub fn run(self) -> Server {
527 self.builder.run()
528 }
529}
530
531#[cfg(feature = "openssl")]
532fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> {
534 builder.set_alpn_select_callback(|_, protos| {
535 const H2: &[u8] = b"\x02h2";
536 const H11: &[u8] = b"\x08http/1.1";
537 if protos.windows(3).any(|window| window == H2) {
538 Ok(b"h2")
539 } else if protos.windows(9).any(|window| window == H11) {
540 Ok(b"http/1.1")
541 } else {
542 Err(AlpnError::NOACK)
543 }
544 });
545 builder.set_alpn_protos(b"\x08http/1.1\x02h2")?;
546
547 Ok(builder.build())
548}