From 2dbc5008d70d1a17605893d2b9572fcdf1abe7c4 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:20:34 -0400 Subject: [PATCH 01/12] ci: sync on.push Match the new convention we use for the "ci/" prefix. Include "rel-*" and '**' tags. --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2298e246..aa03830f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: - - main - - "*_dev" + branches: ['main', 'rel-*', 'ci/*'] + tags: + - '**' pull_request: {} schedule: - cron: "33 4 * * 5" From a446ed753e9a8e26757e8661d3744d4705497750 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:21:27 -0400 Subject: [PATCH 02/12] ci: add merge_group, workflow_dispatch Arrange all of the 'on' config the same way as in the main rustls repo. --- .github/workflows/CI.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index aa03830f..91a97522 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -5,9 +5,11 @@ on: branches: ['main', 'rel-*', 'ci/*'] tags: - '**' - pull_request: {} + pull_request: + merge_group: schedule: - cron: "33 4 * * 5" + workflow_dispatch: jobs: check: From 3238823578029f050f03b5274903b4258a271430 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:22:17 -0400 Subject: [PATCH 03/12] ci: sync concurrency settings Use the same values as the main rustls repo. This auto-cancels CI when you push updates to the branch. --- .github/workflows/CI.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 91a97522..00a086b6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,6 +11,10 @@ on: - cron: "33 4 * * 5" workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: check: runs-on: ubuntu-latest From 51d6678834367ced4ac7f6aead0e2582caaa983d Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:23:31 -0400 Subject: [PATCH 04/12] ci: use top-level --deny warnings --- .github/workflows/CI.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 00a086b6..13eaff3d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,13 +15,13 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +env: + RUSTFLAGS: --deny warnings + jobs: check: runs-on: ubuntu-latest - env: - RUSTFLAGS: "-D warnings" - steps: - name: Checkout sources uses: actions/checkout@v6 @@ -41,9 +41,6 @@ jobs: os: [ubuntu-latest, macOS-latest, windows-latest] rust: [stable] - env: - RUSTFLAGS: "-D warnings" - steps: - name: Checkout sources uses: actions/checkout@v6 @@ -88,7 +85,7 @@ jobs: - name: Run cargo clippy if: always() - run: cargo clippy --locked --all-features -- -D warnings + run: cargo clippy --locked --all-features msrv: name: MSRV From b02d72f46c4746b4545b94a09fd62915fc6b1cfb Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:26:01 -0400 Subject: [PATCH 05/12] ci: explicit read-only permissions --- .github/workflows/CI.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 13eaff3d..866caa54 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,5 +1,8 @@ name: CI +permissions: + contents: read + on: push: branches: ['main', 'rel-*', 'ci/*'] From 2dc5f8d6c202f7943c13b2e1a122c06abde9686e Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:27:18 -0400 Subject: [PATCH 06/12] ci: add RUST_BACKTRACE to test job --- .github/workflows/CI.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 866caa54..d2fc55b5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -63,11 +63,12 @@ jobs: if: runner.os == 'Windows' uses: seanmiddleditch/gha-setup-ninja@v6 - - name: Test run: | cargo test --locked --all cargo test --locked -p tokio-rustls --features early-data --test early-data + env: + RUST_BACKTRACE: 1 lints: name: Lints From f2182487523d3f73bbcea9997560e92d341afe19 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:28:55 -0400 Subject: [PATCH 07/12] ci: add cargo-semver-checks-action --- .github/workflows/CI.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d2fc55b5..4ec3e5b7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -120,3 +120,15 @@ jobs: toolchain: "1.75" - run: cargo check --locked --lib --all-features + + semver: + name: Check semver compatibility + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Check semver + uses: obi1kenobi/cargo-semver-checks-action@v2 From 3a6fc1f7a81909f2cce36ba8311ac4180b41386c Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:29:48 -0400 Subject: [PATCH 08/12] ci: add docs job --- .github/workflows/CI.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4ec3e5b7..43d873a3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -91,6 +91,24 @@ jobs: if: always() run: cargo clippy --locked --all-features + docs: + name: Check for documentation errors + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install rust toolchain + # use nightly to mirror docs.rs + uses: dtolnay/rust-toolchain@nightly + + - name: cargo doc + run: cargo doc --locked --all-features --no-deps --document-private-items + env: + RUSTDOCFLAGS: -Dwarnings + msrv: name: MSRV runs-on: ubuntu-latest From 933bed78c2234829ea9e209352c8dac9e074d77b Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:42:15 -0400 Subject: [PATCH 09/12] ci: add cargo-check-external-types --- .github/workflows/CI.yml | 23 +++++++++++++++++++++++ Cargo.toml | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 43d873a3..fa41b5d1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -150,3 +150,26 @@ jobs: - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 + + check-external-types: + name: Validate external types appearing in public API + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2026-03-20 + # ^ sync with https://github.com/awslabs/cargo-check-external-types/blob/main/rust-toolchain.toml + + - name: Install cargo-check-external-types + uses: taiki-e/cache-cargo-install-action@v3 + with: + tool: cargo-check-external-types + + - name: run cargo-check-external-types + run: cargo check-external-types diff --git a/Cargo.toml b/Cargo.toml index 60dde864..d917a5cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,15 @@ lazy_static = "1.1" rcgen = { version = "0.14", features = ["pem"] } tokio = { version = "1.0", features = ["full"] } webpki-roots = "1" + +[package.metadata.cargo_check_external_types] +allowed_external_types = [ + # --- + "rustls", + "rustls::*", + "rustls_pki_types", + "rustls_pki_types::*", + "tokio::io::async_read::AsyncRead", + "tokio::io::async_buf_read::AsyncBufRead", + "tokio::io::async_write::AsyncWrite", +] From 119190d4e73e766fdc67235f55581e224b94306a Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:51:04 -0400 Subject: [PATCH 10/12] reformat with .rustfmt.unstable.toml --- .cargo/config.toml | 2 ++ .rustfmt.unstable.toml | 3 +++ examples/client.rs | 4 +-- examples/server.rs | 7 +++--- src/client.rs | 9 +++---- src/lib.rs | 1 - src/server.rs | 4 +-- tests/badssl.rs | 4 +-- tests/certs/main.rs | 5 ++-- tests/early-data.rs | 56 ++++++++++++++++++++++-------------------- tests/test.rs | 6 ++--- tests/utils.rs | 7 +++--- 12 files changed, 56 insertions(+), 52 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 .rustfmt.unstable.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..a0ce3fc0 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +fmt-unstable = ["fmt", "--all", "--", "--config-path", ".rustfmt.unstable.toml"] diff --git a/.rustfmt.unstable.toml b/.rustfmt.unstable.toml new file mode 100644 index 00000000..3eb9271c --- /dev/null +++ b/.rustfmt.unstable.toml @@ -0,0 +1,3 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Module" +style_edition = "2024" diff --git a/examples/client.rs b/examples/client.rs index 347a827e..601fc668 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -7,9 +7,9 @@ use std::sync::Arc; use argh::FromArgs; use rustls::pki_types::pem::PemObject; use rustls::pki_types::{CertificateDer, ServerName}; -use tokio::io::{copy, split, stdin as tokio_stdin, stdout as tokio_stdout, AsyncWriteExt}; +use tokio::io::{AsyncWriteExt, copy, split, stdin as tokio_stdin, stdout as tokio_stdout}; use tokio::net::TcpStream; -use tokio_rustls::{rustls, TlsConnector}; +use tokio_rustls::{TlsConnector, rustls}; /// Tokio Rustls client example #[derive(FromArgs)] diff --git a/examples/server.rs b/examples/server.rs index 7666b07e..e4b1cb35 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,16 +1,15 @@ +use std::error::Error as StdError; use std::io; use std::net::ToSocketAddrs; use std::path::PathBuf; - -use std::error::Error as StdError; use std::sync::Arc; use argh::FromArgs; use rustls::pki_types::pem::PemObject; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use tokio::io::{copy, sink, split, AsyncWriteExt}; +use tokio::io::{AsyncWriteExt, copy, sink, split}; use tokio::net::TcpListener; -use tokio_rustls::{rustls, TlsAcceptor}; +use tokio_rustls::{TlsAcceptor, rustls}; /// Tokio Rustls server example #[derive(FromArgs)] diff --git a/src/client.rs b/src/client.rs index 128e840e..a94b64a8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,18 +1,17 @@ use std::future::Future; +use std::io::{self, BufRead as _}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; use std::pin::Pin; +use std::sync::Arc; #[cfg(feature = "early-data")] use std::task::Waker; use std::task::{Context, Poll}; -use std::{ - io::{self, BufRead as _}, - sync::Arc, -}; -use rustls::{pki_types::ServerName, ClientConfig, ClientConnection}; +use rustls::pki_types::ServerName; +use rustls::{ClientConfig, ClientConnection}; use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf}; use crate::common::{IoSession, MidHandshake, Stream, TlsState}; diff --git a/src/lib.rs b/src/lib.rs index 62387a45..8c6fd795 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; pub use rustls; - use rustls::CommonState; use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf}; diff --git a/src/server.rs b/src/server.rs index e8cada38..a43dd8fc 100644 --- a/src/server.rs +++ b/src/server.rs @@ -147,7 +147,7 @@ where return Poll::Ready(Err(io::Error::new( io::ErrorKind::Other, "acceptor cannot be polled after acceptance", - ))) + ))); } }; @@ -158,7 +158,7 @@ where return Poll::Pending; } Ok(0) | Err(_) => { - return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidData, err))) + return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidData, err))); } Ok(_) => { this.alert = Some((err, alert)); diff --git a/tests/badssl.rs b/tests/badssl.rs index 6334b5cc..424ed91d 100644 --- a/tests/badssl.rs +++ b/tests/badssl.rs @@ -4,12 +4,12 @@ use std::io; use std::net::ToSocketAddrs; use std::sync::Arc; -use rustls::pki_types::ServerName; use rustls::ClientConfig; +use rustls::pki_types::ServerName; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; -use tokio_rustls::client::TlsStream; use tokio_rustls::TlsConnector; +use tokio_rustls::client::TlsStream; async fn get( config: Arc, diff --git a/tests/certs/main.rs b/tests/certs/main.rs index a72ea75e..da50fc4e 100644 --- a/tests/certs/main.rs +++ b/tests/certs/main.rs @@ -2,12 +2,13 @@ //! Run with `cargo test -- --ignored` when test certificates need updating. //! Suitable for test certificates only. Not a production CA ;-) +use std::fs::File; +use std::io::Write; + use rcgen::{ BasicConstraints, CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, Issuer, KeyPair, KeyUsagePurpose, }; -use std::fs::File; -use std::io::Write; #[test] #[ignore] diff --git a/tests/early-data.rs b/tests/early-data.rs index dfee81be..3ec91deb 100644 --- a/tests/early-data.rs +++ b/tests/early-data.rs @@ -9,8 +9,8 @@ use rustls::pki_types::ServerName; use rustls::{self, ClientConfig, ServerConnection, Stream}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; -use tokio_rustls::client::TlsStream; use tokio_rustls::TlsConnector; +use tokio_rustls::client::TlsStream; async fn send( config: Arc, @@ -59,35 +59,37 @@ async fn test_0rtt_impl( let listener = TcpListener::bind("127.0.0.1:0")?; let server_port = listener.local_addr().unwrap().port(); - thread::spawn(move || loop { - let (mut sock, _addr) = listener.accept().unwrap(); - - let server = Arc::clone(&server); - thread::spawn(move || { - let mut conn = ServerConnection::new(server).unwrap(); - conn.complete_io(&mut sock).unwrap(); + thread::spawn(move || { + loop { + let (mut sock, _addr) = listener.accept().unwrap(); + + let server = Arc::clone(&server); + thread::spawn(move || { + let mut conn = ServerConnection::new(server).unwrap(); + conn.complete_io(&mut sock).unwrap(); + + if let Some(mut early_data) = conn.early_data() { + let mut buf = Vec::new(); + early_data.read_to_end(&mut buf).unwrap(); + let mut stream = Stream::new(&mut conn, &mut sock); + stream.write_all(b"EARLY:").unwrap(); + stream.write_all(&buf).unwrap(); + } - if let Some(mut early_data) = conn.early_data() { - let mut buf = Vec::new(); - early_data.read_to_end(&mut buf).unwrap(); let mut stream = Stream::new(&mut conn, &mut sock); - stream.write_all(b"EARLY:").unwrap(); - stream.write_all(&buf).unwrap(); - } - - let mut stream = Stream::new(&mut conn, &mut sock); - stream.write_all(b"LATE:").unwrap(); - loop { - let mut buf = [0; 1024]; - let n = stream.read(&mut buf).unwrap(); - if n == 0 { - conn.send_close_notify(); - conn.complete_io(&mut sock).unwrap(); - break; + stream.write_all(b"LATE:").unwrap(); + loop { + let mut buf = [0; 1024]; + let n = stream.read(&mut buf).unwrap(); + if n == 0 { + conn.send_close_notify(); + conn.complete_io(&mut sock).unwrap(); + break; + } + stream.write_all(&buf[..n]).unwrap(); } - stream.write_all(&buf[..n]).unwrap(); - } - }); + }); + } }); client.enable_early_data = true; diff --git a/tests/test.rs b/tests/test.rs index 8147e92d..e64cb05b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2,16 +2,16 @@ use std::io::{Cursor, ErrorKind}; use std::net::SocketAddr; -use std::sync::mpsc::channel; use std::sync::Arc; +use std::sync::mpsc::channel; use std::time::Duration; use std::{io, thread}; use futures_util::future::TryFutureExt; use lazy_static::lazy_static; -use rustls::pki_types::ServerName; use rustls::ClientConfig; -use tokio::io::{copy, split, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use rustls::pki_types::ServerName; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, copy, split}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot; use tokio::{runtime, time}; diff --git a/tests/utils.rs b/tests/utils.rs index 80339918..da0cd4b3 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -4,10 +4,9 @@ mod utils { use std::pin::Pin; use std::task::{Context, Poll}; - use rustls::{ - pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer}, - ClientConfig, RootCertStore, ServerConfig, - }; + use rustls::pki_types::pem::PemObject; + use rustls::pki_types::{CertificateDer, PrivateKeyDer}; + use rustls::{ClientConfig, RootCertStore, ServerConfig}; use tokio::io::{self, AsyncRead, AsyncWrite, AsyncWriteExt}; #[allow(dead_code)] From da281b48e2a6974286826b187825af140151f7d5 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 16:51:25 -0400 Subject: [PATCH 11/12] ci: split clippy/fmt, use .rustfmt.unstable.toml --- .github/workflows/CI.yml | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fa41b5d1..c7319140 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -70,8 +70,8 @@ jobs: env: RUST_BACKTRACE: 1 - lints: - name: Lints + clippy: + name: Clippy runs-on: ubuntu-latest steps: - name: Checkout sources @@ -82,15 +82,28 @@ jobs: - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy - - - name: Run cargo fmt - run: cargo fmt --all --check + components: clippy - name: Run cargo clippy - if: always() run: cargo clippy --locked --all-features + rustfmt: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install nightly toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + + - name: Check formatting + run: cargo +nightly fmt-unstable --check + docs: name: Check for documentation errors runs-on: ubuntu-latest From 3360a1ebd9cede4899479be683a7cb43e6e22c31 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 19 Jun 2026 17:02:47 -0400 Subject: [PATCH 12/12] ci: actions/checkout@v6 -> v7 --- .github/workflows/CI.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c7319140..84242d2e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -75,7 +75,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -92,7 +92,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -109,7 +109,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -127,7 +127,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -142,7 +142,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -157,7 +157,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -169,7 +169,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false