Refactor gzip and brotli bools into an Accepts struct

This commit is contained in:
Sean McArthur
2020-03-03 16:10:06 -08:00
parent 2f875255e1
commit a06e03edf4
4 changed files with 94 additions and 44 deletions

View File

@@ -28,6 +28,7 @@ use tokio::time::Delay;
use log::debug; use log::debug;
use super::decoder::Accepts;
use super::request::{Request, RequestBuilder}; use super::request::{Request, RequestBuilder};
use super::response::Response; use super::response::Response;
use super::Body; use super::Body;
@@ -62,8 +63,7 @@ pub struct ClientBuilder {
struct Config { struct Config {
// NOTE: When adding a new field, update `fmt::Debug for ClientBuilder` // NOTE: When adding a new field, update `fmt::Debug for ClientBuilder`
gzip: bool, accepts: Accepts,
brotli: bool,
headers: HeaderMap, headers: HeaderMap,
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
hostname_verification: bool, hostname_verification: bool,
@@ -112,8 +112,7 @@ impl ClientBuilder {
ClientBuilder { ClientBuilder {
config: Config { config: Config {
error: None, error: None,
gzip: cfg!(feature = "gzip"), accepts: Accepts::default(),
brotli: cfg!(feature = "brotli"),
headers, headers,
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
hostname_verification: true, hostname_verification: true,
@@ -312,10 +311,9 @@ impl ClientBuilder {
Ok(Client { Ok(Client {
inner: Arc::new(ClientRef { inner: Arc::new(ClientRef {
accepts: config.accepts,
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookie_store: config.cookie_store.map(RwLock::new), cookie_store: config.cookie_store.map(RwLock::new),
gzip: config.gzip,
brotli: config.brotli,
hyper: hyper_client, hyper: hyper_client,
headers: config.headers, headers: config.headers,
redirect_policy: config.redirect_policy, redirect_policy: config.redirect_policy,
@@ -448,7 +446,7 @@ impl ClientBuilder {
/// This requires the optional `gzip` feature to be enabled /// This requires the optional `gzip` feature to be enabled
#[cfg(feature = "gzip")] #[cfg(feature = "gzip")]
pub fn gzip(mut self, enable: bool) -> ClientBuilder { pub fn gzip(mut self, enable: bool) -> ClientBuilder {
self.config.gzip = enable; self.config.accepts.gzip = enable;
self self
} }
@@ -470,7 +468,7 @@ impl ClientBuilder {
/// This requires the optional `brotli` feature to be enabled /// This requires the optional `brotli` feature to be enabled
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
pub fn brotli(mut self, enable: bool) -> ClientBuilder { pub fn brotli(mut self, enable: bool) -> ClientBuilder {
self.config.brotli = enable; self.config.accepts.brotli = enable;
self self
} }
@@ -976,12 +974,7 @@ impl Client {
} }
} }
let accept_encoding = match (self.inner.gzip, self.inner.brotli) { let accept_encoding = self.inner.accepts.as_str();
(true, true) => Some("gzip, br"),
(true, false) => Some("gzip"),
(false, true) => Some("br"),
_ => None,
};
if accept_encoding.is_some() if accept_encoding.is_some()
&& !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(ACCEPT_ENCODING)
@@ -1092,8 +1085,7 @@ impl Config {
} }
} }
f.field("gzip", &self.gzip); f.field("accepts", &self.accepts);
f.field("brotli", &self.brotli);
if !self.proxies.is_empty() { if !self.proxies.is_empty() {
f.field("proxies", &self.proxies); f.field("proxies", &self.proxies);
@@ -1155,10 +1147,9 @@ impl Config {
} }
struct ClientRef { struct ClientRef {
accepts: Accepts,
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookie_store: Option<RwLock<cookie::CookieStore>>, cookie_store: Option<RwLock<cookie::CookieStore>>,
gzip: bool,
brotli: bool,
headers: HeaderMap, headers: HeaderMap,
hyper: HyperClient, hyper: HyperClient,
redirect_policy: redirect::Policy, redirect_policy: redirect::Policy,
@@ -1180,8 +1171,7 @@ impl ClientRef {
} }
} }
f.field("gzip", &self.gzip); f.field("accepts", &self.accepts);
f.field("brotli", &self.brotli);
if !self.proxies.is_empty() { if !self.proxies.is_empty() {
f.field("proxies", &self.proxies); f.field("proxies", &self.proxies);
@@ -1417,8 +1407,7 @@ impl Future for PendingRequest {
let res = Response::new( let res = Response::new(
res, res,
self.url.clone(), self.url.clone(),
self.client.gzip, self.client.accepts,
self.client.brotli,
self.timeout.take(), self.timeout.take(),
); );
return Poll::Ready(Ok(res)); return Poll::Ready(Ok(res));

View File

@@ -18,6 +18,14 @@ use hyper::body::HttpBody;
use super::super::Body; use super::super::Body;
use crate::error; 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. /// A response decompressor over a non-blocking stream of chunks.
/// ///
/// The inner decoder may be constructed asynchronously. /// The inner decoder may be constructed asynchronously.
@@ -25,13 +33,6 @@ pub(crate) struct Decoder {
inner: Inner, inner: Inner,
} }
enum DecoderType {
#[cfg(feature = "gzip")]
Gzip,
#[cfg(feature = "brotli")]
Brotli,
}
enum Inner { enum Inner {
/// A `PlainText` decoder just returns the response content as is. /// A `PlainText` decoder just returns the response content as is.
PlainText(super::body::ImplStream), PlainText(super::body::ImplStream),
@@ -54,6 +55,13 @@ struct Pending(Peekable<IoStream>, DecoderType);
struct IoStream(super::body::ImplStream); struct IoStream(super::body::ImplStream);
enum DecoderType {
#[cfg(feature = "gzip")]
Gzip,
#[cfg(feature = "brotli")]
Brotli,
}
impl fmt::Debug for Decoder { impl fmt::Debug for Decoder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Decoder").finish() f.debug_struct("Decoder").finish()
@@ -177,26 +185,21 @@ impl Decoder {
/// how to decode the content body of the request. /// how to decode the content body of the request.
/// ///
/// Uses the correct variant by inspecting the Content-Encoding header. /// Uses the correct variant by inspecting the Content-Encoding header.
pub(crate) fn detect( pub(super) fn detect(
_headers: &mut HeaderMap, _headers: &mut HeaderMap,
body: Body, body: Body,
check_gzip: bool, _accepts: Accepts,
check_brotli: bool,
) -> Decoder { ) -> Decoder {
if !check_gzip && !check_brotli {
return Decoder::plain_text(body);
}
#[cfg(feature = "gzip")] #[cfg(feature = "gzip")]
{ {
if Decoder::detect_gzip(_headers) { if _accepts.gzip && Decoder::detect_gzip(_headers) {
return Decoder::gzip(body); return Decoder::gzip(body);
} }
} }
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
{ {
if Decoder::detect_brotli(_headers) { if _accepts.brotli && Decoder::detect_brotli(_headers) {
return Decoder::brotli(body); 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,
}
}
}

View File

@@ -1,9 +1,11 @@
pub use self::body::Body; pub use self::body::Body;
pub use self::client::{Client, ClientBuilder}; pub use self::client::{Client, ClientBuilder};
pub(crate) use self::decoder::Decoder;
pub use self::request::{Request, RequestBuilder}; pub use self::request::{Request, RequestBuilder};
pub use self::response::{Response, ResponseBuilderExt}; pub use self::response::{Response, ResponseBuilderExt};
#[cfg(feature = "blocking")]
pub(crate) use self::decoder::Decoder;
pub mod body; pub mod body;
pub mod client; pub mod client;
pub mod decoder; pub mod decoder;

View File

@@ -17,7 +17,7 @@ use tokio::time::Delay;
use url::Url; use url::Url;
use super::body::Body; use super::body::Body;
use super::Decoder; use super::decoder::{Accepts, Decoder};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use crate::cookie; use crate::cookie;
@@ -37,8 +37,7 @@ impl Response {
pub(super) fn new( pub(super) fn new(
res: hyper::Response<hyper::Body>, res: hyper::Response<hyper::Body>,
url: Url, url: Url,
gzip: bool, accepts: Accepts,
brotli: bool,
timeout: Option<Delay>, timeout: Option<Delay>,
) -> Response { ) -> Response {
let (parts, body) = res.into_parts(); let (parts, body) = res.into_parts();
@@ -47,7 +46,7 @@ impl Response {
let extensions = parts.extensions; let extensions = parts.extensions;
let mut headers = parts.headers; 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 { Response {
status, status,
@@ -400,7 +399,7 @@ impl<T: Into<Body>> From<http::Response<T>> for Response {
fn from(r: http::Response<T>) -> Response { fn from(r: http::Response<T>) -> Response {
let (mut parts, body) = r.into_parts(); let (mut parts, body) = r.into_parts();
let body = body.into(); 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 let url = parts
.extensions .extensions
.remove::<ResponseUrl>() .remove::<ResponseUrl>()