hyper-tls
A minimal HTTPS connector that pairs hyper with native-tls for secure Rust HTTP clients.
Repository Health
Technical Analysis
hyper-tls provides the HTTPS connector most Rust services reach for when building an HTTP client on top of hyper. It implements tower’s Service<Uri> trait so it drops directly into hyper-util’s legacy Client builder, inspecting each request’s scheme and transparently performing a TLS handshake only when the URI is https. Rather than bundling a pure-Rust TLS stack, it delegates the handshake to native-tls, which in turn uses the host operating system’s TLS implementation — OpenSSL on Linux, Secure Transport on macOS, SChannel on Windows — so certificate trust and platform TLS policy come from the OS rather than a vendored trust store.
The crate is intentionally small: a HttpsConnector<T> wrapping an inner connector (defaulting to hyper-util’s HttpConnector) and TLS connector, a MaybeHttpsStream<T> enum unifying plain and TLS-wrapped streams behind hyper’s Read/Write traits, and an optional https_only(true) mode that rejects non-HTTPS URIs outright instead of silently falling back to plain text. It’s the connector used in hyper’s own official examples for making outbound HTTPS requests.
What You Get
- A
Service<Uri>-implementingHttpsConnector<T>ready to hand to hyper-util’s legacyClientbuilder. - A
MaybeHttpsStream<T>enum unifying plain and TLS-wrapped streams behind hyper’sRead/Writetraits. - An
HttpsConnecting<T>future type representing the in-flight connect-plus-handshake operation. - Optional
alpnandvendoredCargo features for HTTP/2 negotiation and bundled-OpenSSL builds.
Common Use Cases
- Outbound HTTPS calls from Rust backend services and CLIs built on hyper.
- Enforcing TLS-only connections for security-sensitive clients via
https_only(true). - Wrapping a custom base connector (e.g. one with connection-pooling tweaks) with TLS support.
- Building lightweight HTTP clients where OS-native TLS is preferable to a vendored stack.
Under The Hood
Architecture
The crate exposes HttpsConnector<T>, which implements tower’s Service<Uri> trait so it can be dropped into hyper-util’s Client builder as a pluggable connector. Its call() method inspects the request URI’s scheme, delegates the underlying TCP connect to the wrapped inner service (defaulting to hyper-util’s HttpConnector), and only layers a TLS handshake through TokioIo adapters when the scheme is https, producing a MaybeHttpsStream<T> enum with Http/Https variants that implement hyper’s Read/Write traits by dispatching to the active branch. With the whole implementation living in two files (client.rs, stream.rs) totaling a few hundred lines, it’s a lean decorator around hyper’s connector abstraction rather than a layered system, but as the sole hyper/native-tls integration point, any change to the core Service<Uri> contract ripples directly into every client built on it.
Tech Stack
Built for Rust 2018 edition (minimum Rust version 1.63) against hyper 1.x and hyper-util 0.1 (client-legacy and tokio features), with native-tls 0.2 providing the actual TLS handshake and tokio-native-tls 0.3 wrapping it for async use; tower-service 0.3 supplies the Service trait the connector implements, and bytes handles buffer types. There’s no build script or bespoke tooling — it’s a pure Cargo library crate consumed as a dependency, with optional alpn and vendored Cargo features toggling native-tls’s ALPN support and vendored OpenSSL respectively.
Code Quality
No test files exist anywhere in the repository — validation relies entirely on the GitHub Actions CI workflow and the doc-tested example in the crate’s top-level documentation rather than a dedicated test suite. Error handling favors a boxed dyn std::error::Error (BoxError) over a typed error enum, with a single custom ForceHttpsButUriNotHttps struct for the one domain-specific failure case. Naming is consistent, idiomatic Rust, and the crate enables #![deny(missing_docs)] and #![deny(missing_debug_implementations)] at the root, enforcing documentation coverage and Debug implementations at compile time even without a formal test suite.
API Design
The public surface is deliberately narrow: HttpsConnector::new() for the common case, HttpsConnector::from((connector, tls)) for swapping in a custom base connector or preconfigured TlsConnector, and https_only(bool) for enforcing HTTPS. Getting started requires almost no boilerplate — the crate’s own README example wires the connector into a Client builder in three lines — though the API doesn’t expose any way to customize TLS certificate verification without dropping to native-tls’s own builder first.