From 8dbf7da89feb4f2127f72b97b9d51b7e7847a067 Mon Sep 17 00:00:00 2001 From: PeterPierinakos <101414157+PeterPierinakos@users.noreply.github.com> Date: Sat, 25 Jun 2022 14:01:06 +0000 Subject: [PATCH 1/4] Fix common grammar mistakes and add small documentation for AppConfig's Default implementation (#2793) --- actix-web/src/app.rs | 2 +- actix-web/src/app_service.rs | 2 +- actix-web/src/config.rs | 10 ++++++++++ actix-web/src/guard.rs | 4 ++-- actix-web/src/handler.rs | 2 +- actix-web/src/response/response.rs | 2 +- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/actix-web/src/app.rs b/actix-web/src/app.rs index 119980a03..213c8beff 100644 --- a/actix-web/src/app.rs +++ b/actix-web/src/app.rs @@ -60,7 +60,7 @@ where /// [`HttpRequest::app_data`](crate::HttpRequest::app_data) method at runtime. /// /// # [`Data`] - /// Any [`Data`] type added here can utilize it's extractor implementation in handlers. + /// Any [`Data`] type added here can utilize its extractor implementation in handlers. /// Types not wrapped in `Data` cannot use this extractor. See [its docs](Data) for more /// about its usage and patterns. /// diff --git a/actix-web/src/app_service.rs b/actix-web/src/app_service.rs index 3ef31ac75..28ff8c614 100644 --- a/actix-web/src/app_service.rs +++ b/actix-web/src/app_service.rs @@ -257,7 +257,7 @@ impl ServiceFactory for AppRoutingFactory { type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - // construct all services factory future with it's resource def and guards. + // construct all services factory future with its resource def and guards. let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| { let path = path.clone(); let guards = guards.borrow_mut().take().unwrap_or_default(); diff --git a/actix-web/src/config.rs b/actix-web/src/config.rs index dab309175..58a099c75 100644 --- a/actix-web/src/config.rs +++ b/actix-web/src/config.rs @@ -153,6 +153,16 @@ impl AppConfig { } impl Default for AppConfig { + /// Returns the default AppConfig. + /// Note: The included socket address is "127.0.0.1". + /// + /// 127.0.0.1: non-routable meta address that denotes an unknown, invalid or non-applicable target. + /// If you need a service only accessed by itself, use a loopback address. + /// A loopback address for IPv4 is any loopback address that begins with "127". + /// Loopback addresses should be only used to test your application locally. + /// The default configuration provides a loopback address. + /// + /// 0.0.0.0: if configured to use this special address, the application will listen to any IP address configured on the machine. fn default() -> Self { AppConfig::new( false, diff --git a/actix-web/src/guard.rs b/actix-web/src/guard.rs index 9f7514644..ef1301075 100644 --- a/actix-web/src/guard.rs +++ b/actix-web/src/guard.rs @@ -254,7 +254,7 @@ impl Guard for AllGuard { } } -/// Wraps a guard and inverts the outcome of it's `Guard` implementation. +/// Wraps a guard and inverts the outcome of its `Guard` implementation. /// /// # Examples /// The handler below will be called for any request method apart from `GET`. @@ -459,7 +459,7 @@ impl Guard for HostGuard { return scheme == req_host_uri_scheme; } - // TODO: is the the correct behavior? + // TODO: is this the correct behavior? // falls through if scheme cannot be determined } diff --git a/actix-web/src/handler.rs b/actix-web/src/handler.rs index cf86cb38b..522a48b82 100644 --- a/actix-web/src/handler.rs +++ b/actix-web/src/handler.rs @@ -37,7 +37,7 @@ use crate::{ /// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the /// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on /// that tuple, and the `Handler::call` implementation for that particular function arity -/// destructures the tuple into it's component types and calls your handler function with them. +/// destructures the tuple into its component types and calls your handler function with them. /// /// In pseudo-code the process looks something like this: /// ```ignore diff --git a/actix-web/src/response/response.rs b/actix-web/src/response/response.rs index 630acc3f2..ead8badba 100644 --- a/actix-web/src/response/response.rs +++ b/actix-web/src/response/response.rs @@ -343,7 +343,7 @@ mod response_fut_impl { // Future is only implemented for BoxBody payload type because it's the most useful for making // simple handlers without async blocks. Making it generic over all MessageBody types requires a - // future impl on Response which would cause it's body field to be, undesirably, Option. + // future impl on Response which would cause its body field to be, undesirably, Option. // // This impl is not particularly efficient due to the Response construction and should probably // not be invoked if performance is important. Prefer an async fn/block in such cases. From 3d6ea7fe9b9027205ee88dd64c3d9c932240928e Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 26 Jun 2022 18:45:02 +0200 Subject: [PATCH 2/4] Improve documentation for `actix-web-actors` (#2788) --- actix-web-actors/Cargo.toml | 3 + actix-web-actors/src/context.rs | 56 +++++++++++++++- actix-web-actors/src/lib.rs | 55 ++++++++++++++++ actix-web-actors/src/ws.rs | 109 ++++++++++++++++++++++++++++---- 4 files changed, 210 insertions(+), 13 deletions(-) diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index c939f6ab5..284351ed3 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -29,6 +29,9 @@ tokio = { version = "1.13.1", features = ["sync"] } actix-rt = "2.2" actix-test = "0.1.0-beta.13" awc = { version = "3", default-features = false } +actix-web = { version = "4", features = ["macros"] } + +mime = "0.3" env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-web-actors/src/context.rs b/actix-web-actors/src/context.rs index d83969ff7..f7b11c780 100644 --- a/actix-web-actors/src/context.rs +++ b/actix-web-actors/src/context.rs @@ -14,6 +14,58 @@ use futures_core::Stream; use tokio::sync::oneshot::Sender; /// Execution context for HTTP actors +/// +/// # Example +/// +/// A demonstration of [server-sent events](https://developer.mozilla.org/docs/Web/API/Server-sent_events) using actors: +/// +/// ```no_run +/// use std::time::Duration; +/// +/// use actix::{Actor, AsyncContext}; +/// use actix_web::{get, http::header, App, HttpResponse, HttpServer}; +/// use actix_web_actors::HttpContext; +/// use bytes::Bytes; +/// +/// struct MyActor { +/// count: usize, +/// } +/// +/// impl Actor for MyActor { +/// type Context = HttpContext; +/// +/// fn started(&mut self, ctx: &mut Self::Context) { +/// ctx.run_later(Duration::from_millis(100), Self::write); +/// } +/// } +/// +/// impl MyActor { +/// fn write(&mut self, ctx: &mut HttpContext) { +/// self.count += 1; +/// if self.count > 3 { +/// ctx.write_eof() +/// } else { +/// ctx.write(Bytes::from(format!("event: count\ndata: {}\n\n", self.count))); +/// ctx.run_later(Duration::from_millis(100), Self::write); +/// } +/// } +/// } +/// +/// #[get("/")] +/// async fn index() -> HttpResponse { +/// HttpResponse::Ok() +/// .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM)) +/// .streaming(HttpContext::create(MyActor { count: 0 })) +/// } +/// +/// #[actix_web::main] +/// async fn main() -> std::io::Result<()> { +/// HttpServer::new(|| App::new().service(index)) +/// .bind(("127.0.0.1", 8080))? +/// .run() +/// .await +/// } +/// ``` pub struct HttpContext where A: Actor>, @@ -210,7 +262,7 @@ mod tests { type Context = HttpContext; fn started(&mut self, ctx: &mut Self::Context) { - ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx)); + ctx.run_later(Duration::from_millis(100), Self::write); } } @@ -221,7 +273,7 @@ mod tests { ctx.write_eof() } else { ctx.write(Bytes::from(format!("LINE-{}", self.count))); - ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx)); + ctx.run_later(Duration::from_millis(100), Self::write); } } } diff --git a/actix-web-actors/src/lib.rs b/actix-web-actors/src/lib.rs index 70c957020..106bc5202 100644 --- a/actix-web-actors/src/lib.rs +++ b/actix-web-actors/src/lib.rs @@ -1,4 +1,59 @@ //! Actix actors support for Actix Web. +//! +//! # Examples +//! +//! ```no_run +//! use actix::{Actor, StreamHandler}; +//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +//! use actix_web_actors::ws; +//! +//! /// Define Websocket actor +//! struct MyWs; +//! +//! impl Actor for MyWs { +//! type Context = ws::WebsocketContext; +//! } +//! +//! /// Handler for ws::Message message +//! impl StreamHandler> for MyWs { +//! fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { +//! match msg { +//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg), +//! Ok(ws::Message::Text(text)) => ctx.text(text), +//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin), +//! _ => (), +//! } +//! } +//! } +//! +//! #[get("/ws")] +//! async fn index(req: HttpRequest, stream: web::Payload) -> Result { +//! ws::start(MyWs, &req, stream) +//! } +//! +//! #[actix_web::main] +//! async fn main() -> std::io::Result<()> { +//! HttpServer::new(|| App::new().service(index)) +//! .bind(("127.0.0.1", 8080))? +//! .run() +//! .await +//! } +//! ``` +//! +//! # Documentation & Community Resources +//! In addition to this API documentation, several other resources are available: +//! +//! * [Website & User Guide](https://actix.rs/) +//! * [Documentation for `actix_web`](actix_web) +//! * [Examples Repository](https://github.com/actix/examples) +//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x) +//! +//! To get started navigating the API docs, you may consider looking at the following pages first: +//! +//! * [`ws`]: This module provides actor support for WebSockets. +//! +//! * [`HttpContext`]: This struct provides actor support for streaming HTTP responses. +//! #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible)] diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index 6fde10192..9a4880159 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -1,4 +1,60 @@ //! Websocket integration. +//! +//! # Examples +//! +//! ```no_run +//! use actix::{Actor, StreamHandler}; +//! use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +//! use actix_web_actors::ws; +//! +//! /// Define Websocket actor +//! struct MyWs; +//! +//! impl Actor for MyWs { +//! type Context = ws::WebsocketContext; +//! } +//! +//! /// Handler for ws::Message message +//! impl StreamHandler> for MyWs { +//! fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { +//! match msg { +//! Ok(ws::Message::Ping(msg)) => ctx.pong(&msg), +//! Ok(ws::Message::Text(text)) => ctx.text(text), +//! Ok(ws::Message::Binary(bin)) => ctx.binary(bin), +//! _ => (), +//! } +//! } +//! } +//! +//! #[get("/ws")] +//! async fn websocket(req: HttpRequest, stream: web::Payload) -> Result { +//! ws::start(MyWs, &req, stream) +//! } +//! +//! const MAX_FRAME_SIZE: usize = 16_384; // 16KiB +//! +//! #[get("/custom-ws")] +//! async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result { +//! // Create a Websocket session with a specific max frame size, and protocols. +//! ws::WsResponseBuilder::new(MyWs, &req, stream) +//! .frame_size(MAX_FRAME_SIZE) +//! .protocols(&["A", "B"]) +//! .start() +//! } +//! +//! #[actix_web::main] +//! async fn main() -> std::io::Result<()> { +//! HttpServer::new(|| { +//! App::new() +//! .service(websocket) +//! .service(custom_websocket) +//! }) +//! .bind(("127.0.0.1", 8080))? +//! .run() +//! .await +//! } +//! ``` +//! use std::{ collections::VecDeque, @@ -41,20 +97,51 @@ use tokio::sync::oneshot; /// /// # Examples /// -/// Create a Websocket session response with default configuration. -/// ```ignore -/// WsResponseBuilder::new(WsActor, &req, stream).start() -/// ``` +/// ```no_run +/// # use actix::{Actor, StreamHandler}; +/// # use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +/// # use actix_web_actors::ws; +/// # +/// # struct MyWs; +/// # +/// # impl Actor for MyWs { +/// # type Context = ws::WebsocketContext; +/// # } +/// # +/// # /// Handler for ws::Message message +/// # impl StreamHandler> for MyWs { +/// # fn handle(&mut self, msg: Result, ctx: &mut Self::Context) {} +/// # } +/// # +/// #[get("/ws")] +/// async fn websocket(req: HttpRequest, stream: web::Payload) -> Result { +/// ws::WsResponseBuilder::new(MyWs, &req, stream).start() +/// } /// -/// Create a Websocket session with a specific max frame size, [`Codec`], and protocols. -/// ```ignore /// const MAX_FRAME_SIZE: usize = 16_384; // 16KiB /// -/// ws::WsResponseBuilder::new(WsActor, &req, stream) -/// .codec(Codec::new()) -/// .protocols(&["A", "B"]) -/// .frame_size(MAX_FRAME_SIZE) -/// .start() +/// #[get("/custom-ws")] +/// async fn custom_websocket(req: HttpRequest, stream: web::Payload) -> Result { +/// // Create a Websocket session with a specific max frame size, codec, and protocols. +/// ws::WsResponseBuilder::new(MyWs, &req, stream) +/// .codec(actix_http::ws::Codec::new()) +/// // This will overwrite the codec's max frame-size +/// .frame_size(MAX_FRAME_SIZE) +/// .protocols(&["A", "B"]) +/// .start() +/// } +/// # +/// # #[actix_web::main] +/// # async fn main() -> std::io::Result<()> { +/// # HttpServer::new(|| { +/// # App::new() +/// # .service(websocket) +/// # .service(custom_websocket) +/// # }) +/// # .bind(("127.0.0.1", 8080))? +/// # .run() +/// # .await +/// # } /// ``` pub struct WsResponseBuilder<'a, A, T> where From f7d7d92984f0ed9204c7e69a8e16d0582a7efdcd Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 Jun 2022 03:12:36 +0100 Subject: [PATCH 3/4] address clippy lints --- actix-http/src/body/message_body.rs | 1 + actix-http/src/responses/head.rs | 4 ++-- actix-web/src/types/path.rs | 1 + actix-web/src/types/payload.rs | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 9090e34d5..ab742b9cd 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -481,6 +481,7 @@ mod tests { assert_poll_next_none!(pl); } + #[allow(clippy::let_unit_value)] #[actix_rt::test] async fn test_unit() { let pl = (); diff --git a/actix-http/src/responses/head.rs b/actix-http/src/responses/head.rs index cb47c4b7a..01bca6c5b 100644 --- a/actix-http/src/responses/head.rs +++ b/actix-http/src/responses/head.rs @@ -237,7 +237,7 @@ mod tests { .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream + stream .write_all(b"GET /camel HTTP/1.1\r\nConnection: Close\r\n\r\n") .unwrap(); let mut data = vec![]; @@ -251,7 +251,7 @@ mod tests { assert!(memmem::find(&data, b"content-length").is_none()); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream + stream .write_all(b"GET /lower HTTP/1.1\r\nConnection: Close\r\n\r\n") .unwrap(); let mut data = vec![]; diff --git a/actix-web/src/types/path.rs b/actix-web/src/types/path.rs index 0fcac2c19..34c335ba9 100644 --- a/actix-web/src/types/path.rs +++ b/actix-web/src/types/path.rs @@ -183,6 +183,7 @@ mod tests { assert!(Path::::from_request(&req, &mut pl).await.is_err()); } + #[allow(clippy::let_unit_value)] #[actix_rt::test] async fn test_tuple_extract() { let resource = ResourceDef::new("/{key}/{value}/"); diff --git a/actix-web/src/types/payload.rs b/actix-web/src/types/payload.rs index b47a39e97..af195b867 100644 --- a/actix-web/src/types/payload.rs +++ b/actix-web/src/types/payload.rs @@ -113,7 +113,7 @@ pub struct BytesExtractFut { body_fut: HttpMessageBody, } -impl<'a> Future for BytesExtractFut { +impl Future for BytesExtractFut { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -167,7 +167,7 @@ pub struct StringExtractFut { encoding: &'static Encoding, } -impl<'a> Future for StringExtractFut { +impl Future for StringExtractFut { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { From 0dba6310c66c3a0fd5bc02b072fc1ec696814bbf Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 27 Jun 2022 04:57:21 +0200 Subject: [PATCH 4/4] Expose option for setting TLS handshake timeout (#2752) Co-authored-by: Rob Ede --- actix-http/src/lib.rs | 3 ++ actix-http/src/service.rs | 65 +++++++++++++++++++++++++++++++- actix-http/tests/test_openssl.rs | 9 +++-- actix-http/tests/test_rustls.rs | 8 +++- actix-web/CHANGES.md | 7 +++- actix-web/src/server.rs | 39 ++++++++++++++++++- 6 files changed, 121 insertions(+), 10 deletions(-) diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 360cb86fc..184049860 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -25,6 +25,7 @@ )] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] +#![cfg_attr(docsrs, feature(doc_cfg))] pub use ::http::{uri, uri::Uri}; pub use ::http::{Method, StatusCode, Version}; @@ -69,6 +70,8 @@ pub use self::payload::{BoxedPayloadStream, Payload, PayloadStream}; 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"))] +pub use self::service::TlsAcceptorConfig; /// A major HTTP protocol version. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index f4fe625a3..27029cb8e 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -181,6 +181,25 @@ where } } +/// Configuration options used when accepting TLS connection. +#[cfg(any(feature = "openssl", feature = "rustls"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))] +#[derive(Debug, Default)] +pub struct TlsAcceptorConfig { + pub(crate) handshake_timeout: Option, +} + +#[cfg(any(feature = "openssl", feature = "rustls"))] +impl TlsAcceptorConfig { + /// Set TLS handshake timeout duration. + pub fn handshake_timeout(self, dur: std::time::Duration) -> Self { + Self { + handshake_timeout: Some(dur), + // ..self + } + } +} + #[cfg(feature = "openssl")] mod openssl { use actix_service::ServiceFactoryExt as _; @@ -230,7 +249,28 @@ mod openssl { Error = TlsError, InitError = (), > { - Acceptor::new(acceptor) + self.openssl_with_config(acceptor, TlsAcceptorConfig::default()) + } + + /// Create OpenSSL based service with custom TLS acceptor configuration. + pub fn openssl_with_config( + self, + acceptor: SslAcceptor, + tls_acceptor_config: TlsAcceptorConfig, + ) -> impl ServiceFactory< + TcpStream, + Config = (), + Response = (), + Error = TlsError, + InitError = (), + > { + let mut acceptor = Acceptor::new(acceptor); + + if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout { + acceptor.set_handshake_timeout(handshake_timeout); + } + + acceptor .map_init_err(|_| { unreachable!("TLS acceptor service factory does not error on init") }) @@ -293,8 +333,23 @@ mod rustls { { /// Create Rustls based service. pub fn rustls( + self, + config: ServerConfig, + ) -> impl ServiceFactory< + TcpStream, + Config = (), + Response = (), + Error = TlsError, + InitError = (), + > { + self.rustls_with_config(config, TlsAcceptorConfig::default()) + } + + /// Create Rustls based service with custom TLS acceptor configuration. + pub fn rustls_with_config( self, mut config: ServerConfig, + tls_acceptor_config: TlsAcceptorConfig, ) -> impl ServiceFactory< TcpStream, Config = (), @@ -306,7 +361,13 @@ mod rustls { protos.extend_from_slice(&config.alpn_protocols); config.alpn_protocols = protos; - Acceptor::new(config) + let mut acceptor = Acceptor::new(config); + + if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout { + acceptor.set_handshake_timeout(handshake_timeout); + } + + acceptor .map_init_err(|_| { unreachable!("TLS acceptor service factory does not error on init") }) diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 35321ac98..b97b2e45b 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -2,13 +2,13 @@ extern crate tls_openssl as openssl; -use std::{convert::Infallible, io}; +use std::{convert::Infallible, io, time::Duration}; use actix_http::{ body::{BodyStream, BoxBody, SizedStream}, error::PayloadError, header::{self, HeaderValue}, - Error, HttpService, Method, Request, Response, StatusCode, Version, + Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version, }; use actix_http_test::test_server; use actix_service::{fn_service, ServiceFactoryExt}; @@ -89,7 +89,10 @@ async fn h2_1() -> io::Result<()> { assert_eq!(req.version(), Version::HTTP_2); ok::<_, Error>(Response::ok()) }) - .openssl(tls_config()) + .openssl_with_config( + tls_config(), + TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)), + ) .map_err(|_| ()) }) .await; diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 550375296..2bbf1524b 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -8,13 +8,14 @@ use std::{ net::{SocketAddr, TcpStream as StdTcpStream}, sync::Arc, task::Poll, + time::Duration, }; use actix_http::{ body::{BodyStream, BoxBody, SizedStream}, error::PayloadError, header::{self, HeaderName, HeaderValue}, - Error, HttpService, Method, Request, Response, StatusCode, Version, + Error, HttpService, Method, Request, Response, StatusCode, TlsAcceptorConfig, Version, }; use actix_http_test::test_server; use actix_rt::pin; @@ -160,7 +161,10 @@ async fn h2_1() -> io::Result<()> { assert_eq!(req.version(), Version::HTTP_2); ok::<_, Error>(Response::ok()) }) - .rustls(tls_config()) + .rustls_with_config( + tls_config(), + TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5)), + ) }) .await; diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index 3fa2f8f21..0144cb912 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -1,12 +1,17 @@ # Changelog ## Unreleased - 2022-xx-xx -- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency. ### Added - Add `ServiceRequest::{parts, request}()` getter methods. [#2786] +- Add configuration options for TLS handshake timeout via `HttpServer::{rustls, openssl}_with_config` methods. [#2752] +### Changed +- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency. + +[#2752]: https://github.com/actix/actix-web/pull/2752 [#2786]: https://github.com/actix/actix-web/pull/2786 + ## 4.1.0 - 2022-06-11 ### Added - Add `ServiceRequest::extract()` to make it easier to use extractors when writing middlewares. [#2647] diff --git a/actix-web/src/server.rs b/actix-web/src/server.rs index 99812600c..169eafab0 100644 --- a/actix-web/src/server.rs +++ b/actix-web/src/server.rs @@ -18,6 +18,9 @@ use actix_tls::accept::openssl::reexports::{AlpnError, SslAcceptor, SslAcceptorB #[cfg(feature = "rustls")] use actix_tls::accept::rustls::reexports::ServerConfig as RustlsServerConfig; +#[cfg(any(feature = "openssl", feature = "rustls"))] +use actix_http::TlsAcceptorConfig; + use crate::{config::AppConfig, Error}; struct Socket { @@ -30,6 +33,8 @@ struct Config { keep_alive: KeepAlive, client_request_timeout: Duration, client_disconnect_timeout: Duration, + #[cfg(any(feature = "openssl", feature = "rustls"))] + tls_handshake_timeout: Option, } /// An HTTP Server. @@ -92,6 +97,8 @@ where keep_alive: KeepAlive::default(), client_request_timeout: Duration::from_secs(5), client_disconnect_timeout: Duration::from_secs(1), + #[cfg(any(feature = "rustls", feature = "openssl"))] + tls_handshake_timeout: None, })), backlog: 1024, sockets: Vec::new(), @@ -225,6 +232,24 @@ where self } + /// Set TLS handshake timeout. + /// + /// 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"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))] + pub fn tls_handshake_timeout(self, dur: Duration) -> Self { + self.config + .lock() + .unwrap() + .tls_handshake_timeout + .replace(dur); + + self + } + #[doc(hidden)] #[deprecated(since = "4.0.0", note = "Renamed to `client_disconnect_timeout`.")] pub fn client_shutdown(self, dur: u64) -> Self { @@ -376,10 +401,15 @@ where .into_factory() .map_err(|err| err.into().error_response()); + 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(acceptor.clone()) + .openssl_with_config(acceptor.clone(), acceptor_config) })?; Ok(self) @@ -434,10 +464,15 @@ where .into_factory() .map_err(|err| err.into().error_response()); + 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) })) - .rustls(config.clone()) + .rustls_with_config(config.clone(), acceptor_config) })?; Ok(self)