blob: e2d7e7971b0b8c6087e2c9ae2d7e570fd8decc2e [file] [log] [blame]
use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;
use std::borrow::Cow;
use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream};
use http;
use mime::Mime;
use serde::de::DeserializeOwned;
use serde_json;
use client::KeepCoreThreadAlive;
use hyper::header::HeaderMap;
use {async_impl, StatusCode, Url, Version, wait};
/// A Response to a submitted `Request`.
pub struct Response {
inner: async_impl::Response,
body: async_impl::ReadableChunks<WaitBody>,
content_length: Option<u64>,
_thread_handle: KeepCoreThreadAlive,
}
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl Response {
pub(crate) fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
let len = body.content_length();
let body = async_impl::ReadableChunks::new(WaitBody {
inner: wait::stream(body, timeout)
});
Response {
inner: res,
body: body,
content_length: len,
_thread_handle: thread,
}
}
/// Get the `StatusCode` of this `Response`.
///
/// # Examples
///
/// Checking for general status class:
///
/// ```rust
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/get")?;
/// if resp.status().is_success() {
/// println!("success!");
/// } else if resp.status().is_server_error() {
/// println!("server error!");
/// } else {
/// println!("Something else happened. Status: {:?}", resp.status());
/// }
/// # Ok(())
/// # }
/// ```
///
/// Checking for specific status codes:
///
/// ```rust
/// use reqwest::Client;
/// use reqwest::StatusCode;
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let client = Client::new();
///
/// let resp = client.post("http://httpbin.org/post")
/// .body("possibly too large")
/// .send()?;
///
/// match resp.status() {
/// StatusCode::OK => println!("success!"),
/// StatusCode::PAYLOAD_TOO_LARGE => {
/// println!("Request payload is too large!");
/// }
/// s => println!("Received response status: {:?}", s),
/// };
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn status(&self) -> StatusCode {
self.inner.status()
}
/// Get the `Headers` of this `Response`.
///
/// # Example
///
/// Saving an etag when caching a file:
///
/// ```
/// use reqwest::Client;
/// use reqwest::header::ETAG;
///
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let client = Client::new();
///
/// let mut resp = client.get("http://httpbin.org/cache").send()?;
/// if resp.status().is_success() {
/// if let Some(etag) = resp.headers().get(ETAG) {
/// std::fs::write("etag", etag.as_bytes());
/// }
/// let mut file = std::fs::File::create("file")?;
/// resp.copy_to(&mut file)?;
/// }
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn headers(&self) -> &HeaderMap {
self.inner.headers()
}
/// Get the HTTP `Version` of this `Response`.
#[inline]
pub fn version(&self) -> Version {
self.inner.version()
}
/// Get the final `Url` of this `Response`.
///
/// # Example
///
/// ```rust
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
/// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn url(&self) -> &Url {
self.inner.url()
}
/// Get the remote address used to get this `Response`.
///
/// # Example
///
/// ```rust
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
/// println!("httpbin.org address: {:?}", resp.remote_addr());
/// # Ok(())
/// # }
/// ```
pub fn remote_addr(&self) -> Option<SocketAddr> {
self.inner.remote_addr()
}
/// Get the content-length of the response, if it is known.
///
/// Reasons it may not be known:
///
/// - The server didn't send a `content-length` header.
/// - The response is gzipped and automatically decoded (thus changing
/// the actual decoded length).
pub fn content_length(&self) -> Option<u64> {
self.inner.content_length()
}
/// Try and deserialize the response body as JSON using `serde`.
///
/// # Examples
///
/// ```rust
/// # extern crate reqwest;
/// # #[macro_use] extern crate serde_derive;
/// #
/// # use reqwest::Error;
/// #
/// #[derive(Deserialize)]
/// struct Ip {
/// origin: String,
/// }
///
/// # fn run() -> Result<(), Error> {
/// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
/// # Ok(())
/// # }
/// #
/// # fn main() { }
/// ```
///
/// # Errors
///
/// This method fails whenever the response body is not in JSON format
/// or it cannot be properly deserialized to target type `T`. For more
/// details please see [`serde_json::from_reader`].
/// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
#[inline]
pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
// There's 2 ways we could implement this:
//
// 1. Just using from_reader(self), making use of our blocking read adapter
// 2. Just use self.inner.json().wait()
//
// Doing 1 is pretty easy, but it means we have the `serde_json` code
// in more than one place, doing basically the same thing.
//
// Doing 2 would mean `serde_json` is only in one place, but we'd
// need to update the sync Response to lazily make a blocking read
// adapter, so that our `inner` could possibly still have the original
// body.
//
// Went for easier for now, just to get it working.
serde_json::from_reader(self).map_err(::error::from)
}
/// Get the response text.
///
/// This method decodes the response body with BOM sniffing
/// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
/// Encoding is determinated from the `charset` parameter of `Content-Type` header,
/// and defaults to `utf-8` if not presented.
///
/// # Example
///
/// ```rust
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
/// # Ok(())
/// # }
/// ```
///
/// # Note
///
/// This consumes the body. Trying to read more, or use of `response.json()`
/// will return empty values.
pub fn text(&mut self) -> ::Result<String> {
let len = self.content_length.unwrap_or(0);
let mut content = Vec::with_capacity(len as usize);
self.read_to_end(&mut content).map_err(::error::from)?;
let content_type = self.headers().get(::header::CONTENT_TYPE)
.and_then(|value| {
value.to_str().ok()
})
.and_then(|value| {
value.parse::<Mime>().ok()
});
let encoding_name = content_type
.as_ref()
.and_then(|mime| {
mime
.get_param("charset")
.map(|charset| charset.as_str())
})
.unwrap_or("utf-8");
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
// a block because of borrow checker
{
let (text, _, _) = encoding.decode(&content);
match text {
Cow::Owned(s) => return Ok(s),
_ => (),
}
}
unsafe {
// decoding returned Cow::Borrowed, meaning these bytes
// are already valid utf8
Ok(String::from_utf8_unchecked(content))
}
}
/// Copy the response body into a writer.
///
/// This function internally uses [`std::io::copy`] and hence will continuously read data from
/// the body and then write it into writer in a streaming fashion until EOF is met.
///
/// On success, the total number of bytes that were copied to `writer` is returned.
///
/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
///
/// # Example
///
/// ```rust
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
/// let mut buf: Vec<u8> = vec![];
/// resp.copy_to(&mut buf)?;
/// assert_eq!(b"abcde", buf.as_slice());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> ::Result<u64>
where W: io::Write
{
io::copy(self, w).map_err(::error::from)
}
/// Turn a response into an error if the server returned an error.
///
/// # Example
///
/// ```rust,no_run
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let res = reqwest::get("http://httpbin.org/status/400")?
/// .error_for_status();
/// if let Err(err) = res {
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
/// }
/// # Ok(())
/// # }
/// # fn main() {}
/// ```
#[inline]
pub fn error_for_status(self) -> ::Result<Self> {
let Response { body, content_length, inner, _thread_handle } = self;
inner.error_for_status().map(move |inner| {
Response {
inner,
body,
content_length,
_thread_handle,
}
})
}
/// Turn a reference to a response into an error if the server returned an error.
///
/// # Example
///
/// ```rust,no_run
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let res = reqwest::get("http://httpbin.org/status/400")?;
/// let res = res.error_for_status_ref();
/// if let Err(err) = res {
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
/// }
/// # Ok(())
/// # }
/// # fn main() {}
/// ```
#[inline]
pub fn error_for_status_ref(&self) -> ::Result<&Self> {
self.inner.error_for_status_ref().and_then(|_| Ok(self))
}
}
impl Read for Response {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.body.read(buf)
}
}
struct WaitBody {
inner: wait::WaitStream<async_impl::Decoder>
}
impl Stream for WaitBody {
type Item = <async_impl::Decoder as Stream>::Item;
type Error = <async_impl::Decoder as Stream>::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self.inner.next() {
Some(Ok(chunk)) => Ok(Async::Ready(Some(chunk))),
Some(Err(e)) => {
let req_err = match e {
wait::Waited::TimedOut => ::error::timedout(None),
wait::Waited::Err(e) => e,
};
Err(req_err)
},
None => Ok(Async::Ready(None)),
}
}
}
impl<T: Into<async_impl::body::Body>> From<http::Response<T>> for Response {
fn from(r: http::Response<T>) -> Response {
let response = async_impl::Response::from(r);
Response::new(response, None, KeepCoreThreadAlive::empty())
}
}