Skip to main content

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