reqwest/async_impl/
response.rs

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