Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

actix-web: Add rustls 0.23 #3363

Merged
merged 20 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion actix-http/examples/tls_rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
//! Protocol: HTTP/1.1
//! ```

extern crate tls_rustls_023 as rustls;

use std::io;

use actix_http::{Error, HttpService, Request, Response};
use actix_utils::future::ok;
use tls_rustls_023 as rustls;

#[actix_rt::main]
async fn main() -> io::Result<()> {
Expand Down
1 change: 1 addition & 0 deletions actix-test/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Add `TestServerConfig::rustls_0_23()` method for Rustls v0.23 support behind new `rustls-0_23` crate feature.
- Minimum supported Rust version (MSRV) is now 1.72.

## 0.1.3
Expand Down
3 changes: 3 additions & 0 deletions actix-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ rustls-0_20 = ["tls-rustls-0_20", "actix-http/rustls-0_20", "awc/rustls-0_20"]
rustls-0_21 = ["tls-rustls-0_21", "actix-http/rustls-0_21", "awc/rustls-0_21"]
# TLS via Rustls v0.22
rustls-0_22 = ["tls-rustls-0_22", "actix-http/rustls-0_22", "awc/rustls-0_22-webpki-roots"]
# TLS via Rustls v0.23
rustls-0_23 = ["tls-rustls-0_23", "actix-http/rustls-0_23", "awc/rustls-0_23-webpki-roots"]

# TLS via OpenSSL
openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
Expand All @@ -53,4 +55,5 @@ tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
tls-rustls-0_20 = { package = "rustls", version = "0.20", optional = true }
tls-rustls-0_21 = { package = "rustls", version = "0.21", optional = true }
tls-rustls-0_22 = { package = "rustls", version = "0.22", optional = true }
tls-rustls-0_23 = { package = "rustls", version = "0.23", default-features = false, optional = true }
tokio = { version = "1.24.2", features = ["sync"] }
53 changes: 53 additions & 0 deletions actix-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ where
StreamType::Rustls021(_) => true,
#[cfg(feature = "rustls-0_22")]
StreamType::Rustls022(_) => true,
#[cfg(feature = "rustls-0_23")]
StreamType::Rustls023(_) => true,
};

// run server in separate orphaned thread
Expand Down Expand Up @@ -371,6 +373,48 @@ where
.rustls_0_22(config.clone())
}),
},
#[cfg(feature = "rustls-0_23")]
StreamType::Rustls023(config) => match cfg.tp {
HttpVer::Http1 => builder.listen("test", tcp, move || {
let app_cfg =
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);

let fac = factory()
.into_factory()
.map_err(|err| err.into().error_response());

HttpService::build()
.client_request_timeout(timeout)
.h1(map_config(fac, move |_| app_cfg.clone()))
.rustls_0_23(config.clone())
}),
HttpVer::Http2 => builder.listen("test", tcp, move || {
let app_cfg =
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);

let fac = factory()
.into_factory()
.map_err(|err| err.into().error_response());

HttpService::build()
.client_request_timeout(timeout)
.h2(map_config(fac, move |_| app_cfg.clone()))
.rustls_0_23(config.clone())
}),
HttpVer::Both => builder.listen("test", tcp, move || {
let app_cfg =
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);

let fac = factory()
.into_factory()
.map_err(|err| err.into().error_response());

HttpService::build()
.client_request_timeout(timeout)
.finish(map_config(fac, move |_| app_cfg.clone()))
.rustls_0_23(config.clone())
}),
},
}
.expect("test server could not be created");

Expand Down Expand Up @@ -447,6 +491,8 @@ enum StreamType {
Rustls021(tls_rustls_0_21::ServerConfig),
#[cfg(feature = "rustls-0_22")]
Rustls022(tls_rustls_0_22::ServerConfig),
#[cfg(feature = "rustls-0_23")]
Rustls023(tls_rustls_0_23::ServerConfig),
}

/// Create default test server config.
Expand Down Expand Up @@ -537,6 +583,13 @@ impl TestServerConfig {
self
}

/// Accepts secure connections via Rustls v0.23.
#[cfg(feature = "rustls-0_23")]
pub fn rustls_0_23(mut self, config: tls_rustls_0_23::ServerConfig) -> Self {
self.stream = StreamType::Rustls023(config);
self
}

/// Sets client timeout for first request.
pub fn client_request_timeout(mut self, dur: Duration) -> Self {
self.client_request_timeout = dur;
Expand Down
3 changes: 3 additions & 0 deletions actix-web/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
### Added

- Add `unicode` crate feature (on-by-default) to switch between `regex` and `regex-lite` as a trade-off between full unicode support and binary size.
- Add `rustls-0_23` crate feature.
- Add `HttpServer::{bind_rustls_0_23, listen_rustls_0_23}()` builder methods.
- Add `HttpServer::tls_handshake_timeout()` builder method for `rustls-0_22` and `rustls-0_23`.

### Changed

Expand Down
7 changes: 5 additions & 2 deletions actix-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ features = [
"rustls-0_20",
"rustls-0_21",
"rustls-0_22",
"rustls-0_23",
"compress-brotli",
"compress-gzip",
"compress-zstd",
Expand Down Expand Up @@ -71,6 +72,8 @@ rustls-0_20 = ["http2", "actix-http/rustls-0_20", "actix-tls/accept", "actix-tls
rustls-0_21 = ["http2", "actix-http/rustls-0_21", "actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22
rustls-0_22 = ["http2", "actix-http/rustls-0_22", "actix-tls/accept", "actix-tls/rustls-0_22"]
# TLS via Rustls v0.23
rustls-0_23 = ["http2", "actix-http/rustls-0_23", "actix-tls/accept", "actix-tls/rustls-0_23"]

# Full unicode support
unicode = ["dep:regex", "actix-router/unicode"]
Expand Down Expand Up @@ -122,7 +125,7 @@ url = "2.1"

[dev-dependencies]
actix-files = "0.6"
actix-test = { version = "0.1", features = ["openssl", "rustls-0_22"] }
actix-test = { version = "0.1", features = ["openssl", "rustls-0_23"] }
awc = { version = "3", features = ["openssl"] }

brotli = "6"
Expand All @@ -137,7 +140,7 @@ rustls-pemfile = "2"
serde = { version = "1.0", features = ["derive"] }
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.55" }
tls-rustls = { package = "rustls", version = "0.22" }
tls-rustls = { package = "rustls", version = "0.23" }
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.13"

Expand Down
5 changes: 4 additions & 1 deletion actix-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@
//! - `compress-gzip` - gzip and deflate content encoding compression support (enabled by default)
//! - `compress-zstd` - zstd content encoding compression support (enabled by default)
//! - `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`
//! - `rustls` - HTTPS support via `rustls` crate, supports `HTTP/2`
//! - `rustls` - HTTPS support via `rustls` 0.20 crate, supports `HTTP/2`
//! - `rustls-0_21` - HTTPS support via `rustls` 0.21 crate, supports `HTTP/2`
//! - `rustls-0_22` - HTTPS support via `rustls` 0.22 crate, supports `HTTP/2`
//! - `rustls-0_23` - HTTPS support via `rustls` 0.23 crate, supports `HTTP/2`
//! - `secure-cookies` - secure cookies support

#![deny(rust_2018_idioms, nonstandard_style)]
Expand Down
102 changes: 99 additions & 3 deletions actix-web/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::{
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
use actix_http::TlsAcceptorConfig;
use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response};
Expand Down Expand Up @@ -242,7 +243,13 @@ where
/// time, the connection is closed.
///
/// By default, the handshake timeout is 3 seconds.
#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
pub fn tls_handshake_timeout(self, dur: Duration) -> Self {
self.config
.lock()
Expand Down Expand Up @@ -270,6 +277,10 @@ where
/// Rustls v0.20.
/// - `actix_tls::accept::rustls_0_21::TlsStream<actix_web::rt::net::TcpStream>` when using
/// Rustls v0.21.
/// - `actix_tls::accept::rustls_0_22::TlsStream<actix_web::rt::net::TcpStream>` when using
/// Rustls v0.22.
/// - `actix_tls::accept::rustls_0_23::TlsStream<actix_web::rt::net::TcpStream>` when using
/// Rustls v0.23.
/// - `actix_web::rt::net::TcpStream` when no encryption is used.
///
/// See the `on_connect` example for additional details.
Expand Down Expand Up @@ -466,6 +477,25 @@ where
Ok(self)
}

/// Resolves socket address(es) and binds server to created listener(s) for TLS connections
/// using Rustls v0.23.
///
/// 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_23")]
pub fn bind_rustls_0_23<A: net::ToSocketAddrs>(
mut self,
addrs: A,
config: actix_tls::accept::rustls_0_23::reexports::ServerConfig,
) -> io::Result<Self> {
let sockets = bind_addrs(addrs, self.backlog)?;
for lst in sockets {
self = self.listen_rustls_0_23_inner(lst, config.clone())?;
}
Ok(self)
}

/// Resolves socket address(es) and binds server to created listener(s) for TLS connections
/// using OpenSSL.
///
Expand Down Expand Up @@ -595,7 +625,7 @@ where
/// 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.
/// 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")]
Expand Down Expand Up @@ -712,7 +742,7 @@ where
/// Binds to existing listener for accepting incoming TLS connection requests using Rustls
/// v0.22.
///
/// See [`listen()`](Self::listen) for more details on the `lst` argument.
/// 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_22")]
Expand Down Expand Up @@ -775,6 +805,72 @@ where
Ok(self)
}

/// Binds to existing listener for accepting incoming TLS connection requests using Rustls
/// v0.23.
///
/// 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_23")]
pub fn listen_rustls_0_23(
self,
lst: net::TcpListener,
config: actix_tls::accept::rustls_0_23::reexports::ServerConfig,
) -> io::Result<Self> {
self.listen_rustls_0_23_inner(lst, config)
}

#[cfg(feature = "rustls-0_23")]
fn listen_rustls_0_23_inner(
mut self,
lst: net::TcpListener,
config: actix_tls::accept::rustls_0_23::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_0_23_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.
Expand Down
6 changes: 3 additions & 3 deletions actix-web/tests/test_server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
#[cfg(feature = "rustls-0_22")]
#[cfg(feature = "rustls-0_23")]
extern crate tls_rustls as rustls;

use std::{
Expand Down Expand Up @@ -704,7 +704,7 @@ async fn test_brotli_encoding_large_openssl() {
srv.stop().await;
}

#[cfg(feature = "rustls-0_22")]
#[cfg(feature = "rustls-0_23")]
mod plus_rustls {
use std::io::BufReader;

Expand Down Expand Up @@ -740,7 +740,7 @@ mod plus_rustls {
.map(char::from)
.collect::<String>();

let srv = actix_test::start_with(actix_test::config().rustls_0_22(tls_config()), || {
let srv = actix_test::start_with(actix_test::config().rustls_0_23(tls_config()), || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| async {
// echo decompressed request body back in response
HttpResponse::Ok()
Expand Down
3 changes: 3 additions & 0 deletions awc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Add `rustls-0_23`, `rustls-0_23-webpki-roots`, and `rustls-0_23-native-roots` crate features.
- Add `awc::Connector::rustls_0_23()` constructor.
- Fix `rustls-0_22-native-roots` root store lookup
- Update `brotli` dependency to `6`.
- Minimum supported Rust version (MSRV) is now 1.72.

Expand Down
Loading
Loading