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