hyper-openssl
TLS support for Hyper HTTP clients and servers via rust-openssl, with an HttpsConnector, session-cache reuse, and ALPN negotiation.
Repository Health
Technical Analysis
hyper-openssl adds OpenSSL-backed TLS to the hyper HTTP library, filling the role that hyper-rustls fills for rustls users. At its core is SslStream<S>, a thin wrapper that adapts any stream implementing hyper’s async Read/Write traits into an openssl::ssl::SslStream, translating OpenSSL’s blocking-style WANT_READ/WANT_WRITE signals into hyper’s Poll-based reactor model.
Behind the optional client-legacy feature, the crate goes further and provides HttpsConnector and HttpsLayer, a tower Service/Layer pair that plugs directly into hyper-util’s legacy client. It inspects each request’s URI scheme, performs OpenSSL’s TLS handshake only for https:// requests, negotiates ALPN for HTTP/1.1 and HTTP/2, and transparently caches and replays TLS sessions across reconnects to the same host, all without requiring callers to manage OpenSSL details themselves beyond an optional configuration callback.
What You Get
- SslStream<S>, wrapping any hyper Read+Write stream in an OpenSSL TLS session
- HttpsConnector / HttpsLayer built on hyper-util’s client-legacy Service/Layer traits
- Automatic TLS session-cache reuse across connections to the same host and port
- ALPN negotiation support for HTTP/1.1 and HTTP/2 (h2) protocol selection
- A set_callback hook for per-connection SSL configuration overrides
Common Use Cases
- Adding HTTPS support to a custom hyper-based HTTP client when OpenSSL is required specifically (FIPS builds, custom cert stores, legacy TLS options) instead of rustls
- Building HTTP/1.1 and HTTP/2 clients that need ALPN-based protocol negotiation
- Implementing a TLS-terminating server on top of raw hyper connections
- Reusing TLS sessions across repeated requests to the same host to skip full handshakes
Under The Hood
Architecture The crate wraps any inner stream implementing hyper’s Read/Write traits in a StreamWrapper that stashes a raw pointer to the current poll Context (src/lib.rs, with_context), letting SslStream drive OpenSSL’s synchronous SslStream API from inside an async poll_read/poll_write call and translate ErrorCode::WANT_READ/WANT_WRITE into Poll::Pending (cvt_ossl). On top of that low-level shim, the optional client-legacy feature (src/client/legacy.rs, gated behind hyper-util) layers a tower Service/Layer implementation: HttpsLayer/HttpsConnector wrap an inner connector such as HttpConnector, inspect the request URI’s scheme to choose a plain passthrough (MaybeHttpsStream::Http) or an OpenSSL handshake (MaybeHttpsStream::Https), and maintain a size-bounded SessionCache (src/client/cache.rs, a LinkedHashSet plus a reverse index) for TLS session resumption. It is a thin, single-purpose adapter layer where nearly all logic lives in two files, and the only real invariant is that the with_context pointer stashing must stay paired with a pinned wrapper; a change to hyper’s Read/Write trait shapes would break the crate immediately.
Tech Stack The crate targets hyper 1.x’s rt traits directly and optionally depends on hyper-util (client-legacy feature) for its tower-based legacy client and HttpConnector, openssl 0.10 plus openssl-sys 0.9 for the actual TLS implementation, tower-layer/tower-service for the Layer/Service abstraction, pin-project for safely projecting the MaybeHttpsStream enum, and parking_lot::Mutex plus once_cell::OnceCell for the session cache and ex_data index, with linked_hash_set providing insertion-ordered session eviction. No async runtime is bundled directly, only the tokio feature flag enables hyper-util’s Tokio-specific HttpConnector; dev-dependencies pull in full hyper and tokio features for integration tests, and cfg flags like ossl102/ossl111 gate ALPN and session-related code behind detected OpenSSL versions.
Code Quality Tests live in src/test.rs behind #[cfg(test)] and are integration-style rather than unit tests: they spin up real TCP listeners with real OpenSSL certificates (test/cert.pem, test/key.pem) to exercise a raw client/server handshake, a legacy-client request/response cycle, and ALPN-negotiated HTTP/2, but there are no isolated unit tests for internal helpers like cvt, cvt_ossl, or SessionCache. Error handling is explicit and typed throughout, ErrorStack and ssl::Error propagate via Result and WouldBlock/WANT_READ conditions are deliberately mapped to Poll::Pending rather than swallowed, and #![warn(missing_docs)] enforces documentation on the public API. A CI workflow and dependabot config are present, and the small amount of unsafe code (the raw context pointer in StreamWrapper) carries explicit safety comments justifying each block.
API Design The public surface is deliberately minimal, one SslStream<S> type plus an optional HttpsConnector that callers layer onto any tower/hyper-util connector via the standard Layer trait, so it composes idiomatically with the rest of the hyper-util ecosystem rather than inventing its own client type. Session-resumption caching and ALPN negotiation happen automatically with no extra caller code, and set_callback offers an escape hatch for per-connection SSL customization without subclassing. Getting started needs little boilerplate, HttpsConnector::new() with the tokio feature yields a working HTTPS connector in one line, though the design itself is a well-established pattern for bridging async Read/Write pollers to OpenSSL that closely mirrors what hyper-rustls and tokio-openssl already do.