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/17 13:47:22 UTC

(opendal) 01/01: feat(transport): add native-tls and webpki-roots TLS options for reqwest

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

commit 43acc61694e3033cddaae1e0b819ad7640ec9f31
Author: Erick Guan <29...@users.noreply.github.com>
AuthorDate: Wed Jun 17 21:46:59 2026 +0800

    feat(transport): add native-tls and webpki-roots TLS options for reqwest
    
    Add TLS backend selection features to the reqwest HTTP transport crate:
    
    - `native-tls`: Platform TLS (SChannel on Windows, Secure Transport on
      macOS, OpenSSL on Linux)
    - `webpki-roots`: Rustls with bundled Mozilla root certificates
    
    The existing `rustls` (default) and `rustls-no-provider` features are
    unchanged. Users enable exactly one feature via the facade crate:
    `reqwest-native-tls` or `reqwest-webpki-roots`.
    
    When the `webpki-roots` feature is active, the default reqwest client is
    built with a preconfigured rustls ClientConfig using the bundled root
    store. For all other backends, reqwest's own feature-flag-driven defaults
    apply.
---
 core/Cargo.lock                         | 21 +++++++++++++++++++
 core/Cargo.toml                         |  8 ++++++++
 core/http-transports/reqwest/Cargo.toml |  4 ++++
 core/http-transports/reqwest/src/lib.rs | 36 ++++++++++++++++++++++++++++++++-
 4 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/core/Cargo.lock b/core/Cargo.lock
index 3d31931e3..b05b47a45 100644
--- a/core/Cargo.lock
+++ b/core/Cargo.lock
@@ -4653,6 +4653,22 @@ dependencies = [
  "tower-service",
 ]
 
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper 1.9.0",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
 [[package]]
 name = "hyper-util"
 version = "0.1.20"
@@ -6499,6 +6515,8 @@ dependencies = [
  "http-body 1.0.1",
  "opendal-core",
  "reqwest",
+ "rustls 0.23.40",
+ "webpki-roots 1.0.7",
 ]
 
 [[package]]
@@ -9372,11 +9390,13 @@ dependencies = [
  "http-body-util",
  "hyper 1.9.0",
  "hyper-rustls 0.27.9",
+ "hyper-tls",
  "hyper-util",
  "js-sys",
  "log",
  "mime",
  "mime_guess",
+ "native-tls",
  "percent-encoding",
  "pin-project-lite",
  "quinn",
@@ -9387,6 +9407,7 @@ dependencies = [
  "serde_json",
  "sync_wrapper 1.0.2",
  "tokio",
+ "tokio-native-tls",
  "tokio-rustls 0.26.4",
  "tokio-util",
  "tower 0.5.3",
diff --git a/core/Cargo.toml b/core/Cargo.toml
index abd30f49d..4e07d3d7d 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -121,6 +121,10 @@ layers-tail-cut = ["dep:opendal-layer-tail-cut"]
 layers-throttle = ["dep:opendal-layer-throttle"]
 layers-timeout = ["dep:opendal-layer-timeout"]
 layers-tracing = ["dep:opendal-layer-tracing"]
+reqwest-native-tls = [
+  "http-transport-reqwest",
+  "opendal-http-transport-reqwest/native-tls",
+]
 reqwest-rustls-no-provider-tls = [
   "http-transport-reqwest",
   "opendal-http-transport-reqwest/rustls-no-provider",
@@ -129,6 +133,10 @@ reqwest-rustls-tls = [
   "http-transport-reqwest",
   "opendal-http-transport-reqwest/rustls",
 ]
+reqwest-webpki-roots = [
+  "http-transport-reqwest",
+  "opendal-http-transport-reqwest/webpki-roots",
+]
 services-aliyun-drive = ["dep:opendal-service-aliyun-drive"]
 services-alluxio = ["dep:opendal-service-alluxio"]
 services-azblob = ["dep:opendal-service-azblob"]
diff --git a/core/http-transports/reqwest/Cargo.toml b/core/http-transports/reqwest/Cargo.toml
index 1b72144e2..221b7cc36 100644
--- a/core/http-transports/reqwest/Cargo.toml
+++ b/core/http-transports/reqwest/Cargo.toml
@@ -32,8 +32,10 @@ all-features = true
 
 [features]
 default = []
+native-tls = ["reqwest/native-tls"]
 rustls = ["reqwest/rustls"]
 rustls-no-provider = ["reqwest/rustls-no-provider"]
+webpki-roots = ["rustls", "dep:rustls", "dep:webpki-roots"]
 
 [dependencies]
 bytes = { workspace = true }
@@ -44,3 +46,5 @@ opendal-core = { path = "../../core", version = "0.57.0", default-features = fal
 reqwest = { version = "0.13.4", features = [
   "stream",
 ], default-features = false }
+rustls = { version = "0.23", optional = true, default-features = false }
+webpki-roots = { version = "1", optional = true }
diff --git a/core/http-transports/reqwest/src/lib.rs b/core/http-transports/reqwest/src/lib.rs
index 6f7c49b3a..0aa3968b5 100644
--- a/core/http-transports/reqwest/src/lib.rs
+++ b/core/http-transports/reqwest/src/lib.rs
@@ -16,6 +16,22 @@
 // under the License.
 
 //! Reqwest based HTTP transport for Apache OpenDAL.
+//!
+//! # TLS backends
+//!
+//! Enable one of the following features to select a TLS backend:
+//!
+//! | Feature | Backend |
+//! |---|---|
+//! | `rustls` | Rustls with native (platform) root certificates |
+//! | `rustls-no-provider` | Rustls without a built-in crypto provider |
+//! | `native-tls` | Platform TLS (SChannel / Secure Transport / OpenSSL) |
+//! | `webpki-roots` | Rustls with bundled Mozilla root certificates |
+//!
+//! When multiple features are enabled (e.g. via `--all-features`), the
+//! `webpki-roots` client configuration takes precedence; otherwise the
+//! default `reqwest::Client::new()` is used which lets reqwest pick the
+//! backend from its own feature flags.
 
 #![deny(missing_docs)]
 
@@ -38,7 +54,25 @@ use opendal_core::Result;
 use opendal_core::raw::parse_content_encoding;
 use opendal_core::raw::parse_content_length;
 
-static DEFAULT_REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(reqwest::Client::new);
+static DEFAULT_REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(build_default_client);
+
+#[cfg(feature = "webpki-roots")]
+fn build_default_client() -> reqwest::Client {
+    let root_store =
+        rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
+    let tls_config = rustls::ClientConfig::builder()
+        .with_root_certificates(root_store)
+        .with_no_client_auth();
+    reqwest::Client::builder()
+        .use_preconfigured_tls(tls_config)
+        .build()
+        .expect("failed to build reqwest client with webpki-roots")
+}
+
+#[cfg(not(feature = "webpki-roots"))]
+fn build_default_client() -> reqwest::Client {
+    reqwest::Client::new()
+}
 
 /// A [`reqwest::Client`] backed HTTP transport.
 ///