diff --git a/CHANGELOG.md b/CHANGELOG.md index 3995292d..50a6fad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to ## [Unreleased] +## [0.8.0] - 2023-06-29 + +### Added + +- Support for reqwest middleware builder + ## [0.7.0] - 2023-03-25 ### Added diff --git a/Cargo.toml b/Cargo.toml index cf2e054c..5c914b22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vaultrs" -version = "0.7.0" +version = "0.8.0" authors = ["Joshua Gilman "] description = "An asynchronous Rust client library for the Hashicorp Vault API." license = "MIT" @@ -27,8 +27,10 @@ bytes = "1.4.0" derive_builder = "0.12.0" http = "0.2.9" reqwest = { version = "0.11.15", default-features = false } -rustify = { version = "0.5.3", default-features = false } -rustify_derive = "0.5.2" +reqwest-middleware = { version = "0.2.2", default-features = false } +#rustify = { version = "0.5.4", default-features = false } +rustify = { git = "https://github.com/ChorusOne/rustify", rev="ba3a1c68f503a0ecf92af37ee8f0198720786bab", default-features = false, features = ["reqwest", "reqwest-middleware"] } +rustify_derive = {git = "https://github.com/ChorusOne/rustify", rev = "ba3a1c68f503a0ecf92af37ee8f0198720786bab", default-features = false} serde = { version = "1.0.158", features = ["derive"] } serde_json = "1.0.94" thiserror = "1.0.40" diff --git a/src/client.rs b/src/client.rs index 4e182f29..285e7d6a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,7 @@ use crate::api::AuthInfo; use crate::api::{token::responses::LookupTokenResponse, EndpointMiddleware}; use crate::error::ClientError; use async_trait::async_trait; -use rustify::clients::reqwest::Client as HTTPClient; +use rustify::clients::reqwest_middleware::ClientWithMiddleware as HTTPClient; use std::time::Duration; use std::{env, fs}; use url::Url; @@ -77,42 +77,79 @@ impl Client for VaultClient { } } +#[instrument(skip(settings), err)] +pub fn prepare_http_client( + settings: &VaultClientSettings, + mut http_client: reqwest::ClientBuilder, +) -> Result { + // Optionally set timeout on client + http_client = if let Some(timeout) = settings.timeout { + http_client.timeout(timeout) + } else { + http_client + }; + + // Disable TLS checks if specified + if !settings.verify { + event!(tracing::Level::WARN, "Disabling TLS verification"); + } + http_client = http_client.danger_accept_invalid_certs(!settings.verify); + + // Adds CA certificates + for path in &settings.ca_certs { + let content = std::fs::read(path).map_err(|e| ClientError::FileReadError { + source: e, + path: path.clone(), + })?; + let cert = reqwest::Certificate::from_pem(&content).map_err(|e| { + ClientError::ParseCertificateError { + source: e, + path: path.clone(), + } + })?; + + info!("Importing CA certificate from {}", path); + http_client = http_client.add_root_certificate(cert); + } + Ok(http_client) +} + impl VaultClient { /// Creates a new [VaultClient] using the given [VaultClientSettings]. #[instrument(skip(settings), err)] pub fn new(settings: VaultClientSettings) -> Result { - let mut http_client = reqwest::ClientBuilder::new(); - - // Optionally set timeout on client - http_client = if let Some(timeout) = settings.timeout { - http_client.timeout(timeout) - } else { - http_client - }; - - // Disable TLS checks if specified - if !settings.verify { - event!(tracing::Level::WARN, "Disabling TLS verification"); - } - http_client = http_client.danger_accept_invalid_certs(!settings.verify); - - // Adds CA certificates - for path in &settings.ca_certs { - let content = std::fs::read(path).map_err(|e| ClientError::FileReadError { - source: e, - path: path.clone(), - })?; - let cert = reqwest::Certificate::from_pem(&content).map_err(|e| { - ClientError::ParseCertificateError { - source: e, - path: path.clone(), - } - })?; + let http_client = reqwest::ClientBuilder::new(); + Self::with_client_builder(settings, http_client) + } - info!("Importing CA certificate from {}", path); - http_client = http_client.add_root_certificate(cert); - } + /// Creates a new [VaultClient] using the given [VaultClientSettings] and [reqwest::Client][1]. + /// + /// [1]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html + #[instrument(skip(settings, client_builder), err)] + pub fn with_client_builder( + settings: VaultClientSettings, + client_builder: reqwest::ClientBuilder, + ) -> Result { + let client_builder = prepare_http_client(&settings, client_builder)?; + let client = client_builder + .build() + .map_err(|e| ClientError::RestClientBuildError { source: e })?; + let middleware_builder = reqwest_middleware::ClientBuilder::new(client); + Self::with_middleware_client_builder(settings, middleware_builder) + } + /// Creates a new [VaultClient] using the given [VaultClientSettings] and [reqwest_middleware::ClientWithMiddleware][1]. + /// In this case a caller is responsible to provide a correct [reqwest::Client][2] instantiation for [reqwest_middleware::ClientBuilder][3]. + /// In particular, [prepare_http_client] function can be used to configure the client from [VaultClientSettings]. + /// + /// [1]: https://docs.rs/reqwest-middleware/latest/reqwest_middleware/struct.ClientWithMiddleware.html + /// [2]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html + /// [3]: https://docs.rs/reqwest-middleware/latest/reqwest_middleware/struct.ClientBuilder.html + #[instrument(skip(settings, middleware_builder), err)] + pub fn with_middleware_client_builder( + settings: VaultClientSettings, + middleware_builder: reqwest_middleware::ClientBuilder, + ) -> Result { // Configures middleware for endpoints to append API version and token debug!("Using API version {}", settings.version); let version_str = format!("v{}", settings.version); @@ -123,9 +160,7 @@ impl VaultClient { namespace: settings.namespace.clone(), }; - let http_client = http_client - .build() - .map_err(|e| ClientError::RestClientBuildError { source: e })?; + let http_client = middleware_builder.build(); let http = HTTPClient::new(settings.address.as_str(), http_client); Ok(VaultClient { settings,