Skip to content

Commit

Permalink
actix-http: Add rustls 0.23 (#3361)
Browse files Browse the repository at this point in the history
Co-authored-by: Rob Ede <robjtede@icloud.com>
  • Loading branch information
asonix and robjtede authored May 18, 2024
1 parent fff45b2 commit 2d035c0
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 33 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci-post-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.1

- name: Install OpenSSL
if: matrix.target.os == 'windows-latest'
shell: bash
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.1

- name: Install OpenSSL
if: matrix.target.os == 'windows-latest'
shell: bash
Expand Down
5 changes: 5 additions & 0 deletions actix-http/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Added

- Add `rustls-0_23` crate feature
- Add `{h1::H1Service, h2::H2Service, HttpService}::rustls_0_23()` and `HttpService::rustls_0_23_with_config()` service constructors.

### Changed

- Update `brotli` dependency to `6`.
Expand Down
12 changes: 8 additions & 4 deletions actix-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ features = [
"rustls-0_20",
"rustls-0_21",
"rustls-0_22",
"rustls-0_23",
"compress-brotli",
"compress-gzip",
"compress-zstd",
Expand Down Expand Up @@ -66,6 +67,9 @@ rustls-0_21 = ["actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22
rustls-0_22 = ["actix-tls/accept", "actix-tls/rustls-0_22"]

# TLS via Rustls v0.23
rustls-0_23 = ["actix-tls/accept", "actix-tls/rustls-0_23"]

# Compression codecs
compress-brotli = ["__compress", "brotli"]
compress-gzip = ["__compress", "flate2"]
Expand Down Expand Up @@ -121,7 +125,7 @@ zstd = { version = "0.13", optional = true }
[dev-dependencies]
actix-http-test = { version = "3", features = ["openssl"] }
actix-server = "2"
actix-tls = { version = "3.3", features = ["openssl", "rustls-0_22-webpki-roots"] }
actix-tls = { version = "3.4", features = ["openssl", "rustls-0_23-webpki-roots"] }
actix-web = "4"

async-stream = "0.3"
Expand All @@ -139,16 +143,16 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.55" }
tls-rustls_022 = { package = "rustls", version = "0.22" }
tls-rustls_023 = { package = "rustls", version = "0.23" }
tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }

[[example]]
name = "ws"
required-features = ["ws", "rustls-0_22"]
required-features = ["ws", "rustls-0_23"]

[[example]]
name = "tls_rustls"
required-features = ["http2", "rustls-0_22"]
required-features = ["http2", "rustls-0_23"]

[[bench]]
name = "response-body-compression"
Expand Down
5 changes: 2 additions & 3 deletions actix-http/examples/tls_rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@
//! Protocol: HTTP/1.1
//! ```
extern crate tls_rustls_022 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 All @@ -36,7 +35,7 @@ async fn main() -> io::Result<()> {
);
ok::<_, Error>(Response::ok().set_body(body))
})
.rustls_0_22(rustls_config())
.rustls_0_23(rustls_config())
})?
.run()
.await
Expand Down
4 changes: 2 additions & 2 deletions actix-http/examples/ws.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Sets up a WebSocket server over TCP and TLS.
//! Sends a heartbeat message every 4 seconds but does not respond to any incoming frames.
extern crate tls_rustls_022 as rustls;
extern crate tls_rustls_023 as rustls;

use std::{
io,
Expand Down Expand Up @@ -30,7 +30,7 @@ async fn main() -> io::Result<()> {
.bind("tls", ("127.0.0.1", 8443), || {
HttpService::build()
.finish(handler)
.rustls_0_22(tls_config())
.rustls_0_23(tls_config())
})?
.run()
.await
Expand Down
61 changes: 61 additions & 0 deletions actix-http/src/h1/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,67 @@ mod rustls_0_22 {
}
}

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

use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};

use super::*;

impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,

B: MessageBody,

X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,

U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}

impl<T, S, B, X, U> H1Service<T, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
Expand Down
51 changes: 51 additions & 0 deletions actix-http/src/h2/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,57 @@ mod rustls_0_22 {
}
}

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

use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};

use super::*;

impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,

B: MessageBody + 'static,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = S::InitError,
> {
let mut protos = vec![b"h2".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;

Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}

impl<T, S, B> ServiceFactory<(T, Option<net::SocketAddr>)> for H2Service<T, S, B>
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
Expand Down
8 changes: 6 additions & 2 deletions actix-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
//! | ------------------- | ------------------------------------------- |
//! | `http2` | HTTP/2 support via [h2]. |
//! | `openssl` | TLS support via [OpenSSL]. |
//! | `rustls` | TLS support via [rustls]. |
//! | `rustls` | TLS support via [rustls] 0.20. |
//! | `rustls-0_21` | TLS support via [rustls] 0.21. |
//! | `rustls-0_22` | TLS support via [rustls] 0.22. |
//! | `rustls-0_23` | TLS support via [rustls] 0.23. |
//! | `compress-brotli` | Payload compression support: Brotli. |
//! | `compress-gzip` | Payload compression support: Deflate, Gzip. |
//! | `compress-zstd` | Payload compression support: Zstd. |
Expand All @@ -28,7 +31,7 @@
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

pub use ::http::{uri, uri::Uri, Method, StatusCode, Version};
pub use http::{uri, uri::Uri, Method, StatusCode, Version};

pub mod body;
mod builder;
Expand Down Expand Up @@ -63,6 +66,7 @@ pub use self::payload::PayloadStream;
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
pub use self::service::TlsAcceptorConfig;
pub use self::{
Expand Down
98 changes: 98 additions & 0 deletions actix-http/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ where
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[derive(Debug, Default)]
pub struct TlsAcceptorConfig {
Expand All @@ -257,6 +258,7 @@ pub struct TlsAcceptorConfig {
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
impl TlsAcceptorConfig {
/// Set TLS handshake timeout duration.
Expand Down Expand Up @@ -650,6 +652,102 @@ mod rustls_0_22 {
}
}

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

use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};

use super::*;

impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,

B: MessageBody + 'static,

X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,

U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
self.rustls_0_23_with_config(config, TlsAcceptorConfig::default())
}

/// Create Rustls v0.23 based service with custom TLS acceptor configuration.
pub fn rustls_0_23_with_config(
self,
mut config: ServerConfig,
tls_acceptor_config: TlsAcceptorConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;

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")
})
.map_err(TlsError::into_service_error)
.and_then(|io: TlsStream<TcpStream>| async {
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().0.peer_addr().ok();
Ok((io, proto, peer_addr))
})
.and_then(self.map_err(TlsError::Service))
}
}
}

impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
for HttpService<T, S, B, X, U>
where
Expand Down
Loading

0 comments on commit 2d035c0

Please sign in to comment.