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");
+    }
+}