Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use super::Body;
use crate::async_impl::h3_client::connect::{H3ClientConfig, H3Connector};
#[cfg(feature = "http3")]
use crate::async_impl::h3_client::{H3Client, H3ResponseFuture};
use crate::config::{RequestConfig, RequestTimeout};
use crate::config::{self, RequestConfig};
use crate::connect::{
sealed::{Conn, Unnameable},
BoxedConnectorLayer, BoxedConnectorService, Connector, ConnectorBuilder,
Expand Down Expand Up @@ -45,7 +45,7 @@ use http::header::{
CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT,
};
use http::uri::Scheme;
use http::Uri;
use http::{Extensions, Uri};
use hyper_util::client::legacy::connect::HttpConnector;
use log::debug;
#[cfg(feature = "default-tls")]
Expand Down Expand Up @@ -903,7 +903,7 @@ impl ClientBuilder {
},
hyper: builder.build(connector_builder.build(config.connector_layers)),
headers: config.headers,
redirect_policy: config.redirect_policy,
redirect_policy: RequestConfig::new(Some(config.redirect_policy)),
referer: config.referer,
read_timeout: config.read_timeout,
request_timeout: RequestConfig::new(config.timeout),
Expand Down Expand Up @@ -2375,6 +2375,7 @@ impl Client {
method,
url,
headers,
extensions,
body: reusable,

urls: Vec::new(),
Expand Down Expand Up @@ -2600,9 +2601,9 @@ struct ClientRef {
hyper: HyperClient,
#[cfg(feature = "http3")]
h3_client: Option<H3Client>,
redirect_policy: redirect::Policy,
redirect_policy: RequestConfig<config::RedirectPolicy>,
referer: bool,
request_timeout: RequestConfig<RequestTimeout>,
request_timeout: RequestConfig<config::RequestTimeout>,
read_timeout: Option<Duration>,
proxies: Arc<Vec<Proxy>>,
proxies_maybe_http_auth: bool,
Expand All @@ -2627,9 +2628,8 @@ impl ClientRef {
f.field("proxies", &self.proxies);
}

if !self.redirect_policy.is_default() {
f.field("redirect_policy", &self.redirect_policy);
}
self.redirect_policy
.fmt_as_field_when(f, |v| !v.is_default());

if self.referer {
f.field("referer", &true);
Expand Down Expand Up @@ -2662,6 +2662,7 @@ pin_project! {
method: Method,
url: Url,
headers: HeaderMap,
extensions: Extensions,
body: Option<Option<Bytes>>,

urls: Vec<Url>,
Expand Down Expand Up @@ -2954,6 +2955,9 @@ impl Future for PendingRequest {
let action = self
.client
.redirect_policy
.fetch(&self.extensions)
// unwrap-safety: redirect_policy must exist.
.unwrap()
.check(res.status(), &loc, &self.urls);

match action {
Expand Down
32 changes: 22 additions & 10 deletions src/async_impl/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ use super::client::{Client, Pending};
#[cfg(feature = "multipart")]
use super::multipart;
use super::response::Response;
use crate::config::{RequestConfig, RequestTimeout};
use crate::config::{self, RequestConfig};
#[cfg(feature = "multipart")]
use crate::header::CONTENT_LENGTH;
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
use crate::{Method, Url};
use crate::{redirect, Method, Url};
use http::{request::Parts, Extensions, Request as HttpRequest, Version};

/// A request which can be executed with `Client::execute()`.
Expand Down Expand Up @@ -64,6 +64,18 @@ impl Request {
&mut self.method
}

/// Get the http version.
#[inline]
pub fn version(&self) -> Version {
self.version
}

/// Get a mutable reference to the http version.
#[inline]
pub fn version_mut(&mut self) -> &mut Version {
&mut self.version
}

/// Get the url.
#[inline]
pub fn url(&self) -> &Url {
Expand Down Expand Up @@ -115,25 +127,25 @@ impl Request {
/// Get the timeout.
#[inline]
pub fn timeout(&self) -> Option<&Duration> {
RequestConfig::<RequestTimeout>::get(&self.extensions)
RequestConfig::<config::RequestTimeout>::get(&self.extensions)
}

/// Get a mutable reference to the timeout.
#[inline]
pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
RequestConfig::<RequestTimeout>::get_mut(&mut self.extensions)
RequestConfig::<config::RequestTimeout>::get_mut(&mut self.extensions)
}

/// Get the http version.
/// Get the redirect policy.
#[inline]
pub fn version(&self) -> Version {
self.version
pub fn redirect(&self) -> Option<&redirect::Policy> {
RequestConfig::<config::RedirectPolicy>::get(&self.extensions)
}

/// Get a mutable reference to the http version.
/// Get a mutable reference to the redirect policy.
#[inline]
pub fn version_mut(&mut self) -> &mut Version {
&mut self.version
pub fn redirect_mut(&mut self) -> &mut Option<redirect::Policy> {
RequestConfig::<config::RedirectPolicy>::get_mut(&mut self.extensions)
}

/// Attempt to clone the request.
Expand Down
24 changes: 24 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use std::time::Duration;

use http::Extensions;

use crate::redirect;

/// This trait is empty and is only used to associate a configuration key type with its
/// corresponding value type.
pub(crate) trait RequestConfigValue: Copy + Clone + 'static {
Expand Down Expand Up @@ -63,6 +65,21 @@ where
}
}

/// format request config value as struct field while condition met.
///
/// We provide this API directly to avoid leak internal value to callers.
pub(crate) fn fmt_as_field_when(
&self,
f: &mut std::fmt::DebugStruct<'_, '_>,
condition: impl Fn(T::Value) -> bool,
) {
if let Some(v) = &self.0 {
if condition(v.clone()) {
f.field(type_name::<T>(), v);
}
}
}

/// Retrieve the value from the request-scoped configuration.
///
/// If the request specifies a value, use that value; otherwise, attempt to retrieve it from the current instance (typically a client instance).
Expand Down Expand Up @@ -108,3 +125,10 @@ pub(crate) struct RequestTimeout;
impl RequestConfigValue for RequestTimeout {
type Value = Duration;
}

#[derive(Clone, Copy)]
pub(crate) struct RedirectPolicy;

impl RequestConfigValue for RedirectPolicy {
type Value = redirect::Policy;
}
8 changes: 5 additions & 3 deletions src/redirect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! 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::{error::Error as StdError, sync::Arc};

use crate::header::{HeaderMap, AUTHORIZATION, COOKIE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE};
use hyper::StatusCode;
Expand All @@ -21,6 +21,7 @@ use crate::Url;
/// the allowed maximum redirect hops in a chain.
/// - `none` can be used to disable all redirect behavior.
/// - `custom` can be used to create a customized policy.
#[derive(Clone)]
pub struct Policy {
inner: PolicyKind,
}
Expand Down Expand Up @@ -100,7 +101,7 @@ impl Policy {
T: Fn(Attempt) -> Action + Send + Sync + 'static,
{
Self {
inner: PolicyKind::Custom(Box::new(policy)),
inner: PolicyKind::Custom(Arc::new(policy)),
}
}

Expand Down Expand Up @@ -200,8 +201,9 @@ impl<'a> Attempt<'a> {
}
}

#[derive(Clone)]
enum PolicyKind {
Custom(Box<dyn Fn(Attempt) -> Action + Send + Sync + 'static>),
Custom(Arc<dyn Fn(Attempt) -> Action + Send + Sync + 'static>),
Limit(usize),
None,
}
Expand Down
Loading