This is an automated email from the ASF dual-hosted git repository.
kylebarron pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git
The following commit(s) were added to refs/heads/main by this push:
new d339aee Make `reqwest` optional (#724)
d339aee is described below
commit d339aee4b7a8d63eff9df3599ceeced2d4ce03c3
Author: Bram Westerbaan <[email protected]>
AuthorDate: Fri Jun 12 19:07:42 2026 +0200
Make `reqwest` optional (#724)
* makes reqwest optional
* refactor!: make aws/azure/gcp/http depend on reqwest, not cloud
The role of cloud has been taken over by reqwest + cloud-base.
Implements
https://github.com/apache/arrow-rs-object-store/pull/724#discussion_r3342155266
BREAKING CHANGE: according to cargo semver-checks: aws/azure/gcp/http no
longer enable the cloud feature.
* document feature flags exactly as kevinjqliu proposed
* switch from `feature="cloud"` to `feature="reqwest"`
* Apply suggestion from @kevinjqliu
Co-authored-by: Kevin Liu <[email protected]>
---------
Co-authored-by: Andrew Lamb <[email protected]>
Co-authored-by: Kyle Barron <[email protected]>
Co-authored-by: Kyle Barron <[email protected]>
Co-authored-by: Kevin Liu <[email protected]>
---
.github/workflows/ci.yml | 22 ++++++++++++++
Cargo.toml | 33 +++++++++++++++++----
src/aws/builder.rs | 10 ++++++-
src/aws/client.rs | 5 ++++
src/aws/credential.rs | 12 ++++++--
src/aws/mod.rs | 12 ++++----
src/aws/precondition.rs | 6 ++--
src/aws/resolve.rs | 2 +-
src/azure/client.rs | 1 +
src/azure/credential.rs | 4 +++
src/azure/mod.rs | 4 +--
src/client/builder.rs | 22 +++++++++-----
src/client/get.rs | 6 ++--
src/client/header.rs | 6 ++--
src/client/http/body.rs | 6 ++--
src/client/http/connection.rs | 69 ++++++++++++++++++++++++++++++++++++-------
src/client/http/spawn.rs | 2 +-
src/client/mod.rs | 40 +++++++++++++------------
src/client/retry.rs | 13 +++++---
src/client/s3.rs | 2 +-
src/client/token.rs | 2 +-
src/config.rs | 2 +-
src/gcp/builder.rs | 7 +++++
src/gcp/mod.rs | 1 +
src/http/client.rs | 2 +-
src/integration.rs | 4 +--
src/lib.rs | 38 +++++++++++++-----------
src/parse.rs | 24 ++++++++-------
src/prefix.rs | 6 ++--
src/signer.rs | 2 +-
src/util.rs | 12 ++++----
tests/http.rs | 8 ++---
32 files changed, 268 insertions(+), 117 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 50041ee..c14df42 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,6 +57,26 @@ jobs:
run: cargo clippy --features azure -- -D warnings
- name: Run clippy with http feature
run: cargo clippy --features http -- -D warnings
+ - name: Run clippy with aws-base feature
+ run: cargo clippy --no-default-features --features aws-base -- -D
warnings
+ - name: Run clippy with azure-base feature
+ run: cargo clippy --no-default-features --features azure-base -- -D
warnings
+ - name: Run clippy with gcp-base feature
+ run: cargo clippy --no-default-features --features gcp-base -- -D
warnings
+ - name: Run clippy with http-base feature
+ run: cargo clippy --no-default-features --features http-base -- -D
warnings
+ - name: Verify base features do not enable reqwest
+ run: |
+ if cargo tree \
+ --no-default-features \
+ --features aws-base,azure-base,gcp-base,http-base \
+ --edges normal \
+ --prefix none \
+ -f '{p}' \
+ | grep -E '^reqwest v'; then
+ echo "reqwest must not be enabled by *-base features"
+ exit 1
+ fi
- name: Run clippy with integration feature
run: cargo clippy --no-default-features --features integration -- -D
warnings
- name: Run clippy with all features
@@ -196,6 +216,8 @@ jobs:
run: rustup target add wasm32-wasip1
- name: Build wasm32-wasip1
run: cargo build --all-features --target wasm32-wasip1
+ - name: Build wasm32-wasip1 without reqwest
+ run: cargo build --no-default-features --features aws-base --target
wasm32-wasip1
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf
| sh
- uses: actions/setup-node@v6
diff --git a/Cargo.toml b/Cargo.toml
index fa7dfa0..ddb4336 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -82,12 +82,35 @@ futures-channel = {version = "0.3", features = ["sink"]}
[features]
default = ["fs"]
-cloud = ["serde", "serde_json", "quick-xml", "hyper", "reqwest",
"reqwest/stream", "chrono/serde", "base64", "rand", "ring", "http-body-util",
"form_urlencoded", "serde_urlencoded", "tokio"]
-azure = ["cloud", "httparse"]
+
+# Shared cloud/provider implementation dependencies.
+# This intentionally does NOT include reqwest.
+cloud-base = ["serde", "serde_json", "quick-xml", "hyper", "chrono/serde",
"base64", "rand", "ring", "http-body-util", "form_urlencoded",
"serde_urlencoded", "tokio"]
+
+# Built-in reqwest-based HTTP transport.
+reqwest = ["dep:reqwest", "reqwest/stream"]
+
+# Compatibility/convenience alias.
+# Historically, cloud meant "common cloud support", including reqwest.
+# Keep that behavior for existing users.
+cloud = ["cloud-base", "reqwest"]
+
+# Provider base features.
+# These compile provider logic without forcing reqwest.
+azure-base = ["cloud-base", "httparse"]
+gcp-base = ["cloud-base", "rustls-pki-types"]
+aws-base = ["cloud-base", "crc-fast", "md-5"]
+http-base = ["cloud-base"]
+
+# Batteries-included provider features.
+# These preserve existing behavior: enabling aws/azure/gcp/http gives
+# the default reqwest transport.
+azure = ["azure-base", "reqwest"]
+gcp = ["gcp-base", "reqwest"]
+aws = ["aws-base", "reqwest"]
+http = ["http-base", "reqwest"]
+
fs = ["walkdir", "tokio", "nix", "windows-sys"]
-gcp = ["cloud", "rustls-pki-types"]
-aws = ["cloud", "crc-fast", "md-5"]
-http = ["cloud"]
tls-webpki-roots = ["reqwest?/rustls-tls-webpki-roots"]
integration = ["rand", "tokio"]
tokio = ["dep:tokio", "dep:tracing"]
diff --git a/src/aws/builder.rs b/src/aws/builder.rs
index 5a6d2b4..c9fd6e3 100644
--- a/src/aws/builder.rs
+++ b/src/aws/builder.rs
@@ -29,9 +29,9 @@ use crate::config::ConfigValue;
use crate::{ClientConfigKey, ClientOptions, Result, RetryConfig,
StaticCredentialProvider};
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
+use http::header::{HeaderMap, HeaderValue};
use itertools::Itertools;
use md5::{Digest, Md5};
-use reqwest::header::{HeaderMap, HeaderValue};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::sync::Arc;
@@ -1573,6 +1573,7 @@ mod tests {
assert!(builder.unsigned_payload.get().unwrap());
}
+ #[cfg(feature = "reqwest")]
#[test]
fn s3_test_endpoint_url_s3_config() {
// Verify aws_endpoint_url_s3 parses to S3Endpoint config key
@@ -1687,6 +1688,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn s3_default_region() {
let builder = AmazonS3Builder::new()
@@ -1696,6 +1698,7 @@ mod tests {
assert_eq!(builder.client.config.region, "us-east-1");
}
+ #[cfg(feature = "reqwest")]
#[test]
fn s3_test_bucket_endpoint() {
let builder = AmazonS3Builder::new()
@@ -1795,6 +1798,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn s3_test_proxy_url() {
let s3 = AmazonS3Builder::new()
@@ -1823,6 +1827,7 @@ mod tests {
assert_eq!("Generic HTTP client error: builder error", err);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_invalid_config() {
let err = AmazonS3Builder::new()
@@ -1865,6 +1870,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_request_payer_config() {
let s3 = AmazonS3Builder::new()
@@ -1938,6 +1944,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_builder_eks_with_config() {
let builder = AmazonS3Builder::new()
@@ -1960,6 +1967,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_builder_web_identity_with_config() {
let builder = AmazonS3Builder::new()
diff --git a/src/aws/client.rs b/src/aws/client.rs
index d6b2534..f72155f 100644
--- a/src/aws/client.rs
+++ b/src/aws/client.rs
@@ -1041,6 +1041,7 @@ mod tests {
use hyper::Request;
use hyper::body::Incoming;
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_create_multipart_has_content_length() {
let mock = MockServer::new().await;
@@ -1138,6 +1139,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_default_headers_signed_request() {
let mock = MockServer::new().await;
@@ -1162,6 +1164,7 @@ mod tests {
mock.shutdown().await;
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_default_headers_signed_bulk_delete() {
let mock = MockServer::new().await;
@@ -1338,6 +1341,7 @@ mod tests {
mock.shutdown().await;
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_default_headers_signed_get_request() {
let mock = MockServer::new().await;
@@ -1360,6 +1364,7 @@ mod tests {
mock.shutdown().await;
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_default_headers_signed_complete_multipart() {
let mock = MockServer::new().await;
diff --git a/src/aws/credential.rs b/src/aws/credential.rs
index 170c3e4..df2ea67 100644
--- a/src/aws/credential.rs
+++ b/src/aws/credential.rs
@@ -865,11 +865,13 @@ mod tests {
use crate::aws::{AmazonS3Builder, AmazonS3ConfigKey};
use crate::client::HttpClient;
use crate::client::mock_server::MockServer;
- use http::Response;
- use reqwest::{Client, Method};
+ use http::{Method, Response};
+ #[cfg(feature = "reqwest")]
+ use reqwest::Client;
use std::env;
// Test generated using
https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
+ #[cfg(feature = "reqwest")]
#[test]
fn test_sign_with_signed_payload() {
let client = HttpClient::new(Client::new());
@@ -914,6 +916,7 @@ mod tests {
)
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_sign_with_signed_payload_request_payer() {
let client = HttpClient::new(Client::new());
@@ -958,6 +961,7 @@ mod tests {
)
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_sign_with_unsigned_payload() {
let client = HttpClient::new(Client::new());
@@ -1085,6 +1089,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_sign_port() {
let client = HttpClient::new(Client::new());
@@ -1128,6 +1133,7 @@ mod tests {
)
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_instance_metadata() {
if env::var("TEST_INTEGRATION").is_err() {
@@ -1166,6 +1172,7 @@ mod tests {
assert!(!token.is_empty())
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_mock() {
let server = MockServer::new().await;
@@ -1259,6 +1266,7 @@ mod tests {
.unwrap_err();
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_eks_pod_credential_provider() {
use crate::client::mock_server::MockServer;
diff --git a/src/aws/mod.rs b/src/aws/mod.rs
index 765871a..42d84a4 100644
--- a/src/aws/mod.rs
+++ b/src/aws/mod.rs
@@ -31,8 +31,8 @@
use async_trait::async_trait;
use futures_util::stream::BoxStream;
use futures_util::{StreamExt, TryStreamExt};
-use reqwest::header::{HeaderName, IF_MATCH, IF_NONE_MATCH};
-use reqwest::{Method, StatusCode};
+use http::header::{HeaderName, IF_MATCH, IF_NONE_MATCH};
+use http::{Method, StatusCode};
use std::{sync::Arc, time::Duration};
use url::Url;
@@ -58,14 +58,14 @@ mod client;
mod credential;
mod precondition;
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
mod resolve;
pub use builder::{AmazonS3Builder, AmazonS3ConfigKey};
pub use checksum::Checksum;
pub use precondition::{S3ConditionalPut, S3CopyIfNotExists};
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub use resolve::resolve_bucket_region;
/// This struct is used to maintain the URI path encoding
@@ -118,7 +118,7 @@ impl Signer for AmazonS3 {
/// ```
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # use object_store::{aws::AmazonS3Builder, path::Path, signer::Signer};
- /// # use reqwest::Method;
+ /// # use http::Method;
/// # use std::time::Duration;
/// #
/// let region = "us-east-1";
@@ -534,6 +534,7 @@ mod tests {
use super::*;
use crate::ClientOptions;
use crate::ObjectStoreExt;
+ #[cfg(feature = "reqwest")]
use crate::client::SpawnedReqwestConnector;
use crate::client::get::GetClient;
use crate::client::retry::RetryContext;
@@ -946,6 +947,7 @@ mod tests {
/// Integration test that ensures I/O is done on an alternate threadpool
/// when using the `SpawnedReqwestConnector`.
+ #[cfg(feature = "reqwest")]
#[test]
fn s3_alternate_threadpool_spawned_request_connector() {
maybe_skip_integration!();
diff --git a/src/aws/precondition.rs b/src/aws/precondition.rs
index b4ae938..51b8089 100644
--- a/src/aws/precondition.rs
+++ b/src/aws/precondition.rs
@@ -44,7 +44,7 @@ pub enum S3CopyIfNotExists {
/// other than 412.
///
/// Encoded as `header-with-status:<HEADER_NAME>:<HEADER_VALUE>:<STATUS>`
ignoring whitespace
- HeaderWithStatus(String, String, reqwest::StatusCode),
+ HeaderWithStatus(String, String, http::StatusCode),
/// Native Amazon S3 supports copy if not exists through a multipart upload
/// where the upload copies an existing object and is completed only if the
/// new object does not already exist.
@@ -180,7 +180,7 @@ mod tests {
let expected = Some(S3CopyIfNotExists::HeaderWithStatus(
"key".to_owned(),
"value".to_owned(),
- reqwest::StatusCode::FORBIDDEN,
+ http::StatusCode::FORBIDDEN,
));
assert_eq!(expected, S3CopyIfNotExists::from_str(input));
@@ -212,7 +212,7 @@ mod tests {
let expected = Some(S3CopyIfNotExists::HeaderWithStatus(
"key".to_owned(),
"value".to_owned(),
- reqwest::StatusCode::FORBIDDEN,
+ http::StatusCode::FORBIDDEN,
));
const INPUTS: &[&str] = &[
diff --git a/src/aws/resolve.rs b/src/aws/resolve.rs
index 66d1511..a09ea53 100644
--- a/src/aws/resolve.rs
+++ b/src/aws/resolve.rs
@@ -47,7 +47,7 @@ impl From<Error> for crate::Error {
///
/// [HeadBucket API]:
https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html
pub async fn resolve_bucket_region(bucket: &str, client_options:
&ClientOptions) -> Result<String> {
- use reqwest::StatusCode;
+ use http::StatusCode;
let endpoint = format!("https://{bucket}.s3.amazonaws.com");
diff --git a/src/azure/client.rs b/src/azure/client.rs
index fd3e923..5a3fcbc 100644
--- a/src/azure/client.rs
+++ b/src/azure/client.rs
@@ -1385,6 +1385,7 @@ mod tests {
quick_xml::de::from_str(S).unwrap();
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_build_bulk_delete_body() {
let credential_provider = Arc::new(StaticCredentialProvider::new(
diff --git a/src/azure/credential.rs b/src/azure/credential.rs
index 1fbb101..4e84fb8 100644
--- a/src/azure/credential.rs
+++ b/src/azure/credential.rs
@@ -1075,6 +1075,7 @@ mod tests {
use crate::client::mock_server::MockServer;
use crate::{ObjectStoreExt, Path};
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_managed_identity() {
let server = MockServer::new().await;
@@ -1133,6 +1134,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_workload_identity() {
let server = MockServer::new().await;
@@ -1185,6 +1187,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_no_credentials() {
let server = MockServer::new().await;
@@ -1218,6 +1221,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_fabric_refresh_expired_token() {
let server = MockServer::new().await;
diff --git a/src/azure/mod.rs b/src/azure/mod.rs
index 6d7b642..6c4968d 100644
--- a/src/azure/mod.rs
+++ b/src/azure/mod.rs
@@ -33,7 +33,7 @@ use crate::{
};
use async_trait::async_trait;
use futures_util::stream::{BoxStream, StreamExt, TryStreamExt};
-use reqwest::Method;
+use http::Method;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
@@ -197,7 +197,7 @@ impl Signer for MicrosoftAzure {
/// ```
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # use object_store::{azure::MicrosoftAzureBuilder, path::Path,
signer::Signer};
- /// # use reqwest::Method;
+ /// # use http::Method;
/// # use std::time::Duration;
/// #
/// let azure = MicrosoftAzureBuilder::new()
diff --git a/src/client/builder.rs b/src/client/builder.rs
index f74c5ec..fd0a719 100644
--- a/src/client/builder.rs
+++ b/src/client/builder.rs
@@ -63,7 +63,7 @@ impl HttpRequestBuilder {
}
}
- #[cfg(any(feature = "aws", feature = "azure"))]
+ #[cfg(any(feature = "aws-base", feature = "azure-base"))]
pub(crate) fn from_parts(client: HttpClient, request: HttpRequest) -> Self
{
Self {
client,
@@ -116,7 +116,7 @@ impl HttpRequestBuilder {
self
}
- #[cfg(feature = "aws")]
+ #[cfg(feature = "aws-base")]
pub(crate) fn headers(mut self, headers: http::HeaderMap) -> Self {
use http::header::{Entry, OccupiedEntry};
@@ -151,7 +151,7 @@ impl HttpRequestBuilder {
self
}
- #[cfg(feature = "gcp")]
+ #[cfg(feature = "gcp-base")]
pub(crate) fn bearer_auth(mut self, token: &str) -> Self {
let value = HeaderValue::try_from(format!("Bearer {token}"));
match (value, &mut self.request) {
@@ -165,7 +165,7 @@ impl HttpRequestBuilder {
self
}
- #[cfg(feature = "gcp")]
+ #[cfg(feature = "gcp-base")]
pub(crate) fn json<S: serde::Serialize>(mut self, s: S) -> Self {
match (serde_json::to_vec(&s), &mut self.request) {
(Ok(json), Ok(request)) => {
@@ -177,7 +177,12 @@ impl HttpRequestBuilder {
self
}
- #[cfg(any(test, feature = "aws", feature = "gcp", feature = "azure"))]
+ #[cfg(any(
+ test,
+ feature = "aws-base",
+ feature = "gcp-base",
+ feature = "azure-base"
+ ))]
pub(crate) fn query<T: serde::Serialize + ?Sized>(mut self, query: &T) ->
Self {
let mut error = None;
if let Ok(ref mut req) = self.request {
@@ -205,7 +210,7 @@ impl HttpRequestBuilder {
self
}
- #[cfg(any(feature = "gcp", feature = "azure"))]
+ #[cfg(any(feature = "gcp-base", feature = "azure-base"))]
pub(crate) fn form<T: serde::Serialize>(mut self, form: T) -> Self {
let mut error = None;
if let Ok(ref mut req) = self.request {
@@ -226,7 +231,7 @@ impl HttpRequestBuilder {
self
}
- #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+ #[cfg(any(feature = "aws-base", feature = "gcp-base", feature =
"azure-base"))]
pub(crate) fn body(mut self, b: impl Into<HttpRequestBody>) -> Self {
if let Ok(r) = &mut self.request {
*r.body_mut() = b.into();
@@ -239,7 +244,7 @@ impl HttpRequestBuilder {
}
}
-#[cfg(any(test, feature = "azure"))]
+#[cfg(any(test, feature = "azure-base"))]
pub(crate) fn add_query_pairs<I, K, V>(uri: &mut Uri, query_pairs: I)
where
I: IntoIterator,
@@ -300,6 +305,7 @@ mod tests {
assert_eq!(uri.to_string(), "https://[email protected]/?foo=1");
}
+ #[cfg(feature = "reqwest")]
#[test]
fn test_request_builder_query() {
let client = HttpClient::new(reqwest::Client::new());
diff --git a/src/client/get.rs b/src/client/get.rs
index a9de711..c7ec36d 100644
--- a/src/client/get.rs
+++ b/src/client/get.rs
@@ -28,12 +28,12 @@ use bytes::Bytes;
use futures_util::StreamExt;
use futures_util::stream::BoxStream;
use http::StatusCode;
+use http::header::ToStrError;
use http::header::{
CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE,
CONTENT_RANGE,
CONTENT_TYPE,
};
use http_body_util::BodyExt;
-use reqwest::header::ToStrError;
use std::ops::Range;
use std::sync::Arc;
use tracing::info;
@@ -523,7 +523,7 @@ mod tests {
);
}
}
-#[cfg(all(test, feature = "http", not(target_arch = "wasm32")))]
+#[cfg(all(test, feature = "http-base", not(target_arch = "wasm32")))]
mod http_tests {
use crate::client::mock_server::MockServer;
use crate::client::{HttpError, HttpErrorKind, HttpResponseBody};
@@ -590,6 +590,7 @@ mod http_tests {
}
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_stream_retry() {
let mock = MockServer::new().await;
@@ -815,6 +816,7 @@ mod http_tests {
);
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_retry_validate_content_range() {
let mock = MockServer::new().await;
diff --git a/src/client/header.rs b/src/client/header.rs
index d0dd27a..58cc3a8 100644
--- a/src/client/header.rs
+++ b/src/client/header.rs
@@ -49,7 +49,7 @@ pub(crate) enum Error {
MissingEtag,
#[error("Received header containing non-ASCII data")]
- BadHeader { source: reqwest::header::ToStrError },
+ BadHeader { source: http::header::ToStrError },
#[error("Last-Modified Header missing from response")]
MissingLastModified,
@@ -73,7 +73,7 @@ pub(crate) enum Error {
/// Extracts a PutResult from the provided response
///
/// Propagates the extensions of the response into the [`crate::PutResult`]
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) fn get_put_result(
response: crate::client::HttpResponse,
version: &str,
@@ -89,7 +89,7 @@ pub(crate) fn get_put_result(
}
/// Extracts a optional version from the provided [`HeaderMap`]
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) fn get_version(headers: &HeaderMap, version: &str) ->
Result<Option<String>, Error> {
Ok(match headers.get(version) {
Some(x) => Some(
diff --git a/src/client/http/body.rs b/src/client/http/body.rs
index e22ccea..554b0cf 100644
--- a/src/client/http/body.rs
+++ b/src/client/http/body.rs
@@ -39,7 +39,7 @@ impl HttpRequestBody {
Self(Inner::Bytes(Bytes::new()))
}
- #[cfg(not(target_arch = "wasm32"))]
+ #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub(crate) fn into_reqwest(self) -> reqwest::Body {
match self.0 {
Inner::Bytes(b) => b.into(),
@@ -49,7 +49,7 @@ impl HttpRequestBody {
}
}
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
+ #[cfg(all(feature = "reqwest", target_arch = "wasm32", target_os =
"unknown"))]
pub(crate) fn into_reqwest(self) -> reqwest::Body {
match self.0 {
Inner::Bytes(b) => b.into(),
@@ -196,7 +196,7 @@ impl HttpResponseBody {
String::from_utf8(b.into()).map_err(|e|
HttpError::new(HttpErrorKind::Decode, e))
}
- #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+ #[cfg(any(feature = "aws-base", feature = "gcp-base", feature =
"azure-base"))]
pub(crate) async fn json<B: serde::de::DeserializeOwned>(self) ->
Result<B, HttpError> {
let b = self.bytes().await?;
serde_json::from_slice(&b).map_err(|e|
HttpError::new(HttpErrorKind::Decode, e))
diff --git a/src/client/http/connection.rs b/src/client/http/connection.rs
index 69c8436..c7cd2c1 100644
--- a/src/client/http/connection.rs
+++ b/src/client/http/connection.rs
@@ -16,13 +16,17 @@
// under the License.
use crate::ClientOptions;
+#[cfg(feature = "reqwest")]
+use crate::client::HttpResponseBody;
use crate::client::builder::{HttpRequestBuilder, RequestBuilderError};
-use crate::client::{HttpRequest, HttpResponse, HttpResponseBody};
+use crate::client::{HttpRequest, HttpResponse};
use async_trait::async_trait;
use http::{Method, Uri};
+#[cfg(feature = "reqwest")]
use http_body_util::BodyExt;
use std::error::Error;
use std::sync::Arc;
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
use tokio::runtime::Handle;
/// An HTTP protocol error
@@ -83,6 +87,7 @@ impl HttpError {
}
}
+ #[cfg(feature = "reqwest")]
pub(crate) fn reqwest(e: reqwest::Error) -> Self {
#[cfg(not(target_arch = "wasm32"))]
let is_connect = || e.is_connect();
@@ -208,7 +213,7 @@ impl HttpClient {
}
#[async_trait]
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
impl HttpService for reqwest::Client {
async fn call(&self, req: HttpRequest) -> Result<HttpResponse, HttpError> {
let (parts, body) = req.into_parts();
@@ -228,7 +233,7 @@ impl HttpService for reqwest::Client {
}
#[async_trait]
-#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
+#[cfg(all(feature = "reqwest", target_arch = "wasm32", target_os = "unknown"))]
impl HttpService for reqwest::Client {
async fn call(&self, req: HttpRequest) -> Result<HttpResponse, HttpError> {
use futures_channel::{mpsc, oneshot};
@@ -288,10 +293,16 @@ pub trait HttpConnector: std::fmt::Debug + Send + Sync +
'static {
/// [`HttpConnector`] using [`reqwest::Client`]
#[derive(Debug, Default)]
#[allow(missing_copy_implementations)]
-#[cfg(not(all(target_arch = "wasm32", target_os = "wasi")))]
+#[cfg(all(
+ feature = "reqwest",
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
pub struct ReqwestConnector {}
-#[cfg(not(all(target_arch = "wasm32", target_os = "wasi")))]
+#[cfg(all(
+ feature = "reqwest",
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
impl HttpConnector for ReqwestConnector {
fn connect(&self, options: &ClientOptions) -> crate::Result<HttpClient> {
let client = options.client()?;
@@ -336,12 +347,12 @@ impl HttpConnector for ReqwestConnector {
/// ```
#[derive(Debug)]
#[allow(missing_copy_implementations)]
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub struct SpawnedReqwestConnector {
runtime: Handle,
}
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
impl SpawnedReqwestConnector {
/// Create a new [`SpawnedReqwestConnector`] with the provided [`Handle`]
to
/// a tokio [`Runtime`]
@@ -352,7 +363,7 @@ impl SpawnedReqwestConnector {
}
}
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
impl HttpConnector for SpawnedReqwestConnector {
fn connect(&self, options: &ClientOptions) -> crate::Result<HttpClient> {
let spawn_service = super::SpawnService::new(options.client()?,
self.runtime.clone());
@@ -360,21 +371,39 @@ impl HttpConnector for SpawnedReqwestConnector {
}
}
-#[cfg(all(target_arch = "wasm32", target_os = "wasi"))]
+#[cfg(all(feature = "reqwest", target_arch = "wasm32", target_os = "wasi"))]
pub(crate) fn http_connector(
custom: Option<Arc<dyn HttpConnector>>,
) -> crate::Result<Arc<dyn HttpConnector>> {
match custom {
Some(x) => Ok(x),
None => Err(crate::Error::NotSupported {
- source: "WASI architectures must provide an HTTPConnector"
+ source: "reqwest is not supported on the WASI architecture; \
+ supply a custom HttpConnector via `.with_http_connector(...)`"
.to_string()
.into(),
}),
}
}
-#[cfg(not(all(target_arch = "wasm32", target_os = "wasi")))]
+#[cfg(all(not(feature = "reqwest"), target_arch = "wasm32", target_os =
"wasi"))]
+pub(crate) fn http_connector(
+ custom: Option<Arc<dyn HttpConnector>>,
+) -> crate::Result<Arc<dyn HttpConnector>> {
+ match custom {
+ Some(x) => Ok(x),
+ None => Err(crate::Error::NotSupported {
+ source: "WASI architectures must provide an HttpConnector"
+ .to_string()
+ .into(),
+ }),
+ }
+}
+
+#[cfg(all(
+ feature = "reqwest",
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
pub(crate) fn http_connector(
custom: Option<Arc<dyn HttpConnector>>,
) -> crate::Result<Arc<dyn HttpConnector>> {
@@ -383,3 +412,21 @@ pub(crate) fn http_connector(
None => Ok(Arc::new(ReqwestConnector {})),
}
}
+
+#[cfg(all(
+ not(feature = "reqwest"),
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
+pub(crate) fn http_connector(
+ custom: Option<Arc<dyn HttpConnector>>,
+) -> crate::Result<Arc<dyn HttpConnector>> {
+ match custom {
+ Some(x) => Ok(x),
+ None => Err(crate::Error::NotSupported {
+ source: "no built-in HTTP transport: enable the `reqwest` feature \
+ or supply a custom HttpConnector via
`.with_http_connector(...)`"
+ .to_string()
+ .into(),
+ }),
+ }
+}
diff --git a/src/client/http/spawn.rs b/src/client/http/spawn.rs
index 80f3a87..2750e83 100644
--- a/src/client/http/spawn.rs
+++ b/src/client/http/spawn.rs
@@ -125,7 +125,7 @@ impl Body for SpawnBody {
}
}
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 5dceaf4..896fcb2 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -21,7 +21,7 @@
pub(crate) mod backoff;
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
mod dns;
#[cfg(not(target_arch = "wasm32"))]
@@ -30,44 +30,45 @@ pub(crate) mod mock_server;
pub(crate) mod retry;
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) mod pagination;
pub(crate) mod get;
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) mod list;
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) mod token;
pub(crate) mod header;
-#[cfg(any(feature = "aws", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) mod s3;
pub(crate) mod builder;
mod http;
-#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base", feature = "azure-base"))]
pub(crate) mod parts;
pub use http::*;
+use ::http::header::{HeaderMap, HeaderValue};
use async_trait::async_trait;
-use reqwest::header::{HeaderMap, HeaderValue};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
use reqwest::{NoProxy, Proxy};
use crate::config::{ConfigValue, fmt_duration};
use crate::path::Path;
use crate::{GetOptions, Result};
+#[cfg(feature = "reqwest")]
fn map_client_error(e: reqwest::Error) -> super::Error {
super::Error::Generic {
store: "HTTP client",
@@ -75,6 +76,7 @@ fn map_client_error(e: reqwest::Error) -> super::Error {
}
}
+#[cfg(feature = "reqwest")]
static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/",
env!("CARGO_PKG_VERSION"),);
/// Configuration keys for [`ClientOptions`]
@@ -272,10 +274,10 @@ impl FromStr for ClientConfigKey {
/// This is used to configure the client to trust a specific certificate. See
/// [Self::from_pem] for an example
#[derive(Debug, Clone)]
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub struct Certificate(reqwest::tls::Certificate);
-#[cfg(not(target_arch = "wasm32"))]
+#[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
impl Certificate {
/// Create a `Certificate` from a PEM encoded certificate.
///
@@ -322,7 +324,7 @@ impl Certificate {
#[derive(Debug, Clone)]
pub struct ClientOptions {
user_agent: Option<ConfigValue<HeaderValue>>,
- #[cfg(not(target_arch = "wasm32"))]
+ #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
root_certificates: Vec<Certificate>,
content_type_map: HashMap<String, String>,
default_content_type: Option<String>,
@@ -357,7 +359,7 @@ impl Default for ClientOptions {
// we opt for a slightly higher default timeout of 30 seconds
Self {
user_agent: None,
- #[cfg(not(target_arch = "wasm32"))]
+ #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
root_certificates: Default::default(),
content_type_map: Default::default(),
default_content_type: None,
@@ -493,7 +495,7 @@ impl ClientOptions {
///
/// This can be used to connect to a server that has a self-signed
/// certificate for example.
- #[cfg(not(target_arch = "wasm32"))]
+ #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub fn with_root_certificate(mut self, certificate: Certificate) -> Self {
self.root_certificates.push(certificate);
self
@@ -787,14 +789,14 @@ impl ClientOptions {
/// In particular:
/// * Allows HTTP as metadata endpoints do not use TLS
/// * Configures a low connection timeout to provide quick feedback if not
present
- #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
+ #[cfg(any(feature = "aws-base", feature = "gcp-base", feature =
"azure-base"))]
pub(crate) fn metadata_options(&self) -> Self {
self.clone()
.with_allow_http(true)
.with_connect_timeout(Duration::from_secs(1))
}
- #[cfg(not(target_arch = "wasm32"))]
+ #[cfg(all(feature = "reqwest", not(target_arch = "wasm32")))]
pub(crate) fn client(&self) -> Result<reqwest::Client> {
let mut builder = reqwest::ClientBuilder::new();
@@ -894,7 +896,7 @@ impl ClientOptions {
.map_err(map_client_error)
}
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
+ #[cfg(all(feature = "reqwest", target_arch = "wasm32", target_os =
"unknown"))]
pub(crate) fn client(&self) -> Result<reqwest::Client> {
let mut builder = reqwest::ClientBuilder::new();
@@ -994,7 +996,7 @@ where
}
}
-#[cfg(any(feature = "aws", feature = "azure", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "azure-base", feature = "gcp-base"))]
mod cloud {
use super::*;
use crate::RetryConfig;
@@ -1020,7 +1022,7 @@ mod cloud {
}
/// Override the minimum remaining TTL for a cached token to be used
- #[cfg(any(feature = "aws", feature = "gcp"))]
+ #[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) fn with_min_ttl(mut self, min_ttl: Duration) -> Self {
self.cache = self.cache.with_min_ttl(min_ttl);
self
@@ -1051,7 +1053,7 @@ mod cloud {
}
use crate::client::builder::HttpRequestBuilder;
-#[cfg(any(feature = "aws", feature = "azure", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "azure-base", feature = "gcp-base"))]
pub(crate) use cloud::*;
#[cfg(test)]
diff --git a/src/client/retry.rs b/src/client/retry.rs
index 45b73eb..c79012a 100644
--- a/src/client/retry.rs
+++ b/src/client/retry.rs
@@ -22,9 +22,9 @@ use crate::client::backoff::{Backoff, BackoffConfig};
use crate::client::builder::HttpRequestBuilder;
use crate::client::{HttpClient, HttpError, HttpErrorKind, HttpRequest,
HttpResponse};
use futures_util::future::BoxFuture;
+use http::StatusCode;
+use http::header::LOCATION;
use http::{Method, Uri};
-use reqwest::StatusCode;
-use reqwest::header::LOCATION;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use std::time::{Duration, Instant};
use tracing::info;
@@ -280,7 +280,7 @@ impl RetryableRequestBuilder {
}
/// Set whether this request should be retried on a 409 Conflict response.
- #[cfg(feature = "aws")]
+ #[cfg(feature = "aws-base")]
pub(crate) fn retry_on_conflict(mut self, retry_on_conflict: bool) -> Self
{
self.request.retry_on_conflict = retry_on_conflict;
self
@@ -512,13 +512,15 @@ mod tests {
use crate::client::mock_server::MockServer;
use crate::client::retry::{RequestError, RetryContext, RetryExt,
body_contains_error};
use crate::client::{HttpClient, HttpError, HttpErrorKind, HttpResponse};
+ use http::Method;
use http::StatusCode;
use hyper::Response;
use hyper::header::LOCATION;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper_util::rt::TokioIo;
- use reqwest::{Client, Method};
+ #[cfg(feature = "reqwest")]
+ use reqwest::Client;
use std::convert::Infallible;
use std::error::Error;
use std::time::Duration;
@@ -539,6 +541,7 @@ mod tests {
assert!(!body_contains_error(success_response));
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_retry() {
let mock = MockServer::new().await;
@@ -846,6 +849,7 @@ mod tests {
mock.shutdown().await
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn test_503_error_body_captured() {
let mock = MockServer::new().await;
@@ -880,6 +884,7 @@ mod tests {
mock.shutdown().await
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
#[expect(
deprecated,
diff --git a/src/client/s3.rs b/src/client/s3.rs
index 2c8fb63..1bf7636 100644
--- a/src/client/s3.rs
+++ b/src/client/s3.rs
@@ -93,7 +93,7 @@ pub(crate) struct InitiateMultipartUploadResult {
pub upload_id: String,
}
-#[cfg(feature = "aws")]
+#[cfg(feature = "aws-base")]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub(crate) struct CopyPartResult {
diff --git a/src/client/token.rs b/src/client/token.rs
index 5b680bd..147c77a 100644
--- a/src/client/token.rs
+++ b/src/client/token.rs
@@ -58,7 +58,7 @@ impl<T> Default for TokenCache<T> {
impl<T: Clone + Send + Sync> TokenCache<T> {
/// Override the minimum remaining TTL for a cached token to be used
- #[cfg(any(feature = "aws", feature = "gcp"))]
+ #[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) fn with_min_ttl(self, min_ttl: Duration) -> Self {
Self { min_ttl, ..self }
}
diff --git a/src/config.rs b/src/config.rs
index 29a389d..3707a36 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -18,8 +18,8 @@ use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use std::time::Duration;
+use http::header::HeaderValue;
use humantime::{format_duration, parse_duration};
-use reqwest::header::HeaderValue;
use crate::{Error, Result};
diff --git a/src/gcp/builder.rs b/src/gcp/builder.rs
index 82752b0..55cfe63 100644
--- a/src/gcp/builder.rs
+++ b/src/gcp/builder.rs
@@ -762,6 +762,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
async fn gcs_test_proxy_url() {
let mut tfile = NamedTempFile::new().unwrap();
@@ -798,6 +799,7 @@ mod tests {
builder.parse_url("mailto://bucket/path").unwrap_err();
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_service_account_key_only() {
let _ = GoogleCloudStorageBuilder::new()
@@ -807,6 +809,7 @@ mod tests {
.unwrap();
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_with_base_url() {
let no_base_url = GoogleCloudStorageBuilder::new()
@@ -904,6 +907,7 @@ mod tests {
}
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_explicit_creds_skip_invalid_adc() {
// Create a valid service account key file
@@ -932,6 +936,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_explicit_creds_with_service_account_key_skip_invalid_adc() {
// Create invalid ADC file with unsupported credential type
@@ -955,6 +960,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_adc_error_propagated_without_explicit_creds() {
// Create invalid ADC file with unsupported credential type
@@ -982,6 +988,7 @@ mod tests {
);
}
+ #[cfg(feature = "reqwest")]
#[test]
fn gcs_test_with_credentials_skip_invalid_adc() {
use crate::StaticCredentialProvider;
diff --git a/src/gcp/mod.rs b/src/gcp/mod.rs
index 7dc22ad..3402057 100644
--- a/src/gcp/mod.rs
+++ b/src/gcp/mod.rs
@@ -347,6 +347,7 @@ mod test {
response_extensions(&integration, test_multipart).await;
}
+ #[cfg(feature = "reqwest")]
#[tokio::test]
#[ignore]
async fn gcs_test_sign() {
diff --git a/src/http/client.rs b/src/http/client.rs
index 4375a79..580b725 100644
--- a/src/http/client.rs
+++ b/src/http/client.rs
@@ -30,8 +30,8 @@ use http::header::{
CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE,
CONTENT_LENGTH,
CONTENT_TYPE,
};
+use http::{Method, StatusCode};
use percent_encoding::percent_decode_str;
-use reqwest::{Method, StatusCode};
use serde::Deserialize;
use url::Url;
diff --git a/src/integration.rs b/src/integration.rs
index 8b435d3..bcd4dca 100644
--- a/src/integration.rs
+++ b/src/integration.rs
@@ -1438,7 +1438,7 @@ pub async fn list_with_offset_exclusivity(storage:
&DynObjectStore) {
}
#[cfg(all(
- feature = "cloud",
+ feature = "reqwest",
not(all(target_arch = "wasm32", target_os = "wasi"))
))]
mod marker {
@@ -1521,7 +1521,7 @@ mod marker {
}
#[cfg(all(
- feature = "cloud",
+ feature = "reqwest",
not(all(target_arch = "wasm32", target_os = "wasi"))
))]
pub use marker::{MarkerHttpConnector, response_extensions};
diff --git a/src/lib.rs b/src/lib.rs
index cb286f0..48711bb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -80,19 +80,19 @@
doc = "* Local filesystem: [`LocalFileSystem`](local::LocalFileSystem)"
)]
#![cfg_attr(
- feature = "gcp",
+ feature = "gcp-base",
doc = "* [`gcp`]: [Google Cloud
Storage](https://cloud.google.com/storage/) support. See
[`GoogleCloudStorageBuilder`](gcp::GoogleCloudStorageBuilder)"
)]
#![cfg_attr(
- feature = "aws",
+ feature = "aws-base",
doc = "* [`aws`]: [Amazon S3](https://aws.amazon.com/s3/). See
[`AmazonS3Builder`](aws::AmazonS3Builder)"
)]
#![cfg_attr(
- feature = "azure",
+ feature = "azure-base",
doc = "* [`azure`]: [Azure Blob
Storage](https://azure.microsoft.com/en-gb/services/storage/blobs/). See
[`MicrosoftAzureBuilder`](azure::MicrosoftAzureBuilder)"
)]
#![cfg_attr(
- feature = "http",
+ feature = "http-base",
doc = "* [`http`]: [HTTP/WebDAV
Storage](https://datatracker.ietf.org/doc/html/rfc2518). See
[`HttpBuilder`](http::HttpBuilder)"
)]
//!
@@ -537,18 +537,18 @@
//!
//! [`HttpConnector`]: client::HttpConnector
-#[cfg(feature = "aws")]
+#[cfg(feature = "aws-base")]
pub mod aws;
-#[cfg(feature = "azure")]
+#[cfg(feature = "azure-base")]
pub mod azure;
#[cfg(feature = "tokio")]
pub mod buffered;
#[cfg(not(target_arch = "wasm32"))]
pub mod chunked;
pub mod delimited;
-#[cfg(feature = "gcp")]
+#[cfg(feature = "gcp-base")]
pub mod gcp;
-#[cfg(feature = "http")]
+#[cfg(feature = "http-base")]
pub mod http;
#[cfg(feature = "tokio")]
pub mod limit;
@@ -558,24 +558,28 @@ pub mod memory;
pub mod path;
pub mod prefix;
pub mod registry;
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
pub mod signer;
#[cfg(feature = "tokio")]
pub mod throttle;
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
pub mod client;
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
pub use client::{
ClientConfigKey, ClientOptions, CredentialProvider,
StaticCredentialProvider,
backoff::BackoffConfig, retry::RetryConfig,
};
-#[cfg(all(feature = "cloud", not(target_arch = "wasm32")))]
+#[cfg(all(
+ feature = "cloud-base",
+ feature = "reqwest",
+ not(target_arch = "wasm32")
+))]
pub use client::Certificate;
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
mod config;
mod tags;
@@ -2213,12 +2217,12 @@ mod tests {
store.list(Some(&path))
}
- #[cfg(any(feature = "azure", feature = "aws"))]
+ #[cfg(any(feature = "azure-base", feature = "aws-base"))]
pub(crate) async fn signing<T>(integration: &T)
where
T: ObjectStore + signer::Signer,
{
- use reqwest::Method;
+ use ::http::Method;
use std::time::Duration;
let data = Bytes::from("hello world");
@@ -2236,7 +2240,7 @@ mod tests {
assert_eq!(data, loaded);
}
- #[cfg(any(feature = "aws", feature = "azure"))]
+ #[cfg(any(feature = "aws-base", feature = "azure-base"))]
pub(crate) async fn tagging<F, Fut>(storage: Arc<dyn ObjectStore>,
validate: bool, get_tags: F)
where
F: Fn(Path) -> Fut + Send + Sync,
@@ -2409,7 +2413,7 @@ mod tests {
}
#[test]
- #[cfg(feature = "http")]
+ #[cfg(feature = "http-base")]
fn test_reexported_types() {
// Test HeaderMap
let mut headers = HeaderMap::new();
diff --git a/src/parse.rs b/src/parse.rs
index d316b2f..2265547 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -139,7 +139,7 @@ impl ObjectStoreScheme {
}
}
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
macro_rules! builder_opts {
($builder:ty, $url:expr, $options:expr) => {{
let builder = $options.into_iter().fold(
@@ -201,29 +201,29 @@ where
#[cfg(all(feature = "fs", not(target_arch = "wasm32")))]
ObjectStoreScheme::Local => Box::new(LocalFileSystem::new()) as _,
ObjectStoreScheme::Memory => Box::new(InMemory::new()) as _,
- #[cfg(feature = "aws")]
+ #[cfg(feature = "aws-base")]
ObjectStoreScheme::AmazonS3 => {
builder_opts!(crate::aws::AmazonS3Builder, url, _options)
}
- #[cfg(feature = "gcp")]
+ #[cfg(feature = "gcp-base")]
ObjectStoreScheme::GoogleCloudStorage => {
builder_opts!(crate::gcp::GoogleCloudStorageBuilder, url, _options)
}
- #[cfg(feature = "azure")]
+ #[cfg(feature = "azure-base")]
ObjectStoreScheme::MicrosoftAzure => {
builder_opts!(crate::azure::MicrosoftAzureBuilder, url, _options)
}
- #[cfg(feature = "http")]
+ #[cfg(feature = "http-base")]
ObjectStoreScheme::Http => {
let url = &url[..url::Position::BeforePath];
builder_opts!(crate::http::HttpBuilder, url, _options)
}
#[cfg(not(all(
feature = "fs",
- feature = "aws",
- feature = "azure",
- feature = "gcp",
- feature = "http",
+ feature = "aws-base",
+ feature = "azure-base",
+ feature = "gcp-base",
+ feature = "http-base",
not(target_arch = "wasm32")
)))]
s => {
@@ -441,7 +441,11 @@ mod tests {
}
#[tokio::test]
- #[cfg(all(feature = "http", not(target_arch = "wasm32")))]
+ #[cfg(all(
+ feature = "reqwest",
+ feature = "http-base",
+ not(target_arch = "wasm32")
+ ))]
async fn test_url_http() {
use crate::{ObjectStoreExt, client::mock_server::MockServer};
use http::{Response, header::USER_AGENT};
diff --git a/src/prefix.rs b/src/prefix.rs
index 9b3cf19..2b94b48 100644
--- a/src/prefix.rs
+++ b/src/prefix.rs
@@ -22,7 +22,7 @@ use std::ops::Range;
use crate::multipart::{MultipartStore, PartId};
use crate::path::Path;
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
use crate::signer::Signer;
use crate::{
CopyOptions, GetOptions, GetResult, ListResult, MultipartId,
MultipartUpload, ObjectMeta,
@@ -237,7 +237,7 @@ impl<T: MultipartStore> MultipartStore for PrefixStore<T> {
}
}
-#[cfg(feature = "cloud")]
+#[cfg(feature = "cloud-base")]
#[async_trait::async_trait]
impl<T: Signer> Signer for PrefixStore<T> {
async fn signed_url(
@@ -397,7 +397,7 @@ mod tests {
multipart_race_condition(&store, true).await;
}
- #[cfg(feature = "cloud")]
+ #[cfg(feature = "cloud-base")]
#[tokio::test]
async fn signer() {
#[derive(Debug)]
diff --git a/src/signer.rs b/src/signer.rs
index ab3c7f9..47f3baa 100644
--- a/src/signer.rs
+++ b/src/signer.rs
@@ -19,7 +19,7 @@
use crate::{Result, path::Path};
use async_trait::async_trait;
-use reqwest::Method;
+use http::Method;
use std::{fmt, time::Duration};
use url::Url;
diff --git a/src/util.rs b/src/util.rs
index 162c077..21c9f7a 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -25,11 +25,11 @@ use super::Result;
use bytes::Bytes;
use futures_util::{Stream, TryStreamExt, stream::StreamExt};
-#[cfg(any(feature = "azure", feature = "http"))]
+#[cfg(any(feature = "azure-base", feature = "http-base"))]
pub(crate) static RFC1123_FMT: &str = "%a, %d %h %Y %T GMT";
// deserialize dates according to rfc1123
-#[cfg(any(feature = "azure", feature = "http"))]
+#[cfg(any(feature = "azure-base", feature = "http-base"))]
pub(crate) fn deserialize_rfc1123<'de, D>(
deserializer: D,
) -> Result<chrono::DateTime<chrono::Utc>, D::Error>
@@ -42,7 +42,7 @@ where
Ok(chrono::TimeZone::from_utc_datetime(&chrono::Utc, &naive))
}
-#[cfg(any(feature = "aws", feature = "azure"))]
+#[cfg(any(feature = "aws-base", feature = "azure-base"))]
pub(crate) fn hmac_sha256(secret: impl AsRef<[u8]>, bytes: impl AsRef<[u8]>)
-> ring::hmac::Tag {
let key = ring::hmac::Key::new(ring::hmac::HMAC_SHA256, secret.as_ref());
ring::hmac::sign(&key, bytes.as_ref())
@@ -300,7 +300,7 @@ impl<T: RangeBounds<u64>> From<T> for GetRange {
//
// Do not URI-encode any of the unreserved characters that RFC 3986 defines:
// A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), and tilde ( ~
).
-#[cfg(any(feature = "aws", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) const STRICT_ENCODE_SET: percent_encoding::AsciiSet =
percent_encoding::NON_ALPHANUMERIC
.remove(b'-')
.remove(b'.')
@@ -308,14 +308,14 @@ pub(crate) const STRICT_ENCODE_SET:
percent_encoding::AsciiSet = percent_encodin
.remove(b'~');
/// Computes the SHA256 digest of `body` returned as a hex encoded string
-#[cfg(any(feature = "aws", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) fn hex_digest(bytes: &[u8]) -> String {
let digest = ring::digest::digest(&ring::digest::SHA256, bytes);
hex_encode(digest.as_ref())
}
/// Returns `bytes` as a lower-case hex encoded string
-#[cfg(any(feature = "aws", feature = "gcp"))]
+#[cfg(any(feature = "aws-base", feature = "gcp-base"))]
pub(crate) fn hex_encode(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut out = String::with_capacity(bytes.len() * 2);
diff --git a/tests/http.rs b/tests/http.rs
index f23ef74..d1aa930 100644
--- a/tests/http.rs
+++ b/tests/http.rs
@@ -17,16 +17,16 @@
//! Tests the HTTP store implementation
-#[cfg(feature = "http")]
+#[cfg(feature = "http-base")]
use object_store::{GetOptions, GetRange, ObjectStore, http::HttpBuilder,
path::Path};
-#[cfg(all(feature = "http", target_arch = "wasm32", target_os = "unknown"))]
+#[cfg(all(feature = "http-base", target_arch = "wasm32", target_os =
"unknown"))]
use wasm_bindgen_test::*;
/// Tests that even when reqwest has the `gzip` feature enabled, the HTTP store
/// does not error on a missing `Content-Length` header.
#[tokio::test]
-#[cfg(feature = "http")]
+#[cfg(all(feature = "http-base", feature = "reqwest"))]
async fn test_http_store_gzip() {
let http_store = HttpBuilder::new()
.with_url("https://raw.githubusercontent.com/apache/arrow-rs/refs/heads/main")
@@ -42,7 +42,7 @@ async fn test_http_store_gzip() {
.unwrap();
}
-#[cfg(all(feature = "http", target_arch = "wasm32", target_os = "unknown"))]
+#[cfg(all(feature = "http-base", target_arch = "wasm32", target_os =
"unknown"))]
#[wasm_bindgen_test]
async fn basic_wasm_get() {
let http_store = HttpBuilder::new()