From a06e03edf4663cbf0d8ec5bc3bcd5b4a4f101c61 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Tue, 3 Mar 2020 16:10:06 -0800 Subject: [PATCH] Refactor gzip and brotli bools into an Accepts struct --- src/async_impl/client.rs | 33 +++++--------- src/async_impl/decoder.rs | 92 +++++++++++++++++++++++++++++++------- src/async_impl/mod.rs | 4 +- src/async_impl/response.rs | 9 ++-- 4 files changed, 94 insertions(+), 44 deletions(-) diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 6281697..1a7968f 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -28,6 +28,7 @@ use tokio::time::Delay; use log::debug; +use super::decoder::Accepts; use super::request::{Request, RequestBuilder}; use super::response::Response; use super::Body; @@ -62,8 +63,7 @@ pub struct ClientBuilder { struct Config { // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder` - gzip: bool, - brotli: bool, + accepts: Accepts, headers: HeaderMap, #[cfg(feature = "native-tls")] hostname_verification: bool, @@ -112,8 +112,7 @@ impl ClientBuilder { ClientBuilder { config: Config { error: None, - gzip: cfg!(feature = "gzip"), - brotli: cfg!(feature = "brotli"), + accepts: Accepts::default(), headers, #[cfg(feature = "native-tls")] hostname_verification: true, @@ -312,10 +311,9 @@ impl ClientBuilder { Ok(Client { inner: Arc::new(ClientRef { + accepts: config.accepts, #[cfg(feature = "cookies")] cookie_store: config.cookie_store.map(RwLock::new), - gzip: config.gzip, - brotli: config.brotli, hyper: hyper_client, headers: config.headers, redirect_policy: config.redirect_policy, @@ -448,7 +446,7 @@ impl ClientBuilder { /// This requires the optional `gzip` feature to be enabled #[cfg(feature = "gzip")] pub fn gzip(mut self, enable: bool) -> ClientBuilder { - self.config.gzip = enable; + self.config.accepts.gzip = enable; self } @@ -470,7 +468,7 @@ impl ClientBuilder { /// This requires the optional `brotli` feature to be enabled #[cfg(feature = "brotli")] pub fn brotli(mut self, enable: bool) -> ClientBuilder { - self.config.brotli = enable; + self.config.accepts.brotli = enable; self } @@ -976,12 +974,7 @@ impl Client { } } - let accept_encoding = match (self.inner.gzip, self.inner.brotli) { - (true, true) => Some("gzip, br"), - (true, false) => Some("gzip"), - (false, true) => Some("br"), - _ => None, - }; + let accept_encoding = self.inner.accepts.as_str(); if accept_encoding.is_some() && !headers.contains_key(ACCEPT_ENCODING) @@ -1092,8 +1085,7 @@ impl Config { } } - f.field("gzip", &self.gzip); - f.field("brotli", &self.brotli); + f.field("accepts", &self.accepts); if !self.proxies.is_empty() { f.field("proxies", &self.proxies); @@ -1155,10 +1147,9 @@ impl Config { } struct ClientRef { + accepts: Accepts, #[cfg(feature = "cookies")] cookie_store: Option>, - gzip: bool, - brotli: bool, headers: HeaderMap, hyper: HyperClient, redirect_policy: redirect::Policy, @@ -1180,8 +1171,7 @@ impl ClientRef { } } - f.field("gzip", &self.gzip); - f.field("brotli", &self.brotli); + f.field("accepts", &self.accepts); if !self.proxies.is_empty() { f.field("proxies", &self.proxies); @@ -1417,8 +1407,7 @@ impl Future for PendingRequest { let res = Response::new( res, self.url.clone(), - self.client.gzip, - self.client.brotli, + self.client.accepts, self.timeout.take(), ); return Poll::Ready(Ok(res)); diff --git a/src/async_impl/decoder.rs b/src/async_impl/decoder.rs index 2a6ec55..580b03e 100644 --- a/src/async_impl/decoder.rs +++ b/src/async_impl/decoder.rs @@ -18,6 +18,14 @@ use hyper::body::HttpBody; use super::super::Body; use crate::error; +#[derive(Clone, Copy, Debug)] +pub(super) struct Accepts { + #[cfg(feature = "gzip")] + pub(super) gzip: bool, + #[cfg(feature = "brotli")] + pub(super) brotli: bool, +} + /// A response decompressor over a non-blocking stream of chunks. /// /// The inner decoder may be constructed asynchronously. @@ -25,13 +33,6 @@ pub(crate) struct Decoder { inner: Inner, } -enum DecoderType { - #[cfg(feature = "gzip")] - Gzip, - #[cfg(feature = "brotli")] - Brotli, -} - enum Inner { /// A `PlainText` decoder just returns the response content as is. PlainText(super::body::ImplStream), @@ -54,6 +55,13 @@ struct Pending(Peekable, DecoderType); struct IoStream(super::body::ImplStream); +enum DecoderType { + #[cfg(feature = "gzip")] + Gzip, + #[cfg(feature = "brotli")] + Brotli, +} + impl fmt::Debug for Decoder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Decoder").finish() @@ -177,26 +185,21 @@ impl Decoder { /// how to decode the content body of the request. /// /// Uses the correct variant by inspecting the Content-Encoding header. - pub(crate) fn detect( + pub(super) fn detect( _headers: &mut HeaderMap, body: Body, - check_gzip: bool, - check_brotli: bool, + _accepts: Accepts, ) -> Decoder { - if !check_gzip && !check_brotli { - return Decoder::plain_text(body); - } - #[cfg(feature = "gzip")] { - if Decoder::detect_gzip(_headers) { + if _accepts.gzip && Decoder::detect_gzip(_headers) { return Decoder::gzip(body); } } #[cfg(feature = "brotli")] { - if Decoder::detect_brotli(_headers) { + if _accepts.brotli && Decoder::detect_brotli(_headers) { return Decoder::brotli(body); } } @@ -317,3 +320,60 @@ impl Stream for IoStream { } } } + +// ===== impl Accepts ===== + +impl Accepts { + pub(super) fn none() -> Self { + Accepts { + #[cfg(feature = "gzip")] + gzip: false, + #[cfg(feature = "brotli")] + brotli: false, + } + } + + pub(super) fn as_str(&self) -> Option<&'static str> { + match (self.is_gzip(), self.is_brotli()) { + (true, true) => Some("gzip, br"), + (true, false) => Some("gzip"), + (false, true) => Some("br"), + _ => None, + } + } + + fn is_gzip(&self) -> bool { + #[cfg(feature = "gzip")] + { + self.gzip + } + + #[cfg(not(feature = "gzip"))] + { + false + } + } + + fn is_brotli(&self) -> bool { + #[cfg(feature = "brotli")] + { + self.brotli + } + + #[cfg(not(feature = "brotli"))] + { + false + } + } +} + +impl Default for Accepts { + fn default() -> Accepts { + Accepts { + #[cfg(feature = "gzip")] + gzip: true, + #[cfg(feature = "brotli")] + brotli: true, + } + } +} diff --git a/src/async_impl/mod.rs b/src/async_impl/mod.rs index 9f3cd00..15e6ce2 100644 --- a/src/async_impl/mod.rs +++ b/src/async_impl/mod.rs @@ -1,9 +1,11 @@ pub use self::body::Body; pub use self::client::{Client, ClientBuilder}; -pub(crate) use self::decoder::Decoder; pub use self::request::{Request, RequestBuilder}; pub use self::response::{Response, ResponseBuilderExt}; +#[cfg(feature = "blocking")] +pub(crate) use self::decoder::Decoder; + pub mod body; pub mod client; pub mod decoder; diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index fc6781c..02413b2 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -17,7 +17,7 @@ use tokio::time::Delay; use url::Url; use super::body::Body; -use super::Decoder; +use super::decoder::{Accepts, Decoder}; #[cfg(feature = "cookies")] use crate::cookie; @@ -37,8 +37,7 @@ impl Response { pub(super) fn new( res: hyper::Response, url: Url, - gzip: bool, - brotli: bool, + accepts: Accepts, timeout: Option, ) -> Response { let (parts, body) = res.into_parts(); @@ -47,7 +46,7 @@ impl Response { let extensions = parts.extensions; let mut headers = parts.headers; - let decoder = Decoder::detect(&mut headers, Body::response(body, timeout), gzip, brotli); + let decoder = Decoder::detect(&mut headers, Body::response(body, timeout), accepts); Response { status, @@ -400,7 +399,7 @@ impl> From> for Response { fn from(r: http::Response) -> Response { let (mut parts, body) = r.into_parts(); let body = body.into(); - let body = Decoder::detect(&mut parts.headers, body, false, false); + let body = Decoder::detect(&mut parts.headers, body, Accepts::none()); let url = parts .extensions .remove::()