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 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<RwLock<cookie::CookieStore>>,
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));

View File

@@ -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<IoStream>, 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,
}
}
}

View File

@@ -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;

View File

@@ -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<hyper::Body>,
url: Url,
gzip: bool,
brotli: bool,
accepts: Accepts,
timeout: Option<Delay>,
) -> 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<T: Into<Body>> From<http::Response<T>> for Response {
fn from(r: http::Response<T>) -> 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::<ResponseUrl>()