move Response pieces into its own response module
This commit is contained in:
165
src/client.rs
165
src/client.rs
@@ -1,23 +1,23 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{self, Read};
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use hyper::client::IntoUrl;
|
use hyper::client::IntoUrl;
|
||||||
use hyper::header::{Headers, ContentType, Location, Referer, UserAgent, Accept, ContentEncoding, Encoding, ContentLength,
|
use hyper::header::{Headers, ContentType, Location, Referer, UserAgent, Accept, Encoding,
|
||||||
TransferEncoding, AcceptEncoding, Range, qitem};
|
AcceptEncoding, Range, qitem};
|
||||||
use hyper::method::Method;
|
use hyper::method::Method;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
use hyper::version::HttpVersion;
|
use hyper::version::HttpVersion;
|
||||||
use hyper::{Url};
|
use hyper::{Url};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_urlencoded;
|
use serde_urlencoded;
|
||||||
|
|
||||||
use ::body::{self, Body};
|
use ::body::{self, Body};
|
||||||
use ::redirect::{RedirectPolicy, check_redirect};
|
use ::redirect::{RedirectPolicy, check_redirect};
|
||||||
|
use ::response::Response;
|
||||||
|
|
||||||
static DEFAULT_USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
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 {
|
if let Some(loc) = loc {
|
||||||
loc
|
loc
|
||||||
} else {
|
} else {
|
||||||
return Ok(Response {
|
return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed)));
|
||||||
inner: Decoder::from_hyper_response(res, client.auto_ungzip.load(Ordering::Relaxed))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -310,16 +308,14 @@ impl RequestBuilder {
|
|||||||
loc
|
loc
|
||||||
} else {
|
} else {
|
||||||
debug!("redirect_policy disallowed redirection to '{}'", loc);
|
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) => {
|
Err(e) => {
|
||||||
debug!("Location header had invalid URI: {:?}", 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);
|
//TODO: removeSensitiveHeaders(&mut headers, &url);
|
||||||
} else {
|
} else {
|
||||||
return Ok(Response {
|
return Ok(::response::new(res, client.auto_ungzip.load(Ordering::Relaxed)))
|
||||||
inner: Decoder::from_hyper_response(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<T: Deserialize>(&mut self) -> ::Result<T> {
|
|
||||||
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::<ContentEncoding>().map_or(false, |encs|{
|
|
||||||
encs.contains(&Encoding::Gzip)
|
|
||||||
});
|
|
||||||
content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
|
|
||||||
encs.contains(&Encoding::Gzip)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
if content_encoding_gzip {
|
|
||||||
res.headers.remove::<ContentEncoding>();
|
|
||||||
res.headers.remove::<ContentLength>();
|
|
||||||
}
|
|
||||||
if is_gzip {
|
|
||||||
if let Some(content_length) = res.headers.get::<ContentLength>() {
|
|
||||||
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<usize> {
|
|
||||||
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<usize> {
|
|
||||||
self.inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -118,15 +118,17 @@ pub use hyper::version::HttpVersion;
|
|||||||
pub use hyper::Url;
|
pub use hyper::Url;
|
||||||
pub use url::ParseError as UrlError;
|
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::error::{Error, Result};
|
||||||
pub use self::body::Body;
|
pub use self::body::Body;
|
||||||
pub use self::redirect::RedirectPolicy;
|
pub use self::redirect::RedirectPolicy;
|
||||||
|
pub use self::response::Response;
|
||||||
|
|
||||||
mod body;
|
mod body;
|
||||||
mod client;
|
mod client;
|
||||||
mod error;
|
mod error;
|
||||||
mod redirect;
|
mod redirect;
|
||||||
|
mod response;
|
||||||
|
|
||||||
|
|
||||||
/// Shortcut method to quickly make a `GET` request.
|
/// Shortcut method to quickly make a `GET` request.
|
||||||
|
|||||||
155
src/response.rs
Normal file
155
src/response.rs
Normal file
@@ -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<T: Deserialize>(&mut self) -> ::Result<T> {
|
||||||
|
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::<ContentEncoding>().map_or(false, |encs|{
|
||||||
|
encs.contains(&Encoding::Gzip)
|
||||||
|
});
|
||||||
|
content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
|
||||||
|
encs.contains(&Encoding::Gzip)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if content_encoding_gzip {
|
||||||
|
res.headers.remove::<ContentEncoding>();
|
||||||
|
res.headers.remove::<ContentLength>();
|
||||||
|
}
|
||||||
|
if is_gzip {
|
||||||
|
if let Some(content_length) = res.headers.get::<ContentLength>() {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
self.inner.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user