reqwest/blocking/
response.rs

1use std::fmt;
2use std::io::{self, Read};
3use std::mem;
4use std::net::SocketAddr;
5use std::pin::Pin;
6use std::time::Duration;
7
8use bytes::Bytes;
9use http;
10use hyper::header::HeaderMap;
11#[cfg(feature = "json")]
12use serde::de::DeserializeOwned;
13
14use super::client::KeepCoreThreadAlive;
15use super::wait;
16#[cfg(feature = "cookies")]
17use crate::cookie;
18use crate::{async_impl, StatusCode, Url, Version};
19
20/// A Response to a submitted `Request`.
21pub struct Response {
22    inner: async_impl::Response,
23    body: Option<Pin<Box<dyn futures_util::io::AsyncRead + Send + Sync>>>,
24    timeout: Option<Duration>,
25    _thread_handle: KeepCoreThreadAlive,
26}
27
28impl fmt::Debug for Response {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        fmt::Debug::fmt(&self.inner, f)
31    }
32}
33
34impl Response {
35    pub(crate) fn new(
36        res: async_impl::Response,
37        timeout: Option<Duration>,
38        thread: KeepCoreThreadAlive,
39    ) -> Response {
40        Response {
41            inner: res,
42            body: None,
43            timeout,
44            _thread_handle: thread,
45        }
46    }
47
48    /// Get the `StatusCode` of this `Response`.
49    ///
50    /// # Examples
51    ///
52    /// Checking for general status class:
53    ///
54    /// ```rust
55    /// # #[cfg(feature = "json")]
56    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
57    /// let resp = reqwest::blocking::get("http://httpbin.org/get")?;
58    /// if resp.status().is_success() {
59    ///     println!("success!");
60    /// } else if resp.status().is_server_error() {
61    ///     println!("server error!");
62    /// } else {
63    ///     println!("Something else happened. Status: {:?}", resp.status());
64    /// }
65    /// # Ok(())
66    /// # }
67    /// ```
68    ///
69    /// Checking for specific status codes:
70    ///
71    /// ```rust
72    /// use reqwest::blocking::Client;
73    /// use reqwest::StatusCode;
74    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
75    /// let client = Client::new();
76    ///
77    /// let resp = client.post("http://httpbin.org/post")
78    ///     .body("possibly too large")
79    ///     .send()?;
80    ///
81    /// match resp.status() {
82    ///     StatusCode::OK => println!("success!"),
83    ///     StatusCode::PAYLOAD_TOO_LARGE => {
84    ///         println!("Request payload is too large!");
85    ///     }
86    ///     s => println!("Received response status: {s:?}"),
87    /// };
88    /// # Ok(())
89    /// # }
90    /// ```
91    #[inline]
92    pub fn status(&self) -> StatusCode {
93        self.inner.status()
94    }
95
96    /// Get the `Headers` of this `Response`.
97    ///
98    /// # Example
99    ///
100    /// Saving an etag when caching a file:
101    ///
102    /// ```
103    /// use reqwest::blocking::Client;
104    /// use reqwest::header::ETAG;
105    ///
106    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
107    /// let client = Client::new();
108    ///
109    /// let mut resp = client.get("http://httpbin.org/cache").send()?;
110    /// if resp.status().is_success() {
111    ///     if let Some(etag) = resp.headers().get(ETAG) {
112    ///         std::fs::write("etag", etag.as_bytes());
113    ///     }
114    ///     let mut file = std::fs::File::create("file")?;
115    ///     resp.copy_to(&mut file)?;
116    /// }
117    /// # Ok(())
118    /// # }
119    /// ```
120    #[inline]
121    pub fn headers(&self) -> &HeaderMap {
122        self.inner.headers()
123    }
124
125    /// Get a mutable reference to the `Headers` of this `Response`.
126    #[inline]
127    pub fn headers_mut(&mut self) -> &mut HeaderMap {
128        self.inner.headers_mut()
129    }
130
131    /// Retrieve the cookies contained in the response.
132    ///
133    /// Note that invalid 'Set-Cookie' headers will be ignored.
134    ///
135    /// # Optional
136    ///
137    /// This requires the optional `cookies` feature to be enabled.
138    #[cfg(feature = "cookies")]
139    #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
140    pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a {
141        cookie::extract_response_cookies(self.headers()).filter_map(Result::ok)
142    }
143
144    /// Get the HTTP `Version` of this `Response`.
145    #[inline]
146    pub fn version(&self) -> Version {
147        self.inner.version()
148    }
149
150    /// Get the final `Url` of this `Response`.
151    ///
152    /// # Example
153    ///
154    /// ```rust
155    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
156    /// let resp = reqwest::blocking::get("http://httpbin.org/redirect/1")?;
157    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
158    /// # Ok(())
159    /// # }
160    /// ```
161    #[inline]
162    pub fn url(&self) -> &Url {
163        self.inner.url()
164    }
165
166    /// Get the remote address used to get this `Response`.
167    ///
168    /// # Example
169    ///
170    /// ```rust
171    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
172    /// let resp = reqwest::blocking::get("http://httpbin.org/redirect/1")?;
173    /// println!("httpbin.org address: {:?}", resp.remote_addr());
174    /// # Ok(())
175    /// # }
176    /// ```
177    pub fn remote_addr(&self) -> Option<SocketAddr> {
178        self.inner.remote_addr()
179    }
180
181    /// Returns a reference to the associated extensions.
182    pub fn extensions(&self) -> &http::Extensions {
183        self.inner.extensions()
184    }
185
186    /// Returns a mutable reference to the associated extensions.
187    pub fn extensions_mut(&mut self) -> &mut http::Extensions {
188        self.inner.extensions_mut()
189    }
190
191    /// Get the content-length of the response, if it is known.
192    ///
193    /// Reasons it may not be known:
194    ///
195    /// - The server didn't send a `content-length` header.
196    /// - The response is gzipped and automatically decoded (thus changing
197    ///   the actual decoded length).
198    pub fn content_length(&self) -> Option<u64> {
199        self.inner.content_length()
200    }
201
202    /// Try and deserialize the response body as JSON using `serde`.
203    ///
204    /// # Optional
205    ///
206    /// This requires the optional `json` feature enabled.
207    ///
208    /// # Examples
209    ///
210    /// ```rust
211    /// # extern crate reqwest;
212    /// # extern crate serde;
213    /// #
214    /// # use reqwest::Error;
215    /// # use serde::Deserialize;
216    /// #
217    /// // This `derive` requires the `serde` dependency.
218    /// #[derive(Deserialize)]
219    /// struct Ip {
220    ///     origin: String,
221    /// }
222    ///
223    /// # fn run() -> Result<(), Error> {
224    /// let json: Ip = reqwest::blocking::get("http://httpbin.org/ip")?.json()?;
225    /// # Ok(())
226    /// # }
227    /// #
228    /// # fn main() { }
229    /// ```
230    ///
231    /// # Errors
232    ///
233    /// This method fails whenever the response body is not in JSON format,
234    /// or it cannot be properly deserialized to target type `T`. For more
235    /// details please see [`serde_json::from_reader`].
236    ///
237    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
238    #[cfg(feature = "json")]
239    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
240    pub fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
241        wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e {
242            wait::Waited::TimedOut(e) => crate::error::decode(e),
243            wait::Waited::Inner(e) => e,
244        })
245    }
246
247    /// Get the full response body as `Bytes`.
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
253    /// let bytes = reqwest::blocking::get("http://httpbin.org/ip")?.bytes()?;
254    ///
255    /// println!("bytes: {bytes:?}");
256    /// # Ok(())
257    /// # }
258    /// ```
259    pub fn bytes(self) -> crate::Result<Bytes> {
260        wait::timeout(self.inner.bytes(), self.timeout).map_err(|e| match e {
261            wait::Waited::TimedOut(e) => crate::error::decode(e),
262            wait::Waited::Inner(e) => e,
263        })
264    }
265
266    /// Get the response text.
267    ///
268    /// This method decodes the response body with BOM sniffing
269    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
270    /// Encoding is determined from the `charset` parameter of `Content-Type` header,
271    /// and defaults to `utf-8` if not presented.
272    ///
273    /// # Note
274    ///
275    /// If the `charset` feature is disabled the method will only attempt to decode the
276    /// response as UTF-8, regardless of the given `Content-Type`
277    ///
278    /// # Example
279    ///
280    /// ```rust
281    /// # extern crate reqwest;
282    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
283    /// let content = reqwest::blocking::get("http://httpbin.org/range/26")?.text()?;
284    /// # Ok(())
285    /// # }
286    /// ```
287    pub fn text(self) -> crate::Result<String> {
288        wait::timeout(self.inner.text(), self.timeout).map_err(|e| match e {
289            wait::Waited::TimedOut(e) => crate::error::decode(e),
290            wait::Waited::Inner(e) => e,
291        })
292    }
293
294    /// Get the response text given a specific encoding.
295    ///
296    /// This method decodes the response body with BOM sniffing
297    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
298    /// You can provide a default encoding for decoding the raw message, while the
299    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
300    /// about the possible encoding name, please go to [`encoding_rs`] docs.
301    ///
302    /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages
303    ///
304    /// # Optional
305    ///
306    /// This requires the optional `charset` feature enabled.
307    ///
308    /// # Example
309    ///
310    /// ```rust
311    /// # extern crate reqwest;
312    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
313    /// let content = reqwest::blocking::get("http://httpbin.org/range/26")?
314    ///     .text_with_charset("utf-8")?;
315    /// # Ok(())
316    /// # }
317    /// ```
318    #[cfg(feature = "charset")]
319    #[cfg_attr(docsrs, doc(cfg(feature = "charset")))]
320    pub fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
321        wait::timeout(self.inner.text_with_charset(default_encoding), self.timeout).map_err(|e| {
322            match e {
323                wait::Waited::TimedOut(e) => crate::error::decode(e),
324                wait::Waited::Inner(e) => e,
325            }
326        })
327    }
328
329    /// Copy the response body into a writer.
330    ///
331    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
332    /// the body and then write it into writer in a streaming fashion until EOF is met.
333    ///
334    /// On success, the total number of bytes that were copied to `writer` is returned.
335    ///
336    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
337    ///
338    /// # Example
339    ///
340    /// ```rust
341    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
342    /// let mut resp = reqwest::blocking::get("http://httpbin.org/range/5")?;
343    /// let mut buf: Vec<u8> = vec![];
344    /// resp.copy_to(&mut buf)?;
345    /// assert_eq!(b"abcde", buf.as_slice());
346    /// # Ok(())
347    /// # }
348    /// ```
349    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> crate::Result<u64>
350    where
351        W: io::Write,
352    {
353        io::copy(self, w).map_err(crate::error::decode_io)
354    }
355
356    /// Turn a response into an error if the server returned an error.
357    ///
358    /// # Example
359    ///
360    /// ```rust,no_run
361    /// # extern crate reqwest;
362    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
363    /// let res = reqwest::blocking::get("http://httpbin.org/status/400")?
364    ///     .error_for_status();
365    /// if let Err(err) = res {
366    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
367    /// }
368    /// # Ok(())
369    /// # }
370    /// # fn main() {}
371    /// ```
372    pub fn error_for_status(self) -> crate::Result<Self> {
373        let Response {
374            body,
375            inner,
376            timeout,
377            _thread_handle,
378        } = self;
379        inner.error_for_status().map(move |inner| Response {
380            inner,
381            body,
382            timeout,
383            _thread_handle,
384        })
385    }
386
387    /// Turn a reference to a response into an error if the server returned an error.
388    ///
389    /// # Example
390    ///
391    /// ```rust,no_run
392    /// # extern crate reqwest;
393    /// # fn run() -> Result<(), Box<dyn std::error::Error>> {
394    /// let res = reqwest::blocking::get("http://httpbin.org/status/400")?;
395    /// let res = res.error_for_status_ref();
396    /// if let Err(err) = res {
397    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
398    /// }
399    /// # Ok(())
400    /// # }
401    /// # fn main() {}
402    /// ```
403    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
404        self.inner.error_for_status_ref().and_then(|_| Ok(self))
405    }
406
407    // private
408
409    fn body_mut(&mut self) -> Pin<&mut dyn futures_util::io::AsyncRead> {
410        use futures_util::TryStreamExt;
411        if self.body.is_none() {
412            let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty());
413
414            let body = body.into_stream().into_async_read();
415
416            self.body = Some(Box::pin(body));
417        }
418        self.body.as_mut().expect("body was init").as_mut()
419    }
420}
421
422impl Read for Response {
423    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
424        use futures_util::io::AsyncReadExt;
425
426        let timeout = self.timeout;
427        wait::timeout(self.body_mut().read(buf), timeout).map_err(|e| match e {
428            wait::Waited::TimedOut(e) => crate::error::decode(e).into_io(),
429            wait::Waited::Inner(e) => e,
430        })
431    }
432}
433
434impl<T: Into<async_impl::body::Body>> From<http::Response<T>> for Response {
435    fn from(r: http::Response<T>) -> Response {
436        let response = async_impl::Response::from(r);
437        Response::new(response, None, KeepCoreThreadAlive::empty())
438    }
439}