From bb25a7fca049668c48a321235242b2d23645eae5 Mon Sep 17 00:00:00 2001 From: Hinton Date: Mon, 29 Sep 2025 18:04:32 +0200 Subject: [PATCH] Explore using reqwest tracing --- Cargo.lock | 85 ++++++++++++++++++- Cargo.toml | 4 + crates/bitwarden-api-api/Cargo.toml | 1 + .../src/apis/configuration.rs | 4 +- crates/bitwarden-api-api/src/apis/mod.rs | 9 ++ crates/bitwarden-api-identity/Cargo.toml | 1 + .../src/apis/configuration.rs | 4 +- crates/bitwarden-api-identity/src/apis/mod.rs | 9 ++ crates/bitwarden-auth/Cargo.toml | 1 + .../src/send_access/access_token_response.rs | 6 ++ .../bitwarden-auth/src/send_access/client.rs | 4 +- crates/bitwarden-core/Cargo.toml | 5 ++ crates/bitwarden-core/src/client/client.rs | 31 +++++++ crates/bitwarden-core/src/error.rs | 3 + crates/bitwarden-test/Cargo.toml | 1 + crates/bitwarden-test/src/api.rs | 2 +- support/build-api-ci.sh | 4 +- 17 files changed, 163 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 256dcd435..efa1bc0f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,7 @@ dependencies = [ "async-trait", "mockall", "reqwest", + "reqwest-middleware", "serde", "serde_json", "serde_repr", @@ -381,6 +382,7 @@ dependencies = [ "async-trait", "mockall", "reqwest", + "reqwest-middleware", "serde", "serde_json", "serde_repr", @@ -398,6 +400,7 @@ dependencies = [ "bitwarden-test", "chrono", "reqwest", + "reqwest-middleware", "serde", "serde_json", "thiserror 2.0.12", @@ -451,10 +454,13 @@ dependencies = [ "bitwarden-uuid", "chrono", "getrandom 0.2.16", + "http", "log", "rand 0.8.5", "rand_chacha 0.3.1", "reqwest", + "reqwest-middleware", + "reqwest-tracing", "rustls", "rustls-platform-verifier", "schemars 1.0.0", @@ -465,6 +471,8 @@ dependencies = [ "serde_repr", "thiserror 2.0.12", "tokio", + "tracing", + "tracing-subscriber", "tsify", "uniffi", "uuid", @@ -761,6 +769,7 @@ dependencies = [ "bitwarden-api-api", "bitwarden-state", "reqwest", + "reqwest-middleware", "wiremock", ] @@ -2683,6 +2692,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchit" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f926ade0c4e170215ae43342bf13b9310a437609c81f29f86c5df6657582ef9" + [[package]] name = "memchr" version = "2.7.5" @@ -2810,6 +2825,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3543,6 +3567,37 @@ dependencies = [ "web-sys", ] +[[package]] +name = "reqwest-middleware" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +dependencies = [ + "anyhow", + "async-trait", + "http", + "reqwest", + "serde", + "thiserror 1.0.69", + "tower-service", +] + +[[package]] +name = "reqwest-tracing" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70ea85f131b2ee9874f0b160ac5976f8af75f3c9badfe0d955880257d10bd83" +dependencies = [ + "anyhow", + "async-trait", + "getrandom 0.2.16", + "http", + "matchit", + "reqwest", + "reqwest-middleware", + "tracing", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4624,9 +4679,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.34" @@ -4647,15 +4714,29 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ + "nu-ansi-term", "sharded-slab", + "smallvec", "thread_local", "tracing-core", + "tracing-log", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 32c2ac9ee..9a1755769 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,10 @@ reqwest = { version = ">=0.12.5, <0.13", features = [ "multipart", "http2", ], default-features = false } +reqwest-middleware = { version = ">=0.4.2, <0.5", features = [ + "json", + "multipart", +] } schemars = { version = ">=1.0.0, <2.0", features = ["uuid1", "chrono04"] } serde = { version = ">=1.0, <2.0", features = ["derive"] } serde_bytes = { version = ">=0.11.17, <0.12.0" } diff --git a/crates/bitwarden-api-api/Cargo.toml b/crates/bitwarden-api-api/Cargo.toml index ebd53d3df..9cac07ef1 100644 --- a/crates/bitwarden-api-api/Cargo.toml +++ b/crates/bitwarden-api-api/Cargo.toml @@ -19,6 +19,7 @@ mockall = ["dep:mockall"] async-trait = { workspace = true } mockall = { version = ">=0.13.1, <0.14", optional = true } reqwest = { workspace = true } +reqwest-middleware = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_repr = { workspace = true } diff --git a/crates/bitwarden-api-api/src/apis/configuration.rs b/crates/bitwarden-api-api/src/apis/configuration.rs index c184fc18f..bb8223cf1 100644 --- a/crates/bitwarden-api-api/src/apis/configuration.rs +++ b/crates/bitwarden-api-api/src/apis/configuration.rs @@ -12,7 +12,7 @@ pub struct Configuration { pub base_path: String, pub user_agent: Option, - pub client: reqwest::Client, + pub client: reqwest_middleware::ClientWithMiddleware, pub basic_auth: Option, pub oauth_access_token: Option, pub bearer_access_token: Option, @@ -38,7 +38,7 @@ impl Default for Configuration { Configuration { base_path: "https://api.bitwarden.com".to_owned(), user_agent: Some("OpenAPI-Generator/latest/rust".to_owned()), - client: reqwest::Client::new(), + client: reqwest_middleware::ClientBuilder::new(reqwest::Client::new()).build(), basic_auth: None, oauth_access_token: None, bearer_access_token: None, diff --git a/crates/bitwarden-api-api/src/apis/mod.rs b/crates/bitwarden-api-api/src/apis/mod.rs index 2a75f3eb4..99acce856 100644 --- a/crates/bitwarden-api-api/src/apis/mod.rs +++ b/crates/bitwarden-api-api/src/apis/mod.rs @@ -10,6 +10,7 @@ pub struct ResponseContent { #[derive(Debug)] pub enum Error { Reqwest(reqwest::Error), + ReqwestMiddleware(reqwest_middleware::Error), Serde(serde_json::Error), Io(std::io::Error), ResponseError(ResponseContent), @@ -19,6 +20,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (module, e) = match self { Error::Reqwest(e) => ("reqwest", e.to_string()), + Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()), Error::Serde(e) => ("serde", e.to_string()), Error::Io(e) => ("IO", e.to_string()), Error::ResponseError(e) => ("response", format!("status code {}", e.status)), @@ -31,6 +33,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match self { Error::Reqwest(e) => e, + Error::ReqwestMiddleware(e) => e, Error::Serde(e) => e, Error::Io(e) => e, Error::ResponseError(_) => return None, @@ -44,6 +47,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: reqwest_middleware::Error) -> Self { + Error::ReqwestMiddleware(e) + } +} + impl From for Error { fn from(e: serde_json::Error) -> Self { Error::Serde(e) diff --git a/crates/bitwarden-api-identity/Cargo.toml b/crates/bitwarden-api-identity/Cargo.toml index 2e23627fc..af4949d30 100644 --- a/crates/bitwarden-api-identity/Cargo.toml +++ b/crates/bitwarden-api-identity/Cargo.toml @@ -19,6 +19,7 @@ mockall = ["dep:mockall"] async-trait = { workspace = true } mockall = { version = ">=0.13.1, <0.14", optional = true } reqwest = { workspace = true } +reqwest-middleware = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_repr = { workspace = true } diff --git a/crates/bitwarden-api-identity/src/apis/configuration.rs b/crates/bitwarden-api-identity/src/apis/configuration.rs index e8270e9fc..30f7555fa 100644 --- a/crates/bitwarden-api-identity/src/apis/configuration.rs +++ b/crates/bitwarden-api-identity/src/apis/configuration.rs @@ -12,7 +12,7 @@ pub struct Configuration { pub base_path: String, pub user_agent: Option, - pub client: reqwest::Client, + pub client: reqwest_middleware::ClientWithMiddleware, pub basic_auth: Option, pub oauth_access_token: Option, pub bearer_access_token: Option, @@ -38,7 +38,7 @@ impl Default for Configuration { Configuration { base_path: "https://identity.bitwarden.com".to_owned(), user_agent: Some("OpenAPI-Generator/v1/rust".to_owned()), - client: reqwest::Client::new(), + client: reqwest_middleware::ClientBuilder::new(reqwest::Client::new()).build(), basic_auth: None, oauth_access_token: None, bearer_access_token: None, diff --git a/crates/bitwarden-api-identity/src/apis/mod.rs b/crates/bitwarden-api-identity/src/apis/mod.rs index b2330fe1f..5827aafbf 100644 --- a/crates/bitwarden-api-identity/src/apis/mod.rs +++ b/crates/bitwarden-api-identity/src/apis/mod.rs @@ -10,6 +10,7 @@ pub struct ResponseContent { #[derive(Debug)] pub enum Error { Reqwest(reqwest::Error), + ReqwestMiddleware(reqwest_middleware::Error), Serde(serde_json::Error), Io(std::io::Error), ResponseError(ResponseContent), @@ -19,6 +20,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (module, e) = match self { Error::Reqwest(e) => ("reqwest", e.to_string()), + Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()), Error::Serde(e) => ("serde", e.to_string()), Error::Io(e) => ("IO", e.to_string()), Error::ResponseError(e) => ("response", format!("status code {}", e.status)), @@ -31,6 +33,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match self { Error::Reqwest(e) => e, + Error::ReqwestMiddleware(e) => e, Error::Serde(e) => e, Error::Io(e) => e, Error::ResponseError(_) => return None, @@ -44,6 +47,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: reqwest_middleware::Error) -> Self { + Error::ReqwestMiddleware(e) + } +} + impl From for Error { fn from(e: serde_json::Error) -> Self { Error::Serde(e) diff --git a/crates/bitwarden-auth/Cargo.toml b/crates/bitwarden-auth/Cargo.toml index 416b18449..5aa626e76 100644 --- a/crates/bitwarden-auth/Cargo.toml +++ b/crates/bitwarden-auth/Cargo.toml @@ -28,6 +28,7 @@ bitwarden-core = { workspace = true, features = ["internal"] } bitwarden-error = { workspace = true } chrono = { workspace = true } reqwest = { workspace = true } +reqwest-middleware = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } tsify = { workspace = true, optional = true } diff --git a/crates/bitwarden-auth/src/send_access/access_token_response.rs b/crates/bitwarden-auth/src/send_access/access_token_response.rs index 29e7cdbc8..9bfe11038 100644 --- a/crates/bitwarden-auth/src/send_access/access_token_response.rs +++ b/crates/bitwarden-auth/src/send_access/access_token_response.rs @@ -53,6 +53,12 @@ pub enum SendAccessTokenError { } // This is just a utility function so that the ? operator works correctly without manual mapping +impl From for SendAccessTokenError { + fn from(value: reqwest_middleware::Error) -> Self { + Self::Unexpected(UnexpectedIdentityError(format!("{value:?}"))) + } +} + impl From for SendAccessTokenError { fn from(value: reqwest::Error) -> Self { Self::Unexpected(UnexpectedIdentityError(format!("{value:?}"))) diff --git a/crates/bitwarden-auth/src/send_access/client.rs b/crates/bitwarden-auth/src/send_access/client.rs index cfebb7847..2b3d70770 100644 --- a/crates/bitwarden-auth/src/send_access/client.rs +++ b/crates/bitwarden-auth/src/send_access/client.rs @@ -48,7 +48,7 @@ impl SendAccessClient { &configurations.identity_config.base_path ); - let request: reqwest::RequestBuilder = configurations + let request = configurations .identity_config .client .post(&url) @@ -60,7 +60,7 @@ impl SendAccessClient { // wrapped in SendAccessTokenError::Unexpected as an UnexpectedIdentityError::Reqwest // variant and returned. // note: we had to manually built a trait to map reqwest::Error to SendAccessTokenError. - let response: reqwest::Response = request.send().await?; + let response = request.send().await?; let response_status = response.status(); diff --git a/crates/bitwarden-core/Cargo.toml b/crates/bitwarden-core/Cargo.toml index b6093d487..6e5989a0d 100644 --- a/crates/bitwarden-core/Cargo.toml +++ b/crates/bitwarden-core/Cargo.toml @@ -49,9 +49,12 @@ bitwarden-uuid = { workspace = true } chrono = { workspace = true, features = ["std"] } # We don't use this directly (it's used by rand), but we need it here to enable WASM support getrandom = { version = ">=0.2.9, <0.3", features = ["js"] } +http = "1.3.1" log = { workspace = true } rand = ">=0.8.5, <0.9" reqwest = { workspace = true } +reqwest-middleware = { workspace = true } +reqwest-tracing = ">=0.5.8, <0.6" schemars = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } @@ -59,6 +62,8 @@ serde_json = { workspace = true } serde_qs = { workspace = true } serde_repr = { workspace = true } thiserror = { workspace = true } +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.20", features = ["fmt"] } tsify = { workspace = true, optional = true } uniffi = { workspace = true, optional = true, features = ["tokio"] } uuid = { workspace = true } diff --git a/crates/bitwarden-core/src/client/client.rs b/crates/bitwarden-core/src/client/client.rs index 938bd5f09..72380f51d 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -4,6 +4,7 @@ use bitwarden_crypto::KeyStore; #[cfg(feature = "internal")] use bitwarden_state::registry::StateRegistry; use reqwest::header::{self, HeaderValue}; +use reqwest_tracing::{SpanBackendWithUrl, TracingMiddleware}; use super::internal::InternalClient; #[cfg(feature = "internal")] @@ -76,6 +77,10 @@ impl Client { let client = client_builder.build().expect("Build should not fail"); + let client = reqwest_middleware::ClientBuilder::new(client) + .with(TracingMiddleware::::new()) + .build(); + let identity = bitwarden_api_identity::apis::configuration::Configuration { base_path: settings.identity_url, user_agent: Some(settings.user_agent.clone()), @@ -118,3 +123,29 @@ impl Client { } } } + +#[cfg(test)] +mod tests { + use tracing::Level; + use tracing_subscriber::FmtSubscriber; + + use super::*; + + #[tokio::test] + async fn test_client_api_tracing() { + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::TRACE) + .finish(); + + tracing::subscriber::set_global_default(subscriber).unwrap(); + + let client = Client::new(Some(ClientSettings { + ..Default::default() + })); + + let api_config = client.internal.__api_configurations.read().unwrap(); + let configs = api_config.api_client.config_api().get_configs().await; + + assert!(configs.is_ok()); + } +} diff --git a/crates/bitwarden-core/src/error.rs b/crates/bitwarden-core/src/error.rs index 9c6c9ba6f..d12d0cf44 100644 --- a/crates/bitwarden-core/src/error.rs +++ b/crates/bitwarden-core/src/error.rs @@ -15,6 +15,7 @@ macro_rules! impl_bitwarden_error { fn from(e: $name) -> Self { match e { $name::Reqwest(e) => Self::Reqwest(e), + $name::ReqwestMiddleware(e) => Self::ReqwestMiddleware(e), $name::ResponseError(e) => Self::ResponseContent { status: e.status, message: e.content, @@ -35,6 +36,8 @@ pub enum ApiError { #[error(transparent)] Reqwest(#[from] reqwest::Error), #[error(transparent)] + ReqwestMiddleware(#[from] reqwest_middleware::Error), + #[error(transparent)] Serde(#[from] serde_json::Error), #[error(transparent)] Io(#[from] std::io::Error), diff --git a/crates/bitwarden-test/Cargo.toml b/crates/bitwarden-test/Cargo.toml index 0b7f8f481..ea6cfca71 100644 --- a/crates/bitwarden-test/Cargo.toml +++ b/crates/bitwarden-test/Cargo.toml @@ -19,6 +19,7 @@ async-trait = { workspace = true } bitwarden-api-api.workspace = true bitwarden-state = { workspace = true } reqwest = { workspace = true } +reqwest-middleware = { workspace = true } wiremock = { workspace = true } [lints] diff --git a/crates/bitwarden-test/src/api.rs b/crates/bitwarden-test/src/api.rs index 265dee15b..74af6eaae 100644 --- a/crates/bitwarden-test/src/api.rs +++ b/crates/bitwarden-test/src/api.rs @@ -13,7 +13,7 @@ pub async fn start_api_mock(mocks: Vec) -> (wiremock::MockServer let config = Configuration { base_path: server.uri(), user_agent: Some("test-agent".to_string()), - client: reqwest::Client::new(), + client: reqwest_middleware::ClientBuilder::new(reqwest::Client::new()).build(), basic_auth: None, oauth_access_token: None, bearer_access_token: None, diff --git a/support/build-api-ci.sh b/support/build-api-ci.sh index 001f19440..8a2a97f51 100755 --- a/support/build-api-ci.sh +++ b/support/build-api-ci.sh @@ -19,7 +19,7 @@ npx openapi-generator-cli generate \ -o crates/bitwarden-api-api \ --package-name bitwarden-api-api \ -t ./support/openapi-template \ - --additional-properties=library=reqwest-trait,mockall,topLevelApiClient,packageVersion=$VERSION,packageDescription=\"Api bindings for the Bitwarden API.\" + --additional-properties=library=reqwest-trait,mockall,topLevelApiClient,packageVersion=$VERSION,packageDescription=\"Api bindings for the Bitwarden API.\",supportMiddleware=true # Generate new Identity bindings npx openapi-generator-cli generate \ @@ -28,6 +28,6 @@ npx openapi-generator-cli generate \ -o crates/bitwarden-api-identity \ --package-name bitwarden-api-identity \ -t ./support/openapi-template \ - --additional-properties=library=reqwest-trait,mockall,topLevelApiClient,packageVersion=$VERSION,packageDescription=\"Api bindings for the Bitwarden Identity API.\" + --additional-properties=library=reqwest-trait,mockall,topLevelApiClient,packageVersion=$VERSION,packageDescription=\"Api bindings for the Bitwarden Identity API.\",supportMiddleware=true npm run prettier