use std::fmt; use std::io::{self, Read}; use std::mem; use std::net::SocketAddr; use std::pin::Pin; use std::time::Duration; use bytes::Bytes; use http; use hyper::header::HeaderMap; #[cfg(feature = "json")] use serde::de::DeserializeOwned; use super::client::KeepCoreThreadAlive; use super::wait; #[cfg(feature = "cookies")] use crate::cookie; use crate::{async_impl, StatusCode, Url, Version}; /// A Response to a submitted `Request`. pub struct Response { inner: async_impl::Response, body: Option>>, timeout: Option, _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( res: async_impl::Response, timeout: Option, thread: KeepCoreThreadAlive, ) -> Response { Response { inner: res, body: None, timeout, _thread_handle: thread, } } /// Get the `StatusCode` of this `Response`. /// /// # Examples /// /// Checking for general status class: /// /// ```rust /// # #[cfg(feature = "json")] /// # fn run() -> Result<(), Box> { /// let resp = reqwest::blocking::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::blocking::Client; /// use reqwest::StatusCode; /// # fn run() -> Result<(), Box> { /// 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::blocking::Client; /// use reqwest::header::ETAG; /// /// # fn run() -> Result<(), Box> { /// 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 a mutable reference to the `Headers` of this `Response`. #[inline] pub fn headers_mut(&mut self) -> &mut HeaderMap { self.inner.headers_mut() } /// Retrieve the cookies contained in the response. /// /// Note that invalid 'Set-Cookie' headers will be ignored. /// /// # Optional /// /// This requires the optional `cookies` feature to be enabled. #[cfg(feature = "cookies")] #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))] pub fn cookies<'a>(&'a self) -> impl Iterator> + 'a { cookie::extract_response_cookies(self.headers()).filter_map(Result::ok) } /// 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> { /// let resp = reqwest::blocking::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> { /// let resp = reqwest::blocking::get("http://httpbin.org/redirect/1")?; /// println!("httpbin.org address: {:?}", resp.remote_addr()); /// # Ok(()) /// # } /// ``` pub fn remote_addr(&self) -> Option { self.inner.remote_addr() } /// Returns a reference to the associated extensions. pub fn extensions(&self) -> &http::Extensions { self.inner.extensions() } /// Returns a mutable reference to the associated extensions. pub fn extensions_mut(&mut self) -> &mut http::Extensions { self.inner.extensions_mut() } /// 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 { self.inner.content_length() } /// Try and deserialize the response body as JSON using `serde`. /// /// # Optional /// /// This requires the optional `json` feature enabled. /// /// # Examples /// /// ```rust /// # extern crate reqwest; /// # extern crate serde; /// # /// # use reqwest::Error; /// # use serde::Deserialize; /// # /// // This `derive` requires the `serde` dependency. /// #[derive(Deserialize)] /// struct Ip { /// origin: String, /// } /// /// # fn run() -> Result<(), Error> { /// let json: Ip = reqwest::blocking::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 #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json(self) -> crate::Result { wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e { wait::Waited::TimedOut(e) => crate::error::decode(e), wait::Waited::Inner(e) => e, }) } /// Get the full response body as `Bytes`. /// /// # Example /// /// ``` /// # fn run() -> Result<(), Box> { /// let bytes = reqwest::blocking::get("http://httpbin.org/ip")?.bytes()?; /// /// println!("bytes: {:?}", bytes); /// # Ok(()) /// # } /// ``` pub fn bytes(self) -> crate::Result { wait::timeout(self.inner.bytes(), self.timeout).map_err(|e| match e { wait::Waited::TimedOut(e) => crate::error::decode(e), wait::Waited::Inner(e) => e, }) } /// Get the response text. /// /// This method decodes the response body with BOM sniffing /// and with malformed sequences replaced with the REPLACEMENT CHARACTER. /// Encoding is determined 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> { /// let content = reqwest::blocking::get("http://httpbin.org/range/26")?.text()?; /// # Ok(()) /// # } /// ``` pub fn text(self) -> crate::Result { self.text_with_charset("utf-8") } /// Get the response text given a specific encoding. /// /// This method decodes the response body with BOM sniffing /// and with malformed sequences replaced with the REPLACEMENT CHARACTER. /// You can provide a default encoding for decoding the raw message, while the /// `charset` parameter of `Content-Type` header is still prioritized. For more information /// about the possible encoding name, please go to [`encoding_rs`] docs. /// /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages /// /// # Example /// /// ```rust /// # extern crate reqwest; /// # fn run() -> Result<(), Box> { /// let content = reqwest::blocking::get("http://httpbin.org/range/26")? /// .text_with_charset("utf-8")?; /// # Ok(()) /// # } /// ``` pub fn text_with_charset(self, default_encoding: &str) -> crate::Result { wait::timeout(self.inner.text_with_charset(default_encoding), self.timeout).map_err(|e| { match e { wait::Waited::TimedOut(e) => crate::error::decode(e), wait::Waited::Inner(e) => e, } }) } /// 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> { /// let mut resp = reqwest::blocking::get("http://httpbin.org/range/5")?; /// let mut buf: Vec = vec![]; /// resp.copy_to(&mut buf)?; /// assert_eq!(b"abcde", buf.as_slice()); /// # Ok(()) /// # } /// ``` pub fn copy_to(&mut self, w: &mut W) -> crate::Result where W: io::Write, { io::copy(self, w).map_err(crate::error::decode_io) } /// Turn a response into an error if the server returned an error. /// /// # Example /// /// ```rust,no_run /// # extern crate reqwest; /// # fn run() -> Result<(), Box> { /// let res = reqwest::blocking::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() {} /// ``` pub fn error_for_status(self) -> crate::Result { let Response { body, inner, timeout, _thread_handle, } = self; inner.error_for_status().map(move |inner| Response { inner, body, timeout, _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> { /// let res = reqwest::blocking::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() {} /// ``` pub fn error_for_status_ref(&self) -> crate::Result<&Self> { self.inner.error_for_status_ref().and_then(|_| Ok(self)) } // private fn body_mut(&mut self) -> Pin<&mut dyn futures_util::io::AsyncRead> { use futures_util::TryStreamExt; if self.body.is_none() { let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty()); let body = body.map_err(crate::error::into_io).into_async_read(); self.body = Some(Box::pin(body)); } self.body.as_mut().expect("body was init").as_mut() } } impl Read for Response { fn read(&mut self, buf: &mut [u8]) -> io::Result { use futures_util::io::AsyncReadExt; let timeout = self.timeout; wait::timeout(self.body_mut().read(buf), timeout).map_err(|e| match e { wait::Waited::TimedOut(e) => crate::error::decode(e).into_io(), wait::Waited::Inner(e) => e, }) } } impl> From> for Response { fn from(r: http::Response) -> Response { let response = async_impl::Response::from(r); Response::new(response, None, KeepCoreThreadAlive::empty()) } }