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
26 changes: 18 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 @@ -889,7 +889,7 @@ impl ClientBuilder {

Ok(Client {
inner: Arc::new(ClientRef {
accepts: config.accepts,
accepts: RequestConfig::new(Some(config.accepts)),
#[cfg(feature = "cookies")]
cookie_store: config.cookie_store,
// Use match instead of map since config is partially moved,
Expand Down Expand Up @@ -2314,7 +2314,12 @@ impl Client {
}
}

let accept_encoding = self.inner.accepts.as_str();
let accept_encoding = self
.inner
.accepts
.fetch(&extensions)
.expect("accepts must be set in the client ref")
.as_str();

if let Some(accept_encoding) = accept_encoding {
if !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) {
Expand Down Expand Up @@ -2376,6 +2381,7 @@ impl Client {
url,
headers,
body: reusable,
extensions,

urls: Vec::new(),

Expand Down Expand Up @@ -2593,7 +2599,7 @@ impl Config {
}

struct ClientRef {
accepts: Accepts,
accepts: RequestConfig<config::Accepts>,
#[cfg(feature = "cookies")]
cookie_store: Option<Arc<dyn cookie::CookieStore>>,
headers: HeaderMap,
Expand All @@ -2602,7 +2608,7 @@ struct ClientRef {
h3_client: Option<H3Client>,
redirect_policy: redirect::Policy,
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 @@ -2621,7 +2627,7 @@ impl ClientRef {
}
}

f.field("accepts", &self.accepts);
self.accepts.fmt_as_field(f);

if !self.proxies.is_empty() {
f.field("proxies", &self.proxies);
Expand Down Expand Up @@ -2663,6 +2669,7 @@ pin_project! {
url: Url,
headers: HeaderMap,
body: Option<Option<Bytes>>,
extensions: Extensions,

urls: Vec<Url>,

Expand Down Expand Up @@ -3033,7 +3040,10 @@ impl Future for PendingRequest {
let res = Response::new(
res,
self.url.clone(),
self.client.accepts,
self.client
.accepts
.fetch_owned(&self.extensions)
.expect("accepts must has been set"),
self.total_timeout.take(),
self.read_timeout,
);
Expand Down
2 changes: 1 addition & 1 deletion src/async_impl/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ use tokio_util::io::StreamReader;
use super::body::ResponseBody;

#[derive(Clone, Copy, Debug)]
pub(super) struct Accepts {
pub(crate) struct Accepts {
#[cfg(feature = "gzip")]
pub(super) gzip: bool,
#[cfg(feature = "brotli")]
Expand Down
127 changes: 124 additions & 3 deletions src/async_impl/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +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 http::{request::Parts, Extensions, Request as HttpRequest, Version};

Expand Down Expand Up @@ -115,13 +116,13 @@ 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.
Expand All @@ -136,6 +137,126 @@ impl Request {
&mut self.version
}

/// Get the `gzip` option on this request.
#[inline]
pub fn gzip(&self) -> Option<&bool> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my gut feeling: for now, there's less need to provide all these getters on the Request type. I would just put the equivalent gzip(bool) functions on the RequestBuilder.

(Hm, with all these possible options, it occurs to me that a user might just want to say no_(de)compression() on either builder, to indicate never use any of the decoders...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just put the equivalent gzip(bool) functions on the RequestBuilder.

I'm fine with this change. Let me add those functions to RequestBuilder first and gather some feedback.

#[cfg(feature = "gzip")]
{
RequestConfig::<config::Accepts>::get(&self.extensions).map(|v| &v.gzip)
}
#[cfg(not(feature = "gzip"))]
{
None
}
}

/// Set auto gzip decompression by checking the `Content-Encoding` response header.
///
/// Refer to [`crate::ClientBuilder::gzip`] for more details.
#[inline]
pub fn gzip_mut(&mut self) -> Option<&mut bool> {
#[cfg(feature = "gzip")]
{
RequestConfig::<config::Accepts>::get_mut(&mut self.extensions)
.as_mut()
.map(|v| &mut v.gzip)
}
#[cfg(not(feature = "gzip"))]
{
None
}
}

/// Get the `brotli` option on this request.
#[inline]
pub fn brotli(&self) -> Option<&bool> {
#[cfg(feature = "brotli")]
{
RequestConfig::<config::Accepts>::get(&self.extensions).map(|v| &v.brotli)
}
#[cfg(not(feature = "brotli"))]
{
None
}
}

/// Set auto brotli decompression by checking the `Content-Encoding` response header.
///
/// Refer to [`crate::ClientBuilder::brotli`] for more details.
#[inline]
pub fn brotli_mut(&mut self) -> Option<&mut bool> {
#[cfg(feature = "brotli")]
{
RequestConfig::<config::Accepts>::get_mut(&mut self.extensions)
.as_mut()
.map(|v| &mut v.brotli)
}
#[cfg(not(feature = "brotli"))]
{
None
}
}

/// Get the `zstd` option on this request.
#[inline]
pub fn zstd(&self) -> Option<&bool> {
#[cfg(feature = "zstd")]
{
RequestConfig::<config::Accepts>::get(&self.extensions).map(|v| &v.zstd)
}
#[cfg(not(feature = "zstd"))]
{
None
}
}

/// Set auto zstd decompression by checking the `Content-Encoding` response header.
///
/// Refer to [`crate::ClientBuilder::zstd`] for more details.
#[inline]
pub fn zstd_mut(&mut self) -> Option<&mut bool> {
#[cfg(feature = "zstd")]
{
RequestConfig::<config::Accepts>::get_mut(&mut self.extensions)
.as_mut()
.map(|v| &mut v.zstd)
}
#[cfg(not(feature = "zstd"))]
{
None
}
}

/// Get the `deflate` option on this request.
#[inline]
pub fn deflate(&self) -> Option<&bool> {
#[cfg(feature = "deflate")]
{
RequestConfig::<config::Accepts>::get(&self.extensions).map(|v| &v.deflate)
}
#[cfg(not(feature = "deflate"))]
{
None
}
}

/// Enable auto deflate decompression by checking the `Content-Encoding` response header.
///
/// Refer to [`crate::ClientBuilder::deflate`] for more details.
#[inline]
pub fn deflate_mut(&mut self) -> Option<&mut bool> {
#[cfg(feature = "deflate")]
{
RequestConfig::<config::Accepts>::get_mut(&mut self.extensions)
.as_mut()
.map(|v| &mut v.deflate)
}
#[cfg(not(feature = "deflate"))]
{
None
}
}

/// Attempt to clone the request.
///
/// `None` is returned if the request can not be cloned, i.e. if the body is a stream.
Expand Down
18 changes: 18 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ where
.or(self.0.as_ref())
}

/// Retrieve the owned 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).
///
/// This owned version of `fetch` can consume the config value directly to avoid extra clone.
pub(crate) fn fetch_owned<'request>(self, ext: &Extensions) -> Option<T::Value> {
ext.get::<RequestConfig<T>>()
.and_then(|v| v.0.clone())
.or(self.0)
}

/// Retrieve the value from the request's Extensions.
pub(crate) fn get(ext: &Extensions) -> Option<&T::Value> {
ext.get::<RequestConfig<T>>().and_then(|v| v.0.as_ref())
Expand Down Expand Up @@ -108,3 +119,10 @@ pub(crate) struct RequestTimeout;
impl RequestConfigValue for RequestTimeout {
type Value = Duration;
}

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

impl RequestConfigValue for Accepts {
type Value = crate::async_impl::decoder::Accepts;
}