mirror of
https://github.com/fafhrd91/actix-net
synced 2024-11-23 19:51:06 +01:00
Rustls v0.21 support (#480)
This commit is contained in:
parent
6ce8307060
commit
0bc310a656
@ -15,9 +15,11 @@ ci-check-linux = "hack --workspace --feature-powerset check --tests --examples"
|
||||
|
||||
# tests avoiding io-uring feature
|
||||
ci-test = "hack --feature-powerset --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-rustls-020 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_21 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-rustls-021 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
||||
# tests avoiding io-uring feature on Windows
|
||||
ci-test-win = "hack --feature-powerset --depth 2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-win = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
||||
# test with io-uring feature
|
||||
ci-test-linux = "hack --feature-powerset test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-linux = "hack --feature-powerset --exclude-features=rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
16
.github/workflows/ci-post-merge.yml
vendored
16
.github/workflows/ci-post-merge.yml
vendored
@ -16,6 +16,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# prettier-ignore
|
||||
target:
|
||||
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||
@ -37,6 +38,10 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Free Disk Space
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
run: ./scripts/free-disk-space.sh
|
||||
|
||||
- name: Install OpenSSL
|
||||
if: matrix.target.os == 'windows-latest'
|
||||
run: choco install openssl -y --forcex64 --no-progress
|
||||
@ -83,8 +88,15 @@ jobs:
|
||||
run: cargo ci-test
|
||||
- name: tests
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux"
|
||||
run: >-
|
||||
sudo bash -c "
|
||||
ulimit -Sl 512
|
||||
&& ulimit -Hl 512
|
||||
&& PATH=$PATH:/usr/share/rust/.cargo/bin
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
|
||||
"
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
|
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@ -1,6 +1,6 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
on:
|
||||
pull_request: {}
|
||||
push: { branches: [master] }
|
||||
|
||||
@ -16,6 +16,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# prettier-ignore
|
||||
target:
|
||||
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||
@ -37,7 +38,7 @@ jobs:
|
||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Free Disk Space
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
run: ./scripts/free-disk-space.sh
|
||||
@ -99,8 +100,15 @@ jobs:
|
||||
run: cargo ci-test-win
|
||||
- name: tests
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux"
|
||||
run: >-
|
||||
sudo bash -c "
|
||||
ulimit -Sl 512
|
||||
&& ulimit -Hl 512
|
||||
&& PATH=$PATH:/usr/share/rust/.cargo/bin
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
|
||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
|
||||
"
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
|
@ -15,6 +15,7 @@ members = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
|
@ -625,6 +625,7 @@ impl Future for ServerWorker {
|
||||
self.poll(cx)
|
||||
}
|
||||
},
|
||||
|
||||
WorkerState::Restarting(ref mut restart) => {
|
||||
let factory_id = restart.factory_id;
|
||||
let token = restart.token;
|
||||
@ -649,6 +650,7 @@ impl Future for ServerWorker {
|
||||
|
||||
self.poll(cx)
|
||||
}
|
||||
|
||||
WorkerState::Shutdown(ref mut shutdown) => {
|
||||
// drop all pending connections in rx channel.
|
||||
while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) {
|
||||
@ -682,6 +684,7 @@ impl Future for ServerWorker {
|
||||
shutdown.timer.as_mut().poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
// actively poll stream and handle worker command
|
||||
WorkerState::Available => loop {
|
||||
match this.check_readiness(cx) {
|
||||
|
@ -1,40 +1,43 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2023-xx-xx
|
||||
## Unreleased
|
||||
|
||||
- Support Rustls v0.21.
|
||||
- Added `{accept, connect}::rustls_0_21` modules.
|
||||
- Added `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
|
||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||
|
||||
## 3.0.4 - 2022-03-15
|
||||
## 3.0.4
|
||||
|
||||
- Logs emitted now use the `tracing` crate with `log` compatibility. [#451]
|
||||
|
||||
[#451]: https://github.com/actix/actix-net/pull/451
|
||||
|
||||
## 3.0.3 - 2022-02-15
|
||||
## 3.0.3
|
||||
|
||||
- No significant changes since `3.0.2`.
|
||||
|
||||
## 3.0.2 - 2022-01-28
|
||||
## 3.0.2
|
||||
|
||||
- Expose `connect::Connection::new`. [#439]
|
||||
|
||||
[#439]: https://github.com/actix/actix-net/pull/439
|
||||
|
||||
## 3.0.1 - 2022-01-11
|
||||
## 3.0.1
|
||||
|
||||
- No significant changes since `3.0.0`.
|
||||
|
||||
## 3.0.0 - 2021-12-26
|
||||
## 3.0.0
|
||||
|
||||
- No significant changes since `3.0.0-rc.2`.
|
||||
|
||||
## 3.0.0-rc.2 - 2021-12-10
|
||||
## 3.0.0-rc.2
|
||||
|
||||
- Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429]
|
||||
|
||||
[#429]: https://github.com/actix/actix-net/pull/429
|
||||
|
||||
## 3.0.0-rc.1 - 2021-11-29
|
||||
## 3.0.0-rc.1
|
||||
|
||||
### Added
|
||||
|
||||
@ -72,7 +75,7 @@
|
||||
[#422]: https://github.com/actix/actix-net/pull/422
|
||||
[#423]: https://github.com/actix/actix-net/pull/423
|
||||
|
||||
## 3.0.0-beta.9 - 2021-11-22
|
||||
## 3.0.0-beta.9
|
||||
|
||||
- Add configurable timeout for accepting TLS connection. [#393]
|
||||
- Added `TlsError::Timeout` variant. [#393]
|
||||
@ -82,20 +85,20 @@
|
||||
[#393]: https://github.com/actix/actix-net/pull/393
|
||||
[#420]: https://github.com/actix/actix-net/pull/420
|
||||
|
||||
## 3.0.0-beta.8 - 2021-11-15
|
||||
## 3.0.0-beta.8
|
||||
|
||||
- Add `Connect::request` for getting a reference to the connection request. [#415]
|
||||
|
||||
[#415]: https://github.com/actix/actix-net/pull/415
|
||||
|
||||
## 3.0.0-beta.7 - 2021-10-20
|
||||
## 3.0.0-beta.7
|
||||
|
||||
- Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401]
|
||||
- Alias `connect::ssl` to `connect::tls`. [#401]
|
||||
|
||||
[#401]: https://github.com/actix/actix-net/pull/401
|
||||
|
||||
## 3.0.0-beta.6 - 2021-10-19
|
||||
## 3.0.0-beta.6
|
||||
|
||||
- Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396]
|
||||
- Removed a re-export of `Session` from `rustls` as it no longer exist. [#396]
|
||||
@ -103,7 +106,7 @@
|
||||
|
||||
[#396]: https://github.com/actix/actix-net/pull/396
|
||||
|
||||
## 3.0.0-beta.5 - 2021-03-29
|
||||
## 3.0.0-beta.5
|
||||
|
||||
- Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296]
|
||||
- Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
|
||||
@ -117,7 +120,7 @@
|
||||
[#297]: https://github.com/actix/actix-net/pull/297
|
||||
[#299]: https://github.com/actix/actix-net/pull/299
|
||||
|
||||
## 3.0.0-beta.4 - 2021-02-24
|
||||
## 3.0.0-beta.4
|
||||
|
||||
- Rename `accept::openssl::{SslStream => TlsStream}`.
|
||||
- Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
|
||||
@ -125,7 +128,7 @@
|
||||
|
||||
[#282]: https://github.com/actix/actix-net/pull/282
|
||||
|
||||
## 3.0.0-beta.3 - 2021-02-06
|
||||
## 3.0.0-beta.3
|
||||
|
||||
- Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
|
||||
- Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
|
||||
@ -139,13 +142,13 @@
|
||||
[#248]: https://github.com/actix/actix-net/pull/248
|
||||
[#273]: https://github.com/actix/actix-net/pull/273
|
||||
|
||||
## 3.0.0-beta.2 - 2022-xx-xx
|
||||
## 3.0.0-beta.2
|
||||
|
||||
- Depend on stable trust-dns packages. [#204]
|
||||
|
||||
[#204]: https://github.com/actix/actix-net/pull/204
|
||||
|
||||
## 3.0.0-beta.1 - 2020-12-29
|
||||
## 3.0.0-beta.1
|
||||
|
||||
- Move acceptors under `accept` module. [#238]
|
||||
- Merge `actix-connect` crate under `connect` module. [#238]
|
||||
@ -153,7 +156,7 @@
|
||||
|
||||
[#238]: https://github.com/actix/actix-net/pull/238
|
||||
|
||||
## 2.0.0 - 2020-09-03
|
||||
## 2.0.0
|
||||
|
||||
- `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
|
||||
- Where possible, "SSL" terminology is replaced with "TLS".
|
||||
@ -161,28 +164,28 @@
|
||||
- `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
|
||||
- `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
|
||||
|
||||
## 2.0.0-alpha.2 - 2020-08-17
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
- Update `rustls` dependency to 0.18
|
||||
- Update `tokio-rustls` dependency to 0.14
|
||||
- Update `webpki-roots` dependency to 0.20
|
||||
|
||||
## [2.0.0-alpha.1] - 2020-03-03
|
||||
## [2.0.0-alpha.1]
|
||||
|
||||
- Update `rustls` dependency to 0.17
|
||||
- Update `tokio-rustls` dependency to 0.13
|
||||
- Update `webpki-roots` dependency to 0.19
|
||||
|
||||
## [1.0.0] - 2019-12-11
|
||||
## [1.0.0]
|
||||
|
||||
- 1.0.0 release
|
||||
|
||||
## [1.0.0-alpha.3] - 2019-12-07
|
||||
## [1.0.0-alpha.3]
|
||||
|
||||
- Migrate to tokio 0.2
|
||||
- Enable rustls acceptor service
|
||||
- Enable native-tls acceptor service
|
||||
|
||||
## [1.0.0-alpha.1] - 2019-12-02
|
||||
## [1.0.0-alpha.1]
|
||||
|
||||
- Split openssl acceptor from actix-server package
|
||||
|
@ -9,7 +9,7 @@ description = "TLS acceptor and connector services for Actix ecosystem"
|
||||
keywords = ["network", "tls", "ssl", "async", "transport"]
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
categories = ["network-programming", "asynchronous", "cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
@ -29,8 +29,14 @@ connect = []
|
||||
# use openssl impls
|
||||
openssl = ["tls-openssl", "tokio-openssl"]
|
||||
|
||||
# use rustls impls
|
||||
rustls = ["tokio-rustls", "webpki-roots"]
|
||||
# alias for backwards compat
|
||||
rustls = ["rustls-0_20"]
|
||||
|
||||
# use rustls v0.20 impls
|
||||
rustls-0_20 = ["tokio-rustls-023", "webpki-roots-022"]
|
||||
|
||||
# use rustls v0.21 impls
|
||||
rustls-0_21 = ["tokio-rustls-024", "webpki-roots-025"]
|
||||
|
||||
# use native-tls impls
|
||||
native-tls = ["tokio-native-tls"]
|
||||
@ -57,9 +63,13 @@ http = { version = "0.2.3", optional = true }
|
||||
tls-openssl = { package = "openssl", version = "0.10.48", optional = true }
|
||||
tokio-openssl = { version = "0.6", optional = true }
|
||||
|
||||
# rustls
|
||||
tokio-rustls = { version = "0.23", optional = true }
|
||||
webpki-roots = { version = "0.22", optional = true }
|
||||
# rustls v0.20
|
||||
tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
|
||||
webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }
|
||||
|
||||
# rustls v0.21
|
||||
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true }
|
||||
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }
|
||||
|
||||
# native-tls
|
||||
tokio-native-tls = { version = "0.3", optional = true }
|
||||
@ -72,11 +82,11 @@ bytes = "1"
|
||||
env_logger = "0.10"
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
|
||||
log = "0.4"
|
||||
rcgen = "0.10"
|
||||
rcgen = "0.11"
|
||||
rustls-pemfile = "1"
|
||||
tokio-rustls = { version = "0.23", features = ["dangerous_configuration"] }
|
||||
trust-dns-resolver = "0.22"
|
||||
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", features = ["dangerous_configuration"] }
|
||||
trust-dns-resolver = "0.23"
|
||||
|
||||
[[example]]
|
||||
name = "accept-rustls"
|
||||
required-features = ["accept", "rustls"]
|
||||
required-features = ["accept", "rustls-0_21"]
|
||||
|
@ -17,12 +17,13 @@
|
||||
|
||||
#[rustfmt::skip]
|
||||
// this `use` is only exists because of how we have organised the crate
|
||||
// it is not necessary for your actual code; you should import from `rustls` directly
|
||||
use tokio_rustls::rustls;
|
||||
// it is not necessary for your actual code; you should import from `rustls` normally
|
||||
use tokio_rustls_024::rustls;
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
@ -32,7 +33,7 @@ use std::{
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::Server;
|
||||
use actix_service::ServiceFactoryExt as _;
|
||||
use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream};
|
||||
use actix_tls::accept::rustls_0_21::{Acceptor as RustlsAcceptor, TlsStream};
|
||||
use futures_util::future::ok;
|
||||
use rustls::{server::ServerConfig, Certificate, PrivateKey};
|
||||
use rustls_pemfile::{certs, rsa_private_keys};
|
||||
@ -42,9 +43,16 @@ use tracing::info;
|
||||
async fn main() -> io::Result<()> {
|
||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||
|
||||
let root_path = env!("CARGO_MANIFEST_DIR")
|
||||
.parse::<PathBuf>()
|
||||
.unwrap()
|
||||
.join("examples");
|
||||
let cert_path = root_path.clone().join("cert.pem");
|
||||
let key_path = root_path.clone().join("key.pem");
|
||||
|
||||
// Load TLS key and cert files
|
||||
let cert_file = &mut BufReader::new(File::open("./examples/cert.pem").unwrap());
|
||||
let key_file = &mut BufReader::new(File::open("./examples/key.pem").unwrap());
|
||||
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
|
||||
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
|
||||
|
||||
let cert_chain = certs(cert_file)
|
||||
.unwrap()
|
||||
@ -64,7 +72,7 @@ async fn main() -> io::Result<()> {
|
||||
let count = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let addr = ("127.0.0.1", 8443);
|
||||
info!("starting server on port: {}", &addr.0);
|
||||
info!("starting server at: {addr:?}");
|
||||
|
||||
Server::build()
|
||||
.bind("tls-example", addr, move || {
|
||||
|
@ -12,15 +12,27 @@ use actix_utils::counter::Counter;
|
||||
#[cfg(feature = "openssl")]
|
||||
pub mod openssl;
|
||||
|
||||
#[cfg(feature = "rustls")]
|
||||
pub mod rustls;
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
pub mod rustls_0_20;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
pub use rustls_0_20 as rustls;
|
||||
|
||||
#[cfg(feature = "rustls-0_21")]
|
||||
pub mod rustls_0_21;
|
||||
|
||||
#[cfg(feature = "native-tls")]
|
||||
pub mod native_tls;
|
||||
|
||||
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
||||
|
||||
#[cfg(any(feature = "openssl", feature = "rustls", feature = "native-tls"))]
|
||||
#[cfg(any(
|
||||
feature = "openssl",
|
||||
feature = "rustls-0_20",
|
||||
feature = "rustls-0_21",
|
||||
feature = "native-tls",
|
||||
))]
|
||||
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
|
||||
std::time::Duration::from_secs(3);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! `rustls` based TLS connection acceptor service.
|
||||
//! `rustls` v0.20 based TLS connection acceptor service.
|
||||
//!
|
||||
//! See [`Acceptor`] for main service factory docs.
|
||||
|
||||
@ -24,13 +24,14 @@ use actix_utils::{
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor};
|
||||
use tokio_rustls_023 as tokio_rustls;
|
||||
|
||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||
|
||||
pub mod reexports {
|
||||
//! Re-exports from `rustls` that are useful for acceptors.
|
||||
|
||||
pub use tokio_rustls::rustls::ServerConfig;
|
||||
pub use tokio_rustls_023::rustls::ServerConfig;
|
||||
}
|
||||
|
||||
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
198
actix-tls/src/accept/rustls_0_21.rs
Normal file
198
actix-tls/src/accept/rustls_0_21.rs
Normal file
@ -0,0 +1,198 @@
|
||||
//! `rustls` v0.21 based TLS connection acceptor service.
|
||||
//!
|
||||
//! See [`Acceptor`] for main service factory docs.
|
||||
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
future::Future,
|
||||
io::{self, IoSlice},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use actix_rt::{
|
||||
net::{ActixStream, Ready},
|
||||
time::{sleep, Sleep},
|
||||
};
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use actix_utils::{
|
||||
counter::{Counter, CounterGuard},
|
||||
future::{ready, Ready as FutReady},
|
||||
};
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||
|
||||
pub mod reexports {
|
||||
//! Re-exports from `rustls` that are useful for acceptors.
|
||||
|
||||
pub use tokio_rustls_024::rustls::ServerConfig;
|
||||
}
|
||||
|
||||
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
||||
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
||||
|
||||
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
|
||||
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
|
||||
|
||||
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
(**self).is_write_vectored()
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||
IO::poll_read_ready((**self).get_ref().0, cx)
|
||||
}
|
||||
|
||||
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||
IO::poll_write_ready((**self).get_ref().0, cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept TLS connections via the `rustls` crate.
|
||||
pub struct Acceptor {
|
||||
config: Arc<ServerConfig>,
|
||||
handshake_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
/// Constructs `rustls` based acceptor service factory.
|
||||
pub fn new(config: ServerConfig) -> Self {
|
||||
Acceptor {
|
||||
config: Arc::new(config),
|
||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||
}
|
||||
}
|
||||
|
||||
/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
|
||||
///
|
||||
/// Default timeout is 3 seconds.
|
||||
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
|
||||
self.handshake_timeout = handshake_timeout;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Acceptor {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
config: self.config.clone(),
|
||||
handshake_timeout: self.handshake_timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
|
||||
type Response = TlsStream<IO>;
|
||||
type Error = TlsError<io::Error, Infallible>;
|
||||
type Config = ();
|
||||
type Service = AcceptorService;
|
||||
type InitError = ();
|
||||
type Future = FutReady<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, _: ()) -> Self::Future {
|
||||
let res = MAX_CONN_COUNTER.with(|conns| {
|
||||
Ok(AcceptorService {
|
||||
acceptor: self.config.clone().into(),
|
||||
conns: conns.clone(),
|
||||
handshake_timeout: self.handshake_timeout,
|
||||
})
|
||||
});
|
||||
|
||||
ready(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rustls based acceptor service.
|
||||
pub struct AcceptorService {
|
||||
acceptor: TlsAcceptor,
|
||||
conns: Counter,
|
||||
handshake_timeout: Duration,
|
||||
}
|
||||
|
||||
impl<IO: ActixStream> Service<IO> for AcceptorService {
|
||||
type Response = TlsStream<IO>;
|
||||
type Error = TlsError<io::Error, Infallible>;
|
||||
type Future = AcceptFut<IO>;
|
||||
|
||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if self.conns.available(cx) {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, req: IO) -> Self::Future {
|
||||
AcceptFut {
|
||||
fut: self.acceptor.accept(req),
|
||||
timeout: sleep(self.handshake_timeout),
|
||||
_guard: self.conns.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// Accept future for Rustls service.
|
||||
#[doc(hidden)]
|
||||
pub struct AcceptFut<IO: ActixStream> {
|
||||
fut: Accept<IO>,
|
||||
#[pin]
|
||||
timeout: Sleep,
|
||||
_guard: CounterGuard,
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO: ActixStream> Future for AcceptFut<IO> {
|
||||
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.project();
|
||||
match Pin::new(&mut this.fut).poll(cx) {
|
||||
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
|
||||
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
|
||||
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
|
||||
}
|
||||
}
|
||||
}
|
@ -27,8 +27,15 @@ mod uri;
|
||||
#[cfg(feature = "openssl")]
|
||||
pub mod openssl;
|
||||
|
||||
#[cfg(feature = "rustls")]
|
||||
pub mod rustls;
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
pub mod rustls_0_20;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
pub use rustls_0_20 as rustls;
|
||||
|
||||
#[cfg(feature = "rustls-0_21")]
|
||||
pub mod rustls_0_21;
|
||||
|
||||
#[cfg(feature = "native-tls")]
|
||||
pub mod native_tls;
|
||||
|
@ -20,22 +20,21 @@ use tokio_rustls::{
|
||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||
};
|
||||
use tracing::trace;
|
||||
use webpki_roots::TLS_SERVER_ROOTS;
|
||||
use tokio_rustls_023 as tokio_rustls;
|
||||
|
||||
use crate::connect::{Connection, Host};
|
||||
|
||||
pub mod reexports {
|
||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
||||
|
||||
pub use tokio_rustls::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||
pub use webpki_roots::TLS_SERVER_ROOTS;
|
||||
pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||
pub use webpki_roots_022::TLS_SERVER_ROOTS;
|
||||
}
|
||||
|
||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
let mut root_certs = RootCertStore::empty();
|
||||
for cert in TLS_SERVER_ROOTS.0 {
|
||||
for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 {
|
||||
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
cert.subject,
|
||||
cert.spki,
|
||||
@ -102,12 +101,13 @@ where
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||
trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||
let (stream, connection) = connection.replace_io(());
|
||||
|
||||
match ServerName::try_from(connection.hostname()) {
|
||||
Ok(host) => ConnectFut::Future {
|
||||
connect: RustlsTlsConnector::from(self.connector.clone()).connect(host, stream),
|
||||
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||
.connect(host, stream),
|
||||
connection: Some(connection),
|
||||
},
|
||||
Err(_) => ConnectFut::InvalidDns,
|
||||
@ -117,6 +117,7 @@ where
|
||||
|
||||
/// Connect future for Rustls service.
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ConnectFut<R, IO> {
|
||||
/// See issue <https://github.com/briansmith/webpki/issues/54>
|
||||
InvalidDns,
|
||||
@ -131,17 +132,23 @@ where
|
||||
R: Host,
|
||||
IO: ActixStream,
|
||||
{
|
||||
type Output = Result<Connection<R, AsyncTlsStream<IO>>, io::Error>;
|
||||
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
Self::InvalidDns => Poll::Ready(Err(
|
||||
io::Error::new(io::ErrorKind::Other, "rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54")
|
||||
)),
|
||||
Self::Future { connect, connection } => {
|
||||
Self::InvalidDns => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
|
||||
feature and use the Rustls v0.21 utilities to gain this feature.",
|
||||
))),
|
||||
|
||||
Self::Future {
|
||||
connect,
|
||||
connection,
|
||||
} => {
|
||||
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||
let connection = connection.take().unwrap();
|
||||
trace!("TLS handshake success: {:?}", connection.hostname());
|
||||
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||
}
|
||||
}
|
154
actix-tls/src/connect/rustls_0_21.rs
Normal file
154
actix-tls/src/connect/rustls_0_21.rs
Normal file
@ -0,0 +1,154 @@
|
||||
//! Rustls based connector service.
|
||||
//!
|
||||
//! See [`TlsConnector`] for main connector service factory docs.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
future::Future,
|
||||
io,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use actix_rt::net::ActixStream;
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use actix_utils::future::{ok, Ready};
|
||||
use futures_core::ready;
|
||||
use tokio_rustls::{
|
||||
client::TlsStream as AsyncTlsStream,
|
||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||
};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
use crate::connect::{Connection, Host};
|
||||
|
||||
pub mod reexports {
|
||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
||||
|
||||
pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||
pub use webpki_roots_025::TLS_SERVER_ROOTS;
|
||||
}
|
||||
|
||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
let mut root_certs = RootCertStore::empty();
|
||||
for cert in webpki_roots_025::TLS_SERVER_ROOTS {
|
||||
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
cert.subject,
|
||||
cert.spki,
|
||||
cert.name_constraints,
|
||||
);
|
||||
let certs = vec![cert].into_iter();
|
||||
root_certs.add_trust_anchors(certs);
|
||||
}
|
||||
root_certs
|
||||
}
|
||||
|
||||
/// Connector service factory using `rustls`.
|
||||
#[derive(Clone)]
|
||||
pub struct TlsConnector {
|
||||
connector: Arc<ClientConfig>,
|
||||
}
|
||||
|
||||
impl TlsConnector {
|
||||
/// Constructs new connector service factory from a `rustls` client configuration.
|
||||
pub fn new(connector: Arc<ClientConfig>) -> Self {
|
||||
TlsConnector { connector }
|
||||
}
|
||||
|
||||
/// Constructs new connector service from a `rustls` client configuration.
|
||||
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
|
||||
TlsConnectorService { connector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
|
||||
where
|
||||
R: Host,
|
||||
IO: ActixStream + 'static,
|
||||
{
|
||||
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||
type Error = io::Error;
|
||||
type Config = ();
|
||||
type Service = TlsConnectorService;
|
||||
type InitError = ();
|
||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, _: ()) -> Self::Future {
|
||||
ok(TlsConnectorService {
|
||||
connector: self.connector.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Connector service using `rustls`.
|
||||
#[derive(Clone)]
|
||||
pub struct TlsConnectorService {
|
||||
connector: Arc<ClientConfig>,
|
||||
}
|
||||
|
||||
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
|
||||
where
|
||||
R: Host,
|
||||
IO: ActixStream,
|
||||
{
|
||||
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||
type Error = io::Error;
|
||||
type Future = ConnectFut<R, IO>;
|
||||
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||
let (stream, connection) = connection.replace_io(());
|
||||
|
||||
match ServerName::try_from(connection.hostname()) {
|
||||
Ok(host) => ConnectFut::Future {
|
||||
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||
.connect(host, stream),
|
||||
connection: Some(connection),
|
||||
},
|
||||
Err(_) => ConnectFut::InvalidServerName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect future for Rustls service.
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ConnectFut<R, IO> {
|
||||
InvalidServerName,
|
||||
Future {
|
||||
connect: RustlsConnect<IO>,
|
||||
connection: Option<Connection<R, ()>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<R, IO> Future for ConnectFut<R, IO>
|
||||
where
|
||||
R: Host,
|
||||
IO: ActixStream,
|
||||
{
|
||||
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"connection parameters specified invalid server name",
|
||||
))),
|
||||
|
||||
Self::Future {
|
||||
connect,
|
||||
connection,
|
||||
} => {
|
||||
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||
let connection = connection.take().unwrap();
|
||||
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
#![cfg(all(
|
||||
feature = "accept",
|
||||
feature = "connect",
|
||||
feature = "rustls",
|
||||
feature = "rustls-0_21",
|
||||
feature = "openssl"
|
||||
))]
|
||||
|
||||
@ -15,6 +15,7 @@ use actix_service::ServiceFactoryExt as _;
|
||||
use actix_tls::accept::openssl::{Acceptor, TlsStream};
|
||||
use actix_utils::future::ok;
|
||||
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
fn new_cert_and_key() -> (String, String) {
|
||||
let cert =
|
||||
@ -49,7 +50,7 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor
|
||||
mod danger {
|
||||
use std::time::SystemTime;
|
||||
|
||||
use tokio_rustls::rustls::{
|
||||
use tokio_rustls_024::rustls::{
|
||||
self,
|
||||
client::{ServerCertVerified, ServerCertVerifier},
|
||||
};
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![cfg(all(
|
||||
feature = "accept",
|
||||
feature = "connect",
|
||||
feature = "rustls",
|
||||
feature = "rustls-0_21",
|
||||
feature = "openssl"
|
||||
))]
|
||||
|
||||
@ -15,13 +15,14 @@ use actix_rt::net::TcpStream;
|
||||
use actix_server::TestServer;
|
||||
use actix_service::ServiceFactoryExt as _;
|
||||
use actix_tls::{
|
||||
accept::rustls::{Acceptor, TlsStream},
|
||||
accept::rustls_0_21::{Acceptor, TlsStream},
|
||||
connect::openssl::reexports::SslConnector,
|
||||
};
|
||||
use actix_utils::future::ok;
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use tls_openssl::ssl::SslVerifyMode;
|
||||
use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
fn new_cert_and_key() -> (String, String) {
|
||||
let cert =
|
||||
|
@ -30,7 +30,7 @@ async fn test_string() {
|
||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustls")]
|
||||
#[cfg(feature = "rustls-0_21")]
|
||||
#[actix_rt::test]
|
||||
async fn test_rustls_string() {
|
||||
let srv = TestServer::start(|| {
|
||||
@ -114,7 +114,7 @@ async fn test_openssl_uri() {
|
||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rustls", feature = "uri"))]
|
||||
#[cfg(all(feature = "rustls-0_21", feature = "uri"))]
|
||||
#[actix_rt::test]
|
||||
async fn test_rustls_uri() {
|
||||
use std::convert::TryFrom;
|
||||
|
@ -29,7 +29,7 @@ impl Counter {
|
||||
|
||||
/// Returns true if counter is below capacity. Otherwise, register to wake task when it is.
|
||||
#[inline]
|
||||
pub fn available(&self, cx: &mut task::Context<'_>) -> bool {
|
||||
pub fn available(&self, cx: &task::Context<'_>) -> bool {
|
||||
self.0.available(cx)
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ impl CounterInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn available(&self, cx: &mut task::Context<'_>) -> bool {
|
||||
fn available(&self, cx: &task::Context<'_>) -> bool {
|
||||
if self.count.get() < self.capacity {
|
||||
true
|
||||
} else {
|
||||
|
@ -62,6 +62,7 @@ where
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// SAFETY: we are not moving out of the pinned field
|
||||
// see https://github.com/rust-lang/rust/pull/102737
|
||||
#[allow(clippy::needless_borrow)]
|
||||
(unsafe { &mut self.get_unchecked_mut().f })(cx)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user