diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 526a23d53..71b933835 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -211,6 +211,7 @@ where /// Finish service configuration and create a HTTP service for HTTP/2 protocol. #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn h2(self, service: F) -> crate::h2::H2Service where F: IntoServiceFactory, diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 41522a254..2d443369d 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -294,6 +294,7 @@ impl std::error::Error for PayloadError { PayloadError::Overflow => None, PayloadError::UnknownLength => None, #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] PayloadError::Http2Payload(err) => Some(err), PayloadError::Io(err) => Some(err), } @@ -351,6 +352,7 @@ pub enum DispatchError { /// HTTP/2 error. #[display(fmt = "{}", _0)] #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] H2(h2::Error), /// The first request did not complete within the specified timeout. diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index a791ea8c3..e4d90424d 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -134,6 +134,7 @@ mod openssl { U::InitError: fmt::Debug, { /// Create OpenSSL based service. + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] pub fn openssl( self, acceptor: SslAcceptor, @@ -196,6 +197,7 @@ mod rustls { U::InitError: fmt::Debug, { /// Create Rustls based service. + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] pub fn rustls( self, config: ServerConfig, diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index e526918c7..2a45fc1dc 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -117,6 +117,7 @@ mod openssl { B: MessageBody + 'static, { /// Create OpenSSL based service. + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] pub fn openssl( self, acceptor: SslAcceptor, @@ -164,6 +165,7 @@ mod rustls { B: MessageBody + 'static, { /// Create Rustls based service. + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] pub fn rustls( self, mut config: ServerConfig, diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 184049860..864db4986 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -40,6 +40,7 @@ pub mod error; mod extensions; pub mod h1; #[cfg(feature = "http2")] +#[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub mod h2; pub mod header; mod helpers; @@ -54,6 +55,7 @@ mod responses; mod service; pub mod test; #[cfg(feature = "ws")] +#[cfg_attr(docsrs, doc(cfg(feature = "ws")))] pub mod ws; pub use self::builder::HttpServiceBuilder; @@ -71,6 +73,7 @@ pub use self::requests::{Request, RequestHead, RequestHeadType}; pub use self::responses::{Response, ResponseBuilder, ResponseHead}; pub use self::service::HttpService; #[cfg(any(feature = "openssl", feature = "rustls"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))] pub use self::service::TlsAcceptorConfig; /// A major HTTP protocol version. diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 27029cb8e..bcca5b188 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -239,6 +239,7 @@ mod openssl { U::InitError: fmt::Debug, { /// Create OpenSSL based service. + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] pub fn openssl( self, acceptor: SslAcceptor, @@ -253,6 +254,7 @@ mod openssl { } /// Create OpenSSL based service with custom TLS acceptor configuration. + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] pub fn openssl_with_config( self, acceptor: SslAcceptor, @@ -332,6 +334,7 @@ mod rustls { U::InitError: fmt::Debug, { /// Create Rustls based service. + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] pub fn rustls( self, config: ServerConfig, @@ -346,6 +349,7 @@ mod rustls { } /// Create Rustls based service with custom TLS acceptor configuration. + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] pub fn rustls_with_config( self, mut config: ServerConfig, diff --git a/actix-web/src/request.rs b/actix-web/src/request.rs index 2998c7010..e2a9bd4e5 100644 --- a/actix-web/src/request.rs +++ b/actix-web/src/request.rs @@ -311,6 +311,7 @@ impl HttpRequest { /// Load request cookies. #[cfg(feature = "cookies")] + #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))] pub fn cookies(&self) -> Result>>, CookieParseError> { use actix_http::header::COOKIE; @@ -334,6 +335,7 @@ impl HttpRequest { /// Return request cookie. #[cfg(feature = "cookies")] + #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))] pub fn cookie(&self, name: &str) -> Option> { if let Ok(cookies) = self.cookies() { for cookie in cookies.iter() { diff --git a/actix-web/src/server.rs b/actix-web/src/server.rs index 9c5076d36..5021e0a2d 100644 --- a/actix-web/src/server.rs +++ b/actix-web/src/server.rs @@ -41,17 +41,22 @@ struct Config { /// /// Create new HTTP server with application factory. /// +/// # HTTP/2 +/// Currently, HTTP/2 is only supported when using TLS (HTTPS). See `bind_rustls` or `bind_openssl`. +/// +/// # Examples /// ```no_run /// use actix_web::{web, App, HttpResponse, HttpServer}; /// -/// #[actix_rt::main] +/// #[actix_web::main] /// async fn main() -> std::io::Result<()> { -/// HttpServer::new( -/// || App::new() -/// .service(web::resource("/").to(|| HttpResponse::Ok()))) -/// .bind("127.0.0.1:59090")? -/// .run() -/// .await +/// HttpServer::new(|| { +/// App::new() +/// .service(web::resource("/").to(|| async { "hello world" })) +/// }) +/// .bind(("127.0.0.1", 8080))? +/// .run() +/// .await /// } /// ``` pub struct HttpServer @@ -108,32 +113,7 @@ where } } - /// Sets function that will be called once before each connection is handled. - /// It will receive a `&std::any::Any`, which contains underlying connection type and an - /// [Extensions] container so that connection data can be accessed in middleware and handlers. - /// - /// # Connection Types - /// - `actix_tls::accept::openssl::TlsStream` when using openssl. - /// - `actix_tls::accept::rustls::TlsStream` when using rustls. - /// - `actix_web::rt::net::TcpStream` when no encryption is used. - /// - /// See the `on_connect` example for additional details. - pub fn on_connect(self, f: CB) -> HttpServer - where - CB: Fn(&dyn Any, &mut Extensions) + Send + Sync + 'static, - { - HttpServer { - factory: self.factory, - config: self.config, - backlog: self.backlog, - sockets: self.sockets, - builder: self.builder, - on_connect_fn: Some(Arc::new(f)), - _phantom: PhantomData, - } - } - - /// Set number of workers to start. + /// Sets number of workers to start (per bind address). /// /// By default, the number of available physical CPUs is used as the worker count. pub fn workers(mut self, num: usize) -> Self { @@ -141,23 +121,30 @@ where self } - /// Set the maximum number of pending connections. + /// Sets server keep-alive preference. /// - /// This refers to the number of clients that can be waiting to be served. - /// Exceeding this number results in the client getting an error when - /// attempting to connect. It should only affect servers under significant - /// load. + /// By default keep-alive is set to 5 seconds. + pub fn keep_alive>(self, val: T) -> Self { + self.config.lock().unwrap().keep_alive = val.into(); + self + } + + /// Sets the maximum number of pending connections. /// - /// Generally set in the 64-2048 range. Default value is 2048. + /// This refers to the number of clients that can be waiting to be served. Exceeding this number + /// results in the client getting an error when attempting to connect. It should only affect + /// servers under significant load. /// - /// This method should be called before `bind()` method call. + /// Generally set in the 64–2048 range. Default value is 2048. + /// + /// This method will have no effect if called after a `bind()`. pub fn backlog(mut self, backlog: u32) -> Self { self.backlog = backlog; self.builder = self.builder.backlog(backlog); self } - /// Sets the maximum per-worker number of concurrent connections. + /// Sets the per-worker maximum number of concurrent connections. /// /// All socket listeners will stop accepting connections when this limit is reached for /// each worker. @@ -168,7 +155,7 @@ where self } - /// Sets the maximum per-worker concurrent connection establish process. + /// Sets the per-worker maximum concurrent TLS connection limit. /// /// All listeners will stop accepting connections when this limit is reached. It can be used to /// limit the global TLS CPU usage. @@ -181,7 +168,7 @@ where self } - /// Set max number of threads for each worker's blocking task thread pool. + /// Sets max number of threads for each worker's blocking task thread pool. /// /// One thread pool is set up **per worker**; not shared across workers. /// @@ -191,19 +178,10 @@ where self } - /// Set server keep-alive setting. + /// Sets server client timeout for first request. /// - /// By default keep alive is set to a 5 seconds. - pub fn keep_alive>(self, val: T) -> Self { - self.config.lock().unwrap().keep_alive = val.into(); - self - } - - /// Set server client timeout in milliseconds for first request. - /// - /// Defines a timeout for reading client request header. If a client does not transmit - /// the entire set headers within this time, the request is terminated with - /// the 408 (Request Time-out) error. + /// Defines a timeout for reading client request head. If a client does not transmit the entire + /// set headers within this time, the request is terminated with a 408 (Request Timeout) error. /// /// To disable timeout set value to 0. /// @@ -219,10 +197,10 @@ where self.client_request_timeout(dur) } - /// Set server connection shutdown timeout in milliseconds. + /// Sets server connection shutdown timeout. /// - /// Defines a timeout for shutdown connection. If a shutdown procedure does not complete - /// within this time, the request is dropped. + /// Defines a timeout for connection shutdown. If a shutdown procedure does not complete within + /// this time, the request is dropped. /// /// To disable timeout set value to 0. /// @@ -232,10 +210,10 @@ where self } - /// Set TLS handshake timeout. + /// Sets TLS handshake timeout. /// - /// Defines a timeout for TLS handshake. If the TLS handshake does not complete - /// within this time, the connection is closed. + /// Defines a timeout for TLS handshake. If the TLS handshake does not complete within this + /// time, the connection is closed. /// /// By default handshake timeout is set to 3000 milliseconds. #[cfg(any(feature = "openssl", feature = "rustls"))] @@ -256,35 +234,61 @@ where self.client_disconnect_timeout(Duration::from_millis(dur)) } - /// Set server host name. + /// Sets function that will be called once before each connection is handled. /// - /// Host name is used by application router as a hostname for url generation. - /// Check [ConnectionInfo](super::dev::ConnectionInfo::host()) - /// documentation for more information. + /// It will receive a `&std::any::Any`, which contains underlying connection type and an + /// [Extensions] container so that connection data can be accessed in middleware and handlers. /// - /// By default host name is set to a "localhost" value. + /// # Connection Types + /// - `actix_tls::accept::openssl::TlsStream` when using OpenSSL. + /// - `actix_tls::accept::rustls::TlsStream` when using Rustls. + /// - `actix_web::rt::net::TcpStream` when no encryption is used. + /// + /// See the `on_connect` example for additional details. + pub fn on_connect(self, f: CB) -> HttpServer + where + CB: Fn(&dyn Any, &mut Extensions) + Send + Sync + 'static, + { + HttpServer { + factory: self.factory, + config: self.config, + backlog: self.backlog, + sockets: self.sockets, + builder: self.builder, + on_connect_fn: Some(Arc::new(f)), + _phantom: PhantomData, + } + } + + /// Sets server host name. + /// + /// Host name is used by application router as a hostname for url generation. Check + /// [`ConnectionInfo`](crate::dev::ConnectionInfo::host()) docs for more info. + /// + /// By default, hostname is set to "localhost". pub fn server_hostname>(self, val: T) -> Self { self.config.lock().unwrap().host = Some(val.as_ref().to_owned()); self } - /// Stop Actix `System` after server shutdown. + /// Flags the `System` to exit after server shutdown. + /// + /// Does nothing when running under `#[tokio::main]` runtime. pub fn system_exit(mut self) -> Self { self.builder = self.builder.system_exit(); self } - /// Disable signal handling + /// Disables signal handling. pub fn disable_signals(mut self) -> Self { self.builder = self.builder.disable_signals(); self } - /// Timeout for graceful workers shutdown. + /// Sets timeout for graceful worker shutdown of workers. /// - /// After receiving a stop signal, workers have this much time to finish - /// serving requests. Workers still alive after the timeout are force - /// dropped. + /// After receiving a stop signal, workers have this much time to finish serving requests. + /// Workers still alive after the timeout are force dropped. /// /// By default shutdown timeout sets to 30 seconds. pub fn shutdown_timeout(mut self, sec: u64) -> Self { @@ -292,33 +296,141 @@ where self } - /// Get addresses of bound sockets. + /// Returns addresses of bound sockets. pub fn addrs(&self) -> Vec { self.sockets.iter().map(|s| s.addr).collect() } - /// Get addresses of bound sockets and the scheme for it. + /// Returns addresses of bound sockets and the scheme for it. /// - /// This is useful when the server is bound from different sources - /// with some sockets listening on HTTP and some listening on HTTPS - /// and the user should be presented with an enumeration of which - /// socket requires which protocol. + /// This is useful when the server is bound from different sources with some sockets listening + /// on HTTP and some listening on HTTPS and the user should be presented with an enumeration of + /// which socket requires which protocol. pub fn addrs_with_scheme(&self) -> Vec<(net::SocketAddr, &str)> { self.sockets.iter().map(|s| (s.addr, s.scheme)).collect() } - /// Use listener for accepting incoming connection requests + /// Resolves socket address(es) and binds server to created listener(s). /// - /// HttpServer does not change any configuration for TcpListener, - /// it needs to be configured before passing it to listen() method. + /// # Hostname Resolution + /// When `addr` includes a hostname, it is possible for this method to bind to both the IPv4 and + /// IPv6 addresses that result from a DNS lookup. You can test this by passing `localhost:8080` + /// and noting that the server binds to `127.0.0.1:8080` _and_ `[::1]:8080`. To bind additional + /// addresses, call this method multiple times. + /// + /// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation. + /// + /// # Typical Usage + /// In general, use `127.0.0.1:` when testing locally and `0.0.0.0:` when deploying + /// (with or without a reverse proxy or load balancer) so that the server is accessible. + /// + /// # Errors + /// Returns an `io::Error` if: + /// - `addrs` cannot be resolved into one or more socket addresses; + /// - all the resolved socket addresses are already bound. + /// + /// # Example + /// ``` + /// # use actix_web::{App, HttpServer}; + /// # fn inner() -> std::io::Result<()> { + /// HttpServer::new(|| App::new()) + /// .bind(("127.0.0.1", 8080))? + /// .bind("[::1]:9000")? + /// # ; Ok(()) } + /// ``` + pub fn bind(mut self, addrs: A) -> io::Result { + let sockets = self.bind2(addrs)?; + + for lst in sockets { + self = self.listen(lst)?; + } + + Ok(self) + } + + fn bind2(&self, addrs: A) -> io::Result> { + let mut err = None; + let mut success = false; + let mut sockets = Vec::new(); + + for addr in addrs.to_socket_addrs()? { + match create_tcp_listener(addr, self.backlog) { + Ok(lst) => { + success = true; + sockets.push(lst); + } + Err(e) => err = Some(e), + } + } + + if success { + Ok(sockets) + } else if let Some(e) = err.take() { + Err(e) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + "Can not bind to address.", + )) + } + } + + /// Resolves socket address(es) and binds server to created listener(s) for TLS connections + /// using Rustls. + /// + /// See [`bind()`](Self::bind) for more details on `addrs` argument. + /// + /// ALPN protocols "h2" and "http/1.1" are added to any configured ones. + #[cfg(feature = "rustls")] + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] + pub fn bind_rustls( + mut self, + addrs: A, + config: RustlsServerConfig, + ) -> io::Result { + let sockets = self.bind2(addrs)?; + for lst in sockets { + self = self.listen_rustls_inner(lst, config.clone())?; + } + Ok(self) + } + + /// Resolves socket address(es) and binds server to created listener(s) for TLS connections + /// using OpenSSL. + /// + /// See [`bind()`](Self::bind) for more details on `addrs` argument. + /// + /// ALPN protocols "h2" and "http/1.1" are added to any configured ones. + #[cfg(feature = "openssl")] + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] + pub fn bind_openssl(mut self, addrs: A, builder: SslAcceptorBuilder) -> io::Result + where + A: net::ToSocketAddrs, + { + let sockets = self.bind2(addrs)?; + let acceptor = openssl_acceptor(builder)?; + + for lst in sockets { + self = self.listen_openssl_inner(lst, acceptor.clone())?; + } + + Ok(self) + } + + /// Binds to existing listener for accepting incoming connection requests. + /// + /// No changes are made to `lst`'s configuration. Ensure it is configured properly before + /// passing ownership to `listen()`. pub fn listen(mut self, lst: net::TcpListener) -> io::Result { let cfg = self.config.clone(); let factory = self.factory.clone(); let addr = lst.local_addr().unwrap(); + self.sockets.push(Socket { addr, scheme: "http", }); + let on_connect_fn = self.on_connect_fn.clone(); self.builder = @@ -351,76 +463,13 @@ where Ok(self) } - #[cfg(feature = "openssl")] - /// Use listener for accepting incoming tls connection requests + /// Binds to existing listener for accepting incoming TLS connection requests using Rustls. /// - /// This method sets alpn protocols to "h2" and "http/1.1" - pub fn listen_openssl( - self, - lst: net::TcpListener, - builder: SslAcceptorBuilder, - ) -> io::Result { - self.listen_ssl_inner(lst, openssl_acceptor(builder)?) - } - - #[cfg(feature = "openssl")] - fn listen_ssl_inner( - mut self, - lst: net::TcpListener, - acceptor: SslAcceptor, - ) -> io::Result { - let factory = self.factory.clone(); - let cfg = self.config.clone(); - let addr = lst.local_addr().unwrap(); - self.sockets.push(Socket { - addr, - scheme: "https", - }); - - let on_connect_fn = self.on_connect_fn.clone(); - - self.builder = - self.builder - .listen(format!("actix-web-service-{}", addr), lst, move || { - let c = cfg.lock().unwrap(); - let host = c.host.clone().unwrap_or_else(|| format!("{}", addr)); - - let svc = HttpService::build() - .keep_alive(c.keep_alive) - .client_request_timeout(c.client_request_timeout) - .client_disconnect_timeout(c.client_disconnect_timeout) - .local_addr(addr); - - let svc = if let Some(handler) = on_connect_fn.clone() { - svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)) - } else { - svc - }; - - let fac = factory() - .into_factory() - .map_err(|err| err.into().error_response()); - - // false positive lint (?) - #[allow(clippy::significant_drop_in_scrutinee)] - let acceptor_config = match c.tls_handshake_timeout { - Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur), - None => TlsAcceptorConfig::default(), - }; - - svc.finish(map_config(fac, move |_| { - AppConfig::new(true, host.clone(), addr) - })) - .openssl_with_config(acceptor.clone(), acceptor_config) - })?; - - Ok(self) - } - + /// See [`listen()`](Self::listen) for more details on the `lst` argument. + /// + /// ALPN protocols "h2" and "http/1.1" are added to any configured ones. #[cfg(feature = "rustls")] - /// Use listener for accepting incoming tls connection requests - /// - /// This method prepends alpn protocols "h2" and "http/1.1" to configured ones + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] pub fn listen_rustls( self, lst: net::TcpListener, @@ -480,82 +529,125 @@ where Ok(self) } - /// The socket address to bind + /// Binds to existing listener for accepting incoming TLS connection requests using OpenSSL. /// - /// To bind multiple addresses this method can be called multiple times. - pub fn bind(mut self, addr: A) -> io::Result { - let sockets = self.bind2(addr)?; - - for lst in sockets { - self = self.listen(lst)?; - } - - Ok(self) - } - - fn bind2(&self, addr: A) -> io::Result> { - let mut err = None; - let mut success = false; - let mut sockets = Vec::new(); - - for addr in addr.to_socket_addrs()? { - match create_tcp_listener(addr, self.backlog) { - Ok(lst) => { - success = true; - sockets.push(lst); - } - Err(e) => err = Some(e), - } - } - - if success { - Ok(sockets) - } else if let Some(e) = err.take() { - Err(e) - } else { - Err(io::Error::new( - io::ErrorKind::Other, - "Can not bind to address.", - )) - } + /// See [`listen()`](Self::listen) for more details on the `lst` argument. + /// + /// ALPN protocols "h2" and "http/1.1" are added to any configured ones. + #[cfg(feature = "openssl")] + #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] + pub fn listen_openssl( + self, + lst: net::TcpListener, + builder: SslAcceptorBuilder, + ) -> io::Result { + self.listen_openssl_inner(lst, openssl_acceptor(builder)?) } #[cfg(feature = "openssl")] - /// Start listening for incoming tls connections. - /// - /// This method sets alpn protocols to "h2" and "http/1.1" - pub fn bind_openssl(mut self, addr: A, builder: SslAcceptorBuilder) -> io::Result - where - A: net::ToSocketAddrs, - { - let sockets = self.bind2(addr)?; - let acceptor = openssl_acceptor(builder)?; - - for lst in sockets { - self = self.listen_ssl_inner(lst, acceptor.clone())?; - } - - Ok(self) - } - - #[cfg(feature = "rustls")] - /// Start listening for incoming tls connections. - /// - /// This method prepends alpn protocols "h2" and "http/1.1" to configured ones - pub fn bind_rustls( + fn listen_openssl_inner( mut self, - addr: A, - config: RustlsServerConfig, + lst: net::TcpListener, + acceptor: SslAcceptor, ) -> io::Result { - let sockets = self.bind2(addr)?; - for lst in sockets { - self = self.listen_rustls_inner(lst, config.clone())?; - } + let factory = self.factory.clone(); + let cfg = self.config.clone(); + let addr = lst.local_addr().unwrap(); + self.sockets.push(Socket { + addr, + scheme: "https", + }); + + let on_connect_fn = self.on_connect_fn.clone(); + + self.builder = + self.builder + .listen(format!("actix-web-service-{}", addr), lst, move || { + let c = cfg.lock().unwrap(); + let host = c.host.clone().unwrap_or_else(|| format!("{}", addr)); + + let svc = HttpService::build() + .keep_alive(c.keep_alive) + .client_request_timeout(c.client_request_timeout) + .client_disconnect_timeout(c.client_disconnect_timeout) + .local_addr(addr); + + let svc = if let Some(handler) = on_connect_fn.clone() { + svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)) + } else { + svc + }; + + let fac = factory() + .into_factory() + .map_err(|err| err.into().error_response()); + + // false positive lint (?) + #[allow(clippy::significant_drop_in_scrutinee)] + let acceptor_config = match c.tls_handshake_timeout { + Some(dur) => TlsAcceptorConfig::default().handshake_timeout(dur), + None => TlsAcceptorConfig::default(), + }; + + svc.finish(map_config(fac, move |_| { + AppConfig::new(true, host.clone(), addr) + })) + .openssl_with_config(acceptor.clone(), acceptor_config) + })?; + Ok(self) } + /// Opens Unix Domain Socket (UDS) from `uds` path and binds server to created listener. + #[cfg(unix)] + pub fn bind_uds(mut self, uds_path: A) -> io::Result + where + A: AsRef, + { + use actix_http::Protocol; + use actix_rt::net::UnixStream; + use actix_service::{fn_service, ServiceFactoryExt as _}; + + let cfg = self.config.clone(); + let factory = self.factory.clone(); + let socket_addr = + net::SocketAddr::new(net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), 8080); + + self.sockets.push(Socket { + scheme: "http", + addr: socket_addr, + }); + + self.builder = self.builder.bind_uds( + format!("actix-web-service-{:?}", uds_path.as_ref()), + uds_path, + move || { + let c = cfg.lock().unwrap(); + let config = AppConfig::new( + false, + c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), + socket_addr, + ); + + let fac = factory() + .into_factory() + .map_err(|err| err.into().error_response()); + + fn_service(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }).and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_request_timeout(c.client_request_timeout) + .client_disconnect_timeout(c.client_disconnect_timeout) + .finish(map_config(fac, move |_| config.clone())), + ) + }, + )?; + + Ok(self) + } + + /// Binds to existing Unix Domain Socket (UDS) listener. #[cfg(unix)] - /// Start listening for unix domain (UDS) connections on existing listener. pub fn listen_uds(mut self, lst: std::os::unix::net::UnixListener) -> io::Result { use actix_http::Protocol; use actix_rt::net::UnixStream; @@ -602,54 +694,6 @@ where })?; Ok(self) } - - /// Start listening for incoming unix domain connections. - #[cfg(unix)] - pub fn bind_uds(mut self, addr: A) -> io::Result - where - A: AsRef, - { - use actix_http::Protocol; - use actix_rt::net::UnixStream; - use actix_service::{fn_service, ServiceFactoryExt as _}; - - let cfg = self.config.clone(); - let factory = self.factory.clone(); - let socket_addr = - net::SocketAddr::new(net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), 8080); - - self.sockets.push(Socket { - scheme: "http", - addr: socket_addr, - }); - - self.builder = self.builder.bind_uds( - format!("actix-web-service-{:?}", addr.as_ref()), - addr, - move || { - let c = cfg.lock().unwrap(); - let config = AppConfig::new( - false, - c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), - socket_addr, - ); - - let fac = factory() - .into_factory() - .map_err(|err| err.into().error_response()); - - fn_service(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_request_timeout(c.client_request_timeout) - .client_disconnect_timeout(c.client_disconnect_timeout) - .finish(map_config(fac, move |_| config.clone())), - ) - }, - )?; - - Ok(self) - } } impl HttpServer @@ -665,25 +709,16 @@ where { /// Start listening for incoming connections. /// - /// This method starts number of HTTP workers in separate threads. - /// For each address this method starts separate thread which does - /// `accept()` in a loop. + /// # Workers + /// This method starts a number of HTTP workers in separate threads. The number of workers in a + /// set is defined by [`workers()`](Self::workers) or, by default, the number of the machine's + /// physical cores. One worker set is created for each socket address to be bound. For example, + /// if workers is set to 4, and there are 2 addresses to bind, then 8 worker threads will be + /// spawned. /// - /// This methods panics if no socket address can be bound or an `Actix` system is not yet - /// configured. - /// - /// ```no_run - /// use std::io; - /// use actix_web::{web, App, HttpResponse, HttpServer}; - /// - /// #[actix_rt::main] - /// async fn main() -> io::Result<()> { - /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) - /// .bind("127.0.0.1:0")? - /// .run() - /// .await - /// } - /// ``` + /// # Panics + /// This methods panics if no socket addresses were successfully bound or if no Tokio runtime + /// is set up. pub fn run(self) -> Server { self.builder.run() }