Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindings/dotnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ doc = false
# this crate won't be published, we always use the local version
opendal = { version = ">=0", path = "../../core", features = [
"blocking",
"reqwest-rustls-tls",
"http-transport-reqwest",
"executors-tokio",

# enabled layers
Expand Down
2 changes: 1 addition & 1 deletion bindings/java/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jni = { version = "0.22.4" }
# opendal-java won't be published to crates.io, we always use the local version
opendal = { version = ">=0", path = "../../core", default-features = false, features = [
"blocking",
"reqwest-rustls-tls",
"http-transport-reqwest-rustls",
"executors-tokio",

# enabled layers
Expand Down
19 changes: 19 additions & 0 deletions core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 23 additions & 7 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,32 @@ blocking = ["opendal-core/blocking"]
default = [
"auto-register-services",
"http-transport-reqwest",
"reqwest-rustls-tls",
Comment thread
Xuanwo marked this conversation as resolved.
"executors-tokio",
"layers-concurrent-limit",
"layers-logging",
"layers-retry",
"layers-timeout",
]
executors-tokio = ["opendal-core/executors-tokio"]
http-transport-reqwest = ["dep:opendal-http-transport-reqwest"]
# Default http-transport-reqwest transport feature, used for:
# - install default http transport
# - feature gate for various of bindings dependency
http-transport-reqwest = ["http-transport-reqwest-rustls"]
# Use reqwest with platform TLS via native-tls.
http-transport-reqwest-native-tls = [
"dep:opendal-http-transport-reqwest",
"opendal-http-transport-reqwest/native-tls",
]
# Use reqwest with Rustls, with default aws-lc-rs crypto provider.
http-transport-reqwest-rustls = [
"dep:opendal-http-transport-reqwest",
"opendal-http-transport-reqwest/rustls",
]
# Use reqwest with Rustls and no built-in crypto provider.
http-transport-reqwest-rustls-no-provider = [
"dep:opendal-http-transport-reqwest",
"opendal-http-transport-reqwest/rustls-no-provider",
]
internal-tokio-rt = ["opendal-core/internal-tokio-rt"]
layers-async-backtrace = ["dep:opendal-layer-async-backtrace"]
layers-await-tree = ["dep:opendal-layer-await-tree"]
Expand All @@ -121,14 +138,13 @@ 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"]
# Deprecated since 0.58; Will delete in 0.59.
reqwest-rustls-no-provider-tls = [
Comment thread
Xuanwo marked this conversation as resolved.
"http-transport-reqwest",
"opendal-http-transport-reqwest/rustls-no-provider",
]
reqwest-rustls-tls = [
"http-transport-reqwest",
"opendal-http-transport-reqwest/rustls",
"http-transport-reqwest-rustls-no-provider",
]
# Deprecated since 0.58; Will delete in 0.59.
reqwest-rustls-tls = ["http-transport-reqwest", "http-transport-reqwest-rustls"]
services-aliyun-drive = ["dep:opendal-service-aliyun-drive"]
services-alluxio = ["dep:opendal-service-alluxio"]
services-azblob = ["dep:opendal-service-azblob"]
Expand Down
14 changes: 13 additions & 1 deletion core/http-transports/reqwest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ version = { workspace = true }
all-features = true

[features]
default = []
default = ["rustls"]
# Use platform TLS via reqwest's native-tls feature.
native-tls = ["reqwest/native-tls"]
# Use Rustls with reqwest's default crypto provider and platform certificate verification.
rustls = ["reqwest/rustls"]
# Use Rustls without a built-in crypto provider.
#
# `rustls` builds `aws_lc_rs` crypto provider by default. When using other TLS backends,
# consider features like:
# - ring
# - fips
# Also read more about `rustls-native-certs` crate.
#
# To use WebPKI roots, read more about `webpki_roots` crate as well.
rustls-no-provider = ["reqwest/rustls-no-provider"]

[dependencies]
Expand Down
196 changes: 196 additions & 0 deletions core/http-transports/reqwest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# opendal-http-transport-reqwest

Reqwest-based HTTP transport for [Apache OpenDAL](https://opendal.apache.org).

This crate provides `ReqwestTransport`, an implementation of OpenDAL's
`HttpTransport` trait backed by [reqwest](https://crates.io/crates/reqwest).

## TLS configuration

OpenDAL does not add another TLS backend selector on top of reqwest. Pick the
Cargo feature set you need, build a `reqwest::Client` with reqwest's
`ClientBuilder`, and pass that client to `ReqwestTransport::new`.

The default feature is `rustls`. When `ReqwestTransport::default()` is used on
native targets, this crate explicitly asks reqwest for the Rustls backend if the
`rustls` feature is compiled in.

If this crate is built with only `rustls-no-provider`, install a Rustls default
crypto provider before using `ReqwestTransport::default()`, or build a
preconfigured `reqwest::Client` yourself and pass it to `ReqwestTransport::new`.

### Reqwest TLS resolution

Reqwest has its own TLS resolution rules. For OpenDAL users, the practical
resolution order is:

- Explicit reqwest client configuration wins. Call
`ClientBuilder::tls_backend_rustls()`,
`ClientBuilder::tls_backend_native()`, or
`ClientBuilder::tls_backend_preconfigured()`, but reqwest documents that API
as semver-unstable because the concrete TLS config type must match reqwest's
dependency versions.
- Otherwise, reqwest's automatic TLS backend selection applies. The reqwest
`default-tls` feature takes precedence if any crate in the dependency tree
enables it. Disable reqwest defaults with `default-features = false` when you
need deterministic TLS features.
- Cargo features are additive. If multiple TLS backends are compiled by feature
unification, use explicit client configuration instead of relying on automatic
selection.

See reqwest's TLS documentation:

- [reqwest TLS module](https://docs.rs/reqwest/latest/reqwest/tls/index.html)
- [ClientBuilder::tls_backend_rustls](https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.tls_backend_rustls)
- [ClientBuilder::tls_backend_native](https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.tls_backend_native)
- [ClientBuilder::tls_backend_preconfigured](https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.tls_backend_preconfigured)

### Feature matrix
Comment thread
Xuanwo marked this conversation as resolved.

| Feature | Crypto provider | Certificate roots | Use when |
|---------|-----------------|-------------------|----------|
| `rustls` (default) | reqwest default | Platform verifier | Pure-Rust TLS with the system trust store |
| `rustls-no-provider` | You provide | You provide | BYO crypto provider, roots, or FIPS module |
| `native-tls` | OS library | OS trust store | You want platform TLS |

## Cargo features

Most users depend on the `opendal` facade crate:

```toml
[dependencies]
# Default: reqwest transport with rustls.
opendal = { version = "0.57" }
```

To select one transport TLS feature explicitly, disable default features:

```toml
[dependencies]
opendal = { version = "0.57", default-features = false, features = [
"http-transport-reqwest-rustls",
] }
```

## Build different TLS backends

### Rustls

Use reqwest's Rustls backend explicitly when you build the client:

```toml
[dependencies]
opendal = { version = "0.57", default-features = false, features = [
"http-transport-reqwest-rustls",
] }
reqwest = { version = "0.13.4", default-features = false, features = [
"rustls",
"stream",
] }
```

```rust
use std::time::Duration;

use opendal::HttpTransporter;
use opendal::ReqwestTransport;

fn build_transport() -> Result<HttpTransporter, Box<dyn std::error::Error>> {
let client = reqwest::Client::builder()
.tls_backend_rustls()
.connect_timeout(Duration::from_secs(10))
.build()?;

Ok(HttpTransporter::new(ReqwestTransport::new(client)))
}
```

### Native TLS

Use `native-tls` when you want reqwest to delegate TLS to the platform stack:

```toml
[dependencies]
opendal = { version = "0.57", default-features = false, features = [
"http-transport-reqwest-native-tls",
] }
reqwest = { version = "0.13.4", default-features = false, features = [
"native-tls",
"stream",
] }
```

```rust
use opendal::HttpTransporter;
use opendal::ReqwestTransport;

fn build_transport() -> Result<HttpTransporter, Box<dyn std::error::Error>> {
let client = reqwest::Client::builder()
.tls_backend_native()
.build()?;

Ok(HttpTransporter::new(ReqwestTransport::new(client)))
}
```

### rustls-no-provider

When using `wekpki-roots`, you could use `rustls-no-provider`.

Use `rustls-no-provider` when the application owns the Rustls provider and
certificate roots. This example only configures Mozilla roots from
`webpki-roots`; install or configure the Rustls crypto provider in application
startup.

```toml
[dependencies]
opendal = { version = "0.57", default-features = false, features = [
"http-transport-reqwest-rustls-no-provider",
] }
reqwest = { version = "0.13.4", default-features = false, features = [
"rustls-no-provider",
"stream",
] }
rustls = { version = "0.23", default-features = false, features = ["std"] }
webpki-roots = "1"
```

```rust
use opendal::HttpTransporter;
use opendal::ReqwestTransport;

fn build_transport() -> Result<HttpTransporter, Box<dyn std::error::Error>> {
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();

let client = reqwest::Client::builder()
.tls_backend_preconfigured(tls_config)
.build()?;

Ok(HttpTransporter::new(ReqwestTransport::new(client)))
}
```

To use `ring` crate, please refer `rustls` documentation.

## WASM targets

On `wasm32-unknown-unknown` and `wasm32-none`, reqwest switches to its WASM
client implementation. It uses the browser or host Fetch API instead of hyper.
TLS is provided by the browser or host environment, so the effective choice is
the system TLS stack exposed to that environment.

This means `native-tls`, `rustls`, `rustls-no-provider`, custom root stores,
and custom Rustls crypto providers do not select the TLS implementation for
wasm requests. See [reqwest's WASM documentation](https://docs.rs/reqwest/latest/reqwest/#wasm)
for the target-specific limitations.

## License and Trademarks

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0

Apache OpenDAL, OpenDAL, and Apache are either registered trademarks or trademarks of the Apache Software Foundation.
Loading
Loading