diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5cde165..c7b634c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,7 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37ced22..d8e30f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,12 @@ name: rustls permissions: contents: read -on: [push, pull_request] +on: + push: + pull_request: + merge_group: + schedule: + - cron: '23 6 * * 5' jobs: build: @@ -17,8 +22,7 @@ jobs: - stable - beta - nightly - - 1.57 - os: [ubuntu-18.04] + os: [ubuntu-latest] # but only stable on macos/windows (slower platforms) include: - os: macos-latest @@ -27,66 +31,110 @@ jobs: rust: stable steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - override: true + + - name: Install NASM for aws-lc-rs on Windows + if: runner.os == 'Windows' + uses: ilammy/setup-nasm@v1 + + - name: Install ninja-build tool for aws-lc-fips-sys on Windows + if: runner.os == 'Windows' + uses: seanmiddleditch/gha-setup-ninja@v6 - name: cargo check (default features) - run: cargo check --all-targets + run: cargo check --locked --all-targets - name: cargo test (debug; default features) - run: cargo test + run: cargo test --locked env: RUST_BACKTRACE: 1 - name: cargo test (debug; native-tokio only) - run: cargo test --no-default-features --features native-tokio + run: cargo test --locked --no-default-features --features native-tokio env: RUST_BACKTRACE: 1 - name: cargo test (debug; webpki-tokio only) - run: cargo test --no-default-features --features webpki-tokio + run: cargo test --locked --no-default-features --features webpki-tokio + env: + RUST_BACKTRACE: 1 + + - name: cargo test (debug; defaults+ring) + run: cargo test --locked --no-default-features --features ring,native-tokio,http1,tls12,logging env: RUST_BACKTRACE: 1 - name: cargo test (debug; all features) - run: cargo test --all-features + if: runner.os == 'Linux' + run: cargo test --locked --all-features + env: + RUST_BACKTRACE: 1 + + - name: cargo test (debug; all features, excluding FIPS) + if: runner.os != 'Linux' + run: cargo test --locked --features aws-lc-rs,http1,http2,webpki-tokio,native-tokio,ring,tls12,logging env: RUST_BACKTRACE: 1 - name: cargo build (debug; no default features) - run: cargo build --no-default-features + run: cargo build --locked --no-default-features - name: cargo test (debug; no default features; no run) - run: cargo test --no-default-features --no-run + run: cargo test --locked --no-default-features --no-run - name: cargo test (release; no run) - run: cargo test --release --no-run + run: cargo test --locked --release --no-run + + msrv: + 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: "1.71" + + - name: Check MSRV + run: cargo check --lib --locked --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 docs: name: Check for documentation errors - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - default: true + uses: dtolnay/rust-toolchain@nightly - name: cargo doc (all features) - run: cargo doc --all-features --no-deps + # keep features in sync with Cargo.toml `[package.metadata.docs.rs]` section + run: cargo doc --locked --no-default-features --features http1,http2,webpki-tokio,native-tokio,ring,tls12,logging --no-deps env: RUSTDOCFLAGS: -Dwarnings @@ -95,38 +143,40 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - default: true components: rustfmt - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - default: true components: clippy - - uses: actions-rs/cargo@v1 + - run: cargo clippy --locked --all-features -- --deny warnings + + features: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 with: - command: clippy - args: --all-features -- -D warnings + persist-credentials: false + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: Check feature powerset + run: cargo hack --no-dev-deps check --feature-powerset --depth 2 diff --git a/.gitignore b/.gitignore index 1e7caa9..01de0fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -Cargo.lock target/ +/.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b33dd0d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1197 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "aws-lc-fips-sys" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57900537c00a0565a35b63c4c281b372edfc9744b072fd4a3b414350a8f5ed48" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "regex", +] + +[[package]] +name = "aws-lc-rs" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +dependencies = [ + "aws-lc-fips-sys", + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +dependencies = [ + "cfg-if", + "http", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/Cargo.toml b/Cargo.toml index 7f74f61..1bc51a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,41 +1,48 @@ [package] name = "hyper-rustls" -version = "0.23.2" +version = "0.27.7" edition = "2021" -rust-version = "1.57" -license = "Apache-2.0/ISC/MIT" +rust-version = "1.71" +license = "Apache-2.0 OR ISC OR MIT" readme = "README.md" description = "Rustls+hyper integration for pure rust HTTPS" -homepage = "https://github.com/ctz/hyper-rustls" -repository = "https://github.com/ctz/hyper-rustls" +homepage = "https://github.com/rustls/hyper-rustls" +repository = "https://github.com/rustls/hyper-rustls" +documentation = "https://docs.rs/hyper-rustls/" +include = ["Cargo.toml", "LICENSE-MIT", "LICENSE-APACHE", "LICENSE-ICS", "README.md", "src/**/*.rs"] + +[features] +default = ["native-tokio", "http1", "tls12", "logging", "aws-lc-rs"] +aws-lc-rs = ["rustls/aws_lc_rs"] +fips = ["aws-lc-rs", "rustls/fips"] +http1 = ["hyper-util/http1"] +http2 = ["hyper-util/http2"] +logging = ["log", "tokio-rustls/logging", "rustls/logging"] +native-tokio = ["rustls-native-certs"] +ring = ["rustls/ring"] +tls12 = ["tokio-rustls/tls12", "rustls/tls12"] +webpki-tokio = ["webpki-roots"] [dependencies] -http = "0.2" -hyper = { version = "0.14", default-features = false, features = ["client"] } +http = "1" +hyper = { version = "1", default-features = false } +hyper-util = { version = "0.1", default-features = false, features = ["client-legacy", "tokio"] } log = { version = "0.4.4", optional = true } -rustls-native-certs = { version = "0.6", optional = true } -rustls = { version = "0.20.1", default-features = false } +rustls-native-certs = { version = "0.8", optional = true } +rustls-platform-verifier = { version = "0.6", optional = true } +rustls = { version = "0.23", default-features = false } tokio = "1.0" -tokio-rustls = { version = "0.23", default-features = false } -webpki-roots = { version = "0.22", optional = true } +tokio-rustls = { version = "0.26", default-features = false } +tower-service = "0.3" +webpki-roots = { version = "1", optional = true } [dev-dependencies] -futures-util = { version = "0.3.1", default-features = false } -hyper = { version = "0.14", features = ["full"] } -rustls = { version = "0.20.1", default-features = false, features = ["tls12"] } -rustls-pemfile = "1.0.0" +cfg-if = "1" +http-body-util = "0.1" +hyper-util = { version = "0.1", default-features = false, features = ["server-auto"] } +rustls = { version = "0.23", default-features = false, features = ["tls12"] } tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] } -[features] -default = ["native-tokio", "http1", "tls12", "logging"] -http1 = ["hyper/http1"] -http2 = ["hyper/http2"] -webpki-tokio = ["tokio-runtime", "webpki-roots"] -native-tokio = ["tokio-runtime", "rustls-native-certs"] -tokio-runtime = ["hyper/runtime"] -tls12 = ["tokio-rustls/tls12", "rustls/tls12"] -logging = ["log", "tokio-rustls/logging", "rustls/logging"] - [[example]] name = "client" path = "examples/client.rs" @@ -44,8 +51,18 @@ required-features = ["native-tokio", "http1"] [[example]] name = "server" path = "examples/server.rs" -required-features = ["tokio-runtime"] +required-features = ["aws-lc-rs"] [package.metadata.docs.rs] -all-features = true +no-default-features = true +features = [ + "http1", + "http2", + "logging", + "native-tokio", + "ring", + "rustls-platform-verifier", + "tls12", + "webpki-tokio", +] rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index aaabd9f..047c19e 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,64 @@ # hyper-rustls -This is an integration between the [rustls TLS stack](https://github.com/ctz/rustls) -and the [hyper HTTP library](https://github.com/hyperium/hyper). -[![Build Status](https://github.com/ctz/hyper-rustls/workflows/hyper-rustls/badge.svg)](https://github.com/ctz/hyper-rustls/actions) +This is an integration between the [Rustls TLS stack](https://github.com/rustls/rustls) and the +[hyper HTTP library](https://github.com/hyperium/hyper). + +[![Build Status](https://github.com/rustls/hyper-rustls/actions/workflows/build.yml/badge.svg)](https://github.com/rustls/hyper-rustls/actions) [![Crate](https://img.shields.io/crates/v/hyper-rustls.svg)](https://crates.io/crates/hyper-rustls) -[![Documentation](https://docs.rs/hyper-rustls/badge.svg)](https://docs.rs/hyper-rustls/) +[![Documentation](https://docs.rs/hyper-rustls/badge.svg)](https://docs.rs/hyper-rustls) # Release history -- 0.23.2 (2022-12-08): - * Strip brackets from IPv6 addresses in the servername. Thanks to @digitwolf. -- 0.23.1 (2022-10-26): - * Allow overriding the servername. Thanks to @MikailBag. -- 0.23.0 (2021-11-21): - * Upgrade to rustls 0.20. Thanks to @g2p. - * Add new HttpsConnectorBuilder API. Thanks to @g2p. - * Add the tls12, logging, http1 and http2 features. Thanks to @g2p and @marwes. -- 0.22.1 (2020-12-27): - * Fixing docs.rs build; no other changes. -- 0.22.0 (2020-12-26): - * Use tokio 1.0, hyper 0.14, and rustls 0.19. Thanks to @paolobarbolini and @messense. - * Rework how the certificate store is chosen: now by an explicit API rather than - implicitly by crate features. Thanks to @djc. -- 0.21.0 (2020-07-05): - * Update dependencies. -- 0.20.0 (2020-02-24): - * Use newer rustls-native-certs which works in presence of invalid certificates. - * Update dependencies. -- 0.19.1 (2020-01-19): - * Remove dependency on hyper's tcp feature. -- 0.19.0 (2019-12-17): - * First release with async/await support. Many thanks to @CryZe, @alex, @markuskobler and @dbcfd. -- 0.18.0 (2019-11-23) - * Uses [rustls-native-certs](https://crates.io/crates/rustls-native-certs) - instead of compiled-in root certificates. -- 0.17.1 (2019-08-19) - * Fix accidental use of sync read/write. -- 0.17.0 (2019-08-11) - * Update dependencies. + +Release history can be found [on GitHub](https://github.com/rustls/hyper-rustls/releases). # License + hyper-rustls is distributed under the following three licenses: - Apache License version 2.0. - MIT license. - ISC license. -These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC -respectively. You may use this software under the terms of any -of these licenses, at your option. +These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this +software under the terms of any of these licenses, at your option. + +## Running examples + +### server + +```bash +cargo run --example server +``` + +### client + +```bash +cargo run --example client "https://docs.rs/hyper-rustls/latest/hyper_rustls/" +``` + +## Crate features + +This crate exposes a number of features to add support for different portions of `hyper-util`, +`rustls`, and other dependencies. + +| Feature flag | Enabled by default | Description | +| ------------ | ------------------ | ----------- | +| `aws-lc-rs` | **yes** | Enables use of the [AWS-LC][aws-lc-rs] backend for [`rustls`][rustls] | +| `http1` | **yes** | Enables HTTP/1 support in [`hyper-util`][hyper-util] | +| `http2` | **no** | Enables HTTP/2 support in [`hyper-util`][hyper-util] | +| `webpki-tokio` | **no** | Uses a compiled-in set of root certificates trusted by Mozilla (via [`webpki-roots`][webpki-roots]) | +| `native-tokio` | **yes** | Use the platform's native certificate store at runtime (via [`rustls-native-certs`][rustls-native-certs]) | +| `rustls-platform-verifier` | **no** | Use the operating system's verifier for certificate verification (via [`rustls-platform-verifier`][rustls-platform-verifier]) | +| `ring` | **no** | Enables use of the [`ring`][ring] backend for [`rustls`][rustls] | +| `tls12` | **yes** | Enables support for TLS 1.2 (only TLS 1.3 supported when disabled) | +| `logging` | **yes** | Enables logging of protocol-level diagnostics and errors via [`log`][log] | +| `fips` | **no** | Enables support for using a FIPS 140-3 compliant backend via AWS-LC (enables `aws-lc-rs` feature) | + +[aws-lc-rs]: https://docs.rs/aws-lc-rs +[rustls]: https://docs.rs/rustls +[hyper-util]: https://docs.rs/hyper-util +[webpki-roots]: https://docs.rs/webpki-roots +[rustls-native-certs]: https://docs.rs/rustls-native-certs +[rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier +[ring]: https://docs.rs/ring +[log]: https://docs.rs/log diff --git a/admin/pipelines/cargo-steps.yml b/admin/pipelines/cargo-steps.yml deleted file mode 100644 index 0a64af8..0000000 --- a/admin/pipelines/cargo-steps.yml +++ /dev/null @@ -1,16 +0,0 @@ -steps: - - script: cargo build - displayName: "cargo build (debug; default features)" - - - script: cargo test - displayName: "cargo test (debug; default features)" - env: { "RUST_BACKTRACE": "1" } - - script: cargo test --no-default-features --features webpki-tokio - displayName: "cargo test (debug; webpi-roots feature)" - env: { "RUST_BACKTRACE": "1" } - - script: cargo test --no-default-features - displayName: "cargo build (debug; no default features)" - env: { "RUST_BACKTRACE": "1" } - - - script: cargo test --release --no-run - displayName: "cargo test (release; no run)" diff --git a/admin/pipelines/clippy.yml b/admin/pipelines/clippy.yml deleted file mode 100644 index c1d9976..0000000 --- a/admin/pipelines/clippy.yml +++ /dev/null @@ -1,17 +0,0 @@ -parameters: - rustup_toolchain: 'stable' - working_directory: './' -jobs: -- job: clippy - pool: - vmImage: ubuntu-16.04 - steps: - - template: rustup.yml - parameters: - rustup_toolchain: ${{ parameters.rustup_toolchain }} - components: - - clippy - - script: | - cargo clippy - workingDirectory: ${{ parameters.working_directory }} - displayName: Run clippy diff --git a/admin/pipelines/rustup.yml b/admin/pipelines/rustup.yml deleted file mode 100644 index 9ba6868..0000000 --- a/admin/pipelines/rustup.yml +++ /dev/null @@ -1,38 +0,0 @@ -parameters: - rustup_toolchain: 'stable' - rustup_target: '' - components: [] - -steps: -# Linux and macOS. -- script: | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none - export PATH=$PATH:$HOME/.cargo/bin - rustup default ${{ parameters.rustup_toolchain }} - rustup update - echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - displayName: Install rust -# Windows. -- script: | - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe -y --default-toolchain none - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustup default ${{ parameters.rustup_toolchain }} - rustup update - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - displayName: Install rust (windows) -# All platforms. -- script: | - rustc -Vv - cargo -V - displayName: Query rust and cargo versions -- ${{ if ne(parameters.rustup_target, '') }}: - - script: | - rustup target add ${{ parameters.rustup_target }} - displayName: Install support for ${{ parameters.rustup_target }} -- ${{ each component in parameters.components }}: - - script: | - rustup component add ${{ component }} - displayName: Install ${{ component }} diff --git a/examples/client.rs b/examples/client.rs index 4659b6d..abbf0f0 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -2,17 +2,22 @@ //! //! First parameter is the mandatory URL to GET. //! Second parameter is an optional path to CA store. -use hyper::{body::to_bytes, client, Body, Uri}; +use http::Uri; +use http_body_util::{BodyExt, Empty}; +use hyper::body::Bytes; use hyper_rustls::ConfigBuilderExt; +use hyper_util::{client::legacy::Client, rt::TokioExecutor}; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::CertificateDer; use rustls::RootCertStore; use std::str::FromStr; -use std::{env, fs, io}; +use std::{env, io}; fn main() { // Send GET request and inspect result, with proper error handling. if let Err(e) = run_client() { - eprintln!("FAILED: {}", e); + eprintln!("FAILED: {e}"); std::process::exit(1); } } @@ -23,44 +28,39 @@ fn error(err: String) -> io::Error { #[tokio::main] async fn run_client() -> io::Result<()> { + // Set a process wide default crypto provider. + #[cfg(feature = "ring")] + let _ = rustls::crypto::ring::default_provider().install_default(); + #[cfg(feature = "aws-lc-rs")] + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + // First parameter is target URL (mandatory). let url = match env::args().nth(1) { - Some(ref url) => Uri::from_str(url).map_err(|e| error(format!("{}", e)))?, + Some(ref url) => Uri::from_str(url).map_err(|e| error(format!("{e}")))?, None => { println!("Usage: client "); return Ok(()); } }; - // Second parameter is custom Root-CA store (optional, defaults to native cert store). - let mut ca = match env::args().nth(2) { - Some(ref path) => { - let f = fs::File::open(path) - .map_err(|e| error(format!("failed to open {}: {}", path, e)))?; - let rd = io::BufReader::new(f); - Some(rd) - } - None => None, - }; - // Prepare the TLS client config - let tls = match ca { - Some(ref mut rd) => { + let tls = match env::args().nth(2) { + Some(path) => { // Read trust roots - let certs = rustls_pemfile::certs(rd) - .map_err(|_| error("failed to load custom CA store".into()))?; + let certs = CertificateDer::pem_file_iter(&path) + .and_then(|res| res.collect::, _>>()) + .map_err(|err| error(format!("could not read CA store {path}: {err}")))?; + let mut roots = RootCertStore::empty(); - roots.add_parsable_certificates(&certs); + roots.add_parsable_certificates(certs); // TLS client config using the custom CA store for lookups rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth() } // Default TLS client config with native roots None => rustls::ClientConfig::builder() - .with_safe_defaults() - .with_native_roots() + .with_native_roots()? .with_no_client_auth(), }; // Prepare the HTTPS connector @@ -71,7 +71,7 @@ async fn run_client() -> io::Result<()> { .build(); // Build the hyper client from the HTTPS connector. - let client: client::Client<_, hyper::Body> = client::Client::builder().build(https); + let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); // Prepare a chain of futures which sends a GET request, inspects // the returned headers, collects the whole body and prints it to @@ -80,14 +80,16 @@ async fn run_client() -> io::Result<()> { let res = client .get(url) .await - .map_err(|e| error(format!("Could not get: {:?}", e)))?; + .map_err(|e| error(format!("Could not get: {e:?}")))?; println!("Status:\n{}", res.status()); println!("Headers:\n{:#?}", res.headers()); - let body: Body = res.into_body(); - let body = to_bytes(body) + let body = res + .into_body() + .collect() .await - .map_err(|e| error(format!("Could not get body: {:?}", e)))?; + .map_err(|e| error(format!("Could not get body: {e:?}")))? + .to_bytes(); println!("Body:\n{}", String::from_utf8_lossy(&body)); Ok(()) diff --git a/examples/openssl.cnf b/examples/openssl.cnf new file mode 100644 index 0000000..cda95b5 --- /dev/null +++ b/examples/openssl.cnf @@ -0,0 +1,25 @@ + +[ v3_end ] +basicConstraints = critical,CA:false +keyUsage = nonRepudiation, digitalSignature +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +subjectAltName = @alt_names + +[ v3_client ] +basicConstraints = critical,CA:false +keyUsage = nonRepudiation, digitalSignature +extendedKeyUsage = critical, clientAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +[ v3_inter ] +subjectKeyIdentifier = hash +extendedKeyUsage = critical, serverAuth, clientAuth +basicConstraints = CA:true +keyUsage = cRLSign, keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign + +[ alt_names ] +DNS.1 = testserver.com +DNS.2 = second.testserver.com +DNS.3 = localhost diff --git a/examples/refresh-certificates.sh b/examples/refresh-certificates.sh new file mode 100755 index 0000000..db98af7 --- /dev/null +++ b/examples/refresh-certificates.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +set -xe + +openssl req -nodes \ + -x509 \ + -days 3650 \ + -newkey rsa:4096 \ + -keyout ca.key \ + -out ca.cert \ + -sha256 \ + -batch \ + -subj "/CN=ponytown RSA CA" + +openssl req -nodes \ + -newkey rsa:3072 \ + -keyout inter.key \ + -out inter.req \ + -sha256 \ + -batch \ + -subj "/CN=ponytown RSA level 2 intermediate" + +openssl req -nodes \ + -newkey rsa:2048 \ + -keyout end.key \ + -out end.req \ + -sha256 \ + -batch \ + -subj "/CN=testserver.com" + +openssl rsa \ + -in end.key \ + -out sample.rsa + +openssl x509 -req \ + -in inter.req \ + -out inter.cert \ + -CA ca.cert \ + -CAkey ca.key \ + -sha256 \ + -days 3650 \ + -set_serial 123 \ + -extensions v3_inter -extfile openssl.cnf + +openssl x509 -req \ + -in end.req \ + -out end.cert \ + -CA inter.cert \ + -CAkey inter.key \ + -sha256 \ + -days 2000 \ + -set_serial 456 \ + -extensions v3_end -extfile openssl.cnf + +cat end.cert inter.cert ca.cert > sample.pem +rm *.key *.cert *.req diff --git a/examples/sample.pem b/examples/sample.pem index f2e9d92..50b24b4 100644 --- a/examples/sample.pem +++ b/examples/sample.pem @@ -1,113 +1,79 @@ -----BEGIN CERTIFICATE----- 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 -FZygs8miAhWPzqnpmgTj1cPiU1M= +MA0GCSqGSIb3DQEBCwUAA4IBgQBqKNIM/JBGRmGEopm5/WNKV8UoxKPA+2jR020t +RumXMAnJEfhsivF+Zw/rDmSDpmts/3cIlesKi47f13q4Mfj1QytQUDrsuQEyRTrV +Go6BOQQ4dkS+IqnIfSuue70wpvrZHhRHNFdFt9qM5wCLQokXlP988sEWUmyPPCbO +1BEpwWcP1kx+PdY8NKOhMnfq2RfluI/m4MA4NxJqAWajAhIbDNbvP8Ov4a71HPa6 +b1q9qIQE1ut8KycTrm9K32bVbvMHvR/TPUue8W0VvV2rWTGol5TSNgEQb9w6Kyf7 +N5HlRl9kZB4K8ckWH/JVn0pYNBQPgwbcUbJ/jp6w+LHrh+UW75maOY+IGjVICud8 +6Rc5DZZ2+AAbXJQZ1HMPrw9SW/16Eh/A4CIEsvbu9J+7IoSzhgcKFzOCUojzzRSj +iU7w/HsvpltmVCAZcZ/VARFbe1By2wXX2GSw2p2FVC8orXs76QyruPAVgSHCTVes +zzBo6GLScO/3b6uAcPM3MHRGGvE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIGnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 -dG93biBSU0EgQ0EwHhcNMTYwODEzMTYwNzA0WhcNMjYwODExMTYwNzA0WjAsMSow +MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 +dG93biBSU0EgQ0EwHhcNMjIwNzA0MTQzMDU4WhcNMzIwNzAxMTQzMDU4WjAsMSow KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G -CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDIO5ONoB6s6oDayEHmxPGSmpDCfc++ -A5o0aRdIMerPW/KQlr56YggAiPZ/HOVFGP3lIBVQwUXAkyVn6MxKK1IMjVYh7WdP -WzeXgjMwLEL+7r0XXbbo2P2rzC/6tqFvhANA8GiXjTFmOm5h9GS73z78FkxcNgBo -lN1nsZwM5kf+xawusX5YsxjUFkIFTrlHRRl8A407nO3ZGtHzl8et27ooBlNG/Ys0 -fL8kNkyjg/KrowU/qCycSaxL12e066LgINDNdxWWw/jF+dm9gWYrQlRKDWFzw+qG -rnqUMHXwzAw/Qj0UPC1LtCVosysT3KpEPCmYTf1wFrT/TXPXPnYCBlTuo6fOLW0S -YZg5jc94h2JgS4zgCoYghJgnSzxHJ88/SBqEYtxoNbY92mpMHlu8I8XAgRyGjpk/ -wRb5gdsZ1vjwftW+cexyhtQDcdBa3csw064hL6018LgO8hM48EFFE0UcXgLk16Ur -awKj+/QXN2salh85529ysRxY3J0PA269h/cCAwEAAaNeMFwwHQYDVR0OBBYEFJvE -sUi7+D8vp8xcWvnEdVBGkpoWMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF +CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCsTkd2SKiy3yy20lygOhKfOySo3qpq +TZVrpW11vQ58+6EcetXRnzIIK0HyhPmZrv9XKPpQclJvfY9jADNtu2CSj/v15OSB +Love3GzmXSZz2A8QUZBPWx6HczDG1hFGzrCZPKzpeLnFD1LPsKCUkUOHl1acyy24 +DaCacQJPzPQWbMhbGmYRlDNb+2R2K6UKMAEVe4IOTv2aSIKDGLI+xlaBXYAJj48L +//9eNmR3bMP3kkNKOKaaBk8vnYxKpZ+8ZHeHTmYWR9x1ZoMcbA9lKUwRpKAjY5JJ +NVZMDmjlVQVvvBrvhgz/zgXtfuaQCryZ0f1sEY/zXhdealo3fGVomeoniD4XwA1c +oaUFkbo5IM5HU/pXyAGRerDyhYLgRqQZMIRauvKRPN3jLsPOEQ0+gnXUUTr/YGIE +KY3/Axg4P3hzZCFqJ5IgkgWZr/dKr9p/0cxSUGHTVcpEFOlkKIIIdRuR7Ng5sJml +u7PAMWt6T+x02ORs1/WkyP7LyPQmuugYTicCAwEAAaNeMFwwHQYDVR0OBBYEFDMR +UvwxXbYDBCxOdQ9xfBnNWUz0MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC -BAEAAZZhIHjNcLy0oMmi6brkWvaTL42hQPpzgtU8N+N/I8qm+6m3Rgnek8fr1fQX -ndFfrvlbgYUDydlcX7dpy4dJYaNdp+2y1mmor0sSgCuY9jWNpyMQOJ9ihrrIg+7L -CD2Vea/vPl9FAC/R0MOjs1fZ7Pui/ge/dmrF1VrznF3YfjHTbDTQ1rzNHpAhSUb3 -5uLgWttsMHVZevJDUa4PWy3wX7zyJsDPfjhGbVLonTYcLx8RmhliXh9U/v6Tmx/1 -lGtM4ZulO49D1jG0y5fcjFl63QUpaKh+v6AqfGlTfD5opG6WUJmqXClGEscZFj6b -al58GEmnE4aScYCvSFy87fTM/euHqSzIPGS488JwWDCDmGnNmv3x05+4ENo1vktU -xvW/dtFknbtDOP9J3I073lYi5AC4r6mZP0sxy+CbDHYtm7htpR19c2wTMJ01hUo2 -2YHwt412+MlTJjBFCK+PpDEjJ0etJb48lhUXHQJrHC2GL+1zyfA+eoKMMRdGMybm -zwAmqdTwQly1Z6woevtuHJ4fUp3Hv7mK3XtSXQ9kDKpXtcPYbJD2luPM3hDvVsta -SY5SQN1I5lFMDZDoBN8M2suA7RfqleOj6IwNJKS+AbetzMtw4NcNbGJwg6EL5v2n -HbPWoLps8e6MkfqVR9vG8ddz+nBN+QTJvlJv0nDM+z25BlTLrWb2epxyu5vBU5Ln -KE8/objQLSFtN17dnduf11cF08mgq4xaaHOBgvYoI1U/car4f9yZqyZ9+bpjD+4R -/h8N3hlHa7GTnJcrxseZGv/8QtPlJ1SBGlb1/iwCKIv54DLn2r5QEhXML2/SU+RY -LrBJf84TfkTuI4MBluJ+4XfGcLR50s1V7FjIm2lYNzCsvjMvbfcsnZK+krI8Qv4a -0Ivo1mzOBfbk6hpfHjoChvDIM73Rtt44vgdndJWIxvcUxT7dXcAf8ztA62HCicNW -Fh65GboY2VCsdoNfokR5XXJZaEOP9At2wI81XMd8+OvAw01z1aCQGwXHGEFfro9+ -RpYKPpMZn+tH0uzdOgbWruHV+Wm9MAI4TJW1pMjFOPam2YMB58MJ5XyulS15jKWW -JsYBTJ5mRJnJJYBsvGRstGJ+NL3hHND0KFzygREMhKWqDRB6zcyV0cQF59LUp9Ui -CPmLrxBvuJ3twn5dF/H3Df3NpyPYGAVsXRjVi7pyV1Vb3AqgRvDh76Jlq9GxTK5Q -3ld+hPXnSIsyqQukTymBsZCNPso8v8E9goGcfLXsTLuXjXOcanRpcA8I36YeDbI5 -dV+oLt/PGdEX58NyhG2w5pECGNaUnglJQxlzUsE1O2pJl+JlTc0cs4K9VgHi7OPH -TF3D/w2NKHQKH03neMEtR/O9YA== +AgEAYzqmX+cNPgVD2HWgbeimUraTpI9JP5P4TbOHWmaJKecoy3Hwr71xyAOGiVXL +urk1ZZe8n++GwuDEgRajN3HO9LR1Pu9qVIzTYIsz0ORRQHxujnF7CxK/I/vrIgde +pddUdHNS0Y0g8J1emH9BgoD8a2YsGX4iDY4S4hIGBbGvQp9z8U/uG1mViAmlXybM +b8bf0dx0tEFUyu8jsQP6nFLY/HhkEcvU6SnOzZHRsFko6NE44VIsHLd2+LS2LCM/ +NfAoTzgvj41M3zQCZapaHZc9KXfdcCvEFaySKGfEZeQTUR5W0FHsF5I4NLGryf5L +h3ENQ1tgBTO5WnqL/5rbgv6va9VionPM5sbEwAcancejnkVs3NoYPIPPgBFjaFmL +hNTpT9H2owdZvEwNDChVS0b8ukNNd4cERtvy0Ohc3mk0LGN0ABzrud0fIqa51LMh +0N3dkPkiZ4XYk4yLJ5EwCrCNNH50QkGCOWpInKIPeSYcALGgBDbCDLv6rV3oSKrV +tHCZQwXVKKgU4AQu7hlHBwJ61cH44ksydOidW3MNq1kDIp7ST8s7gVrItNgFnG+L +Jpo270riwSUlWDY4hXw5Ff5lE+bWCmFyyOkLevDkD9v8M4HdwEVvafYYwn75fCIS +5OnpSeIB08kKqCtW1WBwki0rYJjWqdzI7Z1MQ/AyScAKiGM= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -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 -uhZuLDgySxlb2xc= +MIIEsDCCApgCCQCfkxy3a+AgNjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9w +b255dG93biBSU0EgQ0EwHhcNMjIwNzA0MTQzMDU3WhcNMzIwNzAxMTQzMDU3WjAa +MRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCj6nW8pnN50UsH2NjL97xZKxlXPe5ptXfvqXczMsw0vB3gI4xJ +Tdmrnqo0K+VOH7vh+UXcOj2ZMY2ou6oDDK5Qpu9bvGPBIJH/rC1Ti2+u5Y4KTIUc +jWAtzQJeFn8+oCMfskpLdtlWLRdAuwqNHjvxXdd2JnsX1Wid85U/rG2SNPLGjJAF +xG7xzZC4VSO2WIXTGRMUkZfFc8fhWMjo3GaeF/qYjzfHDPWN/ll/7vfxyXJO/ohw +FzpJSZtKmI+6PLxqB/oFrKfTDQUGzxjfHp187bI3eyUFMJsp18/tLYkLyxSWIg3o +bq7ZVimHd1UG2Vb5Y+5pZkh22jmJ6bAa/kmNNwbsD+5vJhW1myGhmZSxkreYPWnS +6ELrSMvbXccFfTYmdBlWsZx/zUVUzVCPe9jdJki2VXlicohqtvBQqe6LGGO37vvv +Gwu1yzQ/rJy47rnaao7fSxqM8nsDjNR2Ev1v031QpEMWjfgUW0roW3H58RZSx+kU +gzIS2CjJIqKxCp894FUQbC6r0wwAuKltl3ywz5qWkxY0O9bXS0YdEXiri5pdsWjr +84shVVQwnoVD9539CLSdHZjlOCAzvSWHZH6ta2JZjUfYYz8cLyv2c2+y9BYrlvHw +T7U7BqzngUk72gcRXd5+Onp+16gGxpGJqaxqj94Nh/yTUnr2Jd9YaXeFmQIDAQAB +MA0GCSqGSIb3DQEBCwUAA4ICAQBzIRVRt3Yaw60tpkyz/i1xbKCbtC+HqYTEsXvZ +RvZ5X1qyLAcmu4EW9RHXnlLiawDbES6lCMFfdBUK03Wis7socvoFUCBRW337F4z2 +IivHfIge4u+w5ouUKPzcpj6oeuR06tmNytYbno6l8tXJpm1eeO4KNZ0ZtodmyB5D +yLrplFgxTdGGgyvxt8LoeLwGmPCyVt35x/Mz6x2lcq1+r7QJZ9sENhQYuA8UqHrw +fmNoVIMXMEcPLcWtFl6nKTK9LrqAu1jgTBqGGZKRn5CYBBK3pNEGKiOIsZXDbyFS +F59teFpJjyeJTbUbLxXDa15J6ExkHV9wFLEvfu/nzQzg8D9yzczSdbDkE2rrrL+s +Q/H/pIXO/DesCWQ37VALn3B5gm9UBd5uogbSw8eamiwRFLQ0snP80pJQGJoTNn0P +wrLLUf2gsKC2262igiA+imepm5wxbV9XGVZfHJgxCi5Zqrf6aWnjIqD2YtDvAHhs +V8ZWN3QTjdnEcQbG0544rocoLNX/FzmyDgjfZKY5r6wt+FWNc/R4clkF+KxasxqB +HdBs8j0lGV3ujvNXASLq9HI6VxZayrSfkR73hADCXIM/wzynKwMarvA4SXwYX9Pd +cJ4+FMqrevPpamMHUsNndS0KfDTdjDp+TSBf87yiyRkD1Ri4ePslyfNvRyv3Xs7k +47YFzA== -----END CERTIFICATE----- diff --git a/examples/sample.rsa b/examples/sample.rsa index b13bf5d..aec5347 100644 --- a/examples/sample.rsa +++ b/examples/sample.rsa @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/examples/server.rs b/examples/server.rs index 8e38132..52372b6 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,27 +1,30 @@ -//! Simple HTTPS echo service based on hyper-rustls +//! Simple HTTPS echo service based on hyper_util and rustls //! //! First parameter is the mandatory port to use. //! Certificate and private key are hardcoded to sample files. //! hyper will automatically use HTTP/2 if a client starts talking HTTP/2, //! otherwise HTTP/1.1 will be used. -use core::task::{Context, Poll}; -use futures_util::ready; -use hyper::server::accept::Accept; -use hyper::server::conn::{AddrIncoming, AddrStream}; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Method, Request, Response, Server, StatusCode}; -use std::future::Future; -use std::pin::Pin; + +use std::net::{Ipv4Addr, SocketAddr}; use std::sync::Arc; -use std::vec::Vec; -use std::{env, fs, io, sync}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio_rustls::rustls::ServerConfig; +use std::{env, io}; + +use http::{Method, Request, Response, StatusCode}; +use http_body_util::{BodyExt, Full}; +use hyper::body::{Bytes, Incoming}; +use hyper::service::service_fn; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use hyper_util::server::conn::auto::Builder; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls::ServerConfig; +use tokio::net::TcpListener; +use tokio_rustls::TlsAcceptor; fn main() { // Serve an echo service over HTTPS, with proper error handling. if let Err(e) = run_server() { - eprintln!("FAILED: {}", e); + eprintln!("FAILED: {e}"); std::process::exit(1); } } @@ -32,158 +35,81 @@ fn error(err: String) -> io::Error { #[tokio::main] async fn run_server() -> Result<(), Box> { + // Set a process wide default crypto provider. + #[cfg(feature = "ring")] + let _ = rustls::crypto::ring::default_provider().install_default(); + #[cfg(feature = "aws-lc-rs")] + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + // First parameter is port number (optional, defaults to 1337) let port = match env::args().nth(1) { - Some(ref p) => p.to_owned(), - None => "1337".to_owned(), - }; - let addr = format!("127.0.0.1:{}", port).parse()?; - - // Build TLS configuration. - let tls_cfg = { - // Load public certificate. - let certs = load_certs("examples/sample.pem")?; - // Load private key. - let key = load_private_key("examples/sample.rsa")?; - // Do not use client certificate authentication. - let mut cfg = rustls::ServerConfig::builder() - .with_safe_defaults() - .with_no_client_auth() - .with_single_cert(certs, key) - .map_err(|e| error(format!("{}", e)))?; - // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order. - cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - sync::Arc::new(cfg) + Some(ref p) => p.parse()?, + None => 1337, }; + let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port); - // Create a TCP listener via tokio. - let incoming = AddrIncoming::bind(&addr)?; - let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) }); - let server = Server::builder(TlsAcceptor::new(tls_cfg, incoming)).serve(service); - - // Run the future, keep going until an error occurs. - println!("Starting to serve on https://{}.", addr); - server.await?; - Ok(()) -} - -enum State { - Handshaking(tokio_rustls::Accept), - Streaming(tokio_rustls::server::TlsStream), -} + // Load public certificate. + let certs = CertificateDer::pem_file_iter("examples/sample.pem")? + .collect::, _>>() + .map_err(|e| error(format!("could not read certificate file: {e}")))?; + // Load private key. + let key = PrivateKeyDer::from_pem_file("examples/sample.rsa") + .map_err(|e| error(format!("could not read private key file: {e}")))?; -// tokio_rustls::server::TlsStream doesn't expose constructor methods, -// so we have to TlsAcceptor::accept and handshake to have access to it -// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first -pub struct TlsStream { - state: State, -} + println!("Starting to serve on https://{addr}"); -impl TlsStream { - fn new(stream: AddrStream, config: Arc) -> TlsStream { - let accept = tokio_rustls::TlsAcceptor::from(config).accept(stream); - TlsStream { - state: State::Handshaking(accept), - } - } -} - -impl AsyncRead for TlsStream { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context, - buf: &mut ReadBuf, - ) -> Poll> { - let pin = self.get_mut(); - match pin.state { - State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) { - Ok(mut stream) => { - let result = Pin::new(&mut stream).poll_read(cx, buf); - pin.state = State::Streaming(stream); - result - } - Err(err) => Poll::Ready(Err(err)), - }, - State::Streaming(ref mut stream) => Pin::new(stream).poll_read(cx, buf), - } - } -} + // Create a TCP listener via tokio. + let incoming = TcpListener::bind(&addr).await?; -impl AsyncWrite for TlsStream { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let pin = self.get_mut(); - match pin.state { - State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) { - Ok(mut stream) => { - let result = Pin::new(&mut stream).poll_write(cx, buf); - pin.state = State::Streaming(stream); - result + // Build TLS configuration. + let mut server_config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, key) + .map_err(|e| error(e.to_string()))?; + server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; + let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); + + let service = service_fn(echo); + + loop { + let (tcp_stream, _remote_addr) = incoming.accept().await?; + + let tls_acceptor = tls_acceptor.clone(); + tokio::spawn(async move { + let tls_stream = match tls_acceptor.accept(tcp_stream).await { + Ok(tls_stream) => tls_stream, + Err(err) => { + eprintln!("failed to perform tls handshake: {err:#}"); + return; } - Err(err) => Poll::Ready(Err(err)), - }, - State::Streaming(ref mut stream) => Pin::new(stream).poll_write(cx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state { - State::Handshaking(_) => Poll::Ready(Ok(())), - State::Streaming(ref mut stream) => Pin::new(stream).poll_flush(cx), - } - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state { - State::Handshaking(_) => Poll::Ready(Ok(())), - State::Streaming(ref mut stream) => Pin::new(stream).poll_shutdown(cx), - } - } -} - -pub struct TlsAcceptor { - config: Arc, - incoming: AddrIncoming, -} - -impl TlsAcceptor { - pub fn new(config: Arc, incoming: AddrIncoming) -> TlsAcceptor { - TlsAcceptor { config, incoming } - } -} - -impl Accept for TlsAcceptor { - type Conn = TlsStream; - type Error = io::Error; - - fn poll_accept( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - let pin = self.get_mut(); - match ready!(Pin::new(&mut pin.incoming).poll_accept(cx)) { - Some(Ok(sock)) => Poll::Ready(Some(Ok(TlsStream::new(sock, pin.config.clone())))), - Some(Err(e)) => Poll::Ready(Some(Err(e))), - None => Poll::Ready(None), - } + }; + if let Err(err) = Builder::new(TokioExecutor::new()) + .serve_connection(TokioIo::new(tls_stream), service) + .await + { + eprintln!("failed to serve connection: {err:#}"); + } + }); } } // Custom echo service, handling two different routes and a // catch-all 404 responder. -async fn echo(req: Request) -> Result, hyper::Error> { - let mut response = Response::new(Body::empty()); +async fn echo(req: Request) -> Result>, hyper::Error> { + let mut response = Response::new(Full::default()); match (req.method(), req.uri().path()) { // Help route. (&Method::GET, "/") => { - *response.body_mut() = Body::from("Try POST /echo\n"); + *response.body_mut() = Full::from("Try POST /echo\n"); } // Echo service route. (&Method::POST, "/echo") => { - *response.body_mut() = req.into_body(); + *response.body_mut() = Full::from( + req.into_body() + .collect() + .await? + .to_bytes(), + ); } // Catch-all 404. _ => { @@ -192,36 +118,3 @@ async fn echo(req: Request) -> Result, hyper::Error> { }; Ok(response) } - -// Load public certificate from file. -fn load_certs(filename: &str) -> io::Result> { - // Open certificate file. - let certfile = fs::File::open(filename) - .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; - let mut reader = io::BufReader::new(certfile); - - // Load and return certificate. - let certs = rustls_pemfile::certs(&mut reader) - .map_err(|_| error("failed to load certificate".into()))?; - Ok(certs - .into_iter() - .map(rustls::Certificate) - .collect()) -} - -// Load private key from file. -fn load_private_key(filename: &str) -> io::Result { - // Open keyfile. - let keyfile = fs::File::open(filename) - .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; - let mut reader = io::BufReader::new(keyfile); - - // Load and return a single private key. - let keys = rustls_pemfile::rsa_private_keys(&mut reader) - .map_err(|_| error("failed to load private key".into()))?; - if keys.len() != 1 { - return Err(error("expected a single private key".into())); - } - - Ok(rustls::PrivateKey(keys[0].clone())) -} diff --git a/src/config.rs b/src/config.rs index c7947c0..db911b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,71 +1,134 @@ -use rustls::client::WantsTransparencyPolicyOrClientCert; +#[cfg(feature = "rustls-native-certs")] +use std::io; + +#[cfg(any( + feature = "rustls-platform-verifier", + feature = "rustls-native-certs", + feature = "webpki-roots" +))] +use rustls::client::WantsClientCert; use rustls::{ClientConfig, ConfigBuilder, WantsVerifier}; +#[cfg(feature = "rustls-native-certs")] +use rustls_native_certs::CertificateResult; +#[cfg(feature = "rustls-platform-verifier")] +use rustls_platform_verifier::BuilderVerifierExt; /// Methods for configuring roots /// /// This adds methods (gated by crate features) for easily configuring /// TLS server roots a rustls ClientConfig will trust. -pub trait ConfigBuilderExt { +pub trait ConfigBuilderExt: sealed::Sealed { + /// Use the platform's native verifier to verify server certificates. + /// + /// See the documentation for [rustls-platform-verifier] for more details. + /// + /// # Panics + /// + /// Since 0.27.7, this method will panic if the platform verifier cannot be initialized. + /// Use `try_with_platform_verifier()` instead to handle errors gracefully. + /// + /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier + #[deprecated(since = "0.27.7", note = "use `try_with_platform_verifier` instead")] + #[cfg(feature = "rustls-platform-verifier")] + fn with_platform_verifier(self) -> ConfigBuilder; + + /// Use the platform's native verifier to verify server certificates. + /// + /// See the documentation for [rustls-platform-verifier] for more details. + /// + /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier + #[cfg(feature = "rustls-platform-verifier")] + fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error>; + /// This configures the platform's trusted certs, as implemented by /// rustls-native-certs + /// + /// This will return an error if no valid certs were found. In that case, + /// it's recommended to use `with_webpki_roots`. #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] - fn with_native_roots(self) -> ConfigBuilder; + fn with_native_roots(self) -> Result, io::Error>; /// This configures the webpki roots, which are Mozilla's set of /// trusted roots as packaged by webpki-roots. #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] - fn with_webpki_roots(self) -> ConfigBuilder; + fn with_webpki_roots(self) -> ConfigBuilder; } impl ConfigBuilderExt for ConfigBuilder { + #[cfg(feature = "rustls-platform-verifier")] + fn with_platform_verifier(self) -> ConfigBuilder { + self.try_with_platform_verifier() + .expect("failure to initialize platform verifier") + } + + #[cfg(feature = "rustls-platform-verifier")] + fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error> { + BuilderVerifierExt::with_platform_verifier(self) + } + #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] #[cfg_attr(not(feature = "logging"), allow(unused_variables))] - fn with_native_roots(self) -> ConfigBuilder { + fn with_native_roots(self) -> Result, io::Error> { let mut roots = rustls::RootCertStore::empty(); let mut valid_count = 0; let mut invalid_count = 0; - for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") - { - let cert = rustls::Certificate(cert.0); - match roots.add(&cert) { + let CertificateResult { certs, errors, .. } = rustls_native_certs::load_native_certs(); + if !errors.is_empty() { + crate::log::warn!("native root CA certificate loading errors: {errors:?}"); + } + + if certs.is_empty() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("no native root CA certificates found (errors: {errors:?})"), + )); + } + + for cert in certs { + match roots.add(cert) { Ok(_) => valid_count += 1, Err(err) => { - crate::log::trace!("invalid cert der {:?}", cert.0); - crate::log::debug!("certificate parsing failed: {:?}", err); + crate::log::debug!("certificate parsing failed: {err:?}"); invalid_count += 1 } } } + crate::log::debug!( - "with_native_roots processed {} valid and {} invalid certs", - valid_count, - invalid_count + "with_native_roots processed {valid_count} valid and {invalid_count} invalid certs" ); - assert!(!roots.is_empty(), "no CA certificates found"); + if roots.is_empty() { + crate::log::debug!("no valid native root CA certificates found"); + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("no valid native root CA certificates found ({invalid_count} invalid)"), + ))? + } - self.with_root_certificates(roots) + Ok(self.with_root_certificates(roots)) } #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] - fn with_webpki_roots(self) -> ConfigBuilder { + fn with_webpki_roots(self) -> ConfigBuilder { let mut roots = rustls::RootCertStore::empty(); - roots.add_server_trust_anchors( + roots.extend( webpki_roots::TLS_SERVER_ROOTS - .0 .iter() - .map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }), + .cloned(), ); self.with_root_certificates(roots) } } + +mod sealed { + use super::*; + + pub trait Sealed {} + + impl Sealed for ConfigBuilder {} +} diff --git a/src/connector.rs b/src/connector.rs index f10fbd1..8740b62 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -4,13 +4,17 @@ use std::sync::Arc; use std::task::{Context, Poll}; use std::{fmt, io}; -use hyper::{client::connect::Connection, service::Service, Uri}; -use tokio::io::{AsyncRead, AsyncWrite}; +use http::Uri; +use hyper::rt; +use hyper_util::client::legacy::connect::Connection; +use hyper_util::rt::TokioIo; +use rustls::pki_types::ServerName; use tokio_rustls::TlsConnector; +use tower_service::Service; use crate::stream::MaybeHttpsStream; -pub mod builder; +pub(crate) mod builder; type BoxError = Box; @@ -20,35 +24,46 @@ pub struct HttpsConnector { force_https: bool, http: T, tls_config: Arc, - override_server_name: Option, + server_name_resolver: Arc, } -impl fmt::Debug for HttpsConnector { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("HttpsConnector") - .field("force_https", &self.force_https) - .finish() +impl HttpsConnector { + /// Creates a [`crate::HttpsConnectorBuilder`] to configure a `HttpsConnector`. + /// + /// This is the same as [`crate::HttpsConnectorBuilder::new()`]. + pub fn builder() -> builder::ConnectorBuilder { + builder::ConnectorBuilder::new() } -} -impl From<(H, C)> for HttpsConnector -where - C: Into>, -{ - fn from((http, cfg): (H, C)) -> Self { - HttpsConnector { - force_https: false, + /// Creates a new `HttpsConnector`. + /// + /// The recommended way to create a `HttpsConnector` is to use a [`crate::HttpsConnectorBuilder`]. See [`HttpsConnector::builder()`]. + pub fn new( + http: T, + tls_config: impl Into>, + force_https: bool, + server_name_resolver: Arc, + ) -> Self { + Self { http, - tls_config: cfg.into(), - override_server_name: None, + tls_config: tls_config.into(), + force_https, + server_name_resolver, } } + + /// Force the use of HTTPS when connecting. + /// + /// If a URL is not `https` when connecting, an error is returned. + pub fn enforce_https(&mut self) { + self.force_https = true; + } } impl Service for HttpsConnector where T: Service, - T::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, + T::Response: Connection + rt::Read + rt::Write + Send + Unpin + 'static, T::Future: Send + 'static, T::Error: Into, { @@ -70,62 +85,229 @@ where fn call(&mut self, dst: Uri) -> Self::Future { // dst.scheme() would need to derive Eq to be matchable; // use an if cascade instead - if let Some(sch) = dst.scheme() { - if sch == &http::uri::Scheme::HTTP && !self.force_https { - let connecting_future = self.http.call(dst); - - let f = async move { - let tcp = connecting_future - .await - .map_err(Into::into)?; - - Ok(MaybeHttpsStream::Http(tcp)) - }; - Box::pin(f) - } else if sch == &http::uri::Scheme::HTTPS { - let cfg = self.tls_config.clone(); - let mut hostname = match self.override_server_name.as_deref() { - Some(h) => h, - None => dst.host().unwrap_or_default(), - }; - - // Remove square brackets around IPv6 address. - if let Some(trimmed) = hostname - .strip_prefix('[') - .and_then(|h| h.strip_suffix(']')) - { - hostname = trimmed; - } - - let hostname = match rustls::ServerName::try_from(hostname) { - Ok(dnsname) => dnsname, - Err(_) => { - let err = io::Error::new(io::ErrorKind::Other, "invalid dnsname"); - return Box::pin(async move { Err(Box::new(err).into()) }); - } - }; - let connecting_future = self.http.call(dst); - - let f = async move { - let tcp = connecting_future - .await - .map_err(Into::into)?; - let connector = TlsConnector::from(cfg); - let tls = connector - .connect(hostname, tcp) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - Ok(MaybeHttpsStream::Https(tls)) - }; - Box::pin(f) - } else { - let err = - io::Error::new(io::ErrorKind::Other, format!("Unsupported scheme {}", sch)); - Box::pin(async move { Err(err.into()) }) + match dst.scheme() { + Some(scheme) if scheme == &http::uri::Scheme::HTTP && !self.force_https => { + let future = self.http.call(dst); + return Box::pin(async move { + Ok(MaybeHttpsStream::Http(future.await.map_err(Into::into)?)) + }); + } + Some(scheme) if scheme != &http::uri::Scheme::HTTPS => { + let message = format!("unsupported scheme {scheme}"); + return Box::pin(async move { + Err(io::Error::new(io::ErrorKind::Other, message).into()) + }); + } + Some(_) => {} + None => { + return Box::pin(async move { + Err(io::Error::new(io::ErrorKind::Other, "missing scheme").into()) + }) } - } else { - let err = io::Error::new(io::ErrorKind::Other, "Missing scheme"); - Box::pin(async move { Err(err.into()) }) + }; + + let cfg = self.tls_config.clone(); + let hostname = match self.server_name_resolver.resolve(&dst) { + Ok(hostname) => hostname, + Err(e) => { + return Box::pin(async move { Err(e) }); + } + }; + + let connecting_future = self.http.call(dst); + Box::pin(async move { + let tcp = connecting_future + .await + .map_err(Into::into)?; + Ok(MaybeHttpsStream::Https(TokioIo::new( + TlsConnector::from(cfg) + .connect(hostname, TokioIo::new(tcp)) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?, + ))) + }) + } +} + +impl From<(H, C)> for HttpsConnector +where + C: Into>, +{ + fn from((http, cfg): (H, C)) -> Self { + Self { + force_https: false, + http, + tls_config: cfg.into(), + server_name_resolver: Arc::new(DefaultServerNameResolver::default()), } } } + +impl fmt::Debug for HttpsConnector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("HttpsConnector") + .field("force_https", &self.force_https) + .finish() + } +} + +/// The default server name resolver, which uses the hostname in the URI. +#[derive(Default)] +pub struct DefaultServerNameResolver(()); + +impl ResolveServerName for DefaultServerNameResolver { + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box> { + let mut hostname = uri.host().unwrap_or_default(); + + // Remove square brackets around IPv6 address. + if let Some(trimmed) = hostname + .strip_prefix('[') + .and_then(|h| h.strip_suffix(']')) + { + hostname = trimmed; + } + + ServerName::try_from(hostname.to_string()).map_err(|e| Box::new(e) as _) + } +} + +/// A server name resolver which always returns the same fixed name. +pub struct FixedServerNameResolver { + name: ServerName<'static>, +} + +impl FixedServerNameResolver { + /// Creates a new resolver returning the specified name. + pub fn new(name: ServerName<'static>) -> Self { + Self { name } + } +} + +impl ResolveServerName for FixedServerNameResolver { + fn resolve( + &self, + _: &Uri, + ) -> Result, Box> { + Ok(self.name.clone()) + } +} + +impl ResolveServerName for F +where + F: Fn(&Uri) -> Result, E>, + E: Into>, +{ + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box> { + self(uri).map_err(Into::into) + } +} + +/// A trait implemented by types that can resolve a [`ServerName`] for a request. +pub trait ResolveServerName { + /// Maps a [`Uri`] into a [`ServerName`]. + fn resolve( + &self, + uri: &Uri, + ) -> Result, Box>; +} + +#[cfg(all( + test, + any(feature = "ring", feature = "aws-lc-rs"), + any( + feature = "rustls-native-certs", + feature = "webpki-roots", + feature = "rustls-platform-verifier", + ) +))] +mod tests { + use std::future::poll_fn; + + use http::Uri; + use hyper_util::rt::TokioIo; + use tokio::net::TcpStream; + use tower_service::Service; + + use super::*; + use crate::{ConfigBuilderExt, HttpsConnectorBuilder, MaybeHttpsStream}; + + #[tokio::test] + async fn connects_https() { + connect(Allow::Any, Scheme::Https) + .await + .unwrap(); + } + + #[tokio::test] + async fn connects_http() { + connect(Allow::Any, Scheme::Http) + .await + .unwrap(); + } + + #[tokio::test] + async fn connects_https_only() { + connect(Allow::Https, Scheme::Https) + .await + .unwrap(); + } + + #[tokio::test] + async fn enforces_https_only() { + let message = connect(Allow::Https, Scheme::Http) + .await + .unwrap_err() + .to_string(); + + assert_eq!(message, "unsupported scheme http"); + } + + async fn connect( + allow: Allow, + scheme: Scheme, + ) -> Result>, BoxError> { + let config_builder = rustls::ClientConfig::builder(); + cfg_if::cfg_if! { + if #[cfg(feature = "rustls-platform-verifier")] { + let config_builder = config_builder.try_with_platform_verifier()?; + } else if #[cfg(feature = "rustls-native-certs")] { + let config_builder = config_builder.with_native_roots().unwrap(); + } else if #[cfg(feature = "webpki-roots")] { + let config_builder = config_builder.with_webpki_roots(); + } + } + let config = config_builder.with_no_client_auth(); + + let builder = HttpsConnectorBuilder::new().with_tls_config(config); + let mut service = match allow { + Allow::Https => builder.https_only(), + Allow::Any => builder.https_or_http(), + } + .enable_http1() + .build(); + + poll_fn(|cx| service.poll_ready(cx)).await?; + service + .call(Uri::from_static(match scheme { + Scheme::Https => "https://google.com", + Scheme::Http => "http://google.com", + })) + .await + } + + enum Allow { + Https, + Any, + } + + enum Scheme { + Https, + Http, + } +} diff --git a/src/connector/builder.rs b/src/connector/builder.rs index a73d67c..3937900 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -1,12 +1,23 @@ +use std::sync::Arc; + +use hyper_util::client::legacy::connect::HttpConnector; +#[cfg(any( + feature = "rustls-native-certs", + feature = "rustls-platform-verifier", + feature = "webpki-roots" +))] +use rustls::crypto::CryptoProvider; +use rustls::pki_types::ServerName; use rustls::ClientConfig; -use super::HttpsConnector; -#[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] +use super::{DefaultServerNameResolver, HttpsConnector, ResolveServerName}; +#[cfg(any( + feature = "rustls-native-certs", + feature = "webpki-roots", + feature = "rustls-platform-verifier" +))] use crate::config::ConfigBuilderExt; -#[cfg(feature = "tokio-runtime")] -use hyper::client::HttpConnector; - /// A builder for an [`HttpsConnector`] /// /// This makes configuration flexible and explicit and ensures connector @@ -17,12 +28,15 @@ use hyper::client::HttpConnector; /// ``` /// use hyper_rustls::HttpsConnectorBuilder; /// -/// # #[cfg(all(feature = "webpki-roots", feature = "tokio-runtime", feature = "http1"))] -/// let https = HttpsConnectorBuilder::new() +/// # #[cfg(all(feature = "webpki-roots", feature = "http1", feature="aws-lc-rs"))] +/// # { +/// # let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// let https = HttpsConnectorBuilder::new() /// .with_webpki_roots() /// .https_only() /// .enable_http1() /// .build(); +/// # } /// ``` pub struct ConnectorBuilder(State); @@ -51,39 +65,116 @@ impl ConnectorBuilder { ConnectorBuilder(WantsSchemes { tls_config: config }) } - /// Shorthand for using rustls' [safe defaults][with_safe_defaults] - /// and native roots + /// Shorthand for using rustls' default crypto provider and other defaults, and + /// the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-platform-verifier" + ))] + pub fn with_platform_verifier(self) -> ConnectorBuilder { + self.try_with_platform_verifier() + .expect("failure to initialize platform verifier") + } + + /// Shorthand for using rustls' default crypto provider and other defaults, and + /// the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-platform-verifier" + ))] + pub fn try_with_platform_verifier( + self, + ) -> Result, rustls::Error> { + Ok(self.with_tls_config( + ClientConfig::builder() + .try_with_platform_verifier()? + .with_no_client_auth(), + )) + } + + /// Shorthand for using a custom [`CryptoProvider`] and the platform verifier. + /// + /// See [`ConfigBuilderExt::with_platform_verifier()`]. + #[cfg(feature = "rustls-platform-verifier")] + pub fn with_provider_and_platform_verifier( + self, + provider: impl Into>, + ) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions() + .and_then(|builder| builder.try_with_platform_verifier()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))? + .with_no_client_auth(), + )) + } + + /// Shorthand for using rustls' default crypto provider and safe defaults, with + /// native roots. /// /// See [`ConfigBuilderExt::with_native_roots`] + #[cfg(all( + any(feature = "ring", feature = "aws-lc-rs"), + feature = "rustls-native-certs" + ))] + pub fn with_native_roots(self) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder() + .with_native_roots()? + .with_no_client_auth(), + )) + } + + /// Shorthand for using a custom [`CryptoProvider`] and native roots /// - /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults + /// See [`ConfigBuilderExt::with_native_roots`] #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] - pub fn with_native_roots(self) -> ConnectorBuilder { - self.with_tls_config( - ClientConfig::builder() - .with_safe_defaults() - .with_native_roots() + pub fn with_provider_and_native_roots( + self, + provider: impl Into>, + ) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))? + .with_native_roots()? .with_no_client_auth(), - ) + )) } - /// Shorthand for using rustls' [safe defaults][with_safe_defaults] - /// and Mozilla roots + /// Shorthand for using rustls' default crypto provider and its + /// safe defaults. /// /// See [`ConfigBuilderExt::with_webpki_roots`] - /// - /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults - #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] + #[cfg(all(any(feature = "ring", feature = "aws-lc-rs"), feature = "webpki-roots"))] pub fn with_webpki_roots(self) -> ConnectorBuilder { self.with_tls_config( ClientConfig::builder() - .with_safe_defaults() .with_webpki_roots() .with_no_client_auth(), ) } + + /// Shorthand for using a custom [`CryptoProvider`], Rustls' safe default + /// protocol versions and Mozilla roots + /// + /// See [`ConfigBuilderExt::with_webpki_roots`] + #[cfg(feature = "webpki-roots")] + pub fn with_provider_and_webpki_roots( + self, + provider: impl Into>, + ) -> Result, rustls::Error> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions()? + .with_webpki_roots() + .with_no_client_auth(), + )) + } } impl Default for ConnectorBuilder { @@ -106,7 +197,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: true, - override_server_name: None, + server_name_resolver: None, }) } @@ -118,7 +209,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: false, - override_server_name: None, + server_name_resolver: None, }) } } @@ -130,7 +221,7 @@ impl ConnectorBuilder { pub struct WantsProtocols1 { tls_config: ClientConfig, https_only: bool, - override_server_name: Option, + server_name_resolver: Option>, } impl WantsProtocols1 { @@ -139,11 +230,12 @@ impl WantsProtocols1 { force_https: self.https_only, http: conn, tls_config: std::sync::Arc::new(self.tls_config), - override_server_name: self.override_server_name, + server_name_resolver: self + .server_name_resolver + .unwrap_or_else(|| Arc::new(DefaultServerNameResolver::default())), } } - #[cfg(feature = "tokio-runtime")] fn build(self) -> HttpsConnector { let mut http = HttpConnector::new(); // HttpConnector won't enforce scheme, but HttpsConnector will @@ -165,7 +257,6 @@ impl ConnectorBuilder { /// /// This needs to be called explicitly, no protocol is enabled by default #[cfg(feature = "http2")] - #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn enable_http2(mut self) -> ConnectorBuilder { self.0.tls_config.alpn_protocols = vec![b"h2".to_vec()]; ConnectorBuilder(WantsProtocols3 { @@ -174,6 +265,40 @@ impl ConnectorBuilder { }) } + /// Enable all HTTP versions built into this library (enabled with Cargo features) + /// + /// For now, this could enable both HTTP 1 and 2, depending on active features. + /// In the future, other supported versions will be enabled as well. + #[cfg(feature = "http2")] + pub fn enable_all_versions(mut self) -> ConnectorBuilder { + #[cfg(feature = "http1")] + let alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + #[cfg(not(feature = "http1"))] + let alpn_protocols = vec![b"h2".to_vec()]; + + self.0.tls_config.alpn_protocols = alpn_protocols; + ConnectorBuilder(WantsProtocols3 { + inner: self.0, + enable_http1: cfg!(feature = "http1"), + }) + } + + /// Override server name for the TLS stack + /// + /// By default, for each connection hyper-rustls will extract host portion + /// of the destination URL and verify that server certificate contains + /// this value. + /// + /// If this method is called, hyper-rustls will instead use this resolver + /// to compute the value used to verify the server certificate. + pub fn with_server_name_resolver( + mut self, + resolver: impl ResolveServerName + 'static + Sync + Send, + ) -> Self { + self.0.server_name_resolver = Some(Arc::new(resolver)); + self + } + /// Override server name for the TLS stack /// /// By default, for each connection hyper-rustls will extract host portion @@ -183,9 +308,22 @@ impl ConnectorBuilder { /// If this method is called, hyper-rustls will instead verify that server /// certificate contains `override_server_name`. Domain name included in /// the URL will not affect certificate validation. - pub fn with_server_name(mut self, override_server_name: String) -> Self { - self.0.override_server_name = Some(override_server_name); - self + #[deprecated( + since = "0.27.1", + note = "use Self::with_server_name_resolver with FixedServerNameResolver instead" + )] + pub fn with_server_name(self, mut override_server_name: String) -> Self { + // remove square brackets around IPv6 address. + if let Some(trimmed) = override_server_name + .strip_prefix('[') + .and_then(|s| s.strip_suffix(']')) + { + override_server_name = trimmed.to_string(); + } + + self.with_server_name_resolver(move |_: &_| { + ServerName::try_from(override_server_name.clone()) + }) } } @@ -204,7 +342,6 @@ impl ConnectorBuilder { /// /// This needs to be called explicitly, no protocol is enabled by default #[cfg(feature = "http2")] - #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn enable_http2(mut self) -> ConnectorBuilder { self.0.inner.tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; ConnectorBuilder(WantsProtocols3 { @@ -214,7 +351,6 @@ impl ConnectorBuilder { } /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] - #[cfg(feature = "tokio-runtime")] pub fn build(self) -> HttpsConnector { self.0.inner.build() } @@ -245,7 +381,6 @@ pub struct WantsProtocols3 { #[cfg(feature = "http2")] impl ConnectorBuilder { /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] - #[cfg(feature = "tokio-runtime")] pub fn build(self) -> HttpsConnector { self.0.inner.build() } @@ -261,13 +396,12 @@ impl ConnectorBuilder { #[cfg(test)] mod tests { - use super::ConnectorBuilder as HttpsConnectorBuilder; - // Typical usage #[test] #[cfg(all(feature = "webpki-roots", feature = "http1"))] fn test_builder() { - let _connector = HttpsConnectorBuilder::new() + ensure_global_state(); + let _connector = super::ConnectorBuilder::new() .with_webpki_roots() .https_only() .enable_http1() @@ -278,13 +412,13 @@ mod tests { #[cfg(feature = "http1")] #[should_panic(expected = "ALPN protocols should not be pre-defined")] fn test_reject_predefined_alpn() { + ensure_global_state(); let roots = rustls::RootCertStore::empty(); let mut config_with_alpn = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); config_with_alpn.alpn_protocols = vec![b"fancyprotocol".to_vec()]; - let _connector = HttpsConnectorBuilder::new() + let _connector = super::ConnectorBuilder::new() .with_tls_config(config_with_alpn) .https_only() .enable_http1() @@ -294,12 +428,12 @@ mod tests { #[test] #[cfg(all(feature = "http1", feature = "http2"))] fn test_alpn() { + ensure_global_state(); let roots = rustls::RootCertStore::empty(); let tls_config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); - let connector = HttpsConnectorBuilder::new() + let connector = super::ConnectorBuilder::new() .with_tls_config(tls_config.clone()) .https_only() .enable_http1() @@ -308,14 +442,14 @@ mod tests { .tls_config .alpn_protocols .is_empty()); - let connector = HttpsConnectorBuilder::new() + let connector = super::ConnectorBuilder::new() .with_tls_config(tls_config.clone()) .https_only() .enable_http2() .build(); assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); - let connector = HttpsConnectorBuilder::new() - .with_tls_config(tls_config) + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) .https_only() .enable_http1() .enable_http2() @@ -324,5 +458,43 @@ mod tests { &connector.tls_config.alpn_protocols, &[b"h2".to_vec(), b"http/1.1".to_vec()] ); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_all_versions() + .build(); + assert_eq!( + &connector.tls_config.alpn_protocols, + &[b"h2".to_vec(), b"http/1.1".to_vec()] + ); + } + + #[test] + #[cfg(all(not(feature = "http1"), feature = "http2"))] + fn test_alpn_http2() { + let roots = rustls::RootCertStore::empty(); + let tls_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http2() + .build(); + assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_all_versions() + .build(); + assert_eq!(&connector.tls_config.alpn_protocols, &[b"h2".to_vec()]); + } + + fn ensure_global_state() { + #[cfg(feature = "ring")] + let _ = rustls::crypto::ring::default_provider().install_default(); + #[cfg(feature = "aws-lc-rs")] + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); } } diff --git a/src/lib.rs b/src/lib.rs index 9be896e..89e355a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,34 +1,39 @@ //! # hyper-rustls //! //! A pure-Rust HTTPS connector for [hyper](https://hyper.rs), based on -//! [Rustls](https://github.com/ctz/rustls). +//! [Rustls](https://github.com/rustls/rustls). //! -//! ## Example +//! ## Example client //! //! ```no_run -//! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1"))] +//! # #[cfg(all(feature = "rustls-native-certs", feature = "http1"))] //! # fn main() { -//! use hyper::{Body, Client, StatusCode, Uri}; +//! use http::StatusCode; +//! use http_body_util::Empty; +//! use hyper::body::Bytes; +//! use hyper_util::client::legacy::Client; +//! use hyper_util::rt::TokioExecutor; //! //! let mut rt = tokio::runtime::Runtime::new().unwrap(); //! let url = ("https://hyper.rs").parse().unwrap(); //! let https = hyper_rustls::HttpsConnectorBuilder::new() //! .with_native_roots() +//! .expect("no native root CA certificates found") //! .https_only() //! .enable_http1() //! .build(); //! -//! let client: Client<_, hyper::Body> = Client::builder().build(https); +//! let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); //! //! let res = rt.block_on(client.get(url)).unwrap(); //! assert_eq!(res.status(), StatusCode::OK); //! # } -//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1")))] +//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "http1")))] //! # fn main() {} //! ``` -#![warn(missing_docs)] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs, unreachable_pub, clippy::use_self)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod config; mod connector; @@ -36,19 +41,29 @@ mod stream; #[cfg(feature = "logging")] mod log { - pub use log::{debug, trace}; + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use log::debug; + #[cfg(feature = "rustls-native-certs")] + pub(crate) use log::warn; } #[cfg(not(feature = "logging"))] mod log { - macro_rules! trace ( ($($tt:tt)*) => {{}} ); + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] macro_rules! debug ( ($($tt:tt)*) => {{}} ); - pub(crate) use {debug, trace}; + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use debug; + #[cfg(feature = "rustls-native-certs")] + macro_rules! warn_ ( ($($tt:tt)*) => {{}} ); + #[cfg(feature = "rustls-native-certs")] + pub(crate) use warn_ as warn; } pub use crate::config::ConfigBuilderExt; pub use crate::connector::builder::ConnectorBuilder as HttpsConnectorBuilder; -pub use crate::connector::HttpsConnector; +pub use crate::connector::{ + DefaultServerNameResolver, FixedServerNameResolver, HttpsConnector, ResolveServerName, +}; pub use crate::stream::MaybeHttpsStream; /// The various states of the [`HttpsConnectorBuilder`] diff --git a/src/stream.rs b/src/stream.rs index 64ddc48..f08e7b1 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,9 +4,10 @@ use std::io; use std::pin::Pin; use std::task::{Context, Poll}; -use hyper::client::connect::{Connected, Connection}; +use hyper::rt; +use hyper_util::client::legacy::connect::{Connected, Connection}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use hyper_util::rt::TokioIo; use tokio_rustls::client::TlsStream; /// A stream that might be protected with TLS. @@ -15,19 +16,19 @@ pub enum MaybeHttpsStream { /// A stream over plain text. Http(T), /// A stream protected with TLS. - Https(TlsStream), + Https(TokioIo>>), } -impl Connection for MaybeHttpsStream { +impl Connection for MaybeHttpsStream { fn connected(&self) -> Connected { match self { - MaybeHttpsStream::Http(s) => s.connected(), - MaybeHttpsStream::Https(s) => { - let (tcp, tls) = s.get_ref(); + Self::Http(s) => s.connected(), + Self::Https(s) => { + let (tcp, tls) = s.inner().get_ref(); if tls.alpn_protocol() == Some(b"h2") { - tcp.connected().negotiated_h2() + tcp.inner().connected().negotiated_h2() } else { - tcp.connected() + tcp.inner().connected() } } } @@ -37,39 +38,39 @@ impl Connection for MaybeHttpsSt impl fmt::Debug for MaybeHttpsStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - MaybeHttpsStream::Http(..) => f.pad("Http(..)"), - MaybeHttpsStream::Https(..) => f.pad("Https(..)"), + Self::Http(..) => f.pad("Http(..)"), + Self::Https(..) => f.pad("Https(..)"), } } } impl From for MaybeHttpsStream { fn from(inner: T) -> Self { - MaybeHttpsStream::Http(inner) + Self::Http(inner) } } -impl From> for MaybeHttpsStream { - fn from(inner: TlsStream) -> Self { - MaybeHttpsStream::Https(inner) +impl From>> for MaybeHttpsStream { + fn from(inner: TlsStream>) -> Self { + Self::Https(TokioIo::new(inner)) } } -impl AsyncRead for MaybeHttpsStream { +impl rt::Read for MaybeHttpsStream { #[inline] fn poll_read( self: Pin<&mut Self>, cx: &mut Context, - buf: &mut ReadBuf<'_>, + buf: rt::ReadBufCursor<'_>, ) -> Poll> { match Pin::get_mut(self) { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(cx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(cx, buf), + Self::Http(s) => Pin::new(s).poll_read(cx, buf), + Self::Https(s) => Pin::new(s).poll_read(cx, buf), } } } -impl AsyncWrite for MaybeHttpsStream { +impl rt::Write for MaybeHttpsStream { #[inline] fn poll_write( self: Pin<&mut Self>, @@ -77,24 +78,44 @@ impl AsyncWrite for MaybeHttpsStream { buf: &[u8], ) -> Poll> { match Pin::get_mut(self) { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(cx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(cx, buf), + Self::Http(s) => Pin::new(s).poll_write(cx, buf), + Self::Https(s) => Pin::new(s).poll_write(cx, buf), } } #[inline] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match Pin::get_mut(self) { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(cx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(cx), + Self::Http(s) => Pin::new(s).poll_flush(cx), + Self::Https(s) => Pin::new(s).poll_flush(cx), } } #[inline] fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match Pin::get_mut(self) { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(cx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(cx), + Self::Http(s) => Pin::new(s).poll_shutdown(cx), + Self::Https(s) => Pin::new(s).poll_shutdown(cx), + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + match self { + Self::Http(s) => s.is_write_vectored(), + Self::Https(s) => s.is_write_vectored(), + } + } + + #[inline] + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[io::IoSlice<'_>], + ) -> Poll> { + match Pin::get_mut(self) { + Self::Http(s) => Pin::new(s).poll_write_vectored(cx, bufs), + Self::Https(s) => Pin::new(s).poll_write_vectored(cx, bufs), } } } diff --git a/tests/tests.rs b/tests/tests.rs index f08b7cb..9e59038 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,4 +1,5 @@ use std::env; +use std::net::TcpStream; use std::path::PathBuf; use std::process::Command; use std::thread; @@ -21,6 +22,16 @@ fn client_command() -> Command { Command::new(examples_dir().join("client")) } +fn wait_for_server(addr: &str) { + for i in 0..10 { + if TcpStream::connect(addr).is_ok() { + return; + } + thread::sleep(time::Duration::from_millis(i * 100)); + } + panic!("failed to connect to {addr:?} after 10 tries"); +} + #[test] fn client() { let rc = client_command() @@ -38,20 +49,31 @@ fn server() { .spawn() .expect("cannot run server example"); - thread::sleep(time::Duration::from_secs(1)); + let addr = "localhost:1337"; + wait_for_server(addr); let output = Command::new("curl") .arg("--insecure") .arg("--http1.0") - .arg("--silent") - .arg("https://localhost:1337") + .arg(format!("https://{addr}")) .output() .expect("cannot run curl"); - println!("client output: {:?}", output.stdout); - assert_eq!(output.stdout, b"Try POST /echo\n"); - srv.kill().unwrap(); + srv.wait() + .expect("failed to wait on server process"); + + if !output.status.success() { + let version_stdout = Command::new("curl") + .arg("--version") + .output() + .expect("cannot run curl to collect --version") + .stdout; + println!("curl version: {}", String::from_utf8_lossy(&version_stdout)); + println!("curl stderr:\n{}", String::from_utf8_lossy(&output.stderr)); + } + + assert_eq!(String::from_utf8_lossy(&output.stdout), "Try POST /echo\n"); } #[test] @@ -61,15 +83,18 @@ fn custom_ca_store() { .spawn() .expect("cannot run server example"); - thread::sleep(time::Duration::from_secs(1)); + let addr = "localhost:1338"; + wait_for_server(addr); let rc = client_command() - .arg("https://localhost:1338") + .arg(format!("https://{addr}")) .arg("examples/sample.pem") .output() .expect("cannot run client example"); srv.kill().unwrap(); + srv.wait() + .expect("failed to wait on server process"); if !rc.status.success() { assert_eq!(String::from_utf8_lossy(&rc.stdout), "");