From 6cea785e0e30c57a2f1af11e8394e9611710900e Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 10 May 2024 15:17:52 +0200 Subject: [PATCH 1/4] Update HTTP client support - make the default no-client at all, backwards incompatible but better overall I think. - add reqwest 0.12 support --- .github/workflows/ci-matrix.yml | 7 ++--- Cargo.toml | 14 ++++++++-- README.md | 5 ++-- benches/is_enabled.rs | 4 +++ examples/threads.rs | 8 ++++-- src/client.rs | 2 ++ src/http.rs | 2 ++ src/http/reqwest_11.rs | 48 +++++++++++++++++++++++++++++++++ src/lib.rs | 10 +++++-- tests/clientspec.rs | 2 ++ tests/functional.rs | 18 ++++++++++--- 11 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 src/http/reqwest_11.rs diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index e60842a..6538a61 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -14,6 +14,7 @@ jobs: - surf-client # no need for reqwest-client-rustls, it changes only internals of reqwest - reqwest-client + - reqwest-client-11 steps: - uses: actions/checkout@v2 @@ -58,13 +59,13 @@ jobs: - uses: actions-rs/cargo@v1 with: command: build - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets - uses: actions-rs/cargo@v1 # We test with approximately all-features to ensure that that does build (excludes nightly only backtrace) with: command: test - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets - uses: actions-rs/cargo@v1 with: @@ -74,7 +75,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: clippy - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets -- -D warnings + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets -- -D warnings msrv: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 3397c9d..beebddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,14 @@ status = "experimental" [features] # Enable backtrace feature in anyhow backtrace = ["anyhow/backtrace"] -default = ["surf-client"] +# Force clients to make a choice about which client to use +default = [] # Enable the functional test suite functional = [] reqwest-client = ["reqwest", "reqwest/default-tls"] +reqwest-client-11 = ["reqwest-11", "reqwest-11/default-tls"] # For users that don't want to depend on OpenSSL. +reqwest-client-11-rustls = ["reqwest-11", "reqwest-11/rustls-tls"] reqwest-client-rustls = ["reqwest", "reqwest/rustls-tls"] # To error if an unsupported API feature is present strict = [] @@ -68,7 +71,14 @@ version = "0.4.19" default-features = false features = ["json"] optional = true -version = "0.11.10" +version = "0.12.4" + +[dependencies.reqwest-11] +default-features = false +features = ["json"] +optional = true +package = "reqwest" +version = "0.11.27" [dependencies.serde] features = ["derive"] diff --git a/README.md b/README.md index 1093f49..e77b00e 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ surf or reqwest support is built in, or any async HTTP client can be provided by the user if they implement the thin trait used to abstract over the actual client. -Examples with async-std and tokio are in the examples/ in the source -tree. +Examples with async-std (feature 'surf-client') and tokio (feature +'reqwest-client') are in the examples/ in the source tree. See the API docs for +more feature information. To use it in a sync program, run an async executor and `block_on()` the relevant calls. As the client specification requires sending background metrics to the diff --git a/benches/is_enabled.rs b/benches/is_enabled.rs index 3db59de..3256c93 100644 --- a/benches/is_enabled.rs +++ b/benches/is_enabled.rs @@ -222,6 +222,8 @@ fn batch(c: &mut Criterion) { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } @@ -423,6 +425,8 @@ fn single_call(c: &mut Criterion) { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/examples/threads.rs b/examples/threads.rs index 00c0b30..6e9f46c 100644 --- a/examples/threads.rs +++ b/examples/threads.rs @@ -22,7 +22,7 @@ enum UserFeatures { fn main() -> Result<(), Box> { cfg_if::cfg_if! { - if #[cfg(feature = "surf")] { + if #[cfg(feature = "surf-client")] { use core::future::Future; use surf::Client as HttpClient; use async_std::task; @@ -34,10 +34,14 @@ fn main() -> Result<(), Box> { } } let rt = RT{}; - } else if #[cfg(feature = "reqwest")] { + } else if #[cfg(feature = "reqwest-client")] { use reqwest::Client as HttpClient; use tokio::runtime::Runtime; let rt = Arc::new(Runtime::new().unwrap()); + } else if #[cfg(feature = "reqwest-client-11")] { + use reqwest_11::Client as HttpClient; + use tokio::runtime::Runtime; + let rt = Arc::new(Runtime::new().unwrap()); } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/src/client.rs b/src/client.rs index e63679f..d9e8c77 100644 --- a/src/client.rs +++ b/src/client.rs @@ -925,6 +925,8 @@ mod tests { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/src/http.rs b/src/http.rs index 635f348..5582c94 100644 --- a/src/http.rs +++ b/src/http.rs @@ -3,6 +3,8 @@ #[cfg(feature = "reqwest")] mod reqwest; +#[cfg(feature = "reqwest-11")] +mod reqwest_11; mod shim; #[cfg(feature = "surf")] mod surf; diff --git a/src/http/reqwest_11.rs b/src/http/reqwest_11.rs new file mode 100644 index 0000000..96191a8 --- /dev/null +++ b/src/http/reqwest_11.rs @@ -0,0 +1,48 @@ +//! Shim reqwest into an unleash HTTP client + +// Copyright 2022 Cognite AS + +use async_trait::async_trait; +use serde::{de::DeserializeOwned, Serialize}; + +use super::HttpClient; + +#[async_trait] +impl HttpClient for reqwest_11::Client { + type HeaderName = reqwest_11::header::HeaderName; + type Error = reqwest_11::Error; + type RequestBuilder = reqwest_11::RequestBuilder; + + fn build_header(name: &'static str) -> Result { + Ok(Self::HeaderName::from_static(name)) + } + + fn get(&self, uri: &str) -> Self::RequestBuilder { + self.get(uri) + } + + fn post(&self, uri: &str) -> Self::RequestBuilder { + self.post(uri) + } + + fn header( + builder: Self::RequestBuilder, + key: &Self::HeaderName, + value: &str, + ) -> Self::RequestBuilder { + builder.header(key.clone(), value) + } + + async fn get_json(req: Self::RequestBuilder) -> Result { + req.send().await?.json::().await + } + + async fn post_json( + req: Self::RequestBuilder, + content: &T, + ) -> Result { + let req = req.json(content); + let res = req.send().await?; + Ok(res.status().is_success()) + } +} diff --git a/src/lib.rs b/src/lib.rs index cf99657..9ad807f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ strategy memoization function. ```no_run # mod i { # cfg_if::cfg_if!{ -# if #[cfg(not(feature = "surf"))] { +# if #[cfg(not(feature = "surf-client"))] { # pub fn main() -> Result<(), Box> { # Ok(()) # } @@ -99,13 +99,17 @@ fn main() -> Result<(), Box> { * **backtrace** - Enable backtrace feature in anyhow (nightly only) * **default** - - The default feature enables the async-std/surf feature. + By default no features are enabled. * **functional** - Only relevant to developers: enables the functional test suite. * **reqwest-client** - Enables reqwest with OpenSSL TLS support +* **reqwest-client-11** - + Enables reqwest 0.11 with OpenSSL TLS support * **reqwest-client-rustls** - Enables reqwest with RusTLS support +* **reqwest-client-11-rustls** - + Enables reqwest 0.11 with RusTLS support * **strict** - Turn unexpected fields in API responses into errors * **surf-client** - @@ -159,6 +163,8 @@ pub mod prelude { pub use surf::Client as DefaultClient; } else if #[cfg(feature = "reqwest")] { pub use reqwest::Client as DefaultClient; + } else if #[cfg(feature = "reqwest-11")] { + pub use reqwest_11::Client as DefaultClient; } } } diff --git a/tests/clientspec.rs b/tests/clientspec.rs index b3ca47b..87da0d7 100644 --- a/tests/clientspec.rs +++ b/tests/clientspec.rs @@ -92,6 +92,8 @@ mod tests { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/tests/functional.rs b/tests/functional.rs index 4c08665..947a7b3 100644 --- a/tests/functional.rs +++ b/tests/functional.rs @@ -58,7 +58,7 @@ mod tests { } } - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] struct TokioJoinHandle { inner: tokio::task::JoinHandle<()>, } @@ -80,9 +80,9 @@ mod tests { } } - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] struct TokioAsync; - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] #[async_trait] impl AsyncImpl for TokioAsync { type JoinHandle = TokioJoinHandle; @@ -142,6 +142,11 @@ mod tests { async fn test_smoke_async_reqwest() { test_smoke_async::().await.unwrap(); } + #[cfg(feature = "reqwest-11")] + #[tokio::test] + async fn test_smoke_async_reqwest() { + test_smoke_async::().await.unwrap(); + } async fn test_smoke_threaded( ) -> Result<(), Box> @@ -204,4 +209,11 @@ mod tests { .await .unwrap(); } + #[cfg(feature = "reqwest-11")] + #[tokio::test] + async fn test_smoke_threaded_reqwest() { + test_smoke_threaded::() + .await + .unwrap(); + } } From 29022c3f2bc72c394fbf646f86674d976bf8e9c0 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 10 May 2024 15:53:59 +0200 Subject: [PATCH 2/4] MSRV: bump to 1.60 Concrete clients have higher MSRVs, but 1.60 can build the core code and resolve all deps. --- .github/workflows/ci-matrix.yml | 2 +- Cargo.toml | 18 +++++++++--------- README.md | 2 +- examples/threads.rs | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index 6538a61..a119db5 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -82,7 +82,7 @@ jobs: strategy: matrix: rust: - - 1.59.0 # MSRV + - 1.60.0 # MSRV steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index beebddd..222da80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0" name = "unleash-api-client" readme = "README.md" repository = "https://github.com/cognitedata/unleash-client-rust/" -rust-version = "1.59" +rust-version = "1.60" version = "0.10.3" [badges] @@ -24,11 +24,12 @@ backtrace = ["anyhow/backtrace"] default = [] # Enable the functional test suite functional = [] -reqwest-client = ["reqwest", "reqwest/default-tls"] -reqwest-client-11 = ["reqwest-11", "reqwest-11/default-tls"] +# Built in HTTP clients +reqwest-client = ["reqwest", "reqwest?/default-tls"] +reqwest-client-11 = ["reqwest-11", "reqwest-11?/default-tls"] # For users that don't want to depend on OpenSSL. -reqwest-client-11-rustls = ["reqwest-11", "reqwest-11/rustls-tls"] -reqwest-client-rustls = ["reqwest", "reqwest/rustls-tls"] +reqwest-client-11-rustls = ["reqwest-11", "reqwest-11?/rustls-tls"] +reqwest-client-rustls = ["reqwest", "reqwest?/rustls-tls"] # To error if an unsupported API feature is present strict = [] # For use with --no-default-features @@ -42,7 +43,7 @@ name = "is_enabled" bench = false name = "dump-features" path = "src/bin/dump-features.rs" -required-features = ["async-std", "surf-client"] +required-features = ["surf-client"] [dependencies] anyhow = "1.0.44" @@ -71,21 +72,20 @@ version = "0.4.19" default-features = false features = ["json"] optional = true -version = "0.12.4" +version = "0.12" [dependencies.reqwest-11] default-features = false features = ["json"] optional = true package = "reqwest" -version = "0.11.27" +version = "0.11" [dependencies.serde] features = ["derive"] version = "1.0" [dev-dependencies] -# async-std = "1.10.0" criterion = "0.3.5" # Remember to update audit.toml futures = "0.3.17" maplit = "1.0.2" diff --git a/README.md b/README.md index e77b00e..ee81016 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ enable_string_features | N/A | By default the Rust SDK requires you to define an ## Status -Core Unleash API features work, with Rust 1.59 or above. The MSRV for this project is weakly enforced: when a hard dependency raises its version, so will the minimum version tested against, but if older rust versions work for a user, that is not prevented. `time` in particular is known to enforce a 6-month compiler age, so regular increases with the minimum version tested against are expected. +Core Unleash API features work, with Rust 1.60 or above. The MSRV for this project is weakly enforced: when a hard dependency raises its version, so will the minimum version tested against, but if older rust versions work for a user, that is not prevented. `time` in particular is known to enforce a 6-month compiler age, so regular increases with the minimum version tested against are expected. Unimplemented Unleash specified features: diff --git a/examples/threads.rs b/examples/threads.rs index 6e9f46c..b8a93fd 100644 --- a/examples/threads.rs +++ b/examples/threads.rs @@ -22,7 +22,7 @@ enum UserFeatures { fn main() -> Result<(), Box> { cfg_if::cfg_if! { - if #[cfg(feature = "surf-client")] { + if #[cfg(feature = "surf")] { use core::future::Future; use surf::Client as HttpClient; use async_std::task; @@ -34,11 +34,11 @@ fn main() -> Result<(), Box> { } } let rt = RT{}; - } else if #[cfg(feature = "reqwest-client")] { + } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; use tokio::runtime::Runtime; let rt = Arc::new(Runtime::new().unwrap()); - } else if #[cfg(feature = "reqwest-client-11")] { + } else if #[cfg(feature = "reqwest-11")] { use reqwest_11::Client as HttpClient; use tokio::runtime::Runtime; let rt = Arc::new(Runtime::new().unwrap()); From 483461d0f56c58d5b561a25d3ff54f21cdf754c4 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 10 May 2024 16:13:16 +0200 Subject: [PATCH 3/4] Clippy --- tests/clientspec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/clientspec.rs b/tests/clientspec.rs index 87da0d7..21e82b0 100644 --- a/tests/clientspec.rs +++ b/tests/clientspec.rs @@ -47,7 +47,7 @@ mod tests { } _ => false, }, - None => other.payload.get("type").is_none() && other.payload.get("value").is_none(), + None => !other.payload.contains_key("type") && !other.payload.contains_key("value"), }; self.enabled == other.enabled && self._name == other.name && payload_matches } From 1ad1e33b96acd892bfdd5f4cc2285118253792b5 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 10 May 2024 16:16:30 +0200 Subject: [PATCH 4/4] Release 0.11.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 222da80..3919785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ name = "unleash-api-client" readme = "README.md" repository = "https://github.com/cognitedata/unleash-client-rust/" rust-version = "1.60" -version = "0.10.3" +version = "0.11.0" [badges] [badges.maintenance]