use std::borrow::Cow; use std::fmt; use std::net::SocketAddr; use std::pin::Pin; use bytes::Bytes; use encoding_rs::{Encoding, UTF_8}; use futures_util::stream::StreamExt; use hyper::client::connect::HttpInfo; use hyper::{HeaderMap, StatusCode, Version}; use mime::Mime; #[cfg(feature = "json")] use serde::de::DeserializeOwned; #[cfg(feature = "json")] use serde_json; use tokio::time::Sleep; use url::Url; use super::body::Body; use super::decoder::{Accepts, Decoder}; #[cfg(feature = "cookies")] use crate::cookie; use crate::response::ResponseUrl; /// A Response to a submitted `Request`. pub struct Response { pub(super) res: hyper::Response, // Boxed to save space (11 words to 1 word), and it's not accessed // frequently internally. url: Box, } impl Response { pub(super) fn new( res: hyper::Response, url: Url, accepts: Accepts, timeout: Option>>, ) -> Response { let (mut parts, body) = res.into_parts(); let decoder = Decoder::detect(&mut parts.headers, Body::response(body, timeout), accepts); let res = hyper::Response::from_parts(parts, decoder); Response { res, url: Box::new(url), } } /// Get the `StatusCode` of this `Response`. #[inline] pub fn status(&self) -> StatusCode { self.res.status() } /// Get the HTTP `Version` of this `Response`. #[inline] pub fn version(&self) -> Version { self.res.version() } /// Get the `Headers` of this `Response`. #[inline] pub fn headers(&self) -> &HeaderMap { self.res.headers() } /// Get a mutable reference to the `Headers` of this `Response`. #[inline] pub fn headers_mut(&mut self) -> &mut HeaderMap { self.res.headers_mut() } /// Get the content-length of this response, if known. /// /// Reasons it may not be known: /// /// - The server didn't send a `content-length` header. /// - The response is compressed and automatically decoded (thus changing /// the actual decoded length). pub fn content_length(&self) -> Option { use hyper::body::HttpBody; HttpBody::size_hint(self.res.body()).exact() } /// 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.res.headers()).filter_map(Result::ok) } /// Get the final `Url` of this `Response`. #[inline] pub fn url(&self) -> &Url { &self.url } /// Get the remote address used to get this `Response`. pub fn remote_addr(&self) -> Option { self.res .extensions() .get::() .map(|info| info.remote_addr()) } /// Returns a reference to the associated extensions. pub fn extensions(&self) -> &http::Extensions { self.res.extensions() } /// Returns a mutable reference to the associated extensions. pub fn extensions_mut(&mut self) -> &mut http::Extensions { self.res.extensions_mut() } // body methods /// Get the full 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 /// /// ``` /// # async fn run() -> Result<(), Box> { /// let content = reqwest::get("http://httpbin.org/range/26") /// .await? /// .text() /// .await?; /// /// println!("text: {:?}", content); /// # Ok(()) /// # } /// ``` pub async fn text(self) -> crate::Result { self.text_with_charset("utf-8").await } /// Get the full 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 /// /// ``` /// # async fn run() -> Result<(), Box> { /// let content = reqwest::get("http://httpbin.org/range/26") /// .await? /// .text_with_charset("utf-8") /// .await?; /// /// println!("text: {:?}", content); /// # Ok(()) /// # } /// ``` pub async fn text_with_charset(self, default_encoding: &str) -> crate::Result { let content_type = self .headers() .get(crate::header::CONTENT_TYPE) .and_then(|value| value.to_str().ok()) .and_then(|value| value.parse::().ok()); let encoding_name = content_type .as_ref() .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str())) .unwrap_or(default_encoding); let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8); let full = self.bytes().await?; let (text, _, _) = encoding.decode(&full); if let Cow::Owned(s) = text { return Ok(s); } unsafe { // decoding returned Cow::Borrowed, meaning these bytes // are already valid utf8 Ok(String::from_utf8_unchecked(full.to_vec())) } } /// Try to deserialize the response body as JSON. /// /// # Optional /// /// This requires the optional `json` feature enabled. /// /// # Examples /// /// ``` /// # extern crate reqwest; /// # extern crate serde; /// # /// # use reqwest::Error; /// # use serde::Deserialize; /// # /// // This `derive` requires the `serde` dependency. /// #[derive(Deserialize)] /// struct Ip { /// origin: String, /// } /// /// # async fn run() -> Result<(), Error> { /// let ip = reqwest::get("http://httpbin.org/ip") /// .await? /// .json::() /// .await?; /// /// println!("ip: {}", ip.origin); /// # 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 async fn json(self) -> crate::Result { let full = self.bytes().await?; serde_json::from_slice(&full).map_err(crate::error::decode) } /// Get the full response body as `Bytes`. /// /// # Example /// /// ``` /// # async fn run() -> Result<(), Box> { /// let bytes = reqwest::get("http://httpbin.org/ip") /// .await? /// .bytes() /// .await?; /// /// println!("bytes: {:?}", bytes); /// # Ok(()) /// # } /// ``` pub async fn bytes(self) -> crate::Result { hyper::body::to_bytes(self.res.into_body()).await } /// Stream a chunk of the response body. /// /// When the response body has been exhausted, this will return `None`. /// /// # Example /// /// ``` /// # async fn run() -> Result<(), Box> { /// let mut res = reqwest::get("https://hyper.rs").await?; /// /// while let Some(chunk) = res.chunk().await? { /// println!("Chunk: {:?}", chunk); /// } /// # Ok(()) /// # } /// ``` pub async fn chunk(&mut self) -> crate::Result> { if let Some(item) = self.res.body_mut().next().await { Ok(Some(item?)) } else { Ok(None) } } /// Convert the response into a `Stream` of `Bytes` from the body. /// /// # Example /// /// ``` /// use futures_util::StreamExt; /// /// # async fn run() -> Result<(), Box> { /// let mut stream = reqwest::get("http://httpbin.org/ip") /// .await? /// .bytes_stream(); /// /// while let Some(item) = stream.next().await { /// println!("Chunk: {:?}", item?); /// } /// # Ok(()) /// # } /// ``` /// /// # Optional /// /// This requires the optional `stream` feature to be enabled. #[cfg(feature = "stream")] #[cfg_attr(docsrs, doc(cfg(feature = "stream")))] pub fn bytes_stream(self) -> impl futures_core::Stream> { self.res.into_body() } // util methods /// Turn a response into an error if the server returned an error. /// /// # Example /// /// ``` /// # use reqwest::Response; /// fn on_response(res: Response) { /// match res.error_for_status() { /// Ok(_res) => (), /// Err(err) => { /// // asserting a 400 as an example /// // it could be any status between 400...599 /// assert_eq!( /// err.status(), /// Some(reqwest::StatusCode::BAD_REQUEST) /// ); /// } /// } /// } /// # fn main() {} /// ``` pub fn error_for_status(self) -> crate::Result { let status = self.status(); if status.is_client_error() || status.is_server_error() { Err(crate::error::status_code(*self.url, status)) } else { Ok(self) } } /// Turn a reference to a response into an error if the server returned an error. /// /// # Example /// /// ``` /// # use reqwest::Response; /// fn on_response(res: &Response) { /// match res.error_for_status_ref() { /// Ok(_res) => (), /// Err(err) => { /// // asserting a 400 as an example /// // it could be any status between 400...599 /// assert_eq!( /// err.status(), /// Some(reqwest::StatusCode::BAD_REQUEST) /// ); /// } /// } /// } /// # fn main() {} /// ``` pub fn error_for_status_ref(&self) -> crate::Result<&Self> { let status = self.status(); if status.is_client_error() || status.is_server_error() { Err(crate::error::status_code(*self.url.clone(), status)) } else { Ok(self) } } // private // The Response's body is an implementation detail. // You no longer need to get a reference to it, there are async methods // on the `Response` itself. // // This method is just used by the blocking API. #[cfg(feature = "blocking")] pub(crate) fn body_mut(&mut self) -> &mut Decoder { self.res.body_mut() } } 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> From> for Response { fn from(r: http::Response) -> Response { let (mut parts, body) = r.into_parts(); let body = body.into(); let decoder = Decoder::detect(&mut parts.headers, body, Accepts::none()); let url = parts .extensions .remove::() .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap())); let url = url.0; let res = hyper::Response::from_parts(parts, decoder); Response { res, url: Box::new(url), } } } /// A `Response` can be piped as the `Body` of another request. impl From for Body { fn from(r: Response) -> Body { Body::stream(r.res.into_body()) } } #[cfg(test)] mod tests { use super::Response; use crate::ResponseBuilderExt; use http::response::Builder; use url::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(), url); } }