1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-06-27 07:19:04 +02:00

Actix Web Rustls v0.21 support (#3116)

This commit is contained in:
Rob Ede
2023-08-29 01:11:11 +01:00
committed by GitHub
parent cbf5e948db
commit 905c30af86
23 changed files with 382 additions and 124 deletions

View File

@ -4,13 +4,15 @@
### Added
- Add `HttpServer::{bind, listen}_auto_h2c()` method behind new `http2` crate feature.
- Add `HttpServer::{bind, listen}_auto_h2c()` methods behind new `http2` crate feature.
- Add `HttpServer::{bind, listen}_rustls_021()` methods for Rustls v0.21 support behind new `rustls-0_21` crate feature.
- Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards.
- Add `web::Payload::to_bytes[_limited]()` helper methods.
- Add missing constructors on `HttpResponse` for several status codes.
- Add `http::header::ContentLength` typed header.
- Implement `Default` for `web::Data`.
- Implement `serde::Deserialize` for `web::Data`.
- Add `rustls-0_20` crate feature, which the existing `rustls` feature now aliases.
### Changed

View File

@ -21,7 +21,7 @@ rust-version.workspace = true
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["macros", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]
features = ["macros", "openssl", "rustls-0_20", "rustls-0_21", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]
rustdoc-args = ["--cfg", "docsrs"]
[lib]
@ -52,8 +52,12 @@ http2 = ["actix-http/http2"]
# TLS via OpenSSL
openssl = ["http2", "actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls
rustls = ["http2", "actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
# TLS via Rustls v0.20
rustls = ["rustls-0_20"]
# TLS via Rustls v0.20
rustls-0_20 = ["http2", "actix-http/rustls-0_20", "actix-tls/accept", "actix-tls/rustls-0_20"]
# TLS via Rustls v0.21
rustls-0_21 = ["http2", "actix-http/rustls-0_21", "actix-tls/accept", "actix-tls/rustls-0_21"]
# Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
@ -69,7 +73,7 @@ actix-rt = { version = "2.6", default-features = false }
actix-server = "2"
actix-service = "2"
actix-utils = "3"
actix-tls = { version = "3", default-features = false, optional = true }
actix-tls = { version = "3.1", default-features = false, optional = true }
actix-http = { version = "3.3", features = ["ws"] }
actix-router = "0.5"
@ -101,7 +105,7 @@ url = "2.1"
[dev-dependencies]
actix-files = "0.6"
actix-test = { version = "0.1", features = ["openssl", "rustls"] }
actix-test = { version = "0.1", features = ["openssl", "rustls-0_21"] }
awc = { version = "3", features = ["openssl"] }
brotli = "3.3.3"
@ -116,7 +120,7 @@ rustls-pemfile = "1"
serde = { version = "1.0", features = ["derive"] }
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.55" }
tls-rustls = { package = "rustls", version = "0.20" }
tls-rustls = { package = "rustls", version = "0.21" }
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.12"

View File

@ -112,11 +112,7 @@ where
let endpoint_fut = self.endpoint.new_service(());
// take extensions or create new one as app data container.
let mut app_data = self
.extensions
.borrow_mut()
.take()
.unwrap_or_else(Extensions::new);
let mut app_data = self.extensions.borrow_mut().take().unwrap_or_default();
Box::pin(async move {
// async data factories

View File

@ -167,7 +167,7 @@ mod tests {
async fn handler_min() {}
#[rustfmt::skip]
#[allow(clippy::too_many_arguments, clippy::just_underscores_and_digits)]
#[allow(clippy::too_many_arguments, clippy::just_underscores_and_digits, clippy::let_unit_value)]
async fn handler_max(
_01: (), _02: (), _03: (), _04: (), _05: (), _06: (),
_07: (), _08: (), _09: (), _10: (), _11: (), _12: (),

View File

@ -92,7 +92,7 @@ pub struct RouteService {
}
impl RouteService {
// TODO: does this need to take &mut ?
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn check(&self, req: &mut ServiceRequest) -> bool {
let guard_ctx = req.guard_ctx();

View File

@ -7,7 +7,7 @@ use std::{
time::Duration,
};
#[cfg(any(feature = "openssl", feature = "rustls"))]
#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
use actix_http::TlsAcceptorConfig;
use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response};
use actix_server::{Server, ServerBuilder};
@ -16,8 +16,6 @@ use actix_service::{
};
#[cfg(feature = "openssl")]
use actix_tls::accept::openssl::reexports::{AlpnError, SslAcceptor, SslAcceptorBuilder};
#[cfg(feature = "rustls")]
use actix_tls::accept::rustls::reexports::ServerConfig as RustlsServerConfig;
use crate::{config::AppConfig, Error};
@ -31,7 +29,7 @@ struct Config {
keep_alive: KeepAlive,
client_request_timeout: Duration,
client_disconnect_timeout: Duration,
#[cfg(any(feature = "openssl", feature = "rustls"))]
#[allow(dead_code)] // only dead when no TLS features are enabled
tls_handshake_timeout: Option<Duration>,
}
@ -109,7 +107,6 @@ 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,
@ -170,7 +167,7 @@ where
/// By default max connections is set to a 256.
#[allow(unused_variables)]
pub fn max_connection_rate(self, num: usize) -> Self {
#[cfg(any(feature = "rustls", feature = "openssl"))]
#[cfg(any(feature = "rustls-0_20", feature = "rustls-0_21", feature = "openssl"))]
actix_tls::accept::max_concurrent_tls_connect(num);
self
}
@ -222,8 +219,8 @@ where
/// 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"))]
/// By default, the handshake timeout is 3 seconds.
#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
pub fn tls_handshake_timeout(self, dur: Duration) -> Self {
self.config
.lock()
@ -247,7 +244,10 @@ where
///
/// # Connection Types
/// - `actix_tls::accept::openssl::TlsStream<actix_web::rt::net::TcpStream>` when using OpenSSL.
/// - `actix_tls::accept::rustls::TlsStream<actix_web::rt::net::TcpStream>` when using Rustls.
/// - `actix_tls::accept::rustls_0_20::TlsStream<actix_web::rt::net::TcpStream>` when using
/// Rustls v0.20.
/// - `actix_tls::accept::rustls_0_21::TlsStream<actix_web::rt::net::TcpStream>` when using
/// Rustls v0.21.
/// - `actix_web::rt::net::TcpStream` when no encryption is used.
///
/// See the `on_connect` example for additional details.
@ -368,20 +368,39 @@ where
}
/// Resolves socket address(es) and binds server to created listener(s) for TLS connections
/// using Rustls.
/// using Rustls v0.20.
///
/// 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(feature = "rustls-0_20")]
pub fn bind_rustls<A: net::ToSocketAddrs>(
mut self,
addrs: A,
config: RustlsServerConfig,
config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,
) -> io::Result<Self> {
let sockets = bind_addrs(addrs, self.backlog)?;
for lst in sockets {
self = self.listen_rustls_inner(lst, config.clone())?;
self = self.listen_rustls_0_20_inner(lst, config.clone())?;
}
Ok(self)
}
/// Resolves socket address(es) and binds server to created listener(s) for TLS connections
/// using Rustls v0.21.
///
/// 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-0_21")]
pub fn bind_rustls_021<A: net::ToSocketAddrs>(
mut self,
addrs: A,
config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,
) -> io::Result<Self> {
let sockets = bind_addrs(addrs, self.backlog)?;
for lst in sockets {
self = self.listen_rustls_0_21_inner(lst, config.clone())?;
}
Ok(self)
}
@ -497,25 +516,41 @@ where
Ok(self)
}
/// Binds to existing listener for accepting incoming TLS connection requests using Rustls.
/// Binds to existing listener for accepting incoming TLS connection requests using Rustls
/// v0.20.
///
/// 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")]
#[cfg(feature = "rustls-0_20")]
pub fn listen_rustls(
self,
lst: net::TcpListener,
config: RustlsServerConfig,
config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,
) -> io::Result<Self> {
self.listen_rustls_inner(lst, config)
self.listen_rustls_0_20_inner(lst, config)
}
#[cfg(feature = "rustls")]
fn listen_rustls_inner(
/// Binds to existing listener for accepting incoming TLS connection requests using Rustls
/// v0.21.
///
/// 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-0_21")]
pub fn listen_rustls_0_21(
self,
lst: net::TcpListener,
config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,
) -> io::Result<Self> {
self.listen_rustls_0_21_inner(lst, config)
}
#[cfg(feature = "rustls-0_20")]
fn listen_rustls_0_20_inner(
mut self,
lst: net::TcpListener,
config: RustlsServerConfig,
config: actix_tls::accept::rustls_0_20::reexports::ServerConfig,
) -> io::Result<Self> {
let factory = self.factory.clone();
let cfg = self.config.clone();
@ -562,6 +597,57 @@ where
Ok(self)
}
#[cfg(feature = "rustls-0_21")]
fn listen_rustls_0_21_inner(
mut self,
lst: net::TcpListener,
config: actix_tls::accept::rustls_0_21::reexports::ServerConfig,
) -> io::Result<Self> {
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);
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());
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_021_with_config(config.clone(), acceptor_config)
})?;
Ok(self)
}
/// Binds to existing listener for accepting incoming TLS connection requests using OpenSSL.
///
/// See [`listen()`](Self::listen) for more details on the `lst` argument.

View File

@ -1,6 +1,6 @@
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
#[cfg(feature = "rustls")]
#[cfg(feature = "rustls-0_21")]
extern crate tls_rustls as rustls;
use std::{
@ -704,7 +704,7 @@ async fn test_brotli_encoding_large_openssl() {
srv.stop().await;
}
#[cfg(feature = "rustls")]
#[cfg(feature = "rustls-0_21")]
mod plus_rustls {
use std::io::BufReader;
@ -743,7 +743,7 @@ mod plus_rustls {
.map(char::from)
.collect::<String>();
let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || {
let srv = actix_test::start_with(actix_test::config().rustls_021(tls_config()), || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| async {
// echo decompressed request body back in response
HttpResponse::Ok()