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