Skip to content

Commit

Permalink
Actix Web Rustls v0.21 support (#3116)
Browse files Browse the repository at this point in the history
  • Loading branch information
robjtede authored Aug 29, 2023
1 parent cbf5e94 commit 905c30a
Show file tree
Hide file tree
Showing 23 changed files with 382 additions and 124 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
run: |
cargo test --lib --tests -p=actix-router --all-features
cargo test --lib --tests -p=actix-http --all-features
cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls
cargo test --lib --tests -p=actix-web --features=rustls-0_20,rustls-0_21,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls
cargo test --lib --tests -p=actix-web-codegen --all-features
cargo test --lib --tests -p=awc --all-features
cargo test --lib --tests -p=actix-http-test --all-features
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/clippy-fmt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
with:
reporter: 'github-pr-check'
github_token: ${{ secrets.GITHUB_TOKEN }}
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo -Aunknown_lints

lint-docs:
runs-on: ubuntu-latest
Expand All @@ -63,8 +63,7 @@ jobs:
- uses: actions/checkout@v3

- uses: actions-rust-lang/setup-rust-toolchain@v1
# temp: unpin once https://github.com/rust-lang/rust/issues/113152 is fixed
with: { toolchain: nightly-2023-06-28 }
with: { toolchain: nightly-2023-08-25 }

- uses: taiki-e/cache-cargo-install-action@v1
with: { tool: cargo-public-api }
Expand Down
7 changes: 5 additions & 2 deletions actix-files/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type PathFilter = dyn Fn(&Path, &RequestHead) -> bool;
#[cfg(test)]
mod tests {
use std::{
fmt::Write as _,
fs::{self},
ops::Add,
time::{Duration, SystemTime},
Expand Down Expand Up @@ -848,8 +849,10 @@ mod tests {
let filename_encoded = filename
.as_bytes()
.iter()
.map(|c| format!("%{:02X}", c))
.collect::<String>();
.fold(String::new(), |mut buf, c| {
write!(&mut buf, "%{:02X}", c).unwrap();
buf
});
std::fs::File::create(tmpdir.path().join(filename)).unwrap();

let srv = test::init_service(App::new().service(Files::new("", tmpdir.path()))).await;
Expand Down
2 changes: 1 addition & 1 deletion actix-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rust-version.workspace = true

[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["http2", "ws", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
features = ["http2", "ws", "openssl", "rustls-0_20", "rustls-0_21", "compress-brotli", "compress-gzip", "compress-zstd"]

[lib]
name = "actix_http"
Expand Down
7 changes: 4 additions & 3 deletions actix-http/src/h1/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl PayloadSender {
}
}

#[allow(clippy::needless_pass_by_ref_mut)]
#[inline]
pub fn need_read(&self, cx: &mut Context<'_>) -> PayloadStatus {
// we check need_read only if Payload (other side) is alive,
Expand Down Expand Up @@ -174,7 +175,7 @@ impl Inner {

/// Register future waiting data from payload.
/// Waker would be used in `Inner::wake`
fn register(&mut self, cx: &mut Context<'_>) {
fn register(&mut self, cx: &Context<'_>) {
if self
.task
.as_ref()
Expand All @@ -186,7 +187,7 @@ impl Inner {

// Register future feeding data to payload.
/// Waker would be used in `Inner::wake_io`
fn register_io(&mut self, cx: &mut Context<'_>) {
fn register_io(&mut self, cx: &Context<'_>) {
if self
.io_task
.as_ref()
Expand Down Expand Up @@ -221,7 +222,7 @@ impl Inner {

fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
cx: &Context<'_>,
) -> Poll<Option<Result<Bytes, PayloadError>>> {
if let Some(data) = self.items.pop_front() {
self.len -= data.len();
Expand Down
2 changes: 1 addition & 1 deletion actix-http/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ pub struct TlsAcceptorConfig {
pub(crate) handshake_timeout: Option<std::time::Duration>,
}

#[cfg(any(feature = "openssl", feature = "rustls", feature = "rustls-0_21"))]
#[cfg(any(feature = "openssl", feature = "rustls-0_20", feature = "rustls-0_21"))]
impl TlsAcceptorConfig {
/// Set TLS handshake timeout duration.
pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {
Expand Down
4 changes: 2 additions & 2 deletions actix-multipart/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl InnerMultipart {
fn poll(
&mut self,
safety: &Safety,
cx: &mut Context<'_>,
cx: &Context<'_>,
) -> Poll<Option<Result<Field, MultipartError>>> {
if self.state == InnerState::Eof {
Poll::Ready(None)
Expand Down Expand Up @@ -740,7 +740,7 @@ impl Safety {
self.clean.get()
}

fn clone(&self, cx: &mut Context<'_>) -> Safety {
fn clone(&self, cx: &Context<'_>) -> Safety {
let payload = Rc::clone(&self.payload);
let s = Safety {
task: LocalWaker::new(),
Expand Down
9 changes: 5 additions & 4 deletions actix-router/benches/quoter.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#![allow(clippy::uninlined_format_args)]

use std::borrow::Cow;
use std::{borrow::Cow, fmt::Write as _};

use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn compare_quoters(c: &mut Criterion) {
let mut group = c.benchmark_group("Compare Quoters");

let quoter = actix_router::Quoter::new(b"", b"");
let path_quoted = (0..=0x7f)
.map(|c| format!("%{:02X}", c))
.collect::<String>();
let path_quoted = (0..=0x7f).fold(String::new(), |mut buf, c| {
write!(&mut buf, "%{:02X}", c).unwrap();
buf
});
let path_unquoted = ('\u{00}'..='\u{7f}').collect::<String>();

group.bench_function("quoter_unquoted", |b| {
Expand Down
8 changes: 7 additions & 1 deletion actix-router/src/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ impl ResourcePath for Url {

#[cfg(test)]
mod tests {
use std::fmt::Write as _;

use http::Uri;

use super::*;
Expand All @@ -78,7 +80,11 @@ mod tests {
}

fn percent_encode(data: &[u8]) -> String {
data.iter().map(|c| format!("%{:02X}", c)).collect()
data.iter()
.fold(String::with_capacity(data.len() * 3), |mut buf, c| {
write!(&mut buf, "%{:02X}", c).unwrap();
buf
})
}

#[test]
Expand Down
4 changes: 3 additions & 1 deletion actix-test/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Unreleased - 2023-xx-xx

- Add `TestServerConfig::workers()` setter method.
- Add `TestServerConfig::rustls_021()` method for Rustls v0.21 support behind new `rustls-0_21` crate feature.
- Add `TestServerConfig::workers()` method.
- Add `rustls-0_20` crate feature, which the existing `rustls` feature now aliases.
- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.

## 0.1.1 - 2023-02-26
Expand Down
13 changes: 9 additions & 4 deletions actix-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ edition = "2021"
[features]
default = []

# rustls
rustls = ["tls-rustls", "actix-http/rustls", "awc/rustls"]
# TLS via Rustls v0.20
rustls = ["rustls-0_20"]
# TLS via Rustls v0.20
rustls-0_20 = ["tls-rustls-0_20", "actix-http/rustls-0_20", "awc/rustls-0_20"]
# TLS via Rustls v0.21
rustls-0_21 = ["tls-rustls-0_21", "actix-http/rustls-0_21", "awc/rustls-0_21"]

# openssl
# TLS via OpenSSL
openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]

[dependencies]
Expand All @@ -44,5 +48,6 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
tls-rustls = { package = "rustls", version = "0.20", optional = true }
tls-rustls-0_20 = { package = "rustls", version = "0.20", optional = true }
tls-rustls-0_21 = { package = "rustls", version = "0.21", optional = true }
tokio = { version = "1.24.2", features = ["sync"] }
75 changes: 63 additions & 12 deletions actix-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@

#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
#[cfg(feature = "rustls")]
extern crate tls_rustls as rustls;

use std::{fmt, net, thread, time::Duration};

Expand Down Expand Up @@ -141,8 +139,10 @@ where
StreamType::Tcp => false,
#[cfg(feature = "openssl")]
StreamType::Openssl(_) => true,
#[cfg(feature = "rustls")]
StreamType::Rustls(_) => true,
#[cfg(feature = "rustls-0_20")]
StreamType::Rustls020(_) => true,
#[cfg(feature = "rustls-0_21")]
StreamType::Rustls021(_) => true,
};

// run server in separate orphaned thread
Expand Down Expand Up @@ -243,8 +243,8 @@ where
.openssl(acceptor.clone())
}),
},
#[cfg(feature = "rustls")]
StreamType::Rustls(config) => match cfg.tp {
#[cfg(feature = "rustls-0_20")]
StreamType::Rustls020(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);
Expand Down Expand Up @@ -285,6 +285,48 @@ where
.rustls(config.clone())
}),
},
#[cfg(feature = "rustls-0_21")]
StreamType::Rustls021(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_021(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_021(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_021(config.clone())
}),
},
}
.expect("test server could not be created");

Expand Down Expand Up @@ -316,7 +358,7 @@ where
builder.set_verify(SslVerifyMode::NONE);
let _ = builder
.set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
.map_err(|err| log::error!("Can not set alpn protocol: {err:?}"));
Connector::new()
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
Expand Down Expand Up @@ -355,8 +397,10 @@ enum StreamType {
Tcp,
#[cfg(feature = "openssl")]
Openssl(openssl::ssl::SslAcceptor),
#[cfg(feature = "rustls")]
Rustls(rustls::ServerConfig),
#[cfg(feature = "rustls-0_20")]
Rustls020(tls_rustls_0_20::ServerConfig),
#[cfg(feature = "rustls-0_21")]
Rustls021(tls_rustls_0_21::ServerConfig),
}

/// Create default test server config.
Expand Down Expand Up @@ -411,9 +455,16 @@ impl TestServerConfig {
}

/// Accept secure connections via Rustls.
#[cfg(feature = "rustls")]
pub fn rustls(mut self, config: rustls::ServerConfig) -> Self {
self.stream = StreamType::Rustls(config);
#[cfg(feature = "rustls-0_20")]
pub fn rustls(mut self, config: tls_rustls_0_20::ServerConfig) -> Self {
self.stream = StreamType::Rustls020(config);
self
}

/// Accept secure connections via Rustls.
#[cfg(feature = "rustls-0_21")]
pub fn rustls_021(mut self, config: tls_rustls_0_21::ServerConfig) -> Self {
self.stream = StreamType::Rustls021(config);
self
}

Expand Down
4 changes: 3 additions & 1 deletion actix-web/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 10 additions & 6 deletions actix-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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.
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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"

Expand Down
Loading

0 comments on commit 905c30a

Please sign in to comment.