reqwest_wasm/async_impl/
response.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::net::SocketAddr;
4use std::pin::Pin;
5
6use bytes::Bytes;
7use encoding_rs::{Encoding, UTF_8};
8use futures_util::stream::StreamExt;
9use hyper::client::connect::HttpInfo;
10use hyper::{HeaderMap, StatusCode, Version};
11use mime::Mime;
12#[cfg(feature = "json")]
13use serde::de::DeserializeOwned;
14#[cfg(feature = "json")]
15use serde_json;
16use tokio::time::Sleep;
17use url::Url;
18
19use super::body::Body;
20use super::decoder::{Accepts, Decoder};
21#[cfg(feature = "cookies")]
22use crate::cookie;
23use crate::response::ResponseUrl;
24
25/// A Response to a submitted `Request`.
26pub struct Response {
27    pub(super) res: hyper::Response<Decoder>,
28    // Boxed to save space (11 words to 1 word), and it's not accessed
29    // frequently internally.
30    url: Box<Url>,
31}
32
33impl Response {
34    pub(super) fn new(
35        res: hyper::Response<hyper::Body>,
36        url: Url,
37        accepts: Accepts,
38        timeout: Option<Pin<Box<Sleep>>>,
39    ) -> Response {
40        let (mut parts, body) = res.into_parts();
41        let decoder = Decoder::detect(&mut parts.headers, Body::response(body, timeout), accepts);
42        let res = hyper::Response::from_parts(parts, decoder);
43
44        Response {
45            res,
46            url: Box::new(url),
47        }
48    }
49
50    /// Get the `StatusCode` of this `Response`.
51    #[inline]
52    pub fn status(&self) -> StatusCode {
53        self.res.status()
54    }
55
56    /// Get the HTTP `Version` of this `Response`.
57    #[inline]
58    pub fn version(&self) -> Version {
59        self.res.version()
60    }
61
62    /// Get the `Headers` of this `Response`.
63    #[inline]
64    pub fn headers(&self) -> &HeaderMap {
65        self.res.headers()
66    }
67
68    /// Get a mutable reference to the `Headers` of this `Response`.
69    #[inline]
70    pub fn headers_mut(&mut self) -> &mut HeaderMap {
71        self.res.headers_mut()
72    }
73
74    /// Get the content-length of this response, if known.
75    ///
76    /// Reasons it may not be known:
77    ///
78    /// - The server didn't send a `content-length` header.
79    /// - The response is compressed and automatically decoded (thus changing
80    ///   the actual decoded length).
81    pub fn content_length(&self) -> Option<u64> {
82        use hyper::body::HttpBody;
83
84        HttpBody::size_hint(self.res.body()).exact()
85    }
86
87    /// Retrieve the cookies contained in the response.
88    ///
89    /// Note that invalid 'Set-Cookie' headers will be ignored.
90    ///
91    /// # Optional
92    ///
93    /// This requires the optional `cookies` feature to be enabled.
94    #[cfg(feature = "cookies")]
95    #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
96    pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a {
97        cookie::extract_response_cookies(self.res.headers()).filter_map(Result::ok)
98    }
99
100    /// Get the final `Url` of this `Response`.
101    #[inline]
102    pub fn url(&self) -> &Url {
103        &self.url
104    }
105
106    /// Get the remote address used to get this `Response`.
107    pub fn remote_addr(&self) -> Option<SocketAddr> {
108        self.res
109            .extensions()
110            .get::<HttpInfo>()
111            .map(|info| info.remote_addr())
112    }
113
114    /// Returns a reference to the associated extensions.
115    pub fn extensions(&self) -> &http::Extensions {
116        self.res.extensions()
117    }
118
119    /// Returns a mutable reference to the associated extensions.
120    pub fn extensions_mut(&mut self) -> &mut http::Extensions {
121        self.res.extensions_mut()
122    }
123
124    // body methods
125
126    /// Get the full response text.
127    ///
128    /// This method decodes the response body with BOM sniffing
129    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
130    /// Encoding is determinated from the `charset` parameter of `Content-Type` header,
131    /// and defaults to `utf-8` if not presented.
132    ///
133    /// # Example
134    ///
135    /// ```
136    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
137    /// let content = reqwest::get("http://httpbin.org/range/26")
138    ///     .await?
139    ///     .text()
140    ///     .await?;
141    ///
142    /// println!("text: {:?}", content);
143    /// # Ok(())
144    /// # }
145    /// ```
146    pub async fn text(self) -> crate::Result<String> {
147        self.text_with_charset("utf-8").await
148    }
149
150    /// Get the full response text given a specific encoding.
151    ///
152    /// This method decodes the response body with BOM sniffing
153    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
154    /// You can provide a default encoding for decoding the raw message, while the
155    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
156    /// about the possible encoding name, please go to [`encoding_rs`] docs.
157    ///
158    /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages
159    ///
160    /// # Example
161    ///
162    /// ```
163    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
164    /// let content = reqwest::get("http://httpbin.org/range/26")
165    ///     .await?
166    ///     .text_with_charset("utf-8")
167    ///     .await?;
168    ///
169    /// println!("text: {:?}", content);
170    /// # Ok(())
171    /// # }
172    /// ```
173    pub async fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
174        let content_type = self
175            .headers()
176            .get(crate::header::CONTENT_TYPE)
177            .and_then(|value| value.to_str().ok())
178            .and_then(|value| value.parse::<Mime>().ok());
179        let encoding_name = content_type
180            .as_ref()
181            .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
182            .unwrap_or(default_encoding);
183        let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
184
185        let full = self.bytes().await?;
186
187        let (text, _, _) = encoding.decode(&full);
188        if let Cow::Owned(s) = text {
189            return Ok(s);
190        }
191        unsafe {
192            // decoding returned Cow::Borrowed, meaning these bytes
193            // are already valid utf8
194            Ok(String::from_utf8_unchecked(full.to_vec()))
195        }
196    }
197
198    /// Try to deserialize the response body as JSON.
199    ///
200    /// # Optional
201    ///
202    /// This requires the optional `json` feature enabled.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// # extern crate reqwest;
208    /// # extern crate serde;
209    /// #
210    /// # use reqwest::Error;
211    /// # use serde::Deserialize;
212    /// #
213    /// // This `derive` requires the `serde` dependency.
214    /// #[derive(Deserialize)]
215    /// struct Ip {
216    ///     origin: String,
217    /// }
218    ///
219    /// # async fn run() -> Result<(), Error> {
220    /// let ip = reqwest::get("http://httpbin.org/ip")
221    ///     .await?
222    ///     .json::<Ip>()
223    ///     .await?;
224    ///
225    /// println!("ip: {}", ip.origin);
226    /// # Ok(())
227    /// # }
228    /// #
229    /// # fn main() { }
230    /// ```
231    ///
232    /// # Errors
233    ///
234    /// This method fails whenever the response body is not in JSON format
235    /// or it cannot be properly deserialized to target type `T`. For more
236    /// details please see [`serde_json::from_reader`].
237    ///
238    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
239    #[cfg(feature = "json")]
240    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
241    pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
242        let full = self.bytes().await?;
243
244        serde_json::from_slice(&full).map_err(crate::error::decode)
245    }
246
247    /// Get the full response body as `Bytes`.
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
253    /// let bytes = reqwest::get("http://httpbin.org/ip")
254    ///     .await?
255    ///     .bytes()
256    ///     .await?;
257    ///
258    /// println!("bytes: {:?}", bytes);
259    /// # Ok(())
260    /// # }
261    /// ```
262    pub async fn bytes(self) -> crate::Result<Bytes> {
263        hyper::body::to_bytes(self.res.into_body()).await
264    }
265
266    /// Stream a chunk of the response body.
267    ///
268    /// When the response body has been exhausted, this will return `None`.
269    ///
270    /// # Example
271    ///
272    /// ```
273    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
274    /// let mut res = reqwest::get("https://hyper.rs").await?;
275    ///
276    /// while let Some(chunk) = res.chunk().await? {
277    ///     println!("Chunk: {:?}", chunk);
278    /// }
279    /// # Ok(())
280    /// # }
281    /// ```
282    pub async fn chunk(&mut self) -> crate::Result<Option<Bytes>> {
283        if let Some(item) = self.res.body_mut().next().await {
284            Ok(Some(item?))
285        } else {
286            Ok(None)
287        }
288    }
289
290    /// Convert the response into a `Stream` of `Bytes` from the body.
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// use futures_util::StreamExt;
296    ///
297    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
298    /// let mut stream = reqwest::get("http://httpbin.org/ip")
299    ///     .await?
300    ///     .bytes_stream();
301    ///
302    /// while let Some(item) = stream.next().await {
303    ///     println!("Chunk: {:?}", item?);
304    /// }
305    /// # Ok(())
306    /// # }
307    /// ```
308    ///
309    /// # Optional
310    ///
311    /// This requires the optional `stream` feature to be enabled.
312    #[cfg(feature = "stream")]
313    #[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
314    pub fn bytes_stream(self) -> impl futures_core::Stream<Item = crate::Result<Bytes>> {
315        self.res.into_body()
316    }
317
318    // util methods
319
320    /// Turn a response into an error if the server returned an error.
321    ///
322    /// # Example
323    ///
324    /// ```
325    /// # use reqwest::Response;
326    /// fn on_response(res: Response) {
327    ///     match res.error_for_status() {
328    ///         Ok(_res) => (),
329    ///         Err(err) => {
330    ///             // asserting a 400 as an example
331    ///             // it could be any status between 400...599
332    ///             assert_eq!(
333    ///                 err.status(),
334    ///                 Some(reqwest::StatusCode::BAD_REQUEST)
335    ///             );
336    ///         }
337    ///     }
338    /// }
339    /// # fn main() {}
340    /// ```
341    pub fn error_for_status(self) -> crate::Result<Self> {
342        let status = self.status();
343        if status.is_client_error() || status.is_server_error() {
344            Err(crate::error::status_code(*self.url, status))
345        } else {
346            Ok(self)
347        }
348    }
349
350    /// Turn a reference to a response into an error if the server returned an error.
351    ///
352    /// # Example
353    ///
354    /// ```
355    /// # use reqwest::Response;
356    /// fn on_response(res: &Response) {
357    ///     match res.error_for_status_ref() {
358    ///         Ok(_res) => (),
359    ///         Err(err) => {
360    ///             // asserting a 400 as an example
361    ///             // it could be any status between 400...599
362    ///             assert_eq!(
363    ///                 err.status(),
364    ///                 Some(reqwest::StatusCode::BAD_REQUEST)
365    ///             );
366    ///         }
367    ///     }
368    /// }
369    /// # fn main() {}
370    /// ```
371    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
372        let status = self.status();
373        if status.is_client_error() || status.is_server_error() {
374            Err(crate::error::status_code(*self.url.clone(), status))
375        } else {
376            Ok(self)
377        }
378    }
379
380    // private
381
382    // The Response's body is an implementation detail.
383    // You no longer need to get a reference to it, there are async methods
384    // on the `Response` itself.
385    //
386    // This method is just used by the blocking API.
387    #[cfg(feature = "blocking")]
388    pub(crate) fn body_mut(&mut self) -> &mut Decoder {
389        self.res.body_mut()
390    }
391}
392
393impl fmt::Debug for Response {
394    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395        f.debug_struct("Response")
396            .field("url", self.url())
397            .field("status", &self.status())
398            .field("headers", self.headers())
399            .finish()
400    }
401}
402
403impl<T: Into<Body>> From<http::Response<T>> for Response {
404    fn from(r: http::Response<T>) -> Response {
405        let (mut parts, body) = r.into_parts();
406        let body = body.into();
407        let decoder = Decoder::detect(&mut parts.headers, body, Accepts::none());
408        let url = parts
409            .extensions
410            .remove::<ResponseUrl>()
411            .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap()));
412        let url = url.0;
413        let res = hyper::Response::from_parts(parts, decoder);
414        Response {
415            res,
416            url: Box::new(url),
417        }
418    }
419}
420
421/// A `Response` can be piped as the `Body` of another request.
422impl From<Response> for Body {
423    fn from(r: Response) -> Body {
424        Body::stream(r.res.into_body())
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use super::Response;
431    use crate::ResponseBuilderExt;
432    use http::response::Builder;
433    use url::Url;
434
435    #[test]
436    fn test_from_http_response() {
437        let url = Url::parse("http://example.com").unwrap();
438        let response = Builder::new()
439            .status(200)
440            .url(url.clone())
441            .body("foo")
442            .unwrap();
443        let response = Response::from(response);
444
445        assert_eq!(response.status(), 200);
446        assert_eq!(*response.url(), url);
447    }
448}