Refactor Redirect API (#741)
Changed the redirect types to be from the `redirect` module: - `reqwest::RedirectPolicy` is now `reqwest::redirect::Policy` - `reqwest::RedirectAttempt` is now `reqwest::redirect::Attempt` - `reqwest::RedirectAction` is now `reqwest::redirect::Action` Changed behavior of default policy to no longer check for redirect loops (loops should still be caught eventually by the maximum limit). Removed the `too_many_redirects` and `loop_detected` methods from `Action`. Added `error` to `Action` that can be passed any error type. Closes #717
This commit is contained in:
		| @@ -29,7 +29,7 @@ use crate::connect::Connector; | |||||||
| #[cfg(feature = "cookies")] | #[cfg(feature = "cookies")] | ||||||
| use crate::cookie; | use crate::cookie; | ||||||
| use crate::into_url::{expect_uri, try_uri}; | use crate::into_url::{expect_uri, try_uri}; | ||||||
| use crate::redirect::{self, remove_sensitive_headers, RedirectPolicy}; | use crate::redirect::{self, remove_sensitive_headers}; | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| use crate::tls::TlsBackend; | use crate::tls::TlsBackend; | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| @@ -70,7 +70,7 @@ struct Config { | |||||||
|     identity: Option<Identity>, |     identity: Option<Identity>, | ||||||
|     proxies: Vec<Proxy>, |     proxies: Vec<Proxy>, | ||||||
|     auto_sys_proxy: bool, |     auto_sys_proxy: bool, | ||||||
|     redirect_policy: RedirectPolicy, |     redirect_policy: redirect::Policy, | ||||||
|     referer: bool, |     referer: bool, | ||||||
|     timeout: Option<Duration>, |     timeout: Option<Duration>, | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
| @@ -114,7 +114,7 @@ impl ClientBuilder { | |||||||
|                 max_idle_per_host: std::usize::MAX, |                 max_idle_per_host: std::usize::MAX, | ||||||
|                 proxies: Vec::new(), |                 proxies: Vec::new(), | ||||||
|                 auto_sys_proxy: true, |                 auto_sys_proxy: true, | ||||||
|                 redirect_policy: RedirectPolicy::default(), |                 redirect_policy: redirect::Policy::default(), | ||||||
|                 referer: true, |                 referer: true, | ||||||
|                 timeout: None, |                 timeout: None, | ||||||
|                 #[cfg(feature = "tls")] |                 #[cfg(feature = "tls")] | ||||||
| @@ -372,7 +372,7 @@ impl ClientBuilder { | |||||||
|     /// Set a `RedirectPolicy` for this client. |     /// Set a `RedirectPolicy` for this client. | ||||||
|     /// |     /// | ||||||
|     /// Default will follow redirects up to a maximum of 10. |     /// Default will follow redirects up to a maximum of 10. | ||||||
|     pub fn redirect(mut self, policy: RedirectPolicy) -> ClientBuilder { |     pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder { | ||||||
|         self.config.redirect_policy = policy; |         self.config.redirect_policy = policy; | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| @@ -915,7 +915,7 @@ struct ClientRef { | |||||||
|     gzip: bool, |     gzip: bool, | ||||||
|     headers: HeaderMap, |     headers: HeaderMap, | ||||||
|     hyper: HyperClient, |     hyper: HyperClient, | ||||||
|     redirect_policy: RedirectPolicy, |     redirect_policy: redirect::Policy, | ||||||
|     referer: bool, |     referer: bool, | ||||||
|     request_timeout: Option<Duration>, |     request_timeout: Option<Duration>, | ||||||
|     proxies: Arc<Vec<Proxy>>, |     proxies: Arc<Vec<Proxy>>, | ||||||
| @@ -1124,7 +1124,7 @@ impl Future for PendingRequest { | |||||||
|                         .check(res.status(), &loc, &self.urls); |                         .check(res.status(), &loc, &self.urls); | ||||||
|  |  | ||||||
|                     match action { |                     match action { | ||||||
|                         redirect::Action::Follow => { |                         redirect::ActionKind::Follow => { | ||||||
|                             self.url = loc; |                             self.url = loc; | ||||||
|  |  | ||||||
|                             let mut headers = |                             let mut headers = | ||||||
| @@ -1159,14 +1159,12 @@ impl Future for PendingRequest { | |||||||
|                             *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req); |                             *self.as_mut().in_flight().get_mut() = self.client.hyper.request(req); | ||||||
|                             continue; |                             continue; | ||||||
|                         } |                         } | ||||||
|                         redirect::Action::Stop => { |                         redirect::ActionKind::Stop => { | ||||||
|                             debug!("redirect_policy disallowed redirection to '{}'", loc); |                             debug!("redirect_policy disallowed redirection to '{}'", loc); | ||||||
|                         } |                         } | ||||||
|                         redirect::Action::LoopDetected => { |                         redirect::ActionKind::Error(err) => { | ||||||
|                             return Poll::Ready(Err(crate::error::loop_detected(self.url.clone()))); |                             return Poll::Ready(Err(crate::error::redirect( | ||||||
|                         } |                                 err, | ||||||
|                         redirect::Action::TooManyRedirects => { |  | ||||||
|                             return Poll::Ready(Err(crate::error::too_many_redirects( |  | ||||||
|                                 self.url.clone(), |                                 self.url.clone(), | ||||||
|                             ))); |                             ))); | ||||||
|                         } |                         } | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ use log::{error, trace}; | |||||||
| use super::request::{Request, RequestBuilder}; | use super::request::{Request, RequestBuilder}; | ||||||
| use super::response::Response; | use super::response::Response; | ||||||
| use super::wait; | use super::wait; | ||||||
| use crate::{async_impl, header, IntoUrl, Method, Proxy, RedirectPolicy}; | use crate::{async_impl, header, IntoUrl, Method, Proxy, redirect}; | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| use crate::{Certificate, Identity}; | use crate::{Certificate, Identity}; | ||||||
|  |  | ||||||
| @@ -176,10 +176,10 @@ impl ClientBuilder { | |||||||
|  |  | ||||||
|     // Redirect options |     // Redirect options | ||||||
|  |  | ||||||
|     /// Set a `RedirectPolicy` for this client. |     /// Set a `redirect::Policy` for this client. | ||||||
|     /// |     /// | ||||||
|     /// Default will follow redirects up to a maximum of 10. |     /// Default will follow redirects up to a maximum of 10. | ||||||
|     pub fn redirect(self, policy: RedirectPolicy) -> ClientBuilder { |     pub fn redirect(self, policy: redirect::Policy) -> ClientBuilder { | ||||||
|         self.with_inner(move |inner| inner.redirect(policy)) |         self.with_inner(move |inner| inner.redirect(policy)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -541,7 +541,7 @@ impl Client { | |||||||
|     /// # Errors |     /// # Errors | ||||||
|     /// |     /// | ||||||
|     /// This method fails if there was an error while sending request, |     /// This method fails if there was an error while sending request, | ||||||
|     /// redirect loop was detected or redirect limit was exhausted. |     /// or redirect limit was exhausted. | ||||||
|     pub fn execute(&self, request: Request) -> crate::Result<Response> { |     pub fn execute(&self, request: Request) -> crate::Result<Response> { | ||||||
|         self.inner.execute_request(request) |         self.inner.execute_request(request) | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -212,12 +212,8 @@ pub(crate) fn request<E: Into<BoxError>>(e: E) -> Error { | |||||||
|     Error::new(Kind::Request, Some(e)) |     Error::new(Kind::Request, Some(e)) | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn loop_detected(url: Url) -> Error { | pub(crate) fn redirect<E: Into<BoxError>>(e: E, url: Url) -> Error { | ||||||
|     Error::new(Kind::Redirect, Some("infinite redirect loop detected")).with_url(url) |     Error::new(Kind::Redirect, Some(e)).with_url(url) | ||||||
| } |  | ||||||
|  |  | ||||||
| pub(crate) fn too_many_redirects(url: Url) -> Error { |  | ||||||
|     Error::new(Kind::Redirect, Some("too many redirects")).with_url(url) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn status_code(url: Url, status: StatusCode) -> Error { | pub(crate) fn status_code(url: Url, status: StatusCode) -> Error { | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -119,9 +119,9 @@ | |||||||
| //! | //! | ||||||
| //! ## Redirect Policies | //! ## Redirect Policies | ||||||
| //! | //! | ||||||
| //! By default, a `Client` will automatically handle HTTP redirects, detecting | //! By default, a `Client` will automatically handle HTTP redirects, having a | ||||||
| //! loops, and having a maximum redirect chain of 10 hops. To customize this | //! maximum redirect chain of 10 hops. To customize this behavior, a | ||||||
| //! behavior, a [`RedirectPolicy`][redirect] can used with a `ClientBuilder`. | //! [`redirect::Policy`][redirect] can be used with a `ClientBuilder`. | ||||||
| //! | //! | ||||||
| //! ## Cookies | //! ## Cookies | ||||||
| //! | //! | ||||||
| @@ -175,7 +175,7 @@ | |||||||
| //! [get]: ./fn.get.html | //! [get]: ./fn.get.html | ||||||
| //! [builder]: ./struct.RequestBuilder.html | //! [builder]: ./struct.RequestBuilder.html | ||||||
| //! [serde]: http://serde.rs | //! [serde]: http://serde.rs | ||||||
| //! [redirect]: ./struct.RedirectPolicy.html | //! [redirect]: crate::redirect | ||||||
| //! [Proxy]: ./struct.Proxy.html | //! [Proxy]: ./struct.Proxy.html | ||||||
| //! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section | //! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section | ||||||
|  |  | ||||||
| @@ -237,7 +237,6 @@ pub use self::into_url::IntoUrl; | |||||||
| /// - native TLS backend cannot be initialized | /// - native TLS backend cannot be initialized | ||||||
| /// - supplied `Url` cannot be parsed | /// - supplied `Url` cannot be parsed | ||||||
| /// - there was an error while sending request | /// - there was an error while sending request | ||||||
| /// - redirect loop was detected |  | ||||||
| /// - redirect limit was exhausted | /// - redirect limit was exhausted | ||||||
| pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { | pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { | ||||||
|     Client::builder().build()?.get(url).send().await |     Client::builder().build()?.get(url).send().await | ||||||
| @@ -279,7 +278,6 @@ if_hyper! { | |||||||
|         multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response, ResponseBuilderExt, |         multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response, ResponseBuilderExt, | ||||||
|     }; |     }; | ||||||
|     pub use self::proxy::Proxy; |     pub use self::proxy::Proxy; | ||||||
|     pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy}; |  | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     pub use self::tls::{Certificate, Identity}; |     pub use self::tls::{Certificate, Identity}; | ||||||
|  |  | ||||||
| @@ -293,17 +291,9 @@ if_hyper! { | |||||||
|     //#[cfg(feature = "trust-dns")] |     //#[cfg(feature = "trust-dns")] | ||||||
|     //mod dns; |     //mod dns; | ||||||
|     mod proxy; |     mod proxy; | ||||||
|     mod redirect; |     pub mod redirect; | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     mod tls; |     mod tls; | ||||||
|  |  | ||||||
|     #[doc(hidden)] |  | ||||||
|     #[deprecated(note = "types moved to top of crate")] |  | ||||||
|     pub mod r#async { |  | ||||||
|         pub use crate::async_impl::{ |  | ||||||
|             multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response, |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| if_wasm! { | if_wasm! { | ||||||
|   | |||||||
							
								
								
									
										203
									
								
								src/redirect.rs
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								src/redirect.rs
									
									
									
									
									
								
							| @@ -1,3 +1,10 @@ | |||||||
|  | //! Redirect Handling | ||||||
|  | //! | ||||||
|  | //! By default, a `Client` will automatically handle HTTP redirects, having a | ||||||
|  | //! maximum redirect chain of 10 hops. To customize this behavior, a | ||||||
|  | //! `redirect::Policy` can be used with a `ClientBuilder`. | ||||||
|  |  | ||||||
|  | use std::error::Error as StdError; | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use crate::header::{HeaderMap, AUTHORIZATION, COOKIE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE}; | use crate::header::{HeaderMap, AUTHORIZATION, COOKIE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE}; | ||||||
| @@ -14,14 +21,14 @@ use crate::Url; | |||||||
| ///   the allowed maximum redirect hops in a chain. | ///   the allowed maximum redirect hops in a chain. | ||||||
| /// - `none` can be used to disable all redirect behavior. | /// - `none` can be used to disable all redirect behavior. | ||||||
| /// - `custom` can be used to create a customized policy. | /// - `custom` can be used to create a customized policy. | ||||||
| pub struct RedirectPolicy { | pub struct Policy { | ||||||
|     inner: Policy, |     inner: PolicyKind, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A type that holds information on the next request and previous requests | /// A type that holds information on the next request and previous requests | ||||||
| /// in redirect chain. | /// in redirect chain. | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct RedirectAttempt<'a> { | pub struct Attempt<'a> { | ||||||
|     status: StatusCode, |     status: StatusCode, | ||||||
|     next: &'a Url, |     next: &'a Url, | ||||||
|     previous: &'a [Url], |     previous: &'a [Url], | ||||||
| @@ -29,50 +36,50 @@ pub struct RedirectAttempt<'a> { | |||||||
|  |  | ||||||
| /// An action to perform when a redirect status code is found. | /// An action to perform when a redirect status code is found. | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct RedirectAction { | pub struct Action { | ||||||
|     inner: Action, |     inner: ActionKind, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl RedirectPolicy { | impl Policy { | ||||||
|     /// Create a RedirectPolicy with a maximum number of redirects. |     /// Create a `Policy` with a maximum number of redirects. | ||||||
|     /// |     /// | ||||||
|     /// An `Error` will be returned if the max is reached. |     /// An `Error` will be returned if the max is reached. | ||||||
|     pub fn limited(max: usize) -> RedirectPolicy { |     pub fn limited(max: usize) -> Self { | ||||||
|         RedirectPolicy { |         Self { | ||||||
|             inner: Policy::Limit(max), |             inner: PolicyKind::Limit(max), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Create a RedirectPolicy that does not follow any redirect. |     /// Create a `Policy` that does not follow any redirect. | ||||||
|     pub fn none() -> RedirectPolicy { |     pub fn none() -> Self { | ||||||
|         RedirectPolicy { |         Self { | ||||||
|             inner: Policy::None, |             inner: PolicyKind::None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Create a custom RedirectPolicy using the passed function. |     /// Create a custom `Policy` using the passed function. | ||||||
|     /// |     /// | ||||||
|     /// # Note |     /// # Note | ||||||
|     /// |     /// | ||||||
|     /// The default RedirectPolicy handles redirect loops and a maximum loop |     /// The default `Policy` handles a maximum loop | ||||||
|     /// chain, but the custom variant does not do that for you automatically. |     /// chain, but the custom variant does not do that for you automatically. | ||||||
|     /// The custom policy should have some way of handling those. |     /// The custom policy should have some way of handling those. | ||||||
|     /// |     /// | ||||||
|     /// Information on the next request and previous requests can be found |     /// Information on the next request and previous requests can be found | ||||||
|     /// on the [`RedirectAttempt`] argument passed to the closure. |     /// on the [`Attempt`] argument passed to the closure. | ||||||
|     /// |     /// | ||||||
|     /// Actions can be conveniently created from methods on the |     /// Actions can be conveniently created from methods on the | ||||||
|     /// [`RedirectAttempt`]. |     /// [`Attempt`]. | ||||||
|     /// |     /// | ||||||
|     /// # Example |     /// # Example | ||||||
|     /// |     /// | ||||||
|     /// ```rust |     /// ```rust | ||||||
|     /// # use reqwest::{Error, RedirectPolicy}; |     /// # use reqwest::{Error, redirect}; | ||||||
|     /// # |     /// # | ||||||
|     /// # fn run() -> Result<(), Error> { |     /// # fn run() -> Result<(), Error> { | ||||||
|     /// let custom = RedirectPolicy::custom(|attempt| { |     /// let custom = redirect::Policy::custom(|attempt| { | ||||||
|     ///     if attempt.previous().len() > 5 { |     ///     if attempt.previous().len() > 5 { | ||||||
|     ///         attempt.too_many_redirects() |     ///         attempt.error("too many redirects") | ||||||
|     ///     } else if attempt.url().host_str() == Some("example.domain") { |     ///     } else if attempt.url().host_str() == Some("example.domain") { | ||||||
|     ///         // prevent redirects to 'example.domain' |     ///         // prevent redirects to 'example.domain' | ||||||
|     ///         attempt.stop() |     ///         attempt.stop() | ||||||
| @@ -87,54 +94,52 @@ impl RedirectPolicy { | |||||||
|     /// # } |     /// # } | ||||||
|     /// ``` |     /// ``` | ||||||
|     /// |     /// | ||||||
|     /// [`RedirectAttempt`]: struct.RedirectAttempt.html |     /// [`Attempt`]: struct.Attempt.html | ||||||
|     pub fn custom<T>(policy: T) -> RedirectPolicy |     pub fn custom<T>(policy: T) -> Self | ||||||
|     where |     where | ||||||
|         T: Fn(RedirectAttempt) -> RedirectAction + Send + Sync + 'static, |         T: Fn(Attempt) -> Action + Send + Sync + 'static, | ||||||
|     { |     { | ||||||
|         RedirectPolicy { |         Self { | ||||||
|             inner: Policy::Custom(Box::new(policy)), |             inner: PolicyKind::Custom(Box::new(policy)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Apply this policy to a given [`RedirectAttempt`] to produce a [`RedirectAction`]. |     /// Apply this policy to a given [`Attempt`] to produce a [`Action`]. | ||||||
|     /// |     /// | ||||||
|     /// # Note |     /// # Note | ||||||
|     /// |     /// | ||||||
|     /// This method can be used together with RedirectPolicy::custom() |     /// This method can be used together with `Policy::custom()` | ||||||
|     /// to construct one RedirectPolicy that wraps another. |     /// to construct one `Policy` that wraps another. | ||||||
|     /// |     /// | ||||||
|     /// # Example |     /// # Example | ||||||
|     /// |     /// | ||||||
|     /// ```rust |     /// ```rust | ||||||
|     /// # use reqwest::{Error, RedirectPolicy}; |     /// # use reqwest::{Error, redirect}; | ||||||
|     /// # |     /// # | ||||||
|     /// # fn run() -> Result<(), Error> { |     /// # fn run() -> Result<(), Error> { | ||||||
|     /// let custom = RedirectPolicy::custom(|attempt| { |     /// let custom = redirect::Policy::custom(|attempt| { | ||||||
|     ///     eprintln!("{}, Location: {:?}", attempt.status(), attempt.url()); |     ///     eprintln!("{}, Location: {:?}", attempt.status(), attempt.url()); | ||||||
|     ///     RedirectPolicy::default().redirect(attempt) |     ///     redirect::Policy::default().redirect(attempt) | ||||||
|     /// }); |     /// }); | ||||||
|     /// # Ok(()) |     /// # Ok(()) | ||||||
|     /// # } |     /// # } | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn redirect(&self, attempt: RedirectAttempt) -> RedirectAction { |     pub fn redirect(&self, attempt: Attempt) -> Action { | ||||||
|         match self.inner { |         match self.inner { | ||||||
|             Policy::Custom(ref custom) => custom(attempt), |             PolicyKind::Custom(ref custom) => custom(attempt), | ||||||
|             Policy::Limit(max) => { |             PolicyKind::Limit(max) => { | ||||||
|                 if attempt.previous.len() == max { |                 if attempt.previous.len() == max { | ||||||
|                     attempt.too_many_redirects() |                     attempt.error(TooManyRedirects) | ||||||
|                 } else if attempt.previous.contains(attempt.next) { |  | ||||||
|                     attempt.loop_detected() |  | ||||||
|                 } else { |                 } else { | ||||||
|                     attempt.follow() |                     attempt.follow() | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             Policy::None => attempt.stop(), |             PolicyKind::None => attempt.stop(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn check(&self, status: StatusCode, next: &Url, previous: &[Url]) -> Action { |     pub(crate) fn check(&self, status: StatusCode, next: &Url, previous: &[Url]) -> ActionKind { | ||||||
|         self.redirect(RedirectAttempt { |         self.redirect(Attempt { | ||||||
|             status, |             status, | ||||||
|             next, |             next, | ||||||
|             previous, |             previous, | ||||||
| @@ -144,20 +149,20 @@ impl RedirectPolicy { | |||||||
|  |  | ||||||
|     pub(crate) fn is_default(&self) -> bool { |     pub(crate) fn is_default(&self) -> bool { | ||||||
|         match self.inner { |         match self.inner { | ||||||
|             Policy::Limit(10) => true, |             PolicyKind::Limit(10) => true, | ||||||
|             _ => false, |             _ => false, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Default for RedirectPolicy { | impl Default for Policy { | ||||||
|     fn default() -> RedirectPolicy { |     fn default() -> Policy { | ||||||
|         // Keep `is_default` in sync |         // Keep `is_default` in sync | ||||||
|         RedirectPolicy::limited(10) |         Policy::limited(10) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> RedirectAttempt<'a> { | impl<'a> Attempt<'a> { | ||||||
|     /// Get the type of redirect. |     /// Get the type of redirect. | ||||||
|     pub fn status(&self) -> StatusCode { |     pub fn status(&self) -> StatusCode { | ||||||
|         self.status |         self.status | ||||||
| @@ -173,70 +178,60 @@ impl<'a> RedirectAttempt<'a> { | |||||||
|         self.previous |         self.previous | ||||||
|     } |     } | ||||||
|     /// Returns an action meaning reqwest should follow the next URL. |     /// Returns an action meaning reqwest should follow the next URL. | ||||||
|     pub fn follow(self) -> RedirectAction { |     pub fn follow(self) -> Action { | ||||||
|         RedirectAction { |         Action { | ||||||
|             inner: Action::Follow, |             inner: ActionKind::Follow, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns an action meaning reqwest should not follow the next URL. |     /// Returns an action meaning reqwest should not follow the next URL. | ||||||
|     /// |     /// | ||||||
|     /// The 30x response will be returned as the `Ok` result. |     /// The 30x response will be returned as the `Ok` result. | ||||||
|     pub fn stop(self) -> RedirectAction { |     pub fn stop(self) -> Action { | ||||||
|         RedirectAction { |         Action { | ||||||
|             inner: Action::Stop, |             inner: ActionKind::Stop, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns an action meaning there was a loop of redirects found. |     /// Returns an action failing the redirect with an error. | ||||||
|     /// |     /// | ||||||
|     /// An `Error` will be returned for the result of the sent request. |     /// The `Error` will be returned for the result of the sent request. | ||||||
|     pub fn loop_detected(self) -> RedirectAction { |     pub fn error<E: Into<Box<dyn StdError + Send + Sync>>>(self, error: E) -> Action { | ||||||
|         RedirectAction { |         Action { | ||||||
|             inner: Action::LoopDetected, |             inner: ActionKind::Error(error.into()), | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Returns an action meaning there was a loop of redirects found. |  | ||||||
|     /// |  | ||||||
|     /// An `Error` will be returned for the result of the sent request. |  | ||||||
|     pub fn too_many_redirects(self) -> RedirectAction { |  | ||||||
|         RedirectAction { |  | ||||||
|             inner: Action::TooManyRedirects, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Policy { | enum PolicyKind { | ||||||
|     Custom(Box<dyn Fn(RedirectAttempt) -> RedirectAction + Send + Sync + 'static>), |     Custom(Box<dyn Fn(Attempt) -> Action + Send + Sync + 'static>), | ||||||
|     Limit(usize), |     Limit(usize), | ||||||
|     None, |     None, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for RedirectPolicy { | impl fmt::Debug for Policy { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_tuple("RedirectPolicy").field(&self.inner).finish() |         f.debug_tuple("Policy").field(&self.inner).finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Policy { | impl fmt::Debug for PolicyKind { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match *self { |         match *self { | ||||||
|             Policy::Custom(..) => f.pad("Custom"), |             PolicyKind::Custom(..) => f.pad("Custom"), | ||||||
|             Policy::Limit(max) => f.debug_tuple("Limit").field(&max).finish(), |             PolicyKind::Limit(max) => f.debug_tuple("Limit").field(&max).finish(), | ||||||
|             Policy::None => f.pad("None"), |             PolicyKind::None => f.pad("None"), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // pub(crate) | // pub(crate) | ||||||
|  |  | ||||||
| #[derive(Debug, PartialEq)] | #[derive(Debug)] | ||||||
| pub(crate) enum Action { | pub(crate) enum ActionKind { | ||||||
|     Follow, |     Follow, | ||||||
|     Stop, |     Stop, | ||||||
|     LoopDetected, |     Error(Box<dyn StdError + Send + Sync>), | ||||||
|     TooManyRedirects, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) { | pub(crate) fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) { | ||||||
| @@ -253,48 +248,42 @@ pub(crate) fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, prev | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | #[derive(Debug)] | ||||||
| This was the desired way of doing it, but ran in to inference issues when | struct TooManyRedirects; | ||||||
| using closures, since the arguments received are references (&Url and &[Url]), |  | ||||||
| and the compiler could not infer the lifetimes of those references. That means |  | ||||||
| people would need to annotate the closure's argument types, which is garbase. |  | ||||||
|  |  | ||||||
| pub trait Redirect { | impl fmt::Display for TooManyRedirects { | ||||||
|     fn redirect(&self, next: &Url, previous: &[Url]) -> Result<bool>; |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
| } |         f.write_str("too many redirects") | ||||||
|  |  | ||||||
| impl<F> Redirect for F |  | ||||||
| where F: Fn(&Url, &[Url]) -> Result<bool> { |  | ||||||
|     fn redirect(&self, next: &Url, previous: &[Url]) -> Result<bool> { |  | ||||||
|         self(next, previous) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| */ |  | ||||||
|  | impl StdError for TooManyRedirects {} | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_redirect_policy_limit() { | fn test_redirect_policy_limit() { | ||||||
|     let policy = RedirectPolicy::default(); |     let policy = Policy::default(); | ||||||
|     let next = Url::parse("http://x.y/z").unwrap(); |     let next = Url::parse("http://x.y/z").unwrap(); | ||||||
|     let mut previous = (0..9) |     let mut previous = (0..9) | ||||||
|         .map(|i| Url::parse(&format!("http://a.b/c/{}", i)).unwrap()) |         .map(|i| Url::parse(&format!("http://a.b/c/{}", i)).unwrap()) | ||||||
|         .collect::<Vec<_>>(); |         .collect::<Vec<_>>(); | ||||||
|  |  | ||||||
|     assert_eq!( |  | ||||||
|         policy.check(StatusCode::FOUND, &next, &previous), |     match policy.check(StatusCode::FOUND, &next, &previous) { | ||||||
|         Action::Follow |         ActionKind::Follow => (), | ||||||
|     ); |         other => panic!("unexpected {:?}", other), | ||||||
|  |     } | ||||||
|  |  | ||||||
|     previous.push(Url::parse("http://a.b.d/e/33").unwrap()); |     previous.push(Url::parse("http://a.b.d/e/33").unwrap()); | ||||||
|  |  | ||||||
|     assert_eq!( |     match policy.check(StatusCode::FOUND, &next, &previous) { | ||||||
|         policy.check(StatusCode::FOUND, &next, &previous), |         ActionKind::Error(err) if err.is::<TooManyRedirects>() => (), | ||||||
|         Action::TooManyRedirects |         other => panic!("unexpected {:?}", other), | ||||||
|     ); |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_redirect_policy_custom() { | fn test_redirect_policy_custom() { | ||||||
|     let policy = RedirectPolicy::custom(|attempt| { |     let policy = Policy::custom(|attempt| { | ||||||
|         if attempt.url().host_str() == Some("foo") { |         if attempt.url().host_str() == Some("foo") { | ||||||
|             attempt.stop() |             attempt.stop() | ||||||
|         } else { |         } else { | ||||||
| @@ -303,10 +292,16 @@ fn test_redirect_policy_custom() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     let next = Url::parse("http://bar/baz").unwrap(); |     let next = Url::parse("http://bar/baz").unwrap(); | ||||||
|     assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Follow); |     match policy.check(StatusCode::FOUND, &next, &[]) { | ||||||
|  |         ActionKind::Follow => (), | ||||||
|  |         other => panic!("unexpected {:?}", other), | ||||||
|  |     } | ||||||
|  |  | ||||||
|     let next = Url::parse("http://foo/baz").unwrap(); |     let next = Url::parse("http://foo/baz").unwrap(); | ||||||
|     assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Stop); |      match policy.check(StatusCode::FOUND, &next, &[]) { | ||||||
|  |         ActionKind::Stop => (), | ||||||
|  |         other => panic!("unexpected {:?}", other), | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
|   | |||||||
| @@ -173,7 +173,7 @@ async fn cookie_store_expires() { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     let client = reqwest::r#async::Client::builder() |     let client = reqwest::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
| @@ -201,7 +201,7 @@ async fn cookie_store_path() { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     let client = reqwest::r#async::Client::builder() |     let client = reqwest::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|   | |||||||
| @@ -240,7 +240,7 @@ async fn test_redirect_policy_can_stop_redirects_without_an_error() { | |||||||
|     let url = format!("http://{}/no-redirect", server.addr()); |     let url = format!("http://{}/no-redirect", server.addr()); | ||||||
|  |  | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .redirect(reqwest::RedirectPolicy::none()) |         .redirect(reqwest::redirect::Policy::none()) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get(&url) |         .get(&url) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user