Full Code of rustls/hyper-rustls for AI

main 8489d48693fa cached
23 files
79.7 KB
23.1k tokens
97 symbols
1 requests
Download .txt
Repository: rustls/hyper-rustls
Branch: main
Commit: 8489d48693fa
Files: 23
Total size: 79.7 KB

Directory structure:
gitextract__n53wl2z/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .rustfmt.toml
├── Cargo.toml
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-ISC
├── LICENSE-MIT
├── README.md
├── RELEASING.md
├── examples/
│   ├── client.rs
│   ├── openssl.cnf
│   ├── refresh-certificates.sh
│   ├── sample.pem
│   ├── sample.rsa
│   └── server.rs
├── src/
│   ├── config.rs
│   ├── connector/
│   │   └── builder.rs
│   ├── connector.rs
│   ├── lib.rs
│   └── stream.rs
└── tests/
    └── tests.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: cargo
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
- package-ecosystem: github-actions
  directory: "/"
  schedule:
    interval: weekly


================================================
FILE: .github/workflows/build.yml
================================================
name: rustls

permissions:
  contents: read

on:
  push:
    branches: ['main', 'rel-*', 'ci/*']
    tags: ['**']
  pull_request:
  merge_group:
  schedule:
    - cron: '23 6 * * 5'

jobs:
  build:
    name: Build+test
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        # test a bunch of toolchains on ubuntu
        rust:
          - stable
          - beta
          - nightly
        os: [ubuntu-latest]
        # but only stable on macos/windows (slower platforms)
        include:
          - os: macos-latest
            rust: stable
          - os: windows-latest
            rust: stable
    steps:
      - name: Checkout sources
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Install ${{ matrix.rust }} toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}

      - 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 --locked --all-targets

      - name: cargo test (debug; default features)
        run: cargo test --locked
        env:
          RUST_BACKTRACE: 1

      - name: cargo test (debug; native-tokio only)
        run: cargo test --locked --no-default-features --features native-tokio
        env:
          RUST_BACKTRACE: 1

      - name: cargo test (debug; webpki-tokio only)
        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)
        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 --locked --no-default-features

      - name: cargo test (debug; no default features; no run)
        run: cargo test --locked --no-default-features --no-run

      - name: 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.85"

      - 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-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@nightly

      - name: cargo doc (all features)
        # 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

  format:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - name: Check formatting
        run: cargo fmt --all -- --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - run: cargo clippy --locked --all-features -- --deny warnings

  features:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v6
        with:
          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


================================================
FILE: .gitignore
================================================
target/
/.idea


================================================
FILE: .rustfmt.toml
================================================
chain_width=40


================================================
FILE: Cargo.toml
================================================
[package]
name = "hyper-rustls"
version = "0.27.10"
edition = "2021"
rust-version = "1.85"
license = "Apache-2.0 OR ISC OR MIT"
readme = "README.md"
description = "Rustls+hyper integration for pure rust HTTPS"
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-ISC", "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 = "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.8", optional = true }
rustls-platform-verifier = { version = "0.7", optional = true }
rustls = { version = "0.23", default-features = false }
tokio = "1.0"
tokio-rustls = { version = "0.26", default-features = false }
tower-service = "0.3"
webpki-roots = { version = "1", optional = true }

[dev-dependencies]
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"] }

[[example]]
name = "client"
path = "examples/client.rs"
required-features = ["native-tokio", "http1"]

[[example]]
name = "server"
path = "examples/server.rs"
required-features = ["aws-lc-rs"]

[lints.clippy]
cloned_instead_of_copied = "warn"
manual_let_else = "warn"
needless_collect = "warn"
needless_pass_by_ref_mut = "warn"
or_fun_call = "warn"
redundant_clone = "warn"
upper_case_acronyms = "warn"
use_self = "warn"

[lints.rust]
elided_lifetimes_in_paths = "warn"
trivial_numeric_casts = "warn"
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(hyper_rustls_docsrs)"] }
unnameable_types = "warn"
unreachable_pub = "warn"
unused_extern_crates = "warn"
unused_import_braces = "warn"
unused_qualifications = "warn"

[package.metadata.docs.rs]
no-default-features = true
features = [
    "http1",
    "http2",
    "logging",
    "native-tokio",
    "ring",
    "rustls-platform-verifier",
    "tls12",
    "webpki-tokio",
]
rustdoc-args = ["--cfg", "hyper_rustls_docsrs"]


================================================
FILE: 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.



================================================
FILE: LICENSE-APACHE
================================================
                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

   To apply the Apache License to your work, attach the following
   boilerplate notice, with the fields enclosed by brackets "[]"
   replaced with your own identifying information. (Don't include
   the brackets!)  The text should be enclosed in the appropriate
   comment syntax for the file format. We also recommend that a
   file or class name and description of purpose be included on the
   same "printed page" as the copyright notice for easier
   identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: LICENSE-ISC
================================================
ISC License (ISC)
Copyright (c) 2016, Joseph Birr-Pixton <jpixton@gmail.com>

Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.


================================================
FILE: LICENSE-MIT
================================================
Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# hyper-rustls

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)

# Release history

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.

## 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


================================================
FILE: RELEASING.md
================================================
# Making a hyper-rustls release

This is a checklist for steps to make before/after making a rustls release.

1. Attend to the README.md: this appears on crates.io for the release, and can't be edited after
   the fact.
   - Ensure the version has a good set of release notes.  Move old release notes to OLDCHANGES.md
     if this is getting excessively long.
   - Write the version and date of the release.
2. Run `cargo update` followed by `cargo outdated`, to check if we have any
   dependency updates which are not already automatically taken by their semver specs.
   - If we do, take them if possible with separate commits (but there should've been
     dependabot PRs submitted for these already.)
3. Now run `cargo test --all-features` to ensure our tests continue to pass with the
   updated dependencies.
4. Update `Cargo.toml` to set the correct version.
5. Make a commit with the above changes, something like 'Prepare $VERSION'.  This
   should not contain functional changes: just versions numbers, and markdown changes.
6. Do a dry run: check `cargo publish --dry-run`
7. Push the above commit.  Wait for CI to confirm it as green.
   - Any red _should_ naturally block the release.
   - If rustc nightly is broken, this _may_ be acceptable if the reason is understood
     and does not point to a defect.
8. Tag the released version: `git tag -m '0.20.0' v/0.20.0`
9. Push the tag: `git push --tags`
10. Do the release: `cargo publish`.


================================================
FILE: examples/client.rs
================================================
//! Simple HTTPS GET client based on hyper-rustls
//!
//! First parameter is the mandatory URL to GET.
//! Second parameter is an optional path to CA store.
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, io};

fn main() {
    // Send GET request and inspect result, with proper error handling.
    if let Err(e) = run_client() {
        eprintln!("FAILED: {e}");
        std::process::exit(1);
    }
}

fn error(err: String) -> io::Error {
    io::Error::other(err)
}

#[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}")))?,
        None => {
            println!("Usage: client <url> <ca_store>");
            return Ok(());
        }
    };

    // Prepare the TLS client config
    let tls = match env::args().nth(2) {
        Some(path) => {
            // Read trust roots
            let certs = CertificateDer::pem_file_iter(&path)
                .and_then(|res| res.collect::<Result<Vec<_>, _>>())
                .map_err(|err| error(format!("could not read CA store {path}: {err}")))?;

            let mut roots = RootCertStore::empty();
            roots.add_parsable_certificates(certs);
            // TLS client config using the custom CA store for lookups
            rustls::ClientConfig::builder()
                .with_root_certificates(roots)
                .with_no_client_auth()
        }
        // Default TLS client config with native roots
        None => rustls::ClientConfig::builder()
            .with_native_roots()?
            .with_no_client_auth(),
    };
    // Prepare the HTTPS connector
    let https = hyper_rustls::HttpsConnectorBuilder::new()
        .with_tls_config(tls)
        .https_or_http()
        .enable_http1()
        .build();

    // Build the hyper client from the HTTPS connector.
    let client: Client<_, Empty<Bytes>> = 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
    // stdout.
    let fut = async move {
        let res = client
            .get(url)
            .await
            .map_err(|e| error(format!("Could not get: {e:?}")))?;
        println!("Status:\n{}", res.status());
        println!("Headers:\n{:#?}", res.headers());

        let body = res
            .into_body()
            .collect()
            .await
            .map_err(|e| error(format!("Could not get body: {e:?}")))?
            .to_bytes();
        println!("Body:\n{}", String::from_utf8_lossy(&body));

        Ok(())
    };

    fut.await
}


================================================
FILE: examples/openssl.cnf
================================================

[ 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


================================================
FILE: examples/refresh-certificates.sh
================================================
#!/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


================================================
FILE: examples/sample.pem
================================================
-----BEGIN CERTIFICATE-----
MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u
eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTIyMDcwNDE0MzA1OFoX
DTI3MTIyNTE0MzA1OFowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL35qLQLIqswCmHJxyczYF2p0YxXCq
gMvtRcKVElnifPMFrbGCY1aYBmhIiXPGRwhfythAtYfDQsrXFADZd52JPgZCR/u6
DQMqKD2lcvFQkf7Kee/fNTOuQTQPh1XQx4ntxvicSATwEnuU28NwVnOU//Zzq2xn
Q34gUQNHWp1pN+B1La7emm/Ucgs1/2hMxwCZYUnRoiUoRGXUSzZuWokDOstPNkjc
+AjHmxONgowogmL2jKN9BjBw/8psGoqEOjMO+Lb9iekOCzX4kqHaRUbTlbSAviQu
2Q115xiZCBCZVtNE6DUG25buvpMSEXwpLd96nLywbrSCyueC7cd01/hpAgMBAAGj
gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFHGnzC5Q
A62Wmv4zfMk/kf/BxHevMEIGA1UdIwQ7MDmAFDMRUvwxXbYDBCxOdQ9xfBnNWUz0
oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO
dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0
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-----
MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255
dG93biBSU0EgQ0EwHhcNMjIwNzA0MTQzMDU4WhcNMzIwNzAxMTQzMDU4WjAsMSow
KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G
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
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-----
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-----


================================================
FILE: examples/sample.rsa
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAy9+ai0CyKrMAphyccnM2BdqdGMVwqoDL7UXClRJZ4nzzBa2x
gmNWmAZoSIlzxkcIX8rYQLWHw0LK1xQA2XediT4GQkf7ug0DKig9pXLxUJH+ynnv
3zUzrkE0D4dV0MeJ7cb4nEgE8BJ7lNvDcFZzlP/2c6tsZ0N+IFEDR1qdaTfgdS2u
3ppv1HILNf9oTMcAmWFJ0aIlKERl1Es2blqJAzrLTzZI3PgIx5sTjYKMKIJi9oyj
fQYwcP/KbBqKhDozDvi2/YnpDgs1+JKh2kVG05W0gL4kLtkNdecYmQgQmVbTROg1
BtuW7r6TEhF8KS3fepy8sG60gsrngu3HdNf4aQIDAQABAoIBAFTehqVFj2W7EqAT
9QSn9WtGcHNpbddsunfRvIj2FLj2LuzEO8r9s4Sh1jOsFKgL1e6asJ9vck7UtUAH
sbrV0pzZVx2sfZwb4p9gFRmU2eQigqCjVjnjGdqGhjeYrR62kjKLy96zFGskJpH3
UkqnkoIKc/v+9qeeLxkg4G6JyFGOFHJAZEraxoGydJk9n/yBEZ/+3W7JUJaGOUNU
M7BYsCS2VOJr+cCqmCk1j8NvYvWWxTPsIXgGJl4EOoskzlzJnYLdh9fPFZu3uOIx
hpm3DBNp6X+qXf1lmx9EdpyeXKpLFIgJM7+nw2uWzxW7XMlRERi+5Tprc/pjrqUq
gpfyvMkCgYEA909QcJpS3qHoWyxGbI1zosVIZXdnj8L+GF/2kEQEU5iEYT+2M1U+
gCPLr49gNwkD1FdBSCy+Fw20zi35jGmxNwhgp4V94CGYzqwQzpnvgIRBMiAIoEwI
CD5/t34DZ/82u8Gb7UYVrzOD54rJ628Q+tJEJak3TqoShbvcxJC/rXMCgYEA0wmO
SRoxrBE3rFzNQkqHbMHLe9LksW9YSIXdMBjq4DhzQEwI0YgPLajXnsLurqHaJrQA
JPtYkqiJkV7rvJLBo5wxwU+O2JKKa2jcMwuCZ4hOg5oBfK6ES9QJZUL7kDe2vsWy
rL+rnxJheUjDPBTopGHuuc9Nogid35CE0wy7S7MCgYArxB+KLeVofOKv79/uqgHC
1oL/Yegz6uAo1CLAWSki2iTjSPEnmHhdGPic8xSl6LSCyYZGDZT+Y3CR5FT7YmD4
SkVAoEEsfwWZ3Z2D0n4uEjmvczfTlmD9hIH5qRVVPDcldxfvH64KuWUofslJHvi0
Sq3AtHeTNknc3Ogu6SbivQKBgQC4ZAsMWHS6MTkBwvwdRd1Z62INyNDFL9JlW4FN
uxfN3cTlkwnJeiY48OOk9hFySDzBwFi3910Gl3fLqrIyy8+hUqIuk4LuO+vxuWdc
uluwdmqTlgZimGFDl/q1nXcMJYHo4fgh9D7R+E9ul2Luph43MtJRS447W2gFpNJJ
TUCA/QKBgQC07GFP2BN74UvL12f+FpZvE/UFtWnSZ8yJSq8oYpIbhmoF5EUF+XdA
E2y3l1cvmDJFo4RNZl+IQIbHACR3y1XOnh4/B9fMEsVQHK3x8exPk1vAk687bBG8
TVDmdP52XEKHplcVoYKvGzw/wsObLAGyIbJ00t1VPU+7guTPsc+H/w==
-----END RSA PRIVATE KEY-----


================================================
FILE: examples/server.rs
================================================
//! 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 std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
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}");
        std::process::exit(1);
    }
}

fn error(err: String) -> io::Error {
    io::Error::other(err)
}

#[tokio::main]
async fn run_server() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // 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.parse()?,
        None => 1337,
    };
    let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port);

    // Load public certificate.
    let certs = CertificateDer::pem_file_iter("examples/sample.pem")?
        .collect::<Result<Vec<_>, _>>()
        .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}")))?;

    // Create a TCP listener via tokio.
    let incoming = TcpListener::bind(&addr).await?;
    let addr = incoming.local_addr()?;

    println!("Starting to serve on https://{addr}");

    // 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;
                }
            };
            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<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
    let mut response = Response::new(Full::default());
    match (req.method(), req.uri().path()) {
        // Help route.
        (&Method::GET, "/") => {
            *response.body_mut() = Full::from("Try POST /echo\n");
        }
        // Echo service route.
        (&Method::POST, "/echo") => {
            *response.body_mut() = Full::from(
                req.into_body()
                    .collect()
                    .await?
                    .to_bytes(),
            );
        }
        // Catch-all 404.
        _ => {
            *response.status_mut() = StatusCode::NOT_FOUND;
        }
    };
    Ok(response)
}


================================================
FILE: src/config.rs
================================================
#[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: 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<ClientConfig, WantsClientCert>;

    /// 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<ConfigBuilder<ClientConfig, WantsClientCert>, 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")]
    fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, io::Error>;

    /// This configures the webpki roots, which are Mozilla's set of
    /// trusted roots as packaged by webpki-roots.
    #[cfg(feature = "webpki-roots")]
    fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientCert>;
}

impl ConfigBuilderExt for ConfigBuilder<ClientConfig, WantsVerifier> {
    #[cfg(feature = "rustls-platform-verifier")]
    fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {
        self.try_with_platform_verifier()
            .expect("failure to initialize platform verifier")
    }

    #[cfg(feature = "rustls-platform-verifier")]
    fn try_with_platform_verifier(
        self,
    ) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, rustls::Error> {
        BuilderVerifierExt::with_platform_verifier(self)
    }

    #[cfg(feature = "rustls-native-certs")]
    #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
    fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, io::Error> {
        let mut roots = rustls::RootCertStore::empty();
        let mut valid_count = 0;
        let mut invalid_count = 0;

        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::debug!("certificate parsing failed: {err:?}");
                    invalid_count += 1
                }
            }
        }

        crate::log::debug!(
            "with_native_roots processed {valid_count} valid and {invalid_count} invalid certs"
        );
        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)"),
            ))?
        }

        Ok(self.with_root_certificates(roots))
    }

    #[cfg(feature = "webpki-roots")]
    fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {
        let mut roots = rustls::RootCertStore::empty();
        roots.extend(
            webpki_roots::TLS_SERVER_ROOTS
                .iter()
                .cloned(),
        );
        self.with_root_certificates(roots)
    }
}

mod sealed {
    use super::*;

    #[expect(unnameable_types)]
    pub trait Sealed {}

    impl Sealed for ConfigBuilder<ClientConfig, WantsVerifier> {}
}


================================================
FILE: src/connector/builder.rs
================================================
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::{DefaultServerNameResolver, HttpsConnector, ResolveServerName};
#[cfg(any(
    feature = "rustls-native-certs",
    feature = "webpki-roots",
    feature = "rustls-platform-verifier"
))]
use crate::config::ConfigBuilderExt;

/// A builder for an [`HttpsConnector`]
///
/// This makes configuration flexible and explicit and ensures connector
/// features match crate features
///
/// # Examples
///
/// ```
/// use hyper_rustls::HttpsConnectorBuilder;
///
/// # #[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>(State);

/// State of a builder that needs a TLS client config next
pub struct WantsTlsConfig(());

impl ConnectorBuilder<WantsTlsConfig> {
    /// Creates a new [`ConnectorBuilder`]
    pub fn new() -> Self {
        Self(WantsTlsConfig(()))
    }

    /// Passes a rustls [`ClientConfig`] to configure the TLS connection
    ///
    /// The [`alpn_protocols`](ClientConfig::alpn_protocols) field is
    /// required to be empty (or the function will panic) and will be
    /// rewritten to match the enabled schemes (see
    /// [`enable_http1`](ConnectorBuilder::enable_http1),
    /// [`enable_http2`](ConnectorBuilder::enable_http2)) before the
    /// connector is built.
    pub fn with_tls_config(self, config: ClientConfig) -> ConnectorBuilder<WantsSchemes> {
        assert!(
            config.alpn_protocols.is_empty(),
            "ALPN protocols should not be pre-defined"
        );
        ConnectorBuilder(WantsSchemes { tls_config: config })
    }

    /// 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<WantsSchemes> {
        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<ConnectorBuilder<WantsSchemes>, 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<Arc<CryptoProvider>>,
    ) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {
        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(std::io::Error::other)?
                .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<ConnectorBuilder<WantsSchemes>> {
        Ok(self.with_tls_config(
            ClientConfig::builder()
                .with_native_roots()?
                .with_no_client_auth(),
        ))
    }

    /// Shorthand for using a custom [`CryptoProvider`] and native roots
    ///
    /// See [`ConfigBuilderExt::with_native_roots`]
    #[cfg(feature = "rustls-native-certs")]
    pub fn with_provider_and_native_roots(
        self,
        provider: impl Into<Arc<CryptoProvider>>,
    ) -> std::io::Result<ConnectorBuilder<WantsSchemes>> {
        Ok(self.with_tls_config(
            ClientConfig::builder_with_provider(provider.into())
                .with_safe_default_protocol_versions()
                .map_err(std::io::Error::other)?
                .with_native_roots()?
                .with_no_client_auth(),
        ))
    }

    /// Shorthand for using rustls' default crypto provider and its
    /// safe defaults.
    ///
    /// See [`ConfigBuilderExt::with_webpki_roots`]
    #[cfg(all(any(feature = "ring", feature = "aws-lc-rs"), feature = "webpki-roots"))]
    pub fn with_webpki_roots(self) -> ConnectorBuilder<WantsSchemes> {
        self.with_tls_config(
            ClientConfig::builder()
                .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<Arc<CryptoProvider>>,
    ) -> Result<ConnectorBuilder<WantsSchemes>, 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<WantsTlsConfig> {
    fn default() -> Self {
        Self::new()
    }
}

/// State of a builder that needs schemes (https:// and http://) to be
/// configured next
pub struct WantsSchemes {
    tls_config: ClientConfig,
}

impl ConnectorBuilder<WantsSchemes> {
    /// Enforce the use of HTTPS when connecting
    ///
    /// Only URLs using the HTTPS scheme will be connectable.
    pub fn https_only(self) -> ConnectorBuilder<WantsProtocols1> {
        ConnectorBuilder(WantsProtocols1 {
            tls_config: self.0.tls_config,
            https_only: true,
            server_name_resolver: None,
        })
    }

    /// Allow both HTTPS and HTTP when connecting
    ///
    /// HTTPS URLs will be handled through rustls,
    /// HTTP URLs will be handled by the lower-level connector.
    pub fn https_or_http(self) -> ConnectorBuilder<WantsProtocols1> {
        ConnectorBuilder(WantsProtocols1 {
            tls_config: self.0.tls_config,
            https_only: false,
            server_name_resolver: None,
        })
    }
}

/// State of a builder that needs to have some protocols (HTTP1 or later)
/// enabled next
///
/// No protocol has been enabled at this point.
pub struct WantsProtocols1 {
    tls_config: ClientConfig,
    https_only: bool,
    server_name_resolver: Option<Arc<dyn ResolveServerName + Sync + Send>>,
}

impl WantsProtocols1 {
    fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
        HttpsConnector {
            force_https: self.https_only,
            http: conn,
            tls_config: Arc::new(self.tls_config),
            server_name_resolver: self
                .server_name_resolver
                .unwrap_or_else(|| Arc::new(DefaultServerNameResolver::default())),
        }
    }

    fn build(self) -> HttpsConnector<HttpConnector> {
        let mut http = HttpConnector::new();
        // HttpConnector won't enforce scheme, but HttpsConnector will
        http.enforce_http(false);
        self.wrap_connector(http)
    }
}

impl ConnectorBuilder<WantsProtocols1> {
    /// Enable HTTP1
    ///
    /// This needs to be called explicitly, no protocol is enabled by default
    #[cfg(feature = "http1")]
    pub fn enable_http1(self) -> ConnectorBuilder<WantsProtocols2> {
        ConnectorBuilder(WantsProtocols2 { inner: self.0 })
    }

    /// Enable HTTP2
    ///
    /// This needs to be called explicitly, no protocol is enabled by default
    #[cfg(feature = "http2")]
    pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {
        self.0.tls_config.alpn_protocols = vec![b"h2".to_vec()];
        ConnectorBuilder(WantsProtocols3 {
            inner: self.0,
            enable_http1: false,
        })
    }

    /// 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<WantsProtocols3> {
        #[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
    /// of the destination URL and verify that server certificate contains
    /// this value.
    ///
    /// 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.
    #[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())
        })
    }
}

/// State of a builder with HTTP1 enabled, that may have some other
/// protocols (HTTP2 or later) enabled next
///
/// At this point a connector can be built, see
/// [`build`](ConnectorBuilder<WantsProtocols2>::build) and
/// [`wrap_connector`](ConnectorBuilder<WantsProtocols2>::wrap_connector).
pub struct WantsProtocols2 {
    inner: WantsProtocols1,
}

impl ConnectorBuilder<WantsProtocols2> {
    /// Enable HTTP2
    ///
    /// This needs to be called explicitly, no protocol is enabled by default
    #[cfg(feature = "http2")]
    pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {
        self.0.inner.tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
        ConnectorBuilder(WantsProtocols3 {
            inner: self.0.inner,
            enable_http1: true,
        })
    }

    /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`]
    pub fn build(self) -> HttpsConnector<HttpConnector> {
        self.0.inner.build()
    }

    /// This wraps an arbitrary low-level connector into an [`HttpsConnector`]
    pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
        // HTTP1-only, alpn_protocols stays empty
        // HttpConnector doesn't have a way to say http1-only;
        // its connection pool may still support HTTP2
        // though it won't be used
        self.0.inner.wrap_connector(conn)
    }
}

/// State of a builder with HTTP2 (and possibly HTTP1) enabled
///
/// At this point a connector can be built, see
/// [`build`](ConnectorBuilder<WantsProtocols3>::build) and
/// [`wrap_connector`](ConnectorBuilder<WantsProtocols3>::wrap_connector).
#[cfg(feature = "http2")]
pub struct WantsProtocols3 {
    inner: WantsProtocols1,
    // ALPN is built piecemeal without the need to read back this field
    #[allow(dead_code)]
    enable_http1: bool,
}

#[cfg(feature = "http2")]
impl ConnectorBuilder<WantsProtocols3> {
    /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`]
    pub fn build(self) -> HttpsConnector<HttpConnector> {
        self.0.inner.build()
    }

    /// This wraps an arbitrary low-level connector into an [`HttpsConnector`]
    pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
        // If HTTP1 is disabled, we can set http2_only
        // on the Client (a higher-level object that uses the connector)
        // client.http2_only(!self.0.enable_http1);
        self.0.inner.wrap_connector(conn)
    }
}

#[cfg(test)]
mod tests {
    // Typical usage
    #[test]
    #[cfg(all(feature = "webpki-roots", feature = "http1"))]
    fn test_builder() {
        ensure_global_state();
        let _connector = super::ConnectorBuilder::new()
            .with_webpki_roots()
            .https_only()
            .enable_http1()
            .build();
    }

    #[test]
    #[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_root_certificates(roots)
            .with_no_client_auth();
        config_with_alpn.alpn_protocols = vec![b"fancyprotocol".to_vec()];
        let _connector = super::ConnectorBuilder::new()
            .with_tls_config(config_with_alpn)
            .https_only()
            .enable_http1()
            .build();
    }

    #[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_root_certificates(roots)
            .with_no_client_auth();
        let connector = super::ConnectorBuilder::new()
            .with_tls_config(tls_config.clone())
            .https_only()
            .enable_http1()
            .build();
        assert!(connector
            .tls_config
            .alpn_protocols
            .is_empty());
        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.clone())
            .https_only()
            .enable_http1()
            .enable_http2()
            .build();
        assert_eq!(
            &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();
    }
}


================================================
FILE: src/connector.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{fmt, io};

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(crate) mod builder;

type BoxError = Box<dyn std::error::Error + Send + Sync>;

/// A Connector for the `https` scheme.
#[derive(Clone)]
pub struct HttpsConnector<T> {
    force_https: bool,
    http: T,
    tls_config: Arc<rustls::ClientConfig>,
    server_name_resolver: Arc<dyn ResolveServerName + Sync + Send>,
}

impl<T> HttpsConnector<T> {
    /// Creates a [`crate::HttpsConnectorBuilder`] to configure a `HttpsConnector`.
    ///
    /// This is the same as [`crate::HttpsConnectorBuilder::new()`].
    pub fn builder() -> builder::ConnectorBuilder<builder::WantsTlsConfig> {
        builder::ConnectorBuilder::new()
    }

    /// 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<Arc<rustls::ClientConfig>>,
        force_https: bool,
        server_name_resolver: Arc<dyn ResolveServerName + Send + Sync>,
    ) -> Self {
        Self {
            http,
            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<T> Service<Uri> for HttpsConnector<T>
where
    T: Service<Uri>,
    T::Response: Connection + rt::Read + rt::Write + Send + Unpin + 'static,
    T::Future: Send + 'static,
    T::Error: Into<BoxError>,
{
    type Response = MaybeHttpsStream<T::Response>;
    type Error = BoxError;

    #[allow(clippy::type_complexity)]
    type Future =
        Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T::Response>, BoxError>> + Send>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        match self.http.poll_ready(cx) {
            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
            Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
            Poll::Pending => Poll::Pending,
        }
    }

    fn call(&mut self, dst: Uri) -> Self::Future {
        // dst.scheme() would need to derive Eq to be matchable;
        // use an if cascade instead
        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::other(message).into()) });
            }
            Some(_) => {}
            None => return Box::pin(async move { Err(io::Error::other("missing scheme").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(io::Error::other)?,
            )))
        })
    }
}

impl<H, C> From<(H, C)> for HttpsConnector<H>
where
    C: Into<Arc<rustls::ClientConfig>>,
{
    fn from((http, cfg): (H, C)) -> Self {
        Self {
            force_https: false,
            http,
            tls_config: cfg.into(),
            server_name_resolver: Arc::new(DefaultServerNameResolver::default()),
        }
    }
}

impl<T> fmt::Debug for HttpsConnector<T> {
    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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
        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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
        Ok(self.name.clone())
    }
}

impl<F, E> ResolveServerName for F
where
    F: Fn(&Uri) -> Result<ServerName<'static>, E>,
    E: Into<Box<dyn std::error::Error + Sync + Send>>,
{
    fn resolve(
        &self,
        uri: &Uri,
    ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>> {
        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<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>>;
}

#[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<MaybeHttpsStream<TokioIo<TcpStream>>, 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,
    }
}


================================================
FILE: src/lib.rs
================================================
//! # hyper-rustls
//!
//! A pure-Rust HTTPS connector for [hyper](https://hyper.rs), based on
//! [Rustls](https://github.com/rustls/rustls).
//!
//! ## Example client
//!
//! ```no_run
//! # #[cfg(all(feature = "rustls-native-certs", feature = "http1"))]
//! # fn main() {
//! 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<_, Empty<Bytes>> = 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 = "http1")))]
//! # fn main() {}
//! ```

#![warn(missing_docs)]
#![cfg_attr(hyper_rustls_docsrs, feature(doc_cfg))]

mod config;
mod connector;
mod stream;

#[cfg(feature = "logging")]
mod log {
    #[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 {
    #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))]
    macro_rules! debug    ( ($($tt:tt)*) => {{}} );
    #[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::{
    DefaultServerNameResolver, FixedServerNameResolver, HttpsConnector, ResolveServerName,
};
pub use crate::stream::MaybeHttpsStream;

/// The various states of the [`HttpsConnectorBuilder`]
pub mod builderstates {
    #[cfg(feature = "http2")]
    pub use crate::connector::builder::WantsProtocols3;
    pub use crate::connector::builder::{
        WantsProtocols1, WantsProtocols2, WantsSchemes, WantsTlsConfig,
    };
}


================================================
FILE: src/stream.rs
================================================
// Copied from hyperium/hyper-tls#62e3376/src/stream.rs
use std::fmt;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};

use hyper::rt;
use hyper_util::client::legacy::connect::{Connected, Connection};

use hyper_util::rt::TokioIo;
use tokio_rustls::client::TlsStream;

/// A stream that might be protected with TLS.
#[allow(clippy::large_enum_variant)]
pub enum MaybeHttpsStream<T> {
    /// A stream over plain text.
    Http(T),
    /// A stream protected with TLS.
    Https(TokioIo<TlsStream<TokioIo<T>>>),
}

impl<T: rt::Read + rt::Write + Connection + Unpin> Connection for MaybeHttpsStream<T> {
    fn connected(&self) -> Connected {
        match self {
            Self::Http(s) => s.connected(),
            Self::Https(s) => {
                let (tcp, tls) = s.inner().get_ref();
                if tls.alpn_protocol() == Some(b"h2") {
                    tcp.inner().connected().negotiated_h2()
                } else {
                    tcp.inner().connected()
                }
            }
        }
    }
}

impl<T: fmt::Debug> fmt::Debug for MaybeHttpsStream<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Http(..) => f.pad("Http(..)"),
            Self::Https(..) => f.pad("Https(..)"),
        }
    }
}

impl<T> From<T> for MaybeHttpsStream<T> {
    fn from(inner: T) -> Self {
        Self::Http(inner)
    }
}

impl<T> From<TlsStream<TokioIo<T>>> for MaybeHttpsStream<T> {
    fn from(inner: TlsStream<TokioIo<T>>) -> Self {
        Self::Https(TokioIo::new(inner))
    }
}

impl<T: rt::Read + rt::Write + Unpin> rt::Read for MaybeHttpsStream<T> {
    #[inline]
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: rt::ReadBufCursor<'_>,
    ) -> Poll<Result<(), io::Error>> {
        match Pin::get_mut(self) {
            Self::Http(s) => Pin::new(s).poll_read(cx, buf),
            Self::Https(s) => Pin::new(s).poll_read(cx, buf),
        }
    }
}

impl<T: rt::Write + rt::Read + Unpin> rt::Write for MaybeHttpsStream<T> {
    #[inline]
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<Result<usize, io::Error>> {
        match Pin::get_mut(self) {
            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<Result<(), io::Error>> {
        match Pin::get_mut(self) {
            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<Result<(), io::Error>> {
        match Pin::get_mut(self) {
            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<Result<usize, io::Error>> {
        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),
        }
    }
}


================================================
FILE: tests/tests.rs
================================================
use std::env;
use std::io::{BufRead, BufReader};
use std::net::SocketAddr;
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};

fn examples_dir() -> PathBuf {
    let target_dir: PathBuf = env::var("CARGO_TARGET_DIR")
        .unwrap_or_else(|_| "target".to_string())
        .into();
    target_dir
        .join("debug")
        .join("examples")
}

fn server_command() -> Command {
    Command::new(examples_dir().join("server"))
}

fn start_server() -> (Child, SocketAddr) {
    let mut srv = server_command()
        .arg("0")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("cannot run server example");

    let stdout = srv
        .stdout
        .take()
        .expect("failed to get stdout");

    let mut reader = BufReader::new(stdout);
    let mut line = String::new();
    reader
        .read_line(&mut line)
        .expect("failed to read line");

    let addr = line
        .trim()
        .strip_prefix("Starting to serve on https://")
        .expect("unexpected output")
        .parse()
        .expect("failed to parse socket address");

    (srv, addr)
}

fn client_command() -> Command {
    Command::new(examples_dir().join("client"))
}

#[test]
fn client() {
    let rc = client_command()
        .arg("https://google.com")
        .output()
        .expect("cannot run client example");

    if !rc.status.success() {
        assert_eq!(String::from_utf8_lossy(&rc.stdout), "");
        assert_eq!(String::from_utf8_lossy(&rc.stderr), "");
        panic!("test failed");
    }
}

#[test]
fn server() {
    let (mut srv, addr) = start_server();

    let output = Command::new("curl")
        .arg("--insecure")
        .arg("--http1.0")
        .arg(format!("https://localhost:{}", addr.port()))
        .output()
        .expect("cannot run curl");

    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]
fn custom_ca_store() {
    let (mut srv, addr) = start_server();

    let rc = client_command()
        .arg(format!("https://localhost:{}", addr.port()))
        .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), "");
        assert_eq!(String::from_utf8_lossy(&rc.stderr), "");
        panic!("test failed");
    }
}
Download .txt
gitextract__n53wl2z/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .rustfmt.toml
├── Cargo.toml
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-ISC
├── LICENSE-MIT
├── README.md
├── RELEASING.md
├── examples/
│   ├── client.rs
│   ├── openssl.cnf
│   ├── refresh-certificates.sh
│   ├── sample.pem
│   ├── sample.rsa
│   └── server.rs
├── src/
│   ├── config.rs
│   ├── connector/
│   │   └── builder.rs
│   ├── connector.rs
│   ├── lib.rs
│   └── stream.rs
└── tests/
    └── tests.rs
Download .txt
SYMBOL INDEX (97 symbols across 7 files)

FILE: examples/client.rs
  function main (line 17) | fn main() {
  function error (line 25) | fn error(err: String) -> io::Error {
  function run_client (line 30) | async fn run_client() -> io::Result<()> {

FILE: examples/server.rs
  function main (line 24) | fn main() {
  function error (line 32) | fn error(err: String) -> io::Error {
  function run_server (line 37) | async fn run_server() -> Result<(), Box<dyn std::error::Error + Send + S...
  function echo (line 99) | async fn echo(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, h...

FILE: src/config.rs
  type ConfigBuilderExt (line 20) | pub trait ConfigBuilderExt: sealed::Sealed {
    method with_platform_verifier (line 33) | fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsCl...
    method try_with_platform_verifier (line 41) | fn try_with_platform_verifier(
    method with_native_roots (line 51) | fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, Wants...
    method with_webpki_roots (line 56) | fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientC...
    method with_platform_verifier (line 61) | fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsCl...
    method try_with_platform_verifier (line 67) | fn try_with_platform_verifier(
    method with_native_roots (line 75) | fn with_native_roots(self) -> Result<ConfigBuilder<ClientConfig, Wants...
    method with_webpki_roots (line 117) | fn with_webpki_roots(self) -> ConfigBuilder<ClientConfig, WantsClientC...
  type Sealed (line 132) | pub trait Sealed {}

FILE: src/connector.rs
  type BoxError (line 19) | type BoxError = Box<dyn std::error::Error + Send + Sync>;
  type HttpsConnector (line 23) | pub struct HttpsConnector<T> {
  function builder (line 34) | pub fn builder() -> builder::ConnectorBuilder<builder::WantsTlsConfig> {
  function new (line 41) | pub fn new(
  function enforce_https (line 58) | pub fn enforce_https(&mut self) {
  type Response (line 70) | type Response = MaybeHttpsStream<T::Response>;
  type Error (line 71) | type Error = BoxError;
  type Future (line 74) | type Future =
  function poll_ready (line 77) | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::...
  function call (line 85) | fn call(&mut self, dst: Uri) -> Self::Future {
  function from (line 130) | fn from((http, cfg): (H, C)) -> Self {
  function fmt (line 141) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type DefaultServerNameResolver (line 150) | pub struct DefaultServerNameResolver(());
  type FixedServerNameResolver (line 172) | pub struct FixedServerNameResolver {
    method new (line 178) | pub fn new(name: ServerName<'static>) -> Self {
  type ResolveServerName (line 206) | pub trait ResolveServerName {
    method resolve (line 153) | fn resolve(
    method resolve (line 184) | fn resolve(
    method resolve (line 197) | fn resolve(
    method resolve (line 208) | fn resolve(
  function connects_https (line 235) | async fn connects_https() {
  function connects_http (line 242) | async fn connects_http() {
  function connects_https_only (line 249) | async fn connects_https_only() {
  function enforces_https_only (line 256) | async fn enforces_https_only() {
  function connect (line 265) | async fn connect(
  type Allow (line 298) | enum Allow {
  type Scheme (line 303) | enum Scheme {

FILE: src/connector/builder.rs
  type ConnectorBuilder (line 41) | pub struct ConnectorBuilder<State>(State);
  type WantsTlsConfig (line 44) | pub struct WantsTlsConfig(());
  function new (line 48) | pub fn new() -> Self {
  function with_tls_config (line 60) | pub fn with_tls_config(self, config: ClientConfig) -> ConnectorBuilder<W...
  function with_platform_verifier (line 76) | pub fn with_platform_verifier(self) -> ConnectorBuilder<WantsSchemes> {
  function try_with_platform_verifier (line 89) | pub fn try_with_platform_verifier(
  function with_provider_and_platform_verifier (line 103) | pub fn with_provider_and_platform_verifier(
  function with_native_roots (line 124) | pub fn with_native_roots(self) -> std::io::Result<ConnectorBuilder<Wants...
  function with_provider_and_native_roots (line 136) | pub fn with_provider_and_native_roots(
  function with_webpki_roots (line 154) | pub fn with_webpki_roots(self) -> ConnectorBuilder<WantsSchemes> {
  function with_provider_and_webpki_roots (line 167) | pub fn with_provider_and_webpki_roots(
  method default (line 181) | fn default() -> Self {
  type WantsSchemes (line 188) | pub struct WantsSchemes {
  function https_only (line 196) | pub fn https_only(self) -> ConnectorBuilder<WantsProtocols1> {
  function https_or_http (line 208) | pub fn https_or_http(self) -> ConnectorBuilder<WantsProtocols1> {
  type WantsProtocols1 (line 221) | pub struct WantsProtocols1 {
    method wrap_connector (line 228) | fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
    method build (line 239) | fn build(self) -> HttpsConnector<HttpConnector> {
  function enable_http1 (line 252) | pub fn enable_http1(self) -> ConnectorBuilder<WantsProtocols2> {
  function enable_http2 (line 260) | pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {
  function enable_all_versions (line 273) | pub fn enable_all_versions(mut self) -> ConnectorBuilder<WantsProtocols3> {
  function with_server_name_resolver (line 294) | pub fn with_server_name_resolver(
  function with_server_name (line 315) | pub fn with_server_name(self, mut override_server_name: String) -> Self {
  type WantsProtocols2 (line 336) | pub struct WantsProtocols2 {
  function enable_http2 (line 345) | pub fn enable_http2(mut self) -> ConnectorBuilder<WantsProtocols3> {
  function build (line 354) | pub fn build(self) -> HttpsConnector<HttpConnector> {
  function wrap_connector (line 359) | pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
  type WantsProtocols3 (line 374) | pub struct WantsProtocols3 {
  function build (line 384) | pub fn build(self) -> HttpsConnector<HttpConnector> {
  function wrap_connector (line 389) | pub fn wrap_connector<H>(self, conn: H) -> HttpsConnector<H> {
  function test_builder (line 402) | fn test_builder() {
  function test_reject_predefined_alpn (line 414) | fn test_reject_predefined_alpn() {
  function test_alpn (line 430) | fn test_alpn() {
  function test_alpn_http2 (line 474) | fn test_alpn_http2() {
  function ensure_global_state (line 494) | fn ensure_global_state() {

FILE: src/stream.rs
  type MaybeHttpsStream (line 15) | pub enum MaybeHttpsStream<T> {
  method connected (line 23) | fn connected(&self) -> Connected {
  function fmt (line 39) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function from (line 48) | fn from(inner: T) -> Self {
  function from (line 54) | fn from(inner: TlsStream<TokioIo<T>>) -> Self {
  function poll_read (line 61) | fn poll_read(
  function poll_write (line 75) | fn poll_write(
  function poll_flush (line 87) | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result...
  function poll_shutdown (line 95) | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Res...
  function is_write_vectored (line 103) | fn is_write_vectored(&self) -> bool {
  function poll_write_vectored (line 111) | fn poll_write_vectored(

FILE: tests/tests.rs
  function examples_dir (line 7) | fn examples_dir() -> PathBuf {
  function server_command (line 16) | fn server_command() -> Command {
  function start_server (line 20) | fn start_server() -> (Child, SocketAddr) {
  function client_command (line 49) | fn client_command() -> Command {
  function client (line 54) | fn client() {
  function server (line 68) | fn server() {
  function custom_ca_store (line 96) | fn custom_ca_store() {
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (86K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 213,
    "preview": "version: 2\nupdates:\n- package-ecosystem: cargo\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-lim"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 5140,
    "preview": "name: rustls\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches: ['main', 'rel-*', 'ci/*']\n    tags: ['**']\n  pull"
  },
  {
    "path": ".gitignore",
    "chars": 15,
    "preview": "target/\n/.idea\n"
  },
  {
    "path": ".rustfmt.toml",
    "chars": 15,
    "preview": "chain_width=40\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 2710,
    "preview": "[package]\nname = \"hyper-rustls\"\nversion = \"0.27.10\"\nedition = \"2021\"\nrust-version = \"1.85\"\nlicense = \"Apache-2.0 OR ISC "
  },
  {
    "path": "LICENSE",
    "chars": 292,
    "preview": "hyper-rustls is distributed under the following three licenses:\n\n- Apache License version 2.0.\n- MIT license.\n- ISC lice"
  },
  {
    "path": "LICENSE-APACHE",
    "chars": 10847,
    "preview": "                              Apache License\n                        Version 2.0, January 2004\n                     http"
  },
  {
    "path": "LICENSE-ISC",
    "chars": 775,
    "preview": "ISC License (ISC)\nCopyright (c) 2016, Joseph Birr-Pixton <jpixton@gmail.com>\n\nPermission to use, copy, modify, and/or di"
  },
  {
    "path": "LICENSE-MIT",
    "chars": 1082,
    "preview": "Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>\n\nPermission is hereby granted, free of charge, to any\nperson o"
  },
  {
    "path": "README.md",
    "chars": 2775,
    "preview": "# hyper-rustls\n\nThis is an integration between the [Rustls TLS stack](https://github.com/rustls/rustls) and the\n[hyper H"
  },
  {
    "path": "RELEASING.md",
    "chars": 1454,
    "preview": "# Making a hyper-rustls release\n\nThis is a checklist for steps to make before/after making a rustls release.\n\n1. Attend "
  },
  {
    "path": "examples/client.rs",
    "chars": 3248,
    "preview": "//! Simple HTTPS GET client based on hyper-rustls\n//!\n//! First parameter is the mandatory URL to GET.\n//! Second parame"
  },
  {
    "path": "examples/openssl.cnf",
    "chars": 761,
    "preview": "\n[ v3_end ]\nbasicConstraints = critical,CA:false\nkeyUsage = nonRepudiation, digitalSignature\nsubjectKeyIdentifier = hash"
  },
  {
    "path": "examples/refresh-certificates.sh",
    "chars": 1284,
    "preview": "#!/bin/sh\n\nset -xe\n\nopenssl req -nodes \\\n          -x509 \\\n          -days 3650 \\\n          -newkey rsa:4096 \\\n         "
  },
  {
    "path": "examples/sample.pem",
    "chars": 4799,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u\neXRvd24gUlNBIGxldmVsIDIgaW5"
  },
  {
    "path": "examples/sample.rsa",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAy9+ai0CyKrMAphyccnM2BdqdGMVwqoDL7UXClRJZ4nzzBa2x\ngmNWmAZoSIlzxkcIX8rYQLW"
  },
  {
    "path": "examples/server.rs",
    "chars": 4167,
    "preview": "//! Simple HTTPS echo service based on hyper_util and rustls\n//!\n//! First parameter is the mandatory port to use.\n//! C"
  },
  {
    "path": "src/config.rs",
    "chars": 5028,
    "preview": "#[cfg(feature = \"rustls-native-certs\")]\nuse std::io;\n\n#[cfg(any(\n    feature = \"rustls-platform-verifier\",\n    feature ="
  },
  {
    "path": "src/connector/builder.rs",
    "chars": 17445,
    "preview": "use std::sync::Arc;\n\nuse hyper_util::client::legacy::connect::HttpConnector;\n#[cfg(any(\n    feature = \"rustls-native-cer"
  },
  {
    "path": "src/connector.rs",
    "chars": 9007,
    "preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse std::{fmt, io};\n\nuse"
  },
  {
    "path": "src/lib.rs",
    "chars": 2434,
    "preview": "//! # hyper-rustls\n//!\n//! A pure-Rust HTTPS connector for [hyper](https://hyper.rs), based on\n//! [Rustls](https://gith"
  },
  {
    "path": "src/stream.rs",
    "chars": 3536,
    "preview": "// Copied from hyperium/hyper-tls#62e3376/src/stream.rs\nuse std::fmt;\nuse std::io;\nuse std::pin::Pin;\nuse std::task::{Co"
  },
  {
    "path": "tests/tests.rs",
    "chars": 2953,
    "preview": "use std::env;\nuse std::io::{BufRead, BufReader};\nuse std::net::SocketAddr;\nuse std::path::PathBuf;\nuse std::process::{Ch"
  }
]

About this extraction

This page contains the full source code of the rustls/hyper-rustls GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (79.7 KB), approximately 23.1k tokens, and a symbol index with 97 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!