From 4d6897952398d1ccb5aa79c2d7ae170fd84ecae8 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 5 Apr 2017 16:48:32 -0700 Subject: [PATCH] move Response pieces into its own response module --- src/client.rs | 165 +++--------------------------------------------- src/lib.rs | 4 +- src/response.rs | 155 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 156 deletions(-) create mode 100644 src/response.rs diff --git a/src/client.rs b/src/client.rs index ce755f8..ae1cfb4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,23 +1,23 @@ use std::fmt; -use std::io::{self, Read}; use std::sync::{Arc, Mutex, RwLock}; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; use hyper::client::IntoUrl; -use hyper::header::{Headers, ContentType, Location, Referer, UserAgent, Accept, ContentEncoding, Encoding, ContentLength, - TransferEncoding, AcceptEncoding, Range, qitem}; +use hyper::header::{Headers, ContentType, Location, Referer, UserAgent, Accept, Encoding, + AcceptEncoding, Range, qitem}; use hyper::method::Method; use hyper::status::StatusCode; use hyper::version::HttpVersion; use hyper::{Url}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json; use serde_urlencoded; use ::body::{self, Body}; use ::redirect::{RedirectPolicy, check_redirect}; +use ::response::Response; static DEFAULT_USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); @@ -296,9 +296,7 @@ impl RequestBuilder { if let Some(loc) = loc { loc } else { - return Ok(Response { - inner: Decoder::from_hyper_response(res, client.auto_ungzip.load(Ordering::Relaxed)) - }); + return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed))); } }; @@ -310,16 +308,14 @@ impl RequestBuilder { loc } else { debug!("redirect_policy disallowed redirection to '{}'", loc); - return Ok(Response { - inner: Decoder::from_hyper_response(res, client.auto_ungzip.load(Ordering::Relaxed)) - }) + + return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed))); } }, Err(e) => { debug!("Location header had invalid URI: {:?}", e); - return Ok(Response { - inner: Decoder::from_hyper_response(res, client.auto_ungzip.load(Ordering::Relaxed)) - }) + + return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed))) } }; @@ -327,9 +323,7 @@ impl RequestBuilder { //TODO: removeSensitiveHeaders(&mut headers, &url); } else { - return Ok(Response { - inner: Decoder::from_hyper_response(res, client.auto_ungzip.load(Ordering::Relaxed)) - }); + return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed))) } } } @@ -345,145 +339,6 @@ impl fmt::Debug for RequestBuilder { } } -/// A Response to a submitted `Request`. -pub struct Response { - inner: Decoder, -} - -impl fmt::Debug for Response { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - return match &self.inner { - &Decoder::PlainText(ref hyper_response) => { - f.debug_struct("Response") - .field("status", &hyper_response.status) - .field("headers", &hyper_response.headers) - .field("version", &hyper_response.version) - .finish() - }, - &Decoder::Gzip{ref status, ref version, ref headers, ..} => { - f.debug_struct("Response") - .field("status", &status) - .field("headers", &headers) - .field("version", &version) - .finish() - } - } - } -} - -impl Response { - /// Get the `StatusCode`. - #[inline] - pub fn status(&self) -> &StatusCode { - match &self.inner { - &Decoder::PlainText(ref hyper_response) => &hyper_response.status, - &Decoder::Gzip{ref status, ..} => status - } - } - - /// Get the `Headers`. - #[inline] - pub fn headers(&self) -> &Headers { - match &self.inner { - &Decoder::PlainText(ref hyper_response) => &hyper_response.headers, - &Decoder::Gzip{ref headers, ..} => headers - } - } - - /// Get the `HttpVersion`. - #[inline] - pub fn version(&self) -> &HttpVersion { - match &self.inner { - &Decoder::PlainText(ref hyper_response) => &hyper_response.version, - &Decoder::Gzip{ref version, ..} => version - } - } - - /// Try and deserialize the response body as JSON. - #[inline] - pub fn json(&mut self) -> ::Result { - serde_json::from_reader(self).map_err(::Error::from) - } -} - -enum Decoder { - /// A `PlainText` decoder just returns the response content as is. - PlainText(::hyper::client::Response), - /// A `Gzip` decoder will uncompress the gziped response content before returning it. - Gzip { - decoder: ::libflate::gzip::Decoder<::hyper::client::Response>, - headers: ::hyper::header::Headers, - version: ::hyper::version::HttpVersion, - status: ::hyper::status::StatusCode, - } -} - -impl Decoder { - /// Constructs a Decoder from a hyper request. - /// - /// A decoder is just a wrapper around the hyper request that knows - /// how to decode the content body of the request. - /// - /// Uses the correct variant by inspecting the Content-Encoding header. - fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self { - if !check_gzip { - return Decoder::PlainText(res); - } - let content_encoding_gzip: bool; - let mut is_gzip = { - content_encoding_gzip = res.headers.get::().map_or(false, |encs|{ - encs.contains(&Encoding::Gzip) - }); - content_encoding_gzip || res.headers.get::().map_or(false, |encs|{ - encs.contains(&Encoding::Gzip) - }) - }; - if content_encoding_gzip { - res.headers.remove::(); - res.headers.remove::(); - } - if is_gzip { - if let Some(content_length) = res.headers.get::() { - if content_length.0 == 0 { - warn!("GZipped response with content-length of 0"); - is_gzip = false; - } - } - } - if is_gzip { - return Decoder::Gzip { - status: res.status.clone(), - version: res.version.clone(), - headers: res.headers.clone(), - decoder: ::libflate::gzip::Decoder::new(res).unwrap(), - }; - } else { - return Decoder::PlainText(res); - } - } -} - -impl Read for Decoder { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self { - &mut Decoder::PlainText(ref mut hyper_response) => { - hyper_response.read(buf) - }, - &mut Decoder::Gzip{ref mut decoder, ..} => { - decoder.read(buf) - } - } - } -} - -/// Read the body of the Response. -impl Read for Response { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index ba9ee2a..c6a13da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,15 +118,17 @@ pub use hyper::version::HttpVersion; pub use hyper::Url; pub use url::ParseError as UrlError; -pub use self::client::{Client, Response, RequestBuilder}; +pub use self::client::{Client, RequestBuilder}; pub use self::error::{Error, Result}; pub use self::body::Body; pub use self::redirect::RedirectPolicy; +pub use self::response::Response; mod body; mod client; mod error; mod redirect; +mod response; /// Shortcut method to quickly make a `GET` request. diff --git a/src/response.rs b/src/response.rs new file mode 100644 index 0000000..d7226a7 --- /dev/null +++ b/src/response.rs @@ -0,0 +1,155 @@ +use std::fmt; +use std::io::{self, Read}; + +use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding}; +use hyper::status::StatusCode; +use hyper::version::HttpVersion; +use serde::Deserialize; +use serde_json; + + +/// A Response to a submitted `Request`. +pub struct Response { + inner: Decoder, +} + +pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response { + Response { + inner: Decoder::from_hyper_response(res, gzip) + } +} + +impl fmt::Debug for Response { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + return match &self.inner { + &Decoder::PlainText(ref hyper_response) => { + f.debug_struct("Response") + .field("status", &hyper_response.status) + .field("headers", &hyper_response.headers) + .field("version", &hyper_response.version) + .finish() + }, + &Decoder::Gzip{ref status, ref version, ref headers, ..} => { + f.debug_struct("Response") + .field("status", &status) + .field("headers", &headers) + .field("version", &version) + .finish() + } + } + } +} + +impl Response { + /// Get the `StatusCode`. + #[inline] + pub fn status(&self) -> &StatusCode { + match &self.inner { + &Decoder::PlainText(ref hyper_response) => &hyper_response.status, + &Decoder::Gzip{ref status, ..} => status + } + } + + /// Get the `Headers`. + #[inline] + pub fn headers(&self) -> &Headers { + match &self.inner { + &Decoder::PlainText(ref hyper_response) => &hyper_response.headers, + &Decoder::Gzip{ref headers, ..} => headers + } + } + + /// Get the `HttpVersion`. + #[inline] + pub fn version(&self) -> &HttpVersion { + match &self.inner { + &Decoder::PlainText(ref hyper_response) => &hyper_response.version, + &Decoder::Gzip{ref version, ..} => version + } + } + + /// Try and deserialize the response body as JSON. + #[inline] + pub fn json(&mut self) -> ::Result { + serde_json::from_reader(self).map_err(::Error::from) + } +} + +enum Decoder { + /// A `PlainText` decoder just returns the response content as is. + PlainText(::hyper::client::Response), + /// A `Gzip` decoder will uncompress the gziped response content before returning it. + Gzip { + decoder: ::libflate::gzip::Decoder<::hyper::client::Response>, + headers: ::hyper::header::Headers, + version: ::hyper::version::HttpVersion, + status: ::hyper::status::StatusCode, + } +} + +impl Decoder { + /// Constructs a Decoder from a hyper request. + /// + /// A decoder is just a wrapper around the hyper request that knows + /// how to decode the content body of the request. + /// + /// Uses the correct variant by inspecting the Content-Encoding header. + fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self { + if !check_gzip { + return Decoder::PlainText(res); + } + let content_encoding_gzip: bool; + let mut is_gzip = { + content_encoding_gzip = res.headers.get::().map_or(false, |encs|{ + encs.contains(&Encoding::Gzip) + }); + content_encoding_gzip || res.headers.get::().map_or(false, |encs|{ + encs.contains(&Encoding::Gzip) + }) + }; + if content_encoding_gzip { + res.headers.remove::(); + res.headers.remove::(); + } + if is_gzip { + if let Some(content_length) = res.headers.get::() { + if content_length.0 == 0 { + warn!("GZipped response with content-length of 0"); + is_gzip = false; + } + } + } + if is_gzip { + return Decoder::Gzip { + status: res.status.clone(), + version: res.version.clone(), + headers: res.headers.clone(), + decoder: ::libflate::gzip::Decoder::new(res).unwrap(), + }; + } else { + return Decoder::PlainText(res); + } + } +} + +impl Read for Decoder { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + &mut Decoder::PlainText(ref mut hyper_response) => { + hyper_response.read(buf) + }, + &mut Decoder::Gzip{ref mut decoder, ..} => { + decoder.read(buf) + } + } + } +} + +/// Read the body of the Response. +impl Read for Response { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } +} +