You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by er...@apache.org on 2026/06/18 09:08:43 UTC
(opendal) branch feat/reqwest-tls-options updated: refactor(transport): simplify TLS backend enum and add tests
This is an automated email from the ASF dual-hosted git repository.
erickguan pushed a commit to branch feat/reqwest-tls-options
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/feat/reqwest-tls-options by this push:
new 67e555775 refactor(transport): simplify TLS backend enum and add tests
67e555775 is described below
commit 67e555775d5abb59bb4ab28ee52d00e82d33e584
Author: Erick Guan <29...@users.noreply.github.com>
AuthorDate: Thu Jun 18 17:08:19 2026 +0800
refactor(transport): simplify TLS backend enum and add tests
Remove ReqwestTlsBackend::Default and the native-tls-no-alpn/vendored
variants. NativeTls is now the builder default. Strip the corresponding
Cargo features from both the transport crate and the facade. Add
bullet-point documentation for each TLS feature and unit tests for
FromStr, Display, builder defaults, and transport construction.
---
core/Cargo.lock | 10 --
core/Cargo.toml | 12 --
core/http-transports/reqwest/Cargo.toml | 3 -
core/http-transports/reqwest/README.md | 59 +++----
core/http-transports/reqwest/src/lib.rs | 285 ++++++++++++++------------------
5 files changed, 147 insertions(+), 222 deletions(-)
diff --git a/core/Cargo.lock b/core/Cargo.lock
index c9319c448..b05b47a45 100644
--- a/core/Cargo.lock
+++ b/core/Cargo.lock
@@ -7805,15 +7805,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
-[[package]]
-name = "openssl-src"
-version = "300.6.1+3.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
-dependencies = [
- "cc",
-]
-
[[package]]
name = "openssl-sys"
version = "0.9.115"
@@ -7822,7 +7813,6 @@ checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781"
dependencies = [
"cc",
"libc",
- "openssl-src",
"pkg-config",
"vcpkg",
]
diff --git a/core/Cargo.toml b/core/Cargo.toml
index ec1a25248..6a5c8b67e 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -99,18 +99,6 @@ http-transport-reqwest-native-tls = [
"http-transport-reqwest",
"opendal-http-transport-reqwest/native-tls",
]
-http-transport-reqwest-native-tls-no-alpn = [
- "http-transport-reqwest",
- "opendal-http-transport-reqwest/native-tls-no-alpn",
-]
-http-transport-reqwest-native-tls-vendored = [
- "http-transport-reqwest",
- "opendal-http-transport-reqwest/native-tls-vendored",
-]
-http-transport-reqwest-native-tls-vendored-no-alpn = [
- "http-transport-reqwest",
- "opendal-http-transport-reqwest/native-tls-vendored-no-alpn",
-]
http-transport-reqwest-rustls = [
"http-transport-reqwest",
"opendal-http-transport-reqwest/rustls",
diff --git a/core/http-transports/reqwest/Cargo.toml b/core/http-transports/reqwest/Cargo.toml
index 895e1b38e..4e96877d9 100644
--- a/core/http-transports/reqwest/Cargo.toml
+++ b/core/http-transports/reqwest/Cargo.toml
@@ -33,9 +33,6 @@ all-features = true
[features]
default = ["native-tls"]
native-tls = ["reqwest/native-tls"]
-native-tls-no-alpn = ["reqwest/native-tls-no-alpn"]
-native-tls-vendored = ["reqwest/native-tls-vendored"]
-native-tls-vendored-no-alpn = ["reqwest/native-tls-vendored-no-alpn"]
rustls = ["reqwest/rustls"]
rustls-no-provider = ["reqwest/rustls-no-provider"]
webpki-roots = ["rustls", "dep:rustls", "dep:webpki-roots"]
diff --git a/core/http-transports/reqwest/README.md b/core/http-transports/reqwest/README.md
index bde13c7b2..c7fbb2176 100644
--- a/core/http-transports/reqwest/README.md
+++ b/core/http-transports/reqwest/README.md
@@ -7,55 +7,48 @@ This crate provides `ReqwestTransport`, an implementation of OpenDAL's
## TLS backends
-By default, this crate enables `native-tls`. The `opendal` facade crate also
-enables `http-transport-reqwest-native-tls` by default, which forwards to this
-crate's `native-tls` feature.
+Enable one of the following Cargo features to select a TLS backend.
+The `default` feature enables `native-tls`.
+
+- **`native-tls`** — Platform TLS (SChannel on Windows, Secure Transport
+ on macOS, OpenSSL on Linux). Links against the OS TLS library. Requires
+ system development packages for OpenSSL on Linux.
+
+- **`rustls`** — [Rustls](https://crates.io/crates/rustls) with the
+ aws-lc-rs crypto provider and platform certificate verification.
+ Pure-Rust TLS stack, no system TLS dependency.
+
+- **`rustls-no-provider`** — Rustls without a built-in crypto provider.
+ You must install a `rustls::crypto::CryptoProvider` before building a
+ client. Use this when you want to bring your own provider (e.g. `ring`
+ or a FIPS-certified module).
+
+- **`webpki-roots`** — Rustls with bundled
+ [Mozilla root certificates](https://crates.io/crates/webpki-roots).
+ Fully self-contained: no platform certificate store dependency. Good for
+ reproducible builds and environments with a stripped-down root store.
-Enable one or more of the following features to make TLS backends available.
In application builds, prefer selecting a single backend. In workspace or
-`--all-features` builds, Cargo may enable multiple backend features via feature
-unification.
-
-| Feature | Backend | Notes |
-|---|---|---|
-| `native-tls` | Platform TLS: SChannel on Windows, Secure Transport on macOS, OpenSSL on Linux | Default |
-| `native-tls-no-alpn` | Platform TLS without ALPN | Use when ALPN is unavailable or not wanted |
-| `native-tls-vendored` | Platform TLS with vendored OpenSSL on Linux | Avoids relying on system OpenSSL development packages |
-| `native-tls-vendored-no-alpn` | Vendored platform TLS without ALPN | Combines vendored OpenSSL and no ALPN |
-| `rustls` | [Rustls](https://crates.io/crates/rustls) with platform certificate verification | Uses reqwest's bundled Rustls crypto provider |
-| `rustls-no-provider` | Rustls without a built-in crypto provider | You must install a `CryptoProvider` before building a client |
-| `webpki-roots` | Rustls with bundled [Mozilla root certificates](https://crates.io/crates/webpki-roots) | Fully self-contained, no platform dependency |
-
-`ReqwestTransport::default()` and `ReqwestTlsBackend::Default` prefer native
-TLS whenever any native TLS feature is compiled. If no native TLS feature is
-compiled, the default falls back to `webpki-roots`, then `rustls`, then
-`rustls-no-provider`, and finally reqwest without an explicit TLS backend.
-
-When selecting a concrete `ReqwestTlsBackend`, building the transport fails
-with `ErrorKind::ConfigInvalid` if the corresponding Cargo feature was not
-compiled into this crate.
+`--all-features` builds, Cargo may enable multiple backend features via
+feature unification; the transport picks native TLS when available.
### Usage via the `opendal` facade crate
Most users depend on `opendal` rather than this crate directly. The facade
-installs this transport when `http-transport-reqwest` is enabled.
+installs this transport when any `http-transport-reqwest-*` feature is enabled.
```toml
-# Default - reqwest transport with native-tls
+# Default — reqwest transport with native-tls
opendal = { version = "0.57" }
```
-To select a different TLS backend, enable `http-transport-reqwest` on the
-facade with the backend feature you need:
+To select a different TLS backend, disable default features and enable the
+one you need:
```toml
opendal = { version = "0.57", default-features = false, features = ["http-transport-reqwest-rustls"] }
```
-Cargo builds one `opendal-http-transport-reqwest` package with the union of
-all requested features in the active dependency graph. If multiple crates
-request different TLS features, the transport is compiled with all of them.
-
### Direct usage
```toml
diff --git a/core/http-transports/reqwest/src/lib.rs b/core/http-transports/reqwest/src/lib.rs
index 235cf8032..2faa1e8a7 100644
--- a/core/http-transports/reqwest/src/lib.rs
+++ b/core/http-transports/reqwest/src/lib.rs
@@ -19,30 +19,32 @@
//!
//! # TLS backends
//!
-//! The `default` feature enables `native-tls`. The `opendal` facade crate also
-//! enables `http-transport-reqwest-native-tls` by default, which forwards to
-//! this crate's `native-tls` feature.
+//! Enable one of the following Cargo features to select a TLS backend.
+//! The `default` feature enables `native-tls`.
+//!
+//! - **`native-tls`** — Platform TLS (SChannel on Windows, Secure Transport
+//! on macOS, OpenSSL on Linux). Links against the OS TLS library. Requires
+//! system development packages for OpenSSL on Linux.
+//!
+//! - **`rustls`** — [Rustls](https://crates.io/crates/rustls) with the
+//! aws-lc-rs crypto provider and platform certificate verification.
+//! Pure-Rust TLS stack, no system TLS dependency.
+//!
+//! - **`rustls-no-provider`** — Rustls without a built-in crypto provider.
+//! You must install a [`rustls::crypto::CryptoProvider`] before building a
+//! client. Use this when you want to bring your own provider (e.g. `ring`
+//! or a FIPS-certified module).
+//!
+//! - **`webpki-roots`** — Rustls with bundled
+//! [Mozilla root certificates](https://crates.io/crates/webpki-roots).
+//! Fully self-contained: no platform certificate store dependency. Good for
+//! reproducible builds and environments with a stripped-down root store.
//!
-//! Enable one or more of the following features to make TLS backends available.
//! In application builds, prefer selecting a single backend. In workspace or
//! `--all-features` builds, Cargo may enable multiple backend features via
-//! feature unification.
-//!
-//! | Feature | Backend |
-//! |---|---|
-//! | `native-tls` | Platform TLS (SChannel / Secure Transport / OpenSSL) |
-//! | `native-tls-no-alpn` | Platform TLS without ALPN |
-//! | `native-tls-vendored` | Platform TLS with vendored OpenSSL on Linux |
-//! | `native-tls-vendored-no-alpn` | Vendored platform TLS without ALPN |
-//! | `rustls` | Rustls with platform certificate verification |
-//! | `rustls-no-provider` | Rustls without a built-in crypto provider |
-//! | `webpki-roots` | Rustls with bundled Mozilla root certificates |
+//! feature unification; the transport picks native TLS when available.
//!
-//! [`ReqwestTransport::default`] and [`ReqwestTlsBackend::Default`] prefer
-//! native TLS whenever any native TLS feature is compiled. If no native TLS
-//! feature is compiled, the default falls back to `webpki-roots`, then
-//! `rustls`, then `rustls-no-provider`, and finally reqwest without an explicit
-//! TLS backend.
+//! # Builder example
//!
//! Use [`ReqwestTransport::builder`] when applications need to select a TLS
//! backend at runtime while still configuring reqwest-specific options:
@@ -96,25 +98,20 @@ fn build_default_client() -> reqwest::Client {
}
/// TLS backend used by [`ReqwestTransportBuilder`].
-#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
+///
+/// Each variant corresponds to one of the Cargo features exposed by this
+/// crate. Building a transport with a variant whose feature is not compiled
+/// returns [`ErrorKind::ConfigInvalid`].
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ReqwestTlsBackend {
- /// Use OpenDAL's default TLS backend selection.
- #[default]
- Default,
- /// Use platform TLS through reqwest's `native-tls` feature.
+ /// Platform TLS through reqwest's `native-tls` feature.
NativeTls,
- /// Use platform TLS without ALPN through reqwest's `native-tls-no-alpn` feature.
- NativeTlsNoAlpn,
- /// Use platform TLS with vendored OpenSSL through reqwest's `native-tls-vendored` feature.
- NativeTlsVendored,
- /// Use vendored platform TLS without ALPN through reqwest's `native-tls-vendored-no-alpn` feature.
- NativeTlsVendoredNoAlpn,
- /// Use Rustls through reqwest's `rustls` feature.
+ /// Rustls with the aws-lc-rs crypto provider through reqwest's `rustls` feature.
Rustls,
- /// Use Rustls without a built-in crypto provider through reqwest's `rustls-no-provider` feature.
+ /// Rustls without a built-in crypto provider through reqwest's `rustls-no-provider` feature.
RustlsNoProvider,
- /// Use Rustls with bundled Mozilla root certificates.
+ /// Rustls with bundled Mozilla root certificates.
WebpkiRoots,
}
@@ -122,11 +119,7 @@ impl ReqwestTlsBackend {
/// Return the stable feature-style name for this TLS backend.
pub fn as_str(self) -> &'static str {
match self {
- ReqwestTlsBackend::Default => "default",
ReqwestTlsBackend::NativeTls => "native-tls",
- ReqwestTlsBackend::NativeTlsNoAlpn => "native-tls-no-alpn",
- ReqwestTlsBackend::NativeTlsVendored => "native-tls-vendored",
- ReqwestTlsBackend::NativeTlsVendoredNoAlpn => "native-tls-vendored-no-alpn",
ReqwestTlsBackend::Rustls => "rustls",
ReqwestTlsBackend::RustlsNoProvider => "rustls-no-provider",
ReqwestTlsBackend::WebpkiRoots => "webpki-roots",
@@ -147,11 +140,7 @@ impl FromStr for ReqwestTlsBackend {
let value = value.trim();
match value {
- "default" => Ok(Self::Default),
"native-tls" => Ok(Self::NativeTls),
- "native-tls-no-alpn" => Ok(Self::NativeTlsNoAlpn),
- "native-tls-vendored" => Ok(Self::NativeTlsVendored),
- "native-tls-vendored-no-alpn" => Ok(Self::NativeTlsVendoredNoAlpn),
"rustls" => Ok(Self::Rustls),
"rustls-no-provider" => Ok(Self::RustlsNoProvider),
"webpki-roots" => Ok(Self::WebpkiRoots),
@@ -184,7 +173,7 @@ impl ReqwestTransportBuilder {
pub fn new() -> Self {
Self {
client_builder: reqwest::Client::builder(),
- tls_backend: ReqwestTlsBackend::Default,
+ tls_backend: ReqwestTlsBackend::NativeTls,
}
}
@@ -192,7 +181,7 @@ impl ReqwestTransportBuilder {
pub fn from_client_builder(client_builder: reqwest::ClientBuilder) -> Self {
Self {
client_builder,
- tls_backend: ReqwestTlsBackend::Default,
+ tls_backend: ReqwestTlsBackend::NativeTls,
}
}
@@ -289,7 +278,6 @@ fn apply_tls_backend(
tls_backend: ReqwestTlsBackend,
) -> Result<reqwest::ClientBuilder> {
match tls_backend {
- ReqwestTlsBackend::Default => apply_default_tls_backend(client_builder),
ReqwestTlsBackend::NativeTls => {
#[cfg(feature = "native-tls")]
{
@@ -300,39 +288,6 @@ fn apply_tls_backend(
Err(tls_backend_not_compiled(tls_backend, "native-tls"))
}
}
- ReqwestTlsBackend::NativeTlsNoAlpn => {
- #[cfg(feature = "native-tls-no-alpn")]
- {
- Ok(client_builder.tls_backend_native())
- }
- #[cfg(not(feature = "native-tls-no-alpn"))]
- {
- Err(tls_backend_not_compiled(tls_backend, "native-tls-no-alpn"))
- }
- }
- ReqwestTlsBackend::NativeTlsVendored => {
- #[cfg(feature = "native-tls-vendored")]
- {
- Ok(client_builder.tls_backend_native())
- }
- #[cfg(not(feature = "native-tls-vendored"))]
- {
- Err(tls_backend_not_compiled(tls_backend, "native-tls-vendored"))
- }
- }
- ReqwestTlsBackend::NativeTlsVendoredNoAlpn => {
- #[cfg(feature = "native-tls-vendored-no-alpn")]
- {
- Ok(client_builder.tls_backend_native())
- }
- #[cfg(not(feature = "native-tls-vendored-no-alpn"))]
- {
- Err(tls_backend_not_compiled(
- tls_backend,
- "native-tls-vendored-no-alpn",
- ))
- }
- }
ReqwestTlsBackend::Rustls => {
#[cfg(feature = "rustls")]
{
@@ -366,83 +321,6 @@ fn apply_tls_backend(
}
}
-#[cfg(any(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn"
-))]
-fn apply_default_tls_backend(
- client_builder: reqwest::ClientBuilder,
-) -> Result<reqwest::ClientBuilder> {
- Ok(client_builder.tls_backend_native())
-}
-
-#[cfg(all(
- not(any(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn"
- )),
- feature = "webpki-roots"
-))]
-fn apply_default_tls_backend(
- client_builder: reqwest::ClientBuilder,
-) -> Result<reqwest::ClientBuilder> {
- Ok(client_builder.tls_backend_preconfigured(webpki_roots_tls_config()))
-}
-
-#[cfg(all(
- not(any(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn"
- )),
- not(feature = "webpki-roots"),
- feature = "rustls"
-))]
-fn apply_default_tls_backend(
- client_builder: reqwest::ClientBuilder,
-) -> Result<reqwest::ClientBuilder> {
- Ok(client_builder.tls_backend_rustls())
-}
-
-#[cfg(all(
- not(any(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn"
- )),
- not(feature = "webpki-roots"),
- not(feature = "rustls"),
- feature = "rustls-no-provider"
-))]
-fn apply_default_tls_backend(
- client_builder: reqwest::ClientBuilder,
-) -> Result<reqwest::ClientBuilder> {
- Ok(client_builder.tls_backend_rustls())
-}
-
-#[cfg(all(
- not(any(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn"
- )),
- not(feature = "webpki-roots"),
- not(feature = "rustls"),
- not(feature = "rustls-no-provider")
-))]
-fn apply_default_tls_backend(
- client_builder: reqwest::ClientBuilder,
-) -> Result<reqwest::ClientBuilder> {
- Ok(client_builder)
-}
-
#[cfg(feature = "webpki-roots")]
fn webpki_roots_tls_config() -> rustls::ClientConfig {
let root_store =
@@ -453,15 +331,7 @@ fn webpki_roots_tls_config() -> rustls::ClientConfig {
.with_no_client_auth()
}
-#[cfg(not(all(
- feature = "native-tls",
- feature = "native-tls-no-alpn",
- feature = "native-tls-vendored",
- feature = "native-tls-vendored-no-alpn",
- feature = "rustls",
- feature = "rustls-no-provider",
- feature = "webpki-roots"
-)))]
+#[allow(dead_code)]
fn tls_backend_not_compiled(tls_backend: ReqwestTlsBackend, feature: &'static str) -> Error {
Error::new(
ErrorKind::ConfigInvalid,
@@ -599,3 +469,90 @@ impl http_body::Body for HttpBufferBody {
http_body::SizeHint::with_exact(self.0.len() as u64)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_tls_backend_from_str() {
+ assert_eq!(
+ "native-tls".parse::<ReqwestTlsBackend>().unwrap(),
+ ReqwestTlsBackend::NativeTls
+ );
+ assert_eq!(
+ "rustls".parse::<ReqwestTlsBackend>().unwrap(),
+ ReqwestTlsBackend::Rustls
+ );
+ assert_eq!(
+ "rustls-no-provider".parse::<ReqwestTlsBackend>().unwrap(),
+ ReqwestTlsBackend::RustlsNoProvider
+ );
+ assert_eq!(
+ "webpki-roots".parse::<ReqwestTlsBackend>().unwrap(),
+ ReqwestTlsBackend::WebpkiRoots
+ );
+ }
+
+ #[test]
+ fn test_tls_backend_from_str_trims_whitespace() {
+ assert_eq!(
+ " native-tls ".parse::<ReqwestTlsBackend>().unwrap(),
+ ReqwestTlsBackend::NativeTls
+ );
+ }
+
+ #[test]
+ fn test_tls_backend_from_str_unknown() {
+ let err = "bogus".parse::<ReqwestTlsBackend>().unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
+ }
+
+ #[test]
+ fn test_tls_backend_display_roundtrip() {
+ let backends = [
+ ReqwestTlsBackend::NativeTls,
+ ReqwestTlsBackend::Rustls,
+ ReqwestTlsBackend::RustlsNoProvider,
+ ReqwestTlsBackend::WebpkiRoots,
+ ];
+ for backend in backends {
+ let s = backend.to_string();
+ let parsed: ReqwestTlsBackend = s.parse().unwrap();
+ assert_eq!(parsed, backend);
+ }
+ }
+
+ #[test]
+ fn test_builder_defaults_to_native_tls() {
+ let builder = ReqwestTransportBuilder::new();
+ assert_eq!(builder.tls_backend, ReqwestTlsBackend::NativeTls);
+ }
+
+ #[test]
+ fn test_builder_tls_backend_override() {
+ let builder = ReqwestTransportBuilder::new().tls_backend(ReqwestTlsBackend::Rustls);
+ assert_eq!(builder.tls_backend, ReqwestTlsBackend::Rustls);
+ }
+
+ #[test]
+ fn test_builder_configure() {
+ let transport = ReqwestTransportBuilder::new()
+ .configure(|b| b.connect_timeout(std::time::Duration::from_secs(5)))
+ .build();
+ assert!(transport.is_ok());
+ }
+
+ #[test]
+ fn test_default_transport_succeeds() {
+ let transport = ReqwestTransport::default();
+ assert_eq!(format!("{:?}", transport), "ReqwestTransport");
+ }
+
+ #[test]
+ fn test_from_reqwest_client() {
+ let client = reqwest::Client::new();
+ let transport = ReqwestTransport::from(client);
+ assert_eq!(format!("{:?}", transport), "ReqwestTransport");
+ }
+}