summaryrefslogtreecommitdiffstats
path: root/vendor/reqwest/src/async_impl/response.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/reqwest/src/async_impl/response.rs')
-rw-r--r--vendor/reqwest/src/async_impl/response.rs448
1 files changed, 448 insertions, 0 deletions
diff --git a/vendor/reqwest/src/async_impl/response.rs b/vendor/reqwest/src/async_impl/response.rs
new file mode 100644
index 000000000..340e54174
--- /dev/null
+++ b/vendor/reqwest/src/async_impl/response.rs
@@ -0,0 +1,448 @@
+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<Decoder>,
+ // Boxed to save space (11 words to 1 word), and it's not accessed
+ // frequently internally.
+ url: Box<Url>,
+}
+
+impl Response {
+ pub(super) fn new(
+ res: hyper::Response<hyper::Body>,
+ url: Url,
+ accepts: Accepts,
+ timeout: Option<Pin<Box<Sleep>>>,
+ ) -> 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<u64> {
+ 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<Item = cookie::Cookie<'a>> + '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<SocketAddr> {
+ self.res
+ .extensions()
+ .get::<HttpInfo>()
+ .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<dyn std::error::Error>> {
+ /// let content = reqwest::get("http://httpbin.org/range/26")
+ /// .await?
+ /// .text()
+ /// .await?;
+ ///
+ /// println!("text: {:?}", content);
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub async fn text(self) -> crate::Result<String> {
+ 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<dyn std::error::Error>> {
+ /// 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<String> {
+ let content_type = self
+ .headers()
+ .get(crate::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(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::<Ip>()
+ /// .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<T: DeserializeOwned>(self) -> crate::Result<T> {
+ 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<dyn std::error::Error>> {
+ /// let bytes = reqwest::get("http://httpbin.org/ip")
+ /// .await?
+ /// .bytes()
+ /// .await?;
+ ///
+ /// println!("bytes: {:?}", bytes);
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub async fn bytes(self) -> crate::Result<Bytes> {
+ 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<dyn std::error::Error>> {
+ /// 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<Option<Bytes>> {
+ 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<dyn std::error::Error>> {
+ /// 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<Item = crate::Result<Bytes>> {
+ 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<Self> {
+ 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<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 decoder = Decoder::detect(&mut parts.headers, body, Accepts::none());
+ let url = parts
+ .extensions
+ .remove::<ResponseUrl>()
+ .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<Response> 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);
+ }
+}