reqwest_middleware/
client.rs

1use http::Extensions;
2use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
3use reqwest::{Body, Client, IntoUrl, Method, Request, Response};
4use serde::Serialize;
5use std::convert::TryFrom;
6use std::fmt::{self, Display};
7use std::sync::Arc;
8
9#[cfg(feature = "multipart")]
10use reqwest::multipart;
11
12use crate::error::Result;
13use crate::middleware::{Middleware, Next};
14use crate::RequestInitialiser;
15
16/// A `ClientBuilder` is used to build a [`ClientWithMiddleware`].
17///
18/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
19pub struct ClientBuilder {
20    client: Client,
21    middleware_stack: Vec<Arc<dyn Middleware>>,
22    initialiser_stack: Vec<Arc<dyn RequestInitialiser>>,
23}
24
25impl ClientBuilder {
26    pub fn new(client: Client) -> Self {
27        ClientBuilder {
28            client,
29            middleware_stack: Vec::new(),
30            initialiser_stack: Vec::new(),
31        }
32    }
33
34    /// This method allows creating a ClientBuilder
35    /// from an existing ClientWithMiddleware instance
36    pub fn from_client(client_with_middleware: ClientWithMiddleware) -> Self {
37        Self {
38            client: client_with_middleware.inner,
39            middleware_stack: client_with_middleware.middleware_stack.into_vec(),
40            initialiser_stack: client_with_middleware.initialiser_stack.into_vec(),
41        }
42    }
43
44    /// Convenience method to attach middleware.
45    ///
46    /// If you need to keep a reference to the middleware after attaching, use [`with_arc`].
47    ///
48    /// [`with_arc`]: Self::with_arc
49    pub fn with<M>(self, middleware: M) -> Self
50    where
51        M: Middleware,
52    {
53        self.with_arc(Arc::new(middleware))
54    }
55
56    /// Add middleware to the chain. [`with`] is more ergonomic if you don't need the `Arc`.
57    ///
58    /// [`with`]: Self::with
59    pub fn with_arc(mut self, middleware: Arc<dyn Middleware>) -> Self {
60        self.middleware_stack.push(middleware);
61        self
62    }
63
64    /// Convenience method to attach a request initialiser.
65    ///
66    /// If you need to keep a reference to the initialiser after attaching, use [`with_arc_init`].
67    ///
68    /// [`with_arc_init`]: Self::with_arc_init
69    pub fn with_init<I>(self, initialiser: I) -> Self
70    where
71        I: RequestInitialiser,
72    {
73        self.with_arc_init(Arc::new(initialiser))
74    }
75
76    /// Add a request initialiser to the chain. [`with_init`] is more ergonomic if you don't need the `Arc`.
77    ///
78    /// [`with_init`]: Self::with_init
79    pub fn with_arc_init(mut self, initialiser: Arc<dyn RequestInitialiser>) -> Self {
80        self.initialiser_stack.push(initialiser);
81        self
82    }
83
84    /// Returns a `ClientWithMiddleware` using this builder configuration.
85    pub fn build(self) -> ClientWithMiddleware {
86        ClientWithMiddleware {
87            inner: self.client,
88            middleware_stack: self.middleware_stack.into_boxed_slice(),
89            initialiser_stack: self.initialiser_stack.into_boxed_slice(),
90        }
91    }
92}
93
94/// `ClientWithMiddleware` is a wrapper around [`reqwest::Client`] which runs middleware on every
95/// request.
96#[derive(Clone, Default)]
97pub struct ClientWithMiddleware {
98    inner: reqwest::Client,
99    middleware_stack: Box<[Arc<dyn Middleware>]>,
100    initialiser_stack: Box<[Arc<dyn RequestInitialiser>]>,
101}
102
103impl ClientWithMiddleware {
104    /// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
105    pub fn new<T>(client: Client, middleware_stack: T) -> Self
106    where
107        T: Into<Box<[Arc<dyn Middleware>]>>,
108    {
109        ClientWithMiddleware {
110            inner: client,
111            middleware_stack: middleware_stack.into(),
112            // TODO(conradludgate) - allow downstream code to control this manually if desired
113            initialiser_stack: Box::new([]),
114        }
115    }
116
117    /// Convenience method to make a `GET` request to a URL.
118    ///
119    /// # Errors
120    ///
121    /// This method fails whenever the supplied `Url` cannot be parsed.
122    pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
123        self.request(Method::GET, url)
124    }
125
126    /// Convenience method to make a `POST` request to a URL.
127    ///
128    /// # Errors
129    ///
130    /// This method fails whenever the supplied `Url` cannot be parsed.
131    pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
132        self.request(Method::POST, url)
133    }
134
135    /// Convenience method to make a `PUT` request to a URL.
136    ///
137    /// # Errors
138    ///
139    /// This method fails whenever the supplied `Url` cannot be parsed.
140    pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
141        self.request(Method::PUT, url)
142    }
143
144    /// Convenience method to make a `PATCH` request to a URL.
145    ///
146    /// # Errors
147    ///
148    /// This method fails whenever the supplied `Url` cannot be parsed.
149    pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
150        self.request(Method::PATCH, url)
151    }
152
153    /// Convenience method to make a `DELETE` request to a URL.
154    ///
155    /// # Errors
156    ///
157    /// This method fails whenever the supplied `Url` cannot be parsed.
158    pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
159        self.request(Method::DELETE, url)
160    }
161
162    /// Convenience method to make a `HEAD` request to a URL.
163    ///
164    /// # Errors
165    ///
166    /// This method fails whenever the supplied `Url` cannot be parsed.
167    pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
168        self.request(Method::HEAD, url)
169    }
170
171    /// Convenience method to make a `CONNECT` request to a URL.
172    ///
173    /// # Errors
174    ///
175    /// This method fails whenever the supplied `Url` cannot be parsed.
176    pub fn connect<U: IntoUrl>(&self, url: U) -> RequestBuilder {
177        self.request(Method::CONNECT, url)
178    }
179
180    /// Start building a `Request` with the `Method` and `Url`.
181    ///
182    /// Returns a `RequestBuilder`, which will allow setting headers and
183    /// the request body before sending.
184    ///
185    /// # Errors
186    ///
187    /// This method fails whenever the supplied `Url` cannot be parsed.
188    pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
189        let req = RequestBuilder {
190            inner: self.inner.request(method, url),
191            extensions: Extensions::new(),
192            middleware_stack: self.middleware_stack.clone(),
193            initialiser_stack: self.initialiser_stack.clone(),
194        };
195        self.initialiser_stack
196            .iter()
197            .fold(req, |req, i| i.init(req))
198    }
199
200    /// Executes a `Request`.
201    ///
202    /// A `Request` can be built manually with `Request::new()` or obtained
203    /// from a RequestBuilder with `RequestBuilder::build()`.
204    ///
205    /// You should prefer to use the `RequestBuilder` and
206    /// `RequestBuilder::send()`.
207    ///
208    /// # Errors
209    ///
210    /// This method fails if there was an error while sending request,
211    /// redirect loop was detected or redirect limit was exhausted.
212    pub async fn execute(&self, req: Request) -> Result<Response> {
213        let mut ext = Extensions::new();
214        self.execute_with_extensions(req, &mut ext).await
215    }
216
217    /// Executes a `Request` with initial [`Extensions`].
218    ///
219    /// A `Request` can be built manually with `Request::new()` or obtained
220    /// from a RequestBuilder with `RequestBuilder::build()`.
221    ///
222    /// You should prefer to use the `RequestBuilder` and
223    /// `RequestBuilder::send()`.
224    ///
225    /// # Errors
226    ///
227    /// This method fails if there was an error while sending request,
228    /// redirect loop was detected or redirect limit was exhausted.
229    pub async fn execute_with_extensions(
230        &self,
231        req: Request,
232        ext: &mut Extensions,
233    ) -> Result<Response> {
234        let next = Next::new(&self.inner, &self.middleware_stack);
235        next.run(req, ext).await
236    }
237}
238
239/// Create a `ClientWithMiddleware` without any middleware.
240impl From<Client> for ClientWithMiddleware {
241    fn from(client: Client) -> Self {
242        ClientWithMiddleware {
243            inner: client,
244            middleware_stack: Box::new([]),
245            initialiser_stack: Box::new([]),
246        }
247    }
248}
249
250impl fmt::Debug for ClientWithMiddleware {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        // skipping middleware_stack field for now
253        f.debug_struct("ClientWithMiddleware")
254            .field("inner", &self.inner)
255            .finish_non_exhaustive()
256    }
257}
258
259#[cfg(not(target_arch = "wasm32"))]
260mod service {
261    use std::{
262        future::Future,
263        pin::Pin,
264        task::{Context, Poll},
265    };
266
267    use crate::Result;
268    use http::Extensions;
269    use reqwest::{Request, Response};
270
271    use crate::{middleware::BoxFuture, ClientWithMiddleware, Next};
272
273    // this is meant to be semi-private, same as reqwest's pending
274    pub struct Pending {
275        inner: BoxFuture<'static, Result<Response>>,
276    }
277
278    impl Unpin for Pending {}
279
280    impl Future for Pending {
281        type Output = Result<Response>;
282
283        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
284            self.inner.as_mut().poll(cx)
285        }
286    }
287
288    impl tower_service::Service<Request> for ClientWithMiddleware {
289        type Response = Response;
290        type Error = crate::Error;
291        type Future = Pending;
292
293        fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
294            self.inner.poll_ready(cx).map_err(crate::Error::Reqwest)
295        }
296
297        fn call(&mut self, req: Request) -> Self::Future {
298            let inner = self.inner.clone();
299            let middlewares = self.middleware_stack.clone();
300            let mut extensions = Extensions::new();
301            Pending {
302                inner: Box::pin(async move {
303                    let next = Next::new(&inner, &middlewares);
304                    next.run(req, &mut extensions).await
305                }),
306            }
307        }
308    }
309
310    impl tower_service::Service<Request> for &'_ ClientWithMiddleware {
311        type Response = Response;
312        type Error = crate::Error;
313        type Future = Pending;
314
315        fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
316            (&self.inner).poll_ready(cx).map_err(crate::Error::Reqwest)
317        }
318
319        fn call(&mut self, req: Request) -> Self::Future {
320            let inner = self.inner.clone();
321            let middlewares = self.middleware_stack.clone();
322            let mut extensions = Extensions::new();
323            Pending {
324                inner: Box::pin(async move {
325                    let next = Next::new(&inner, &middlewares);
326                    next.run(req, &mut extensions).await
327                }),
328            }
329        }
330    }
331}
332
333/// This is a wrapper around [`reqwest::RequestBuilder`] exposing the same API.
334#[must_use = "RequestBuilder does nothing until you 'send' it"]
335pub struct RequestBuilder {
336    inner: reqwest::RequestBuilder,
337    middleware_stack: Box<[Arc<dyn Middleware>]>,
338    initialiser_stack: Box<[Arc<dyn RequestInitialiser>]>,
339    extensions: Extensions,
340}
341
342impl RequestBuilder {
343    /// Assemble a builder starting from an existing `Client` and a `Request`.
344    pub fn from_parts(client: ClientWithMiddleware, request: Request) -> RequestBuilder {
345        let inner = reqwest::RequestBuilder::from_parts(client.inner, request);
346        RequestBuilder {
347            inner,
348            middleware_stack: client.middleware_stack,
349            initialiser_stack: client.initialiser_stack,
350            extensions: Extensions::new(),
351        }
352    }
353
354    /// Add a `Header` to this Request.
355    pub fn header<K, V>(self, key: K, value: V) -> Self
356    where
357        HeaderName: TryFrom<K>,
358        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
359        HeaderValue: TryFrom<V>,
360        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
361    {
362        RequestBuilder {
363            inner: self.inner.header(key, value),
364            ..self
365        }
366    }
367
368    /// Add a set of Headers to the existing ones on this Request.
369    ///
370    /// The headers will be merged in to any already set.
371    pub fn headers(self, headers: HeaderMap) -> Self {
372        RequestBuilder {
373            inner: self.inner.headers(headers),
374            ..self
375        }
376    }
377
378    #[cfg(not(target_arch = "wasm32"))]
379    pub fn version(self, version: reqwest::Version) -> Self {
380        RequestBuilder {
381            inner: self.inner.version(version),
382            ..self
383        }
384    }
385
386    /// Enable HTTP basic authentication.
387    ///
388    /// ```rust
389    /// # use anyhow::Error;
390    ///
391    /// # async fn run() -> Result<(), Error> {
392    /// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
393    /// let resp = client.delete("http://httpbin.org/delete")
394    ///     .basic_auth("admin", Some("good password"))
395    ///     .send()
396    ///     .await?;
397    /// # Ok(())
398    /// # }
399    /// ```
400    pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
401    where
402        U: Display,
403        P: Display,
404    {
405        RequestBuilder {
406            inner: self.inner.basic_auth(username, password),
407            ..self
408        }
409    }
410
411    /// Enable HTTP bearer authentication.
412    pub fn bearer_auth<T>(self, token: T) -> Self
413    where
414        T: Display,
415    {
416        RequestBuilder {
417            inner: self.inner.bearer_auth(token),
418            ..self
419        }
420    }
421
422    /// Set the request body.
423    pub fn body<T: Into<Body>>(self, body: T) -> Self {
424        RequestBuilder {
425            inner: self.inner.body(body),
426            ..self
427        }
428    }
429
430    /// Enables a request timeout.
431    ///
432    /// The timeout is applied from when the request starts connecting until the
433    /// response body has finished. It affects only this request and overrides
434    /// the timeout configured using `ClientBuilder::timeout()`.
435    #[cfg(not(target_arch = "wasm32"))]
436    pub fn timeout(self, timeout: std::time::Duration) -> Self {
437        RequestBuilder {
438            inner: self.inner.timeout(timeout),
439            ..self
440        }
441    }
442
443    #[cfg(feature = "multipart")]
444    #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
445    pub fn multipart(self, multipart: multipart::Form) -> Self {
446        RequestBuilder {
447            inner: self.inner.multipart(multipart),
448            ..self
449        }
450    }
451
452    /// Modify the query string of the URL.
453    ///
454    /// Modifies the URL of this request, adding the parameters provided.
455    /// This method appends and does not overwrite. This means that it can
456    /// be called multiple times and that existing query parameters are not
457    /// overwritten if the same key is used. The key will simply show up
458    /// twice in the query string.
459    /// Calling `.query(&[("foo", "a"), ("foo", "b")])` gives `"foo=a&foo=b"`.
460    ///
461    /// # Note
462    /// This method does not support serializing a single key-value
463    /// pair. Instead of using `.query(("key", "val"))`, use a sequence, such
464    /// as `.query(&[("key", "val")])`. It's also possible to serialize structs
465    /// and maps into a key-value pair.
466    ///
467    /// # Errors
468    /// This method will fail if the object you provide cannot be serialized
469    /// into a query string.
470    pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Self {
471        RequestBuilder {
472            inner: self.inner.query(query),
473            ..self
474        }
475    }
476
477    /// Send a form body.
478    ///
479    /// Sets the body to the url encoded serialization of the passed value,
480    /// and also sets the `Content-Type: application/x-www-form-urlencoded`
481    /// header.
482    ///
483    /// ```rust
484    /// # use anyhow::Error;
485    /// # use std::collections::HashMap;
486    /// #
487    /// # async fn run() -> Result<(), Error> {
488    /// let mut params = HashMap::new();
489    /// params.insert("lang", "rust");
490    ///
491    /// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
492    /// let res = client.post("http://httpbin.org")
493    ///     .form(&params)
494    ///     .send()
495    ///     .await?;
496    /// # Ok(())
497    /// # }
498    /// ```
499    ///
500    /// # Errors
501    ///
502    /// This method fails if the passed value cannot be serialized into
503    /// url encoded format
504    pub fn form<T: Serialize + ?Sized>(self, form: &T) -> Self {
505        RequestBuilder {
506            inner: self.inner.form(form),
507            ..self
508        }
509    }
510
511    /// Send a JSON body.
512    ///
513    /// # Optional
514    ///
515    /// This requires the optional `json` feature enabled.
516    ///
517    /// # Errors
518    ///
519    /// Serialization can fail if `T`'s implementation of `Serialize` decides to
520    /// fail, or if `T` contains a map with non-string keys.
521    #[cfg(feature = "json")]
522    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
523    pub fn json<T: Serialize + ?Sized>(self, json: &T) -> Self {
524        RequestBuilder {
525            inner: self.inner.json(json),
526            ..self
527        }
528    }
529
530    /// Build a `Request`, which can be inspected, modified and executed with
531    /// `ClientWithMiddleware::execute()`.
532    pub fn build(self) -> reqwest::Result<Request> {
533        self.inner.build()
534    }
535
536    /// Build a `Request`, which can be inspected, modified and executed with
537    /// `ClientWithMiddleware::execute()`.
538    ///
539    /// This is similar to [`RequestBuilder::build()`], but also returns the
540    /// embedded `Client`.
541    pub fn build_split(self) -> (ClientWithMiddleware, reqwest::Result<Request>) {
542        let Self {
543            inner,
544            middleware_stack,
545            initialiser_stack,
546            ..
547        } = self;
548        let (inner, req) = inner.build_split();
549        let client = ClientWithMiddleware {
550            inner,
551            middleware_stack,
552            initialiser_stack,
553        };
554        (client, req)
555    }
556
557    /// Inserts the extension into this request builder
558    pub fn with_extension<T: Send + Sync + Clone + 'static>(mut self, extension: T) -> Self {
559        self.extensions.insert(extension);
560        self
561    }
562
563    /// Returns a mutable reference to the internal set of extensions for this request
564    pub fn extensions(&mut self) -> &mut Extensions {
565        &mut self.extensions
566    }
567
568    /// Constructs the Request and sends it to the target URL, returning a
569    /// future Response.
570    ///
571    /// # Errors
572    ///
573    /// This method fails if there was an error while sending request,
574    /// redirect loop was detected or redirect limit was exhausted.
575    ///
576    /// # Example
577    ///
578    /// ```no_run
579    /// # use anyhow::Error;
580    /// #
581    /// # async fn run() -> Result<(), Error> {
582    /// let response = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new())
583    ///     .get("https://hyper.rs")
584    ///     .send()
585    ///     .await?;
586    /// # Ok(())
587    /// # }
588    /// ```
589    pub async fn send(mut self) -> Result<Response> {
590        let mut extensions = std::mem::take(self.extensions());
591        let (client, req) = self.build_split();
592        client.execute_with_extensions(req?, &mut extensions).await
593    }
594
595    /// Attempt to clone the RequestBuilder.
596    ///
597    /// `None` is returned if the RequestBuilder can not be cloned,
598    /// i.e. if the request body is a stream.
599    ///
600    /// # Examples
601    ///
602    /// ```
603    /// # use reqwest::Error;
604    /// #
605    /// # fn run() -> Result<(), Error> {
606    /// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
607    /// let builder = client.post("http://httpbin.org/post")
608    ///     .body("from a &str!");
609    /// let clone = builder.try_clone();
610    /// assert!(clone.is_some());
611    /// # Ok(())
612    /// # }
613    /// ```
614    pub fn try_clone(&self) -> Option<Self> {
615        self.inner.try_clone().map(|inner| RequestBuilder {
616            inner,
617            middleware_stack: self.middleware_stack.clone(),
618            initialiser_stack: self.initialiser_stack.clone(),
619            extensions: self.extensions.clone(),
620        })
621    }
622}
623
624impl fmt::Debug for RequestBuilder {
625    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
626        // skipping middleware_stack field for now
627        f.debug_struct("RequestBuilder")
628            .field("inner", &self.inner)
629            .finish_non_exhaustive()
630    }
631}