use std::fmt;
use std::mem;
use std::marker::PhantomData;
use std::net::SocketAddr;
use futures::{Async, Future, Poll, Stream};
use futures::stream::Concat2;
use hyper::{HeaderMap, StatusCode, Version};
use hyper::client::connect::HttpInfo;
use serde::de::DeserializeOwned;
use serde_json;
use url::Url;
use http;
use super::Decoder;
use super::body::Body;
pub struct Response {
status: StatusCode,
headers: HeaderMap,
url: Box<Url>,
body: Decoder,
version: Version,
extensions: http::Extensions,
}
impl Response {
pub(super) fn new(res: ::hyper::Response<::hyper::Body>, url: Url, gzip: bool) -> Response {
let (parts, body) = res.into_parts();
let status = parts.status;
let version = parts.version;
let extensions = parts.extensions;
let mut headers = parts.headers;
let decoder = Decoder::detect(&mut headers, Body::wrap(body), gzip);
debug!("Response: '{}' for {}", status, url);
Response {
status,
headers,
url: Box::new(url),
body: decoder,
version,
extensions,
}
}
#[inline]
pub fn url(&self) -> &Url {
&self.url
}
pub fn remote_addr(&self) -> Option<SocketAddr> {
self
.extensions
.get::<HttpInfo>()
.map(|info| info.remote_addr())
}
#[inline]
pub fn status(&self) -> StatusCode {
self.status
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
pub fn into_body(self) -> Decoder {
self.body
}
#[inline]
pub fn body(&self) -> &Decoder {
&self.body
}
#[inline]
pub fn body_mut(&mut self) -> &mut Decoder {
&mut self.body
}
#[inline]
pub fn version(&self) -> Version {
self.version
}
#[inline]
pub fn json<T: DeserializeOwned>(&mut self) -> Json<T> {
let body = mem::replace(&mut self.body, Decoder::empty());
Json {
concat: body.concat2(),
_marker: PhantomData,
}
}
#[inline]
pub fn error_for_status(self) -> ::Result<Self> {
if self.status.is_client_error() {
Err(::error::client_error(*self.url, self.status))
} else if self.status.is_server_error() {
Err(::error::server_error(*self.url, self.status))
} else {
Ok(self)
}
}
}
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("url", self.url())
.field("status", &self.status())
.field("headers", self.headers())
.finish()
}
}
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);
let url = parts.extensions
.remove::<ResponseUrl>()
.unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap()));
let url = url.0;
Response {
status: parts.status,
headers: parts.headers,
url: Box::new(url),
body: body,
version: parts.version,
extensions: parts.extensions,
}
}
}
pub struct Json<T> {
concat: Concat2<Decoder>,
_marker: PhantomData<T>,
}
impl<T: DeserializeOwned> Future for Json<T> {
type Item = T;
type Error = ::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let bytes = try_ready!(self.concat.poll());
let t = try_!(serde_json::from_slice(&bytes));
Ok(Async::Ready(t))
}
}
impl<T> fmt::Debug for Json<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Json")
.finish()
}
}
#[derive(Debug, Clone, PartialEq)]
struct ResponseUrl(Url);
pub trait ResponseBuilderExt {
fn url(&mut self, url: Url) -> &mut Self;
}
impl ResponseBuilderExt for http::response::Builder {
fn url(&mut self, url: Url) -> &mut Self {
self.extension(ResponseUrl(url))
}
}
#[cfg(test)]
mod tests {
use url::Url;
use http::response::Builder;
use super::{Response, ResponseUrl, ResponseBuilderExt};
#[test]
fn test_response_builder_ext() {
let url = Url::parse("http://example.com").unwrap();
let response = Builder::new()
.status(200)
.url(url.clone())
.body(())
.unwrap();
assert_eq!(response.extensions().get::<ResponseUrl>(), Some(&ResponseUrl(url)));
}
#[test]
fn test_from_http_response() {
let url = Url::parse("http://example.com").unwrap();
let response = Builder::new()
.status(200)
.url(url.clone())
.body("foo")
.unwrap();
let response = Response::from(response);
assert_eq!(response.status, 200);
assert_eq!(response.url, Box::new(url));
}
}