[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: cargo\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n- package-ecosystem: github-actions\n  directory: \"/\"\n  schedule:\n    interval: weekly\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: rustls\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches: ['main', 'rel-*', 'ci/*']\n    tags: ['**']\n  pull_request:\n  merge_group:\n  schedule:\n    - cron: '23 6 * * 5'\n\njobs:\n  build:\n    name: Build+test\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        # test a bunch of toolchains on ubuntu\n        rust:\n          - stable\n          - beta\n          - nightly\n        os: [ubuntu-latest]\n        # but only stable on macos/windows (slower platforms)\n        include:\n          - os: macos-latest\n            rust: stable\n          - os: windows-latest\n            rust: stable\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install ${{ matrix.rust }} toolchain\n        uses: dtolnay/rust-toolchain@master\n        with:\n          toolchain: ${{ matrix.rust }}\n\n      - name: Install NASM for aws-lc-rs on Windows\n        if: runner.os == 'Windows'\n        uses: ilammy/setup-nasm@v1\n\n      - name: Install ninja-build tool for aws-lc-fips-sys on Windows\n        if: runner.os == 'Windows'\n        uses: seanmiddleditch/gha-setup-ninja@v6\n\n      - name: cargo check (default features)\n        run: cargo check --locked --all-targets\n\n      - name: cargo test (debug; default features)\n        run: cargo test --locked\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo test (debug; native-tokio only)\n        run: cargo test --locked --no-default-features --features native-tokio\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo test (debug; webpki-tokio only)\n        run: cargo test --locked --no-default-features --features webpki-tokio\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo test (debug; defaults+ring)\n        run: cargo test --locked --no-default-features --features ring,native-tokio,http1,tls12,logging\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo test (debug; all features)\n        if: runner.os == 'Linux'\n        run: cargo test --locked --all-features\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo test (debug; all features, excluding FIPS)\n        if: runner.os != 'Linux'\n        run: cargo test --locked --features aws-lc-rs,http1,http2,webpki-tokio,native-tokio,ring,tls12,logging\n        env:\n          RUST_BACKTRACE: 1\n\n      - name: cargo build (debug; no default features)\n        run: cargo build --locked --no-default-features\n\n      - name: cargo test (debug; no default features; no run)\n        run: cargo test --locked --no-default-features --no-run\n\n      - name: cargo test (release; no run)\n        run: cargo test --locked --release --no-run\n\n  msrv:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install rust toolchain\n        uses: dtolnay/rust-toolchain@master\n        with:\n          toolchain: \"1.85\"\n\n      - name: Check MSRV\n        run: cargo check --lib --locked --all-features\n\n  semver:\n    name: Check semver compatibility\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Check semver\n        uses: obi1kenobi/cargo-semver-checks-action@v2\n\n  docs:\n    name: Check for documentation errors\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: Install rust toolchain\n        uses: dtolnay/rust-toolchain@nightly\n\n      - name: cargo doc (all features)\n        # keep features in sync with Cargo.toml `[package.metadata.docs.rs]` section\n        run: cargo doc --locked --no-default-features --features http1,http2,webpki-tokio,native-tokio,ring,tls12,logging --no-deps\n        env:\n          RUSTDOCFLAGS: -Dwarnings\n\n  format:\n    name: Format\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Install rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          components: rustfmt\n      - name: Check formatting\n        run: cargo fmt --all -- --check\n\n  clippy:\n    name: Clippy\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Install rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n      - run: cargo clippy --locked --all-features -- --deny warnings\n\n  features:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n      - name: Install rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n      - name: Install cargo-hack\n        uses: taiki-e/install-action@cargo-hack\n      - name: Check feature powerset\n        run: cargo hack --no-dev-deps check --feature-powerset --depth 2\n"
  },
  {
    "path": ".gitignore",
    "content": "target/\n/.idea\n"
  },
  {
    "path": ".rustfmt.toml",
    "content": "chain_width=40\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"hyper-rustls\"\nversion = \"0.27.10\"\nedition = \"2021\"\nrust-version = \"1.85\"\nlicense = \"Apache-2.0 OR ISC OR MIT\"\nreadme = \"README.md\"\ndescription = \"Rustls+hyper integration for pure rust HTTPS\"\nhomepage = \"https://github.com/rustls/hyper-rustls\"\nrepository = \"https://github.com/rustls/hyper-rustls\"\ndocumentation = \"https://docs.rs/hyper-rustls/\"\ninclude = [\"Cargo.toml\", \"LICENSE-MIT\", \"LICENSE-APACHE\", \"LICENSE-ISC\", \"README.md\", \"src/**/*.rs\"]\n\n[features]\ndefault = [\"native-tokio\", \"http1\", \"tls12\", \"logging\", \"aws-lc-rs\"]\naws-lc-rs = [\"rustls/aws_lc_rs\"]\nfips = [\"aws-lc-rs\", \"rustls/fips\"]\nhttp1 = [\"hyper-util/http1\"]\nhttp2 = [\"hyper-util/http2\"]\nlogging = [\"log\", \"tokio-rustls/logging\", \"rustls/logging\"]\nnative-tokio = [\"rustls-native-certs\"]\nring = [\"rustls/ring\"]\ntls12 = [\"tokio-rustls/tls12\", \"rustls/tls12\"]\nwebpki-tokio = [\"webpki-roots\"]\n\n[dependencies]\nhttp = \"1\"\nhyper = { version = \"1\", default-features = false }\nhyper-util = { version = \"0.1\", default-features = false, features = [\"client-legacy\", \"tokio\"] }\nlog = { version = \"0.4.4\", optional = true }\nrustls-native-certs = { version = \"0.8\", optional = true }\nrustls-platform-verifier = { version = \"0.7\", optional = true }\nrustls = { version = \"0.23\", default-features = false }\ntokio = \"1.0\"\ntokio-rustls = { version = \"0.26\", default-features = false }\ntower-service = \"0.3\"\nwebpki-roots = { version = \"1\", optional = true }\n\n[dev-dependencies]\ncfg-if = \"1\"\nhttp-body-util = \"0.1\"\nhyper-util = { version = \"0.1\", default-features = false, features = [\"server-auto\"] }\nrustls = { version = \"0.23\", default-features = false, features = [\"tls12\"] }\ntokio = { version = \"1.0\", features = [\"io-std\", \"macros\", \"net\", \"rt-multi-thread\"] }\n\n[[example]]\nname = \"client\"\npath = \"examples/client.rs\"\nrequired-features = [\"native-tokio\", \"http1\"]\n\n[[example]]\nname = \"server\"\npath = \"examples/server.rs\"\nrequired-features = [\"aws-lc-rs\"]\n\n[lints.clippy]\ncloned_instead_of_copied = \"warn\"\nmanual_let_else = \"warn\"\nneedless_collect = \"warn\"\nneedless_pass_by_ref_mut = \"warn\"\nor_fun_call = \"warn\"\nredundant_clone = \"warn\"\nupper_case_acronyms = \"warn\"\nuse_self = \"warn\"\n\n[lints.rust]\nelided_lifetimes_in_paths = \"warn\"\ntrivial_numeric_casts = \"warn\"\nunexpected_cfgs = { level = \"warn\", check-cfg = [\"cfg(hyper_rustls_docsrs)\"] }\nunnameable_types = \"warn\"\nunreachable_pub = \"warn\"\nunused_extern_crates = \"warn\"\nunused_import_braces = \"warn\"\nunused_qualifications = \"warn\"\n\n[package.metadata.docs.rs]\nno-default-features = true\nfeatures = [\n    \"http1\",\n    \"http2\",\n    \"logging\",\n    \"native-tokio\",\n    \"ring\",\n    \"rustls-platform-verifier\",\n    \"tls12\",\n    \"webpki-tokio\",\n]\nrustdoc-args = [\"--cfg\", \"hyper_rustls_docsrs\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "hyper-rustls is distributed under the following three licenses:\n\n- Apache License version 2.0.\n- MIT license.\n- ISC license.\n\nThese are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC\nrespectively.  You may use this software under the terms of any\nof these licenses, at your option.\n\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-ISC",
    "content": "ISC License (ISC)\nCopyright (c) 2016, Joseph Birr-Pixton <jpixton@gmail.com>\n\nPermission to use, copy, modify, and/or distribute this software for\nany purpose with or without fee is hereby granted, provided that the\nabove copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL\nWARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE\nAUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL\nDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR\nPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS\nACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# hyper-rustls\n\nThis is an integration between the [Rustls TLS stack](https://github.com/rustls/rustls) and the\n[hyper HTTP library](https://github.com/hyperium/hyper).\n\n[![Build Status](https://github.com/rustls/hyper-rustls/actions/workflows/build.yml/badge.svg)](https://github.com/rustls/hyper-rustls/actions)\n[![Crate](https://img.shields.io/crates/v/hyper-rustls.svg)](https://crates.io/crates/hyper-rustls)\n[![Documentation](https://docs.rs/hyper-rustls/badge.svg)](https://docs.rs/hyper-rustls)\n\n# Release history\n\nRelease history can be found [on GitHub](https://github.com/rustls/hyper-rustls/releases).\n\n# License\n\nhyper-rustls is distributed under the following three licenses:\n\n- Apache License version 2.0.\n- MIT license.\n- ISC license.\n\nThese are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this\nsoftware under the terms of any of these licenses, at your option.\n\n## Running examples\n\n### server\n\n```bash\ncargo run --example server\n```\n\n### client\n\n```bash\ncargo run --example client \"https://docs.rs/hyper-rustls/latest/hyper_rustls/\"\n```\n\n## Crate features\n\nThis crate exposes a number of features to add support for different portions of `hyper-util`,\n`rustls`, and other dependencies.\n\n| Feature flag | Enabled by default | Description |\n| ------------ | ------------------ | ----------- |\n| `aws-lc-rs`  | **yes** | Enables use of the [AWS-LC][aws-lc-rs] backend for [`rustls`][rustls] |\n| `http1` | **yes** | Enables HTTP/1 support in [`hyper-util`][hyper-util] |\n| `http2` | **no** | Enables HTTP/2 support in [`hyper-util`][hyper-util] |\n| `webpki-tokio` | **no** | Uses a compiled-in set of root certificates trusted by Mozilla (via [`webpki-roots`][webpki-roots]) |\n| `native-tokio` | **yes** | Use the platform's native certificate store at runtime (via [`rustls-native-certs`][rustls-native-certs]) |\n| `rustls-platform-verifier` | **no** | Use the operating system's verifier for certificate verification (via [`rustls-platform-verifier`][rustls-platform-verifier]) |\n| `ring` | **no** | Enables use of the [`ring`][ring] backend for [`rustls`][rustls] |\n| `tls12` | **yes** | Enables support for TLS 1.2 (only TLS 1.3 supported when disabled) |\n| `logging` | **yes** | Enables logging of protocol-level diagnostics and errors via [`log`][log] |\n| `fips` | **no** | Enables support for using a FIPS 140-3 compliant backend via AWS-LC (enables `aws-lc-rs` feature) |\n\n[aws-lc-rs]: https://docs.rs/aws-lc-rs\n[rustls]: https://docs.rs/rustls\n[hyper-util]: https://docs.rs/hyper-util\n[webpki-roots]: https://docs.rs/webpki-roots\n[rustls-native-certs]: https://docs.rs/rustls-native-certs\n[rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier\n[ring]: https://docs.rs/ring\n[log]: https://docs.rs/log\n"
  },
  {
    "path": "RELEASING.md",
    "content": "# Making a hyper-rustls release\n\nThis is a checklist for steps to make before/after making a rustls release.\n\n1. Attend to the README.md: this appears on crates.io for the release, and can't be edited after\n   the fact.\n   - Ensure the version has a good set of release notes.  Move old release notes to OLDCHANGES.md\n     if this is getting excessively long.\n   - Write the version and date of the release.\n2. Run `cargo update` followed by `cargo outdated`, to check if we have any\n   dependency updates which are not already automatically taken by their semver specs.\n   - If we do, take them if possible with separate commits (but there should've been\n     dependabot PRs submitted for these already.)\n3. Now run `cargo test --all-features` to ensure our tests continue to pass with the\n   updated dependencies.\n4. Update `Cargo.toml` to set the correct version.\n5. Make a commit with the above changes, something like 'Prepare $VERSION'.  This\n   should not contain functional changes: just versions numbers, and markdown changes.\n6. Do a dry run: check `cargo publish --dry-run`\n7. Push the above commit.  Wait for CI to confirm it as green.\n   - Any red _should_ naturally block the release.\n   - If rustc nightly is broken, this _may_ be acceptable if the reason is understood\n     and does not point to a defect.\n8. Tag the released version: `git tag -m '0.20.0' v/0.20.0`\n9. Push the tag: `git push --tags`\n10. Do the release: `cargo publish`.\n"
  },
  {
    "path": "examples/client.rs",
    "content": "//! Simple HTTPS GET client based on hyper-rustls\n//!\n//! First parameter is the mandatory URL to GET.\n//! Second parameter is an optional path to CA store.\nuse http::Uri;\nuse http_body_util::{BodyExt, Empty};\nuse hyper::body::Bytes;\nuse hyper_rustls::ConfigBuilderExt;\nuse hyper_util::{client::legacy::Client, rt::TokioExecutor};\nuse rustls::pki_types::pem::PemObject;\nuse rustls::pki_types::CertificateDer;\nuse rustls::RootCertStore;\n\nuse std::str::FromStr;\nuse std::{env, io};\n\nfn main() {\n    // Send GET request and inspect result, with proper error handling.\n    if let Err(e) = run_client() {\n        eprintln!(\"FAILED: {e}\");\n        std::process::exit(1);\n    }\n}\n\nfn error(err: String) -> io::Error {\n    io::Error::other(err)\n}\n\n#[tokio::main]\nasync fn run_client() -> io::Result<()> {\n    // Set a process wide default crypto provider.\n    #[cfg(feature = \"ring\")]\n    let _ = rustls::crypto::ring::default_provider().install_default();\n    #[cfg(feature = \"aws-lc-rs\")]\n    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();\n\n    // First parameter is target URL (mandatory).\n    let url = match env::args().nth(1) {\n        Some(ref url) => Uri::from_str(url).map_err(|e| error(format!(\"{e}\")))?,\n        None => {\n            println!(\"Usage: client <url> <ca_store>\");\n            return Ok(());\n        }\n    };\n\n    // Prepare the TLS client config\n    let tls = match env::args().nth(2) {\n        Some(path) => {\n            // Read trust roots\n            let certs = CertificateDer::pem_file_iter(&path)\n                .and_then(|res| res.collect::<Result<Vec<_>, _>>())\n                .map_err(|err| error(format!(\"could not read CA store {path}: {err}\")))?;\n\n            let mut roots = RootCertStore::empty();\n            roots.add_parsable_certificates(certs);\n            // TLS client config using the custom CA store for lookups\n            rustls::ClientConfig::builder()\n                .with_root_certificates(roots)\n                .with_no_client_auth()\n        }\n        // Default TLS client config with native roots\n        None => rustls::ClientConfig::builder()\n            .with_native_roots()?\n            .with_no_client_auth(),\n    };\n    // Prepare the HTTPS connector\n    let https = hyper_rustls::HttpsConnectorBuilder::new()\n        .with_tls_config(tls)\n        .https_or_http()\n        .enable_http1()\n        .build();\n\n    // Build the hyper client from the HTTPS connector.\n    let client: Client<_, Empty<Bytes>> = Client::builder(TokioExecutor::new()).build(https);\n\n    // Prepare a chain of futures which sends a GET request, inspects\n    // the returned headers, collects the whole body and prints it to\n    // stdout.\n    let fut = async move {\n        let res = client\n            .get(url)\n            .await\n            .map_err(|e| error(format!(\"Could not get: {e:?}\")))?;\n        println!(\"Status:\\n{}\", res.status());\n        println!(\"Headers:\\n{:#?}\", res.headers());\n\n        let body = res\n            .into_body()\n            .collect()\n            .await\n            .map_err(|e| error(format!(\"Could not get body: {e:?}\")))?\n            .to_bytes();\n        println!(\"Body:\\n{}\", String::from_utf8_lossy(&body));\n\n        Ok(())\n    };\n\n    fut.await\n}\n"
  },
  {
    "path": "examples/openssl.cnf",
    "content": "\n[ v3_end ]\nbasicConstraints = critical,CA:false\nkeyUsage = nonRepudiation, digitalSignature\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer:always\nsubjectAltName = @alt_names\n\n[ v3_client ]\nbasicConstraints = critical,CA:false\nkeyUsage = nonRepudiation, digitalSignature\nextendedKeyUsage = critical, clientAuth\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer:always\n\n[ v3_inter ]\nsubjectKeyIdentifier = hash\nextendedKeyUsage = critical, serverAuth, clientAuth\nbasicConstraints = CA:true\nkeyUsage = cRLSign, keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign\n\n[ alt_names ]\nDNS.1 = testserver.com\nDNS.2 = second.testserver.com\nDNS.3 = localhost\n"
  },
  {
    "path": "examples/refresh-certificates.sh",
    "content": "#!/bin/sh\n\nset -xe\n\nopenssl req -nodes \\\n          -x509 \\\n          -days 3650 \\\n          -newkey rsa:4096 \\\n          -keyout ca.key \\\n          -out ca.cert \\\n          -sha256 \\\n          -batch \\\n          -subj \"/CN=ponytown RSA CA\"\n\nopenssl req -nodes \\\n          -newkey rsa:3072 \\\n          -keyout inter.key \\\n          -out inter.req \\\n          -sha256 \\\n          -batch \\\n          -subj \"/CN=ponytown RSA level 2 intermediate\"\n\nopenssl req -nodes \\\n          -newkey rsa:2048 \\\n          -keyout end.key \\\n          -out end.req \\\n          -sha256 \\\n          -batch \\\n          -subj \"/CN=testserver.com\"\n\nopenssl rsa \\\n          -in end.key \\\n          -out sample.rsa\n\nopenssl x509 -req \\\n            -in inter.req \\\n            -out inter.cert \\\n            -CA ca.cert \\\n            -CAkey ca.key \\\n            -sha256 \\\n            -days 3650 \\\n            -set_serial 123 \\\n            -extensions v3_inter -extfile openssl.cnf\n\nopenssl x509 -req \\\n            -in end.req \\\n            -out end.cert \\\n            -CA inter.cert \\\n            -CAkey inter.key \\\n            -sha256 \\\n            -days 2000 \\\n            -set_serial 456 \\\n            -extensions v3_end -extfile openssl.cnf\n\ncat end.cert inter.cert ca.cert > sample.pem\nrm *.key *.cert *.req\n"
  },
  {
    "path": "examples/sample.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u\neXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTIyMDcwNDE0MzA1OFoX\nDTI3MTIyNTE0MzA1OFowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G\nCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL35qLQLIqswCmHJxyczYF2p0YxXCq\ngMvtRcKVElnifPMFrbGCY1aYBmhIiXPGRwhfythAtYfDQsrXFADZd52JPgZCR/u6\nDQMqKD2lcvFQkf7Kee/fNTOuQTQPh1XQx4ntxvicSATwEnuU28NwVnOU//Zzq2xn\nQ34gUQNHWp1pN+B1La7emm/Ucgs1/2hMxwCZYUnRoiUoRGXUSzZuWokDOstPNkjc\n+AjHmxONgowogmL2jKN9BjBw/8psGoqEOjMO+Lb9iekOCzX4kqHaRUbTlbSAviQu\n2Q115xiZCBCZVtNE6DUG25buvpMSEXwpLd96nLywbrSCyueC7cd01/hpAgMBAAGj\ngb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFHGnzC5Q\nA62Wmv4zfMk/kf/BxHevMEIGA1UdIwQ7MDmAFDMRUvwxXbYDBCxOdQ9xfBnNWUz0\noR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO\ndGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0\nMA0GCSqGSIb3DQEBCwUAA4IBgQBqKNIM/JBGRmGEopm5/WNKV8UoxKPA+2jR020t\nRumXMAnJEfhsivF+Zw/rDmSDpmts/3cIlesKi47f13q4Mfj1QytQUDrsuQEyRTrV\nGo6BOQQ4dkS+IqnIfSuue70wpvrZHhRHNFdFt9qM5wCLQokXlP988sEWUmyPPCbO\n1BEpwWcP1kx+PdY8NKOhMnfq2RfluI/m4MA4NxJqAWajAhIbDNbvP8Ov4a71HPa6\nb1q9qIQE1ut8KycTrm9K32bVbvMHvR/TPUue8W0VvV2rWTGol5TSNgEQb9w6Kyf7\nN5HlRl9kZB4K8ckWH/JVn0pYNBQPgwbcUbJ/jp6w+LHrh+UW75maOY+IGjVICud8\n6Rc5DZZ2+AAbXJQZ1HMPrw9SW/16Eh/A4CIEsvbu9J+7IoSzhgcKFzOCUojzzRSj\niU7w/HsvpltmVCAZcZ/VARFbe1By2wXX2GSw2p2FVC8orXs76QyruPAVgSHCTVes\nzzBo6GLScO/3b6uAcPM3MHRGGvE=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255\ndG93biBSU0EgQ0EwHhcNMjIwNzA0MTQzMDU4WhcNMzIwNzAxMTQzMDU4WjAsMSow\nKAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G\nCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCsTkd2SKiy3yy20lygOhKfOySo3qpq\nTZVrpW11vQ58+6EcetXRnzIIK0HyhPmZrv9XKPpQclJvfY9jADNtu2CSj/v15OSB\nLove3GzmXSZz2A8QUZBPWx6HczDG1hFGzrCZPKzpeLnFD1LPsKCUkUOHl1acyy24\nDaCacQJPzPQWbMhbGmYRlDNb+2R2K6UKMAEVe4IOTv2aSIKDGLI+xlaBXYAJj48L\n//9eNmR3bMP3kkNKOKaaBk8vnYxKpZ+8ZHeHTmYWR9x1ZoMcbA9lKUwRpKAjY5JJ\nNVZMDmjlVQVvvBrvhgz/zgXtfuaQCryZ0f1sEY/zXhdealo3fGVomeoniD4XwA1c\noaUFkbo5IM5HU/pXyAGRerDyhYLgRqQZMIRauvKRPN3jLsPOEQ0+gnXUUTr/YGIE\nKY3/Axg4P3hzZCFqJ5IgkgWZr/dKr9p/0cxSUGHTVcpEFOlkKIIIdRuR7Ng5sJml\nu7PAMWt6T+x02ORs1/WkyP7LyPQmuugYTicCAwEAAaNeMFwwHQYDVR0OBBYEFDMR\nUvwxXbYDBCxOdQ9xfBnNWUz0MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF\nBQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC\nAgEAYzqmX+cNPgVD2HWgbeimUraTpI9JP5P4TbOHWmaJKecoy3Hwr71xyAOGiVXL\nurk1ZZe8n++GwuDEgRajN3HO9LR1Pu9qVIzTYIsz0ORRQHxujnF7CxK/I/vrIgde\npddUdHNS0Y0g8J1emH9BgoD8a2YsGX4iDY4S4hIGBbGvQp9z8U/uG1mViAmlXybM\nb8bf0dx0tEFUyu8jsQP6nFLY/HhkEcvU6SnOzZHRsFko6NE44VIsHLd2+LS2LCM/\nNfAoTzgvj41M3zQCZapaHZc9KXfdcCvEFaySKGfEZeQTUR5W0FHsF5I4NLGryf5L\nh3ENQ1tgBTO5WnqL/5rbgv6va9VionPM5sbEwAcancejnkVs3NoYPIPPgBFjaFmL\nhNTpT9H2owdZvEwNDChVS0b8ukNNd4cERtvy0Ohc3mk0LGN0ABzrud0fIqa51LMh\n0N3dkPkiZ4XYk4yLJ5EwCrCNNH50QkGCOWpInKIPeSYcALGgBDbCDLv6rV3oSKrV\ntHCZQwXVKKgU4AQu7hlHBwJ61cH44ksydOidW3MNq1kDIp7ST8s7gVrItNgFnG+L\nJpo270riwSUlWDY4hXw5Ff5lE+bWCmFyyOkLevDkD9v8M4HdwEVvafYYwn75fCIS\n5OnpSeIB08kKqCtW1WBwki0rYJjWqdzI7Z1MQ/AyScAKiGM=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEsDCCApgCCQCfkxy3a+AgNjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9w\nb255dG93biBSU0EgQ0EwHhcNMjIwNzA0MTQzMDU3WhcNMzIwNzAxMTQzMDU3WjAa\nMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC\nDwAwggIKAoICAQCj6nW8pnN50UsH2NjL97xZKxlXPe5ptXfvqXczMsw0vB3gI4xJ\nTdmrnqo0K+VOH7vh+UXcOj2ZMY2ou6oDDK5Qpu9bvGPBIJH/rC1Ti2+u5Y4KTIUc\njWAtzQJeFn8+oCMfskpLdtlWLRdAuwqNHjvxXdd2JnsX1Wid85U/rG2SNPLGjJAF\nxG7xzZC4VSO2WIXTGRMUkZfFc8fhWMjo3GaeF/qYjzfHDPWN/ll/7vfxyXJO/ohw\nFzpJSZtKmI+6PLxqB/oFrKfTDQUGzxjfHp187bI3eyUFMJsp18/tLYkLyxSWIg3o\nbq7ZVimHd1UG2Vb5Y+5pZkh22jmJ6bAa/kmNNwbsD+5vJhW1myGhmZSxkreYPWnS\n6ELrSMvbXccFfTYmdBlWsZx/zUVUzVCPe9jdJki2VXlicohqtvBQqe6LGGO37vvv\nGwu1yzQ/rJy47rnaao7fSxqM8nsDjNR2Ev1v031QpEMWjfgUW0roW3H58RZSx+kU\ngzIS2CjJIqKxCp894FUQbC6r0wwAuKltl3ywz5qWkxY0O9bXS0YdEXiri5pdsWjr\n84shVVQwnoVD9539CLSdHZjlOCAzvSWHZH6ta2JZjUfYYz8cLyv2c2+y9BYrlvHw\nT7U7BqzngUk72gcRXd5+Onp+16gGxpGJqaxqj94Nh/yTUnr2Jd9YaXeFmQIDAQAB\nMA0GCSqGSIb3DQEBCwUAA4ICAQBzIRVRt3Yaw60tpkyz/i1xbKCbtC+HqYTEsXvZ\nRvZ5X1qyLAcmu4EW9RHXnlLiawDbES6lCMFfdBUK03Wis7socvoFUCBRW337F4z2\nIivHfIge4u+w5ouUKPzcpj6oeuR06tmNytYbno6l8tXJpm1eeO4KNZ0ZtodmyB5D\nyLrplFgxTdGGgyvxt8LoeLwGmPCyVt35x/Mz6x2lcq1+r7QJZ9sENhQYuA8UqHrw\nfmNoVIMXMEcPLcWtFl6nKTK9LrqAu1jgTBqGGZKRn5CYBBK3pNEGKiOIsZXDbyFS\nF59teFpJjyeJTbUbLxXDa15J6ExkHV9wFLEvfu/nzQzg8D9yzczSdbDkE2rrrL+s\nQ/H/pIXO/DesCWQ37VALn3B5gm9UBd5uogbSw8eamiwRFLQ0snP80pJQGJoTNn0P\nwrLLUf2gsKC2262igiA+imepm5wxbV9XGVZfHJgxCi5Zqrf6aWnjIqD2YtDvAHhs\nV8ZWN3QTjdnEcQbG0544rocoLNX/FzmyDgjfZKY5r6wt+FWNc/R4clkF+KxasxqB\nHdBs8j0lGV3ujvNXASLq9HI6VxZayrSfkR73hADCXIM/wzynKwMarvA4SXwYX9Pd\ncJ4+FMqrevPpamMHUsNndS0KfDTdjDp+TSBf87yiyRkD1Ri4ePslyfNvRyv3Xs7k\n47YFzA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/sample.rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAy9+ai0CyKrMAphyccnM2BdqdGMVwqoDL7UXClRJZ4nzzBa2x\ngmNWmAZoSIlzxkcIX8rYQLWHw0LK1xQA2XediT4GQkf7ug0DKig9pXLxUJH+ynnv\n3zUzrkE0D4dV0MeJ7cb4nEgE8BJ7lNvDcFZzlP/2c6tsZ0N+IFEDR1qdaTfgdS2u\n3ppv1HILNf9oTMcAmWFJ0aIlKERl1Es2blqJAzrLTzZI3PgIx5sTjYKMKIJi9oyj\nfQYwcP/KbBqKhDozDvi2/YnpDgs1+JKh2kVG05W0gL4kLtkNdecYmQgQmVbTROg1\nBtuW7r6TEhF8KS3fepy8sG60gsrngu3HdNf4aQIDAQABAoIBAFTehqVFj2W7EqAT\n9QSn9WtGcHNpbddsunfRvIj2FLj2LuzEO8r9s4Sh1jOsFKgL1e6asJ9vck7UtUAH\nsbrV0pzZVx2sfZwb4p9gFRmU2eQigqCjVjnjGdqGhjeYrR62kjKLy96zFGskJpH3\nUkqnkoIKc/v+9qeeLxkg4G6JyFGOFHJAZEraxoGydJk9n/yBEZ/+3W7JUJaGOUNU\nM7BYsCS2VOJr+cCqmCk1j8NvYvWWxTPsIXgGJl4EOoskzlzJnYLdh9fPFZu3uOIx\nhpm3DBNp6X+qXf1lmx9EdpyeXKpLFIgJM7+nw2uWzxW7XMlRERi+5Tprc/pjrqUq\ngpfyvMkCgYEA909QcJpS3qHoWyxGbI1zosVIZXdnj8L+GF/2kEQEU5iEYT+2M1U+\ngCPLr49gNwkD1FdBSCy+Fw20zi35jGmxNwhgp4V94CGYzqwQzpnvgIRBMiAIoEwI\nCD5/t34DZ/82u8Gb7UYVrzOD54rJ628Q+tJEJak3TqoShbvcxJC/rXMCgYEA0wmO\nSRoxrBE3rFzNQkqHbMHLe9LksW9YSIXdMBjq4DhzQEwI0YgPLajXnsLurqHaJrQA\nJPtYkqiJkV7rvJLBo5wxwU+O2JKKa2jcMwuCZ4hOg5oBfK6ES9QJZUL7kDe2vsWy\nrL+rnxJheUjDPBTopGHuuc9Nogid35CE0wy7S7MCgYArxB+KLeVofOKv79/uqgHC\n1oL/Yegz6uAo1CLAWSki2iTjSPEnmHhdGPic8xSl6LSCyYZGDZT+Y3CR5FT7YmD4\nSkVAoEEsfwWZ3Z2D0n4uEjmvczfTlmD9hIH5qRVVPDcldxfvH64KuWUofslJHvi0\nSq3AtHeTNknc3Ogu6SbivQKBgQC4ZAsMWHS6MTkBwvwdRd1Z62INyNDFL9JlW4FN\nuxfN3cTlkwnJeiY48OOk9hFySDzBwFi3910Gl3fLqrIyy8+hUqIuk4LuO+vxuWdc\nuluwdmqTlgZimGFDl/q1nXcMJYHo4fgh9D7R+E9ul2Luph43MtJRS447W2gFpNJJ\nTUCA/QKBgQC07GFP2BN74UvL12f+FpZvE/UFtWnSZ8yJSq8oYpIbhmoF5EUF+XdA\nE2y3l1cvmDJFo4RNZl+IQIbHACR3y1XOnh4/B9fMEsVQHK3x8exPk1vAk687bBG8\nTVDmdP52XEKHplcVoYKvGzw/wsObLAGyIbJ00t1VPU+7guTPsc+H/w==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/server.rs",
    "content": "//! Simple HTTPS echo service based on hyper_util and rustls\n//!\n//! First parameter is the mandatory port to use.\n//! Certificate and private key are hardcoded to sample files.\n//! hyper will automatically use HTTP/2 if a client starts talking HTTP/2,\n//! otherwise HTTP/1.1 will be used.\n\nuse std::net::{Ipv4Addr, SocketAddr};\nuse std::sync::Arc;\nuse std::{env, io};\n\nuse http::{Method, Request, Response, StatusCode};\nuse http_body_util::{BodyExt, Full};\nuse hyper::body::{Bytes, Incoming};\nuse hyper::service::service_fn;\nuse hyper_util::rt::{TokioExecutor, TokioIo};\nuse hyper_util::server::conn::auto::Builder;\nuse rustls::pki_types::pem::PemObject;\nuse rustls::pki_types::{CertificateDer, PrivateKeyDer};\nuse rustls::ServerConfig;\nuse tokio::net::TcpListener;\nuse tokio_rustls::TlsAcceptor;\n\nfn main() {\n    // Serve an echo service over HTTPS, with proper error handling.\n    if let Err(e) = run_server() {\n        eprintln!(\"FAILED: {e}\");\n        std::process::exit(1);\n    }\n}\n\nfn error(err: String) -> io::Error {\n    io::Error::other(err)\n}\n\n#[tokio::main]\nasync fn run_server() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {\n    // Set a process wide default crypto provider.\n    #[cfg(feature = \"ring\")]\n    let _ = rustls::crypto::ring::default_provider().install_default();\n    #[cfg(feature = \"aws-lc-rs\")]\n    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();\n\n    // First parameter is port number (optional, defaults to 1337)\n    let port = match env::args().nth(1) {\n        Some(ref p) => p.parse()?,\n        None => 1337,\n    };\n    let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port);\n\n    // Load public certificate.\n    let certs = CertificateDer::pem_file_iter(\"examples/sample.pem\")?\n        .collect::<Result<Vec<_>, _>>()\n        .map_err(|e| error(format!(\"could not read certificate file: {e}\")))?;\n    // Load private key.\n    let key = PrivateKeyDer::from_pem_file(\"examples/sample.rsa\")\n        .map_err(|e| error(format!(\"could not read private key file: {e}\")))?;\n\n    // Create a TCP listener via tokio.\n    let incoming = TcpListener::bind(&addr).await?;\n    let addr = incoming.local_addr()?;\n\n    println!(\"Starting to serve on https://{addr}\");\n\n    // Build TLS configuration.\n    let mut server_config = ServerConfig::builder()\n        .with_no_client_auth()\n        .with_single_cert(certs, key)\n        .map_err(|e| error(e.to_string()))?;\n    server_config.alpn_protocols = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec(), b\"http/1.0\".to_vec()];\n    let tls_acceptor = TlsAcceptor::from(Arc::new(server_config));\n\n    let service = service_fn(echo);\n\n    loop {\n        let (tcp_stream, _remote_addr) = incoming.accept().await?;\n\n        let tls_acceptor = tls_acceptor.clone();\n        tokio::spawn(async move {\n            let tls_stream = match tls_acceptor.accept(tcp_stream).await {\n                Ok(tls_stream) => tls_stream,\n                Err(err) => {\n                    eprintln!(\"failed to perform tls handshake: {err:#}\");\n                    return;\n                }\n            };\n            if let Err(err) = Builder::new(TokioExecutor::new())\n                .serve_connection(TokioIo::new(tls_stream), service)\n                .await\n            {\n                eprintln!(\"failed to serve connection: {err:#}\");\n            }\n        });\n    }\n}\n\n// Custom echo service, handling two different routes and a\n// catch-all 404 responder.\nasync fn echo(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {\n    let mut response = Response::new(Full::default());\n    match (req.method(), req.uri().path()) {\n        // Help route.\n        (&Method::GET, \"/\") => {\n            *response.body_mut() = Full::from(\"Try POST /echo\\n\");\n        }\n        // Echo service route.\n        (&Method::POST, \"/echo\") => {\n            *response.body_mut() = Full::from(\n                req.into_body()\n                    .collect()\n                    .await?\n                    .to_bytes(),\n            );\n        }\n        // Catch-all 404.\n        _ => {\n            *response.status_mut() = StatusCode::NOT_FOUND;\n        }\n    };\n    Ok(response)\n}\n"
  },
  {
    "path": "src/config.rs",
    "content": "#[cfg(feature = \"rustls-native-certs\")]\nuse std::io;\n\n#[cfg(any(\n    feature = \"rustls-platform-verifier\",\n    feature = \"rustls-native-certs\",\n    feature = \"webpki-roots\"\n))]\nuse rustls::client::WantsClientCert;\nuse rustls::{ClientConfig, ConfigBuilder, WantsVerifier};\n#[cfg(feature = \"rustls-native-certs\")]\nuse rustls_native_certs::CertificateResult;\n#[cfg(feature = \"rustls-platform-verifier\")]\nuse rustls_platform_verifier::BuilderVerifierExt;\n\n/// Methods for configuring roots\n///\n/// This adds methods (gated by crate features) for easily configuring\n/// TLS server roots a rustls ClientConfig will trust.\npub trait ConfigBuilderExt: sealed::Sealed {\n    /// Use the platform's native verifier to verify server certificates.\n    ///\n    /// See the documentation for [rustls-platform-verifier] for more details.\n    ///\n    /// # Panics\n    ///\n    /// Since 0.27.7, this method will panic if the platform verifier cannot be initialized.\n    /// Use `try_with_platform_verifier()` instead to handle errors gracefully.\n    ///\n    /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier\n    #[deprecated(since = \"0.27.7\", note = \"use `try_with_platform_verifier` instead\")]\n    #[cfg(feature = \"rustls-platform-verifier\")]\n    fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert>;\n\n    /// Use the platform's native verifier to verify server certificates.\n    ///\n    /// See the documentation for [rustls-platform-verifier] for more details.\n    ///\n    /// [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier\n    #[cfg(feature = \"rustls-platform-verifier\")]\n    fn try_with_platform_verifier(\n        self,\n    ) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, rustls::Error>;\n\n    /// This configures the platform's trusted certs, as implemented by\n    /// rustls-native-certs\n    ///\n    /// This will return an error if no valid certs were found. In that case,\n    /// it's recommended to use `with_webpki_roots`.\n    #[cfg(feature = \"rustls-native-certs\")]\n    fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, io::Error>;\n\n    /// This configures the webpki roots, which are Mozilla's set of\n    /// trusted roots as packaged by webpki-roots.\n    #[cfg(feature = \"webpki-roots\")]\n    fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientCert>;\n}\n\nimpl ConfigBuilderExt for ConfigBuilder<ClientConfig, WantsVerifier> {\n    #[cfg(feature = \"rustls-platform-verifier\")]\n    fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {\n        self.try_with_platform_verifier()\n            .expect(\"failure to initialize platform verifier\")\n    }\n\n    #[cfg(feature = \"rustls-platform-verifier\")]\n    fn try_with_platform_verifier(\n        self,\n    ) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, rustls::Error> {\n        BuilderVerifierExt::with_platform_verifier(self)\n    }\n\n    #[cfg(feature = \"rustls-native-certs\")]\n    #[cfg_attr(not(feature = \"logging\"), allow(unused_variables))]\n    fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, io::Error> {\n        let mut roots = rustls::RootCertStore::empty();\n        let mut valid_count = 0;\n        let mut invalid_count = 0;\n\n        let CertificateResult { certs, errors, .. } = rustls_native_certs::load_native_certs();\n        if !errors.is_empty() {\n            crate::log::warn!(\"native root CA certificate loading errors: {errors:?}\");\n        }\n\n        if certs.is_empty() {\n            return Err(io::Error::new(\n                io::ErrorKind::NotFound,\n                format!(\"no native root CA certificates found (errors: {errors:?})\"),\n            ));\n        }\n\n        for cert in certs {\n            match roots.add(cert) {\n                Ok(_) => valid_count += 1,\n                Err(err) => {\n                    crate::log::debug!(\"certificate parsing failed: {err:?}\");\n                    invalid_count += 1\n                }\n            }\n        }\n\n        crate::log::debug!(\n            \"with_native_roots processed {valid_count} valid and {invalid_count} invalid certs\"\n        );\n        if roots.is_empty() {\n            crate::log::debug!(\"no valid native root CA certificates found\");\n            Err(io::Error::new(\n                io::ErrorKind::NotFound,\n                format!(\"no valid native root CA certificates found ({invalid_count} invalid)\"),\n            ))?\n        }\n\n        Ok(self.with_root_certificates(roots))\n    }\n\n    #[cfg(feature = \"webpki-roots\")]\n    fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {\n        let mut roots = rustls::RootCertStore::empty();\n        roots.extend(\n            webpki_roots::TLS_SERVER_ROOTS\n                .iter()\n                .cloned(),\n        );\n        self.with_root_certificates(roots)\n    }\n}\n\nmod sealed {\n    use super::*;\n\n    #[expect(unnameable_types)]\n    pub trait Sealed {}\n\n    impl Sealed for ConfigBuilder<ClientConfig, WantsVerifier> {}\n}\n"
  },
  {
    "path": "src/connector/builder.rs",
    "content": "use std::sync::Arc;\n\nuse hyper_util::client::legacy::connect::HttpConnector;\n#[cfg(any(\n    feature = \"rustls-native-certs\",\n    feature = \"rustls-platform-verifier\",\n    feature = \"webpki-roots\"\n))]\nuse rustls::crypto::CryptoProvider;\nuse rustls::pki_types::ServerName;\nuse rustls::ClientConfig;\n\nuse super::{DefaultServerNameResolver, HttpsConnector, ResolveServerName};\n#[cfg(any(\n    feature = \"rustls-native-certs\",\n    feature = \"webpki-roots\",\n    feature = \"rustls-platform-verifier\"\n))]\nuse crate::config::ConfigBuilderExt;\n\n/// A builder for an [`HttpsConnector`]\n///\n/// This makes configuration flexible and explicit and ensures connector\n/// features match crate features\n///\n/// # Examples\n///\n/// ```\n/// use hyper_rustls::HttpsConnectorBuilder;\n///\n/// # #[cfg(all(feature = \"webpki-roots\", feature = \"http1\", feature=\"aws-lc-rs\"))]\n/// # {\n/// # let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();\n///     let https = HttpsConnectorBuilder::new()\n///     .with_webpki_roots()\n///     .https_only()\n///     .enable_http1()\n///     .build();\n/// # }\n/// ```\npub struct ConnectorBuilder<State>(State);\n\n/// State of a builder that needs a TLS client config next\npub struct WantsTlsConfig(());\n\nimpl ConnectorBuilder<WantsTlsConfig> {\n    /// Creates a new [`ConnectorBuilder`]\n    pub fn new() -> Self {\n        Self(WantsTlsConfig(()))\n    }\n\n    /// Passes a rustls [`ClientConfig`] to configure the TLS connection\n    ///\n    /// The [`alpn_protocols`](ClientConfig::alpn_protocols) field is\n    /// required to be empty (or the function will panic) and will be\n    /// rewritten to match the enabled schemes (see\n    /// [`enable_http1`](ConnectorBuilder::enable_http1),\n    /// [`enable_http2`](ConnectorBuilder::enable_http2)) before the\n    /// connector is built.\n    pub fn with_tls_config(self, config: ClientConfig) -> ConnectorBuilder<WantsSchemes> {\n        assert!(\n            config.alpn_protocols.is_empty(),\n            \"ALPN protocols should not be pre-defined\"\n        );\n        ConnectorBuilder(WantsSchemes { tls_config: config })\n    }\n\n    /// Shorthand for using rustls' default crypto provider and other defaults, and\n    /// the platform verifier.\n    ///\n    /// See [`ConfigBuilderExt::with_platform_verifier()`].\n    #[cfg(all(\n        any(feature = \"ring\", feature = \"aws-lc-rs\"),\n        feature = \"rustls-platform-verifier\"\n    ))]\n    pub fn with_platform_verifier(self) -> ConnectorBuilder<WantsSchemes> {\n        self.try_with_platform_verifier()\n            .expect(\"failure to initialize platform verifier\")\n    }\n\n    /// Shorthand for using rustls' default crypto provider and other defaults, and\n    /// the platform verifier.\n    ///\n    /// See [`ConfigBuilderExt::with_platform_verifier()`].\n    #[cfg(all(\n        any(feature = \"ring\", feature = \"aws-lc-rs\"),\n        feature = \"rustls-platform-verifier\"\n    ))]\n    pub fn try_with_platform_verifier(\n        self,\n    ) -> Result<ConnectorBuilder<WantsSchemes>, rustls::Error> {\n        Ok(self.with_tls_config(\n            ClientConfig::builder()\n                .try_with_platform_verifier()?\n                .with_no_client_auth(),\n        ))\n    }\n\n    /// Shorthand for using a custom [`CryptoProvider`] and the platform verifier.\n    ///\n    /// See [`ConfigBuilderExt::with_platform_verifier()`].\n    #[cfg(feature = \"rustls-platform-verifier\")]\n    pub fn with_provider_and_platform_verifier(\n        self,\n        provider: impl Into<Arc<CryptoProvider>>,\n    ) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {\n        Ok(self.with_tls_config(\n            ClientConfig::builder_with_provider(provider.into())\n                .with_safe_default_protocol_versions()\n                .and_then(|builder| builder.try_with_platform_verifier())\n                .map_err(std::io::Error::other)?\n                .with_no_client_auth(),\n        ))\n    }\n\n    /// Shorthand for using rustls' default crypto provider and safe defaults, with\n    /// native roots.\n    ///\n    /// See [`ConfigBuilderExt::with_native_roots`]\n    #[cfg(all(\n        any(feature = \"ring\", feature = \"aws-lc-rs\"),\n        feature = \"rustls-native-certs\"\n    ))]\n    pub fn with_native_roots(self) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {\n        Ok(self.with_tls_config(\n            ClientConfig::builder()\n                .with_native_roots()?\n                .with_no_client_auth(),\n        ))\n    }\n\n    /// Shorthand for using a custom [`CryptoProvider`] and native roots\n    ///\n    /// See [`ConfigBuilderExt::with_native_roots`]\n    #[cfg(feature = \"rustls-native-certs\")]\n    pub fn with_provider_and_native_roots(\n        self,\n        provider: impl Into<Arc<CryptoProvider>>,\n    ) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {\n        Ok(self.with_tls_config(\n            ClientConfig::builder_with_provider(provider.into())\n                .with_safe_default_protocol_versions()\n                .map_err(std::io::Error::other)?\n                .with_native_roots()?\n                .with_no_client_auth(),\n        ))\n    }\n\n    /// Shorthand for using rustls' default crypto provider and its\n    /// safe defaults.\n    ///\n    /// See [`ConfigBuilderExt::with_webpki_roots`]\n    #[cfg(all(any(feature = \"ring\", feature = \"aws-lc-rs\"), feature = \"webpki-roots\"))]\n    pub fn with_webpki_roots(self) -> ConnectorBuilder<WantsSchemes> {\n        self.with_tls_config(\n            ClientConfig::builder()\n                .with_webpki_roots()\n                .with_no_client_auth(),\n        )\n    }\n\n    /// Shorthand for using a custom [`CryptoProvider`], Rustls' safe default\n    /// protocol versions and Mozilla roots\n    ///\n    /// See [`ConfigBuilderExt::with_webpki_roots`]\n    #[cfg(feature = \"webpki-roots\")]\n    pub fn with_provider_and_webpki_roots(\n        self,\n        provider: impl Into<Arc<CryptoProvider>>,\n    ) -> Result<ConnectorBuilder<WantsSchemes>, rustls::Error> {\n        Ok(self.with_tls_config(\n            ClientConfig::builder_with_provider(provider.into())\n                .with_safe_default_protocol_versions()?\n                .with_webpki_roots()\n                .with_no_client_auth(),\n        ))\n    }\n}\n\nimpl Default for ConnectorBuilder<WantsTlsConfig> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// State of a builder that needs schemes (https:// and http://) to be\n/// configured next\npub struct WantsSchemes {\n    tls_config: ClientConfig,\n}\n\nimpl ConnectorBuilder<WantsSchemes> {\n    /// Enforce the use of HTTPS when connecting\n    ///\n    /// Only URLs using the HTTPS scheme will be connectable.\n    pub fn https_only(self) -> ConnectorBuilder<WantsProtocols1> {\n        ConnectorBuilder(WantsProtocols1 {\n            tls_config: self.0.tls_config,\n            https_only: true,\n            server_name_resolver: None,\n        })\n    }\n\n    /// Allow both HTTPS and HTTP when connecting\n    ///\n    /// HTTPS URLs will be handled through rustls,\n    /// HTTP URLs will be handled by the lower-level connector.\n    pub fn https_or_http(self) -> ConnectorBuilder<WantsProtocols1> {\n        ConnectorBuilder(WantsProtocols1 {\n            tls_config: self.0.tls_config,\n            https_only: false,\n            server_name_resolver: None,\n        })\n    }\n}\n\n/// State of a builder that needs to have some protocols (HTTP1 or later)\n/// enabled next\n///\n/// No protocol has been enabled at this point.\npub struct WantsProtocols1 {\n    tls_config: ClientConfig,\n    https_only: bool,\n    server_name_resolver: Option<Arc<dyn ResolveServerName + Sync + Send>>,\n}\n\nimpl WantsProtocols1 {\n    fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {\n        HttpsConnector {\n            force_https: self.https_only,\n            http: conn,\n            tls_config: Arc::new(self.tls_config),\n            server_name_resolver: self\n                .server_name_resolver\n                .unwrap_or_else(|| Arc::new(DefaultServerNameResolver::default())),\n        }\n    }\n\n    fn build(self) -> HttpsConnector<HttpConnector> {\n        let mut http = HttpConnector::new();\n        // HttpConnector won't enforce scheme, but HttpsConnector will\n        http.enforce_http(false);\n        self.wrap_connector(http)\n    }\n}\n\nimpl ConnectorBuilder<WantsProtocols1> {\n    /// Enable HTTP1\n    ///\n    /// This needs to be called explicitly, no protocol is enabled by default\n    #[cfg(feature = \"http1\")]\n    pub fn enable_http1(self) -> ConnectorBuilder<WantsProtocols2> {\n        ConnectorBuilder(WantsProtocols2 { inner: self.0 })\n    }\n\n    /// Enable HTTP2\n    ///\n    /// This needs to be called explicitly, no protocol is enabled by default\n    #[cfg(feature = \"http2\")]\n    pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {\n        self.0.tls_config.alpn_protocols = vec![b\"h2\".to_vec()];\n        ConnectorBuilder(WantsProtocols3 {\n            inner: self.0,\n            enable_http1: false,\n        })\n    }\n\n    /// Enable all HTTP versions built into this library (enabled with Cargo features)\n    ///\n    /// For now, this could enable both HTTP 1 and 2, depending on active features.\n    /// In the future, other supported versions will be enabled as well.\n    #[cfg(feature = \"http2\")]\n    pub fn enable_all_versions(mut self) -> ConnectorBuilder<WantsProtocols3> {\n        #[cfg(feature = \"http1\")]\n        let alpn_protocols = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n        #[cfg(not(feature = \"http1\"))]\n        let alpn_protocols = vec![b\"h2\".to_vec()];\n\n        self.0.tls_config.alpn_protocols = alpn_protocols;\n        ConnectorBuilder(WantsProtocols3 {\n            inner: self.0,\n            enable_http1: cfg!(feature = \"http1\"),\n        })\n    }\n\n    /// Override server name for the TLS stack\n    ///\n    /// By default, for each connection hyper-rustls will extract host portion\n    /// of the destination URL and verify that server certificate contains\n    /// this value.\n    ///\n    /// If this method is called, hyper-rustls will instead use this resolver\n    /// to compute the value used to verify the server certificate.\n    pub fn with_server_name_resolver(\n        mut self,\n        resolver: impl ResolveServerName + 'static + Sync + Send,\n    ) -> Self {\n        self.0.server_name_resolver = Some(Arc::new(resolver));\n        self\n    }\n\n    /// Override server name for the TLS stack\n    ///\n    /// By default, for each connection hyper-rustls will extract host portion\n    /// of the destination URL and verify that server certificate contains\n    /// this value.\n    ///\n    /// If this method is called, hyper-rustls will instead verify that server\n    /// certificate contains `override_server_name`. Domain name included in\n    /// the URL will not affect certificate validation.\n    #[deprecated(\n        since = \"0.27.1\",\n        note = \"use Self::with_server_name_resolver with FixedServerNameResolver instead\"\n    )]\n    pub fn with_server_name(self, mut override_server_name: String) -> Self {\n        // remove square brackets around IPv6 address.\n        if let Some(trimmed) = override_server_name\n            .strip_prefix('[')\n            .and_then(|s| s.strip_suffix(']'))\n        {\n            override_server_name = trimmed.to_string();\n        }\n\n        self.with_server_name_resolver(move |_: &_| {\n            ServerName::try_from(override_server_name.clone())\n        })\n    }\n}\n\n/// State of a builder with HTTP1 enabled, that may have some other\n/// protocols (HTTP2 or later) enabled next\n///\n/// At this point a connector can be built, see\n/// [`build`](ConnectorBuilder<WantsProtocols2>::build) and\n/// [`wrap_connector`](ConnectorBuilder<WantsProtocols2>::wrap_connector).\npub struct WantsProtocols2 {\n    inner: WantsProtocols1,\n}\n\nimpl ConnectorBuilder<WantsProtocols2> {\n    /// Enable HTTP2\n    ///\n    /// This needs to be called explicitly, no protocol is enabled by default\n    #[cfg(feature = \"http2\")]\n    pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {\n        self.0.inner.tls_config.alpn_protocols = vec![b\"h2\".to_vec(), b\"http/1.1\".to_vec()];\n        ConnectorBuilder(WantsProtocols3 {\n            inner: self.0.inner,\n            enable_http1: true,\n        })\n    }\n\n    /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`]\n    pub fn build(self) -> HttpsConnector<HttpConnector> {\n        self.0.inner.build()\n    }\n\n    /// This wraps an arbitrary low-level connector into an [`HttpsConnector`]\n    pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {\n        // HTTP1-only, alpn_protocols stays empty\n        // HttpConnector doesn't have a way to say http1-only;\n        // its connection pool may still support HTTP2\n        // though it won't be used\n        self.0.inner.wrap_connector(conn)\n    }\n}\n\n/// State of a builder with HTTP2 (and possibly HTTP1) enabled\n///\n/// At this point a connector can be built, see\n/// [`build`](ConnectorBuilder<WantsProtocols3>::build) and\n/// [`wrap_connector`](ConnectorBuilder<WantsProtocols3>::wrap_connector).\n#[cfg(feature = \"http2\")]\npub struct WantsProtocols3 {\n    inner: WantsProtocols1,\n    // ALPN is built piecemeal without the need to read back this field\n    #[allow(dead_code)]\n    enable_http1: bool,\n}\n\n#[cfg(feature = \"http2\")]\nimpl ConnectorBuilder<WantsProtocols3> {\n    /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`]\n    pub fn build(self) -> HttpsConnector<HttpConnector> {\n        self.0.inner.build()\n    }\n\n    /// This wraps an arbitrary low-level connector into an [`HttpsConnector`]\n    pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {\n        // If HTTP1 is disabled, we can set http2_only\n        // on the Client (a higher-level object that uses the connector)\n        // client.http2_only(!self.0.enable_http1);\n        self.0.inner.wrap_connector(conn)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    // Typical usage\n    #[test]\n    #[cfg(all(feature = \"webpki-roots\", feature = \"http1\"))]\n    fn test_builder() {\n        ensure_global_state();\n        let _connector = super::ConnectorBuilder::new()\n            .with_webpki_roots()\n            .https_only()\n            .enable_http1()\n            .build();\n    }\n\n    #[test]\n    #[cfg(feature = \"http1\")]\n    #[should_panic(expected = \"ALPN protocols should not be pre-defined\")]\n    fn test_reject_predefined_alpn() {\n        ensure_global_state();\n        let roots = rustls::RootCertStore::empty();\n        let mut config_with_alpn = rustls::ClientConfig::builder()\n            .with_root_certificates(roots)\n            .with_no_client_auth();\n        config_with_alpn.alpn_protocols = vec![b\"fancyprotocol\".to_vec()];\n        let _connector = super::ConnectorBuilder::new()\n            .with_tls_config(config_with_alpn)\n            .https_only()\n            .enable_http1()\n            .build();\n    }\n\n    #[test]\n    #[cfg(all(feature = \"http1\", feature = \"http2\"))]\n    fn test_alpn() {\n        ensure_global_state();\n        let roots = rustls::RootCertStore::empty();\n        let tls_config = rustls::ClientConfig::builder()\n            .with_root_certificates(roots)\n            .with_no_client_auth();\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config.clone())\n            .https_only()\n            .enable_http1()\n            .build();\n        assert!(connector\n            .tls_config\n            .alpn_protocols\n            .is_empty());\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config.clone())\n            .https_only()\n            .enable_http2()\n            .build();\n        assert_eq!(&connector.tls_config.alpn_protocols, &[b\"h2\".to_vec()]);\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config.clone())\n            .https_only()\n            .enable_http1()\n            .enable_http2()\n            .build();\n        assert_eq!(\n            &connector.tls_config.alpn_protocols,\n            &[b\"h2\".to_vec(), b\"http/1.1\".to_vec()]\n        );\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config)\n            .https_only()\n            .enable_all_versions()\n            .build();\n        assert_eq!(\n            &connector.tls_config.alpn_protocols,\n            &[b\"h2\".to_vec(), b\"http/1.1\".to_vec()]\n        );\n    }\n\n    #[test]\n    #[cfg(all(not(feature = \"http1\"), feature = \"http2\"))]\n    fn test_alpn_http2() {\n        let roots = rustls::RootCertStore::empty();\n        let tls_config = rustls::ClientConfig::builder()\n            .with_safe_defaults()\n            .with_root_certificates(roots)\n            .with_no_client_auth();\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config.clone())\n            .https_only()\n            .enable_http2()\n            .build();\n        assert_eq!(&connector.tls_config.alpn_protocols, &[b\"h2\".to_vec()]);\n        let connector = super::ConnectorBuilder::new()\n            .with_tls_config(tls_config)\n            .https_only()\n            .enable_all_versions()\n            .build();\n        assert_eq!(&connector.tls_config.alpn_protocols, &[b\"h2\".to_vec()]);\n    }\n\n    fn ensure_global_state() {\n        #[cfg(feature = \"ring\")]\n        let _ = rustls::crypto::ring::default_provider().install_default();\n        #[cfg(feature = \"aws-lc-rs\")]\n        let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();\n    }\n}\n"
  },
  {
    "path": "src/connector.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse std::{fmt, io};\n\nuse http::Uri;\nuse hyper::rt;\nuse hyper_util::client::legacy::connect::Connection;\nuse hyper_util::rt::TokioIo;\nuse rustls::pki_types::ServerName;\nuse tokio_rustls::TlsConnector;\nuse tower_service::Service;\n\nuse crate::stream::MaybeHttpsStream;\n\npub(crate) mod builder;\n\ntype BoxError = Box<dyn std::error::Error + Send + Sync>;\n\n/// A Connector for the `https` scheme.\n#[derive(Clone)]\npub struct HttpsConnector<T> {\n    force_https: bool,\n    http: T,\n    tls_config: Arc<rustls::ClientConfig>,\n    server_name_resolver: Arc<dyn ResolveServerName + Sync + Send>,\n}\n\nimpl<T> HttpsConnector<T> {\n    /// Creates a [`crate::HttpsConnectorBuilder`] to configure a `HttpsConnector`.\n    ///\n    /// This is the same as [`crate::HttpsConnectorBuilder::new()`].\n    pub fn builder() -> builder::ConnectorBuilder<builder::WantsTlsConfig> {\n        builder::ConnectorBuilder::new()\n    }\n\n    /// Creates a new `HttpsConnector`.\n    ///\n    /// The recommended way to create a `HttpsConnector` is to use a [`crate::HttpsConnectorBuilder`]. See [`HttpsConnector::builder()`].\n    pub fn new(\n        http: T,\n        tls_config: impl Into<Arc<rustls::ClientConfig>>,\n        force_https: bool,\n        server_name_resolver: Arc<dyn ResolveServerName + Send + Sync>,\n    ) -> Self {\n        Self {\n            http,\n            tls_config: tls_config.into(),\n            force_https,\n            server_name_resolver,\n        }\n    }\n\n    /// Force the use of HTTPS when connecting.\n    ///\n    /// If a URL is not `https` when connecting, an error is returned.\n    pub fn enforce_https(&mut self) {\n        self.force_https = true;\n    }\n}\n\nimpl<T> Service<Uri> for HttpsConnector<T>\nwhere\n    T: Service<Uri>,\n    T::Response: Connection + rt::Read + rt::Write + Send + Unpin + 'static,\n    T::Future: Send + 'static,\n    T::Error: Into<BoxError>,\n{\n    type Response = MaybeHttpsStream<T::Response>;\n    type Error = BoxError;\n\n    #[allow(clippy::type_complexity)]\n    type Future =\n        Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T::Response>, BoxError>> + Send>>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        match self.http.poll_ready(cx) {\n            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),\n            Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n\n    fn call(&mut self, dst: Uri) -> Self::Future {\n        // dst.scheme() would need to derive Eq to be matchable;\n        // use an if cascade instead\n        match dst.scheme() {\n            Some(scheme) if scheme == &http::uri::Scheme::HTTP && !self.force_https => {\n                let future = self.http.call(dst);\n                return Box::pin(async move {\n                    Ok(MaybeHttpsStream::Http(future.await.map_err(Into::into)?))\n                });\n            }\n            Some(scheme) if scheme != &http::uri::Scheme::HTTPS => {\n                let message = format!(\"unsupported scheme {scheme}\");\n                return Box::pin(async move { Err(io::Error::other(message).into()) });\n            }\n            Some(_) => {}\n            None => return Box::pin(async move { Err(io::Error::other(\"missing scheme\").into()) }),\n        };\n\n        let cfg = self.tls_config.clone();\n        let hostname = match self.server_name_resolver.resolve(&dst) {\n            Ok(hostname) => hostname,\n            Err(e) => {\n                return Box::pin(async move { Err(e) });\n            }\n        };\n\n        let connecting_future = self.http.call(dst);\n        Box::pin(async move {\n            let tcp = connecting_future\n                .await\n                .map_err(Into::into)?;\n            Ok(MaybeHttpsStream::Https(TokioIo::new(\n                TlsConnector::from(cfg)\n                    .connect(hostname, TokioIo::new(tcp))\n                    .await\n                    .map_err(io::Error::other)?,\n            )))\n        })\n    }\n}\n\nimpl<H, C> From<(H, C)> for HttpsConnector<H>\nwhere\n    C: Into<Arc<rustls::ClientConfig>>,\n{\n    fn from((http, cfg): (H, C)) -> Self {\n        Self {\n            force_https: false,\n            http,\n            tls_config: cfg.into(),\n            server_name_resolver: Arc::new(DefaultServerNameResolver::default()),\n        }\n    }\n}\n\nimpl<T> fmt::Debug for HttpsConnector<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"HttpsConnector\")\n            .field(\"force_https\", &self.force_https)\n            .finish()\n    }\n}\n\n/// The default server name resolver, which uses the hostname in the URI.\n#[derive(Default)]\npub struct DefaultServerNameResolver(());\n\nimpl ResolveServerName for DefaultServerNameResolver {\n    fn resolve(\n        &self,\n        uri: &Uri,\n    ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {\n        let mut hostname = uri.host().unwrap_or_default();\n\n        // Remove square brackets around IPv6 address.\n        if let Some(trimmed) = hostname\n            .strip_prefix('[')\n            .and_then(|h| h.strip_suffix(']'))\n        {\n            hostname = trimmed;\n        }\n\n        ServerName::try_from(hostname.to_string()).map_err(|e| Box::new(e) as _)\n    }\n}\n\n/// A server name resolver which always returns the same fixed name.\npub struct FixedServerNameResolver {\n    name: ServerName<'static>,\n}\n\nimpl FixedServerNameResolver {\n    /// Creates a new resolver returning the specified name.\n    pub fn new(name: ServerName<'static>) -> Self {\n        Self { name }\n    }\n}\n\nimpl ResolveServerName for FixedServerNameResolver {\n    fn resolve(\n        &self,\n        _: &Uri,\n    ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {\n        Ok(self.name.clone())\n    }\n}\n\nimpl<F, E> ResolveServerName for F\nwhere\n    F: Fn(&Uri) -> Result<ServerName<'static>, E>,\n    E: Into<Box<dyn std::error::Error + Sync + Send>>,\n{\n    fn resolve(\n        &self,\n        uri: &Uri,\n    ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {\n        self(uri).map_err(Into::into)\n    }\n}\n\n/// A trait implemented by types that can resolve a [`ServerName`] for a request.\npub trait ResolveServerName {\n    /// Maps a [`Uri`] into a [`ServerName`].\n    fn resolve(\n        &self,\n        uri: &Uri,\n    ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>>;\n}\n\n#[cfg(all(\n    test,\n    any(feature = \"ring\", feature = \"aws-lc-rs\"),\n    any(\n        feature = \"rustls-native-certs\",\n        feature = \"webpki-roots\",\n        feature = \"rustls-platform-verifier\",\n    )\n))]\nmod tests {\n    use std::future::poll_fn;\n\n    use http::Uri;\n    use hyper_util::rt::TokioIo;\n    use tokio::net::TcpStream;\n    use tower_service::Service;\n\n    use super::*;\n    use crate::{ConfigBuilderExt, HttpsConnectorBuilder, MaybeHttpsStream};\n\n    #[tokio::test]\n    async fn connects_https() {\n        connect(Allow::Any, Scheme::Https)\n            .await\n            .unwrap();\n    }\n\n    #[tokio::test]\n    async fn connects_http() {\n        connect(Allow::Any, Scheme::Http)\n            .await\n            .unwrap();\n    }\n\n    #[tokio::test]\n    async fn connects_https_only() {\n        connect(Allow::Https, Scheme::Https)\n            .await\n            .unwrap();\n    }\n\n    #[tokio::test]\n    async fn enforces_https_only() {\n        let message = connect(Allow::Https, Scheme::Http)\n            .await\n            .unwrap_err()\n            .to_string();\n\n        assert_eq!(message, \"unsupported scheme http\");\n    }\n\n    async fn connect(\n        allow: Allow,\n        scheme: Scheme,\n    ) -> Result<MaybeHttpsStream<TokioIo<TcpStream>>, BoxError> {\n        let config_builder = rustls::ClientConfig::builder();\n        cfg_if::cfg_if! {\n            if #[cfg(feature = \"rustls-platform-verifier\")] {\n                let config_builder = config_builder.try_with_platform_verifier()?;\n            } else if #[cfg(feature = \"rustls-native-certs\")] {\n                let config_builder = config_builder.with_native_roots().unwrap();\n            } else if #[cfg(feature = \"webpki-roots\")] {\n                let config_builder = config_builder.with_webpki_roots();\n            }\n        }\n        let config = config_builder.with_no_client_auth();\n\n        let builder = HttpsConnectorBuilder::new().with_tls_config(config);\n        let mut service = match allow {\n            Allow::Https => builder.https_only(),\n            Allow::Any => builder.https_or_http(),\n        }\n        .enable_http1()\n        .build();\n\n        poll_fn(|cx| service.poll_ready(cx)).await?;\n        service\n            .call(Uri::from_static(match scheme {\n                Scheme::Https => \"https://google.com\",\n                Scheme::Http => \"http://google.com\",\n            }))\n            .await\n    }\n\n    enum Allow {\n        Https,\n        Any,\n    }\n\n    enum Scheme {\n        Https,\n        Http,\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! # hyper-rustls\n//!\n//! A pure-Rust HTTPS connector for [hyper](https://hyper.rs), based on\n//! [Rustls](https://github.com/rustls/rustls).\n//!\n//! ## Example client\n//!\n//! ```no_run\n//! # #[cfg(all(feature = \"rustls-native-certs\", feature = \"http1\"))]\n//! # fn main() {\n//! use http::StatusCode;\n//! use http_body_util::Empty;\n//! use hyper::body::Bytes;\n//! use hyper_util::client::legacy::Client;\n//! use hyper_util::rt::TokioExecutor;\n//!\n//! let mut rt = tokio::runtime::Runtime::new().unwrap();\n//! let url = (\"https://hyper.rs\").parse().unwrap();\n//! let https = hyper_rustls::HttpsConnectorBuilder::new()\n//!     .with_native_roots()\n//!     .expect(\"no native root CA certificates found\")\n//!     .https_only()\n//!     .enable_http1()\n//!     .build();\n//!\n//! let client: Client<_, Empty<Bytes>> = Client::builder(TokioExecutor::new()).build(https);\n//!\n//! let res = rt.block_on(client.get(url)).unwrap();\n//! assert_eq!(res.status(), StatusCode::OK);\n//! # }\n//! # #[cfg(not(all(feature = \"rustls-native-certs\", feature = \"http1\")))]\n//! # fn main() {}\n//! ```\n\n#![warn(missing_docs)]\n#![cfg_attr(hyper_rustls_docsrs, feature(doc_cfg))]\n\nmod config;\nmod connector;\nmod stream;\n\n#[cfg(feature = \"logging\")]\nmod log {\n    #[cfg(any(feature = \"rustls-native-certs\", feature = \"webpki-roots\"))]\n    pub(crate) use log::debug;\n    #[cfg(feature = \"rustls-native-certs\")]\n    pub(crate) use log::warn;\n}\n\n#[cfg(not(feature = \"logging\"))]\nmod log {\n    #[cfg(any(feature = \"rustls-native-certs\", feature = \"webpki-roots\"))]\n    macro_rules! debug    ( ($($tt:tt)*) => {{}} );\n    #[cfg(any(feature = \"rustls-native-certs\", feature = \"webpki-roots\"))]\n    pub(crate) use debug;\n    #[cfg(feature = \"rustls-native-certs\")]\n    macro_rules! warn_    ( ($($tt:tt)*) => {{}} );\n    #[cfg(feature = \"rustls-native-certs\")]\n    pub(crate) use warn_ as warn;\n}\n\npub use crate::config::ConfigBuilderExt;\npub use crate::connector::builder::ConnectorBuilder as HttpsConnectorBuilder;\npub use crate::connector::{\n    DefaultServerNameResolver, FixedServerNameResolver, HttpsConnector, ResolveServerName,\n};\npub use crate::stream::MaybeHttpsStream;\n\n/// The various states of the [`HttpsConnectorBuilder`]\npub mod builderstates {\n    #[cfg(feature = \"http2\")]\n    pub use crate::connector::builder::WantsProtocols3;\n    pub use crate::connector::builder::{\n        WantsProtocols1, WantsProtocols2, WantsSchemes, WantsTlsConfig,\n    };\n}\n"
  },
  {
    "path": "src/stream.rs",
    "content": "// Copied from hyperium/hyper-tls#62e3376/src/stream.rs\nuse std::fmt;\nuse std::io;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse hyper::rt;\nuse hyper_util::client::legacy::connect::{Connected, Connection};\n\nuse hyper_util::rt::TokioIo;\nuse tokio_rustls::client::TlsStream;\n\n/// A stream that might be protected with TLS.\n#[allow(clippy::large_enum_variant)]\npub enum MaybeHttpsStream<T> {\n    /// A stream over plain text.\n    Http(T),\n    /// A stream protected with TLS.\n    Https(TokioIo<TlsStream<TokioIo<T>>>),\n}\n\nimpl<T: rt::Read + rt::Write + Connection + Unpin> Connection for MaybeHttpsStream<T> {\n    fn connected(&self) -> Connected {\n        match self {\n            Self::Http(s) => s.connected(),\n            Self::Https(s) => {\n                let (tcp, tls) = s.inner().get_ref();\n                if tls.alpn_protocol() == Some(b\"h2\") {\n                    tcp.inner().connected().negotiated_h2()\n                } else {\n                    tcp.inner().connected()\n                }\n            }\n        }\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for MaybeHttpsStream<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            Self::Http(..) => f.pad(\"Http(..)\"),\n            Self::Https(..) => f.pad(\"Https(..)\"),\n        }\n    }\n}\n\nimpl<T> From<T> for MaybeHttpsStream<T> {\n    fn from(inner: T) -> Self {\n        Self::Http(inner)\n    }\n}\n\nimpl<T> From<TlsStream<TokioIo<T>>> for MaybeHttpsStream<T> {\n    fn from(inner: TlsStream<TokioIo<T>>) -> Self {\n        Self::Https(TokioIo::new(inner))\n    }\n}\n\nimpl<T: rt::Read + rt::Write + Unpin> rt::Read for MaybeHttpsStream<T> {\n    #[inline]\n    fn poll_read(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: rt::ReadBufCursor<'_>,\n    ) -> Poll<Result<(), io::Error>> {\n        match Pin::get_mut(self) {\n            Self::Http(s) => Pin::new(s).poll_read(cx, buf),\n            Self::Https(s) => Pin::new(s).poll_read(cx, buf),\n        }\n    }\n}\n\nimpl<T: rt::Write + rt::Read + Unpin> rt::Write for MaybeHttpsStream<T> {\n    #[inline]\n    fn poll_write(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<Result<usize, io::Error>> {\n        match Pin::get_mut(self) {\n            Self::Http(s) => Pin::new(s).poll_write(cx, buf),\n            Self::Https(s) => Pin::new(s).poll_write(cx, buf),\n        }\n    }\n\n    #[inline]\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {\n        match Pin::get_mut(self) {\n            Self::Http(s) => Pin::new(s).poll_flush(cx),\n            Self::Https(s) => Pin::new(s).poll_flush(cx),\n        }\n    }\n\n    #[inline]\n    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {\n        match Pin::get_mut(self) {\n            Self::Http(s) => Pin::new(s).poll_shutdown(cx),\n            Self::Https(s) => Pin::new(s).poll_shutdown(cx),\n        }\n    }\n\n    #[inline]\n    fn is_write_vectored(&self) -> bool {\n        match self {\n            Self::Http(s) => s.is_write_vectored(),\n            Self::Https(s) => s.is_write_vectored(),\n        }\n    }\n\n    #[inline]\n    fn poll_write_vectored(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        bufs: &[io::IoSlice<'_>],\n    ) -> Poll<Result<usize, io::Error>> {\n        match Pin::get_mut(self) {\n            Self::Http(s) => Pin::new(s).poll_write_vectored(cx, bufs),\n            Self::Https(s) => Pin::new(s).poll_write_vectored(cx, bufs),\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tests.rs",
    "content": "use std::env;\nuse std::io::{BufRead, BufReader};\nuse std::net::SocketAddr;\nuse std::path::PathBuf;\nuse std::process::{Child, Command, Stdio};\n\nfn examples_dir() -> PathBuf {\n    let target_dir: PathBuf = env::var(\"CARGO_TARGET_DIR\")\n        .unwrap_or_else(|_| \"target\".to_string())\n        .into();\n    target_dir\n        .join(\"debug\")\n        .join(\"examples\")\n}\n\nfn server_command() -> Command {\n    Command::new(examples_dir().join(\"server\"))\n}\n\nfn start_server() -> (Child, SocketAddr) {\n    let mut srv = server_command()\n        .arg(\"0\")\n        .stdout(Stdio::piped())\n        .stderr(Stdio::inherit())\n        .spawn()\n        .expect(\"cannot run server example\");\n\n    let stdout = srv\n        .stdout\n        .take()\n        .expect(\"failed to get stdout\");\n\n    let mut reader = BufReader::new(stdout);\n    let mut line = String::new();\n    reader\n        .read_line(&mut line)\n        .expect(\"failed to read line\");\n\n    let addr = line\n        .trim()\n        .strip_prefix(\"Starting to serve on https://\")\n        .expect(\"unexpected output\")\n        .parse()\n        .expect(\"failed to parse socket address\");\n\n    (srv, addr)\n}\n\nfn client_command() -> Command {\n    Command::new(examples_dir().join(\"client\"))\n}\n\n#[test]\nfn client() {\n    let rc = client_command()\n        .arg(\"https://google.com\")\n        .output()\n        .expect(\"cannot run client example\");\n\n    if !rc.status.success() {\n        assert_eq!(String::from_utf8_lossy(&rc.stdout), \"\");\n        assert_eq!(String::from_utf8_lossy(&rc.stderr), \"\");\n        panic!(\"test failed\");\n    }\n}\n\n#[test]\nfn server() {\n    let (mut srv, addr) = start_server();\n\n    let output = Command::new(\"curl\")\n        .arg(\"--insecure\")\n        .arg(\"--http1.0\")\n        .arg(format!(\"https://localhost:{}\", addr.port()))\n        .output()\n        .expect(\"cannot run curl\");\n\n    srv.kill().unwrap();\n    srv.wait()\n        .expect(\"failed to wait on server process\");\n\n    if !output.status.success() {\n        let version_stdout = Command::new(\"curl\")\n            .arg(\"--version\")\n            .output()\n            .expect(\"cannot run curl to collect --version\")\n            .stdout;\n        println!(\"curl version: {}\", String::from_utf8_lossy(&version_stdout));\n        println!(\"curl stderr:\\n{}\", String::from_utf8_lossy(&output.stderr));\n    }\n\n    assert_eq!(String::from_utf8_lossy(&output.stdout), \"Try POST /echo\\n\");\n}\n\n#[test]\nfn custom_ca_store() {\n    let (mut srv, addr) = start_server();\n\n    let rc = client_command()\n        .arg(format!(\"https://localhost:{}\", addr.port()))\n        .arg(\"examples/sample.pem\")\n        .output()\n        .expect(\"cannot run client example\");\n\n    srv.kill().unwrap();\n    srv.wait()\n        .expect(\"failed to wait on server process\");\n\n    if !rc.status.success() {\n        assert_eq!(String::from_utf8_lossy(&rc.stdout), \"\");\n        assert_eq!(String::from_utf8_lossy(&rc.stderr), \"\");\n        panic!(\"test failed\");\n    }\n}\n"
  }
]