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:
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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: (),
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user