From 47fb634310c0c83a377d33ab3523bd7ad48dc9dd Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 12:00:14 +0300 Subject: [PATCH 1/6] Add with_final_init feature --- reqwest-middleware/src/client.rs | 37 ++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/reqwest-middleware/src/client.rs b/reqwest-middleware/src/client.rs index 7740536..3d846cb 100644 --- a/reqwest-middleware/src/client.rs +++ b/reqwest-middleware/src/client.rs @@ -20,6 +20,7 @@ pub struct ClientBuilder { client: Client, middleware_stack: Vec>, initialiser_stack: Vec>, + final_initialiser_stack: Vec>, } impl ClientBuilder { @@ -28,6 +29,7 @@ impl ClientBuilder { client, middleware_stack: Vec::new(), initialiser_stack: Vec::new(), + final_initialiser_stack: Vec::new(), } } @@ -38,6 +40,7 @@ impl ClientBuilder { client: client_with_middleware.inner, middleware_stack: client_with_middleware.middleware_stack.into_vec(), initialiser_stack: client_with_middleware.initialiser_stack.into_vec(), + final_initialiser_stack: client_with_middleware.final_initialiser_stack.into_vec(), } } @@ -81,12 +84,25 @@ impl ClientBuilder { self } + pub fn with_final_init(self, initialiser: I) -> Self + where + I: RequestInitialiser, + { + self.with_arc_final_init(Arc::new(initialiser)) + } + + pub fn with_arc_final_init(mut self, initialiser: Arc) -> Self { + self.final_initialiser_stack.push(initialiser); + self + } + /// Returns a `ClientWithMiddleware` using this builder configuration. pub fn build(self) -> ClientWithMiddleware { ClientWithMiddleware { inner: self.client, middleware_stack: self.middleware_stack.into_boxed_slice(), initialiser_stack: self.initialiser_stack.into_boxed_slice(), + final_initialiser_stack: self.final_initialiser_stack.into_boxed_slice(), } } } @@ -98,6 +114,7 @@ pub struct ClientWithMiddleware { inner: reqwest::Client, middleware_stack: Box<[Arc]>, initialiser_stack: Box<[Arc]>, + final_initialiser_stack: Box<[Arc]>, } impl ClientWithMiddleware { @@ -111,6 +128,7 @@ impl ClientWithMiddleware { middleware_stack: middleware_stack.into(), // TODO(conradludgate) - allow downstream code to control this manually if desired initialiser_stack: Box::new([]), + final_initialiser_stack: Box::new([]), } } @@ -182,6 +200,7 @@ impl ClientWithMiddleware { extensions: Extensions::new(), middleware_stack: self.middleware_stack.clone(), initialiser_stack: self.initialiser_stack.clone(), + final_initialiser_stack: self.final_initialiser_stack.clone(), }; self.initialiser_stack .iter() @@ -234,6 +253,7 @@ impl From for ClientWithMiddleware { inner: client, middleware_stack: Box::new([]), initialiser_stack: Box::new([]), + final_initialiser_stack: Box::new([]), } } } @@ -327,6 +347,7 @@ pub struct RequestBuilder { inner: reqwest::RequestBuilder, middleware_stack: Box<[Arc]>, initialiser_stack: Box<[Arc]>, + final_initialiser_stack: Box<[Arc]>, extensions: Extensions, } @@ -338,6 +359,7 @@ impl RequestBuilder { inner, middleware_stack: client.middleware_stack, initialiser_stack: client.initialiser_stack, + final_initialiser_stack: client.final_initialiser_stack, extensions: Extensions::new(), } } @@ -537,7 +559,12 @@ impl RequestBuilder { /// Build a `Request`, which can be inspected, modified and executed with /// `ClientWithMiddleware::execute()`. pub fn build(self) -> reqwest::Result { - self.inner.build() + let final_initialiser_stack = self.final_initialiser_stack.clone(); + final_initialiser_stack + .iter() + .fold(self, |req, i| i.init(req)) + .inner + .build() } /// Build a `Request`, which can be inspected, modified and executed with @@ -546,17 +573,22 @@ impl RequestBuilder { /// This is similar to [`RequestBuilder::build()`], but also returns the /// embedded `Client`. pub fn build_split(self) -> (ClientWithMiddleware, reqwest::Result) { + let final_initialiser_stack = self.final_initialiser_stack.clone(); let Self { inner, middleware_stack, initialiser_stack, .. - } = self; + } = final_initialiser_stack + .iter() + .fold(self, |req, i| i.init(req)); + let (inner, req) = inner.build_split(); let client = ClientWithMiddleware { inner, middleware_stack, initialiser_stack, + final_initialiser_stack, }; (client, req) } @@ -623,6 +655,7 @@ impl RequestBuilder { inner, middleware_stack: self.middleware_stack.clone(), initialiser_stack: self.initialiser_stack.clone(), + final_initialiser_stack: self.final_initialiser_stack.clone(), extensions: self.extensions.clone(), }) } From f47be0b60e01493904fbd2710164375202801a3f Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 16:34:37 +0300 Subject: [PATCH 2/6] Add doc --- reqwest-middleware/src/client.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reqwest-middleware/src/client.rs b/reqwest-middleware/src/client.rs index 3d846cb..ac47097 100644 --- a/reqwest-middleware/src/client.rs +++ b/reqwest-middleware/src/client.rs @@ -84,6 +84,16 @@ impl ClientBuilder { self } + /// This is similar to the [`with_init`] method, but the initializers are added + /// using [`with_init`] is called when the [`RequestBuilder`] is created. + /// Whereas initializers added with [`with_final_init`] are called + /// just before the request build is completed. + /// + /// If you need to keep a reference to the initialiser after attaching, use [`with_arc_final_init`] + /// + /// [`with_arc_final_init`]: Self::with_arc_final_init + /// [`with_final_init`]: Self::with_final_init + /// [`with_init`]: Self::with_init pub fn with_final_init(self, initialiser: I) -> Self where I: RequestInitialiser, @@ -91,6 +101,9 @@ impl ClientBuilder { self.with_arc_final_init(Arc::new(initialiser)) } + /// Add a request initialiser to the chain. [`with_final_init`] is more ergonomic if you don't need the `Arc`. + /// + /// [`with_final_init`]: Self::with_final_init pub fn with_arc_final_init(mut self, initialiser: Arc) -> Self { self.final_initialiser_stack.push(initialiser); self From 46e50d0a704142347823452efdd4cb453b56a210 Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 17:53:17 +0300 Subject: [PATCH 3/6] Add example to doc Fix access to extensions when call send --- reqwest-middleware/src/client.rs | 46 +++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/reqwest-middleware/src/client.rs b/reqwest-middleware/src/client.rs index ac47097..f4dbb01 100644 --- a/reqwest-middleware/src/client.rs +++ b/reqwest-middleware/src/client.rs @@ -91,9 +91,53 @@ impl ClientBuilder { /// /// If you need to keep a reference to the initialiser after attaching, use [`with_arc_final_init`] /// + /// Adding initializers with this function allows you to access extensions + /// that are added with [`with_extension`] when creating a request. + /// + /// ```rust + /// use reqwest_middleware::{ClientBuilder, RequestBuilder, RequestInitialiser}; + /// + /// #[derive(Clone)] + /// struct Mark; + /// + /// struct Init; + /// + /// impl RequestInitialiser for Init { + /// fn init(&self, mut req: RequestBuilder) -> RequestBuilder { + /// let exists = req.extensions().get::().is_some(); + /// assert!(!exists); + /// req + /// } + /// } + /// + /// struct FinalInit; + /// + /// impl RequestInitialiser for FinalInit { + /// fn init(&self, mut req: RequestBuilder) -> RequestBuilder { + /// let exists = req.extensions().get::().is_some(); + /// assert!(exists); + /// req + /// } + /// } + /// async fn run() { + /// let request_client = reqwest::ClientBuilder::new() + /// .build() + /// .unwrap(); + /// let client = ClientBuilder::new(request_client) + /// .with_init(Init) + /// .with_final_init(FinalInit) + /// .build(); + /// let resp = client.get("https://truelayer.com") + /// .with_extension(Mark) + /// .send().await + /// .unwrap(); + /// println!("TrueLayer page HTML: {}", resp.text().await.unwrap()); + /// } + /// ``` /// [`with_arc_final_init`]: Self::with_arc_final_init /// [`with_final_init`]: Self::with_final_init /// [`with_init`]: Self::with_init + /// [`with_extension`]: RequestBuilder::with_extension pub fn with_final_init(self, initialiser: I) -> Self where I: RequestInitialiser, @@ -639,7 +683,7 @@ impl RequestBuilder { /// # } /// ``` pub async fn send(mut self) -> Result { - let mut extensions = std::mem::take(self.extensions()); + let mut extensions = self.extensions().clone(); let (client, req) = self.build_split(); client.execute_with_extensions(req?, &mut extensions).await } From 82954a92ff106fa27a83c4584fbe6399e4b793f1 Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 18:05:07 +0300 Subject: [PATCH 4/6] Add changes to CHANGELOG.md --- reqwest-middleware/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reqwest-middleware/CHANGELOG.md b/reqwest-middleware/CHANGELOG.md index 685f086..511f850 100644 --- a/reqwest-middleware/CHANGELOG.md +++ b/reqwest-middleware/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added `ClientBuilder::with_final_init` and `ClientBuilder::with_arc_final_init` + ## [0.4.1] - 2025-02-24 - Fixed wasm32 by disabling incompatible parts. On that target, `ClientWithMiddleware` is no longer From d37520285beb0be72e0c9242e01693bad3824e1e Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 18:49:58 +0300 Subject: [PATCH 5/6] clippy --- reqwest-retry/src/retryable_strategy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reqwest-retry/src/retryable_strategy.rs b/reqwest-retry/src/retryable_strategy.rs index 55c5409..153d5a0 100644 --- a/reqwest-retry/src/retryable_strategy.rs +++ b/reqwest-retry/src/retryable_strategy.rs @@ -7,10 +7,10 @@ use reqwest_middleware::Error; /// A [`RetryableStrategy`] has a single `handler` functions. /// The result of calling the request could be: /// - [`reqwest::Response`] In case the request has been sent and received correctly -/// This could however still mean that the server responded with a erroneous response. -/// For example a HTTP statuscode of 500 +/// This could however still mean that the server responded with a erroneous response. +/// For example a HTTP statuscode of 500 /// - [`reqwest_middleware::Error`] In this case the request actually failed. -/// This could, for example, be caused by a timeout on the connection. +/// This could, for example, be caused by a timeout on the connection. /// /// Example: /// From 3010677a53f5b2da510f699e172ebe5c3ee10077 Mon Sep 17 00:00:00 2001 From: "Oleh.Kulish" Date: Sat, 5 Apr 2025 18:50:39 +0300 Subject: [PATCH 6/6] clippy 2.0 --- reqwest-retry/src/retryable_strategy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reqwest-retry/src/retryable_strategy.rs b/reqwest-retry/src/retryable_strategy.rs index 153d5a0..8a790c4 100644 --- a/reqwest-retry/src/retryable_strategy.rs +++ b/reqwest-retry/src/retryable_strategy.rs @@ -7,10 +7,10 @@ use reqwest_middleware::Error; /// A [`RetryableStrategy`] has a single `handler` functions. /// The result of calling the request could be: /// - [`reqwest::Response`] In case the request has been sent and received correctly -/// This could however still mean that the server responded with a erroneous response. -/// For example a HTTP statuscode of 500 +/// This could however still mean that the server responded with a erroneous response. +/// For example a HTTP statuscode of 500 /// - [`reqwest_middleware::Error`] In this case the request actually failed. -/// This could, for example, be caused by a timeout on the connection. +/// This could, for example, be caused by a timeout on the connection. /// /// Example: ///