reqwest_wasm/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<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<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<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<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<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 determinated from the `charset` parameter of `Content-Type` header,
271    /// and defaults to `utf-8` if not presented.
272    ///
273    /// # Example
274    ///
275    /// ```rust
276    /// # extern crate reqwest;
277    /// # fn run() -> Result<(), Box<std::error::Error>> {
278    /// let content = reqwest::blocking::get("http://httpbin.org/range/26")?.text()?;
279    /// # Ok(())
280    /// # }
281    /// ```
282    pub fn text(self) -> crate::Result<String> {
283        self.text_with_charset("utf-8")
284    }
285
286    /// Get the response text given a specific encoding.
287    ///
288    /// This method decodes the response body with BOM sniffing
289    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
290    /// You can provide a default encoding for decoding the raw message, while the
291    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
292    /// about the possible encoding name, please go to [`encoding_rs`] docs.
293    ///
294    /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages
295    ///
296    /// # Example
297    ///
298    /// ```rust
299    /// # extern crate reqwest;
300    /// # fn run() -> Result<(), Box<std::error::Error>> {
301    /// let content = reqwest::blocking::get("http://httpbin.org/range/26")?
302    ///     .text_with_charset("utf-8")?;
303    /// # Ok(())
304    /// # }
305    /// ```
306    pub fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
307        wait::timeout(self.inner.text_with_charset(default_encoding), self.timeout).map_err(|e| {
308            match e {
309                wait::Waited::TimedOut(e) => crate::error::decode(e),
310                wait::Waited::Inner(e) => e,
311            }
312        })
313    }
314
315    /// Copy the response body into a writer.
316    ///
317    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
318    /// the body and then write it into writer in a streaming fashion until EOF is met.
319    ///
320    /// On success, the total number of bytes that were copied to `writer` is returned.
321    ///
322    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
323    ///
324    /// # Example
325    ///
326    /// ```rust
327    /// # fn run() -> Result<(), Box<std::error::Error>> {
328    /// let mut resp = reqwest::blocking::get("http://httpbin.org/range/5")?;
329    /// let mut buf: Vec<u8> = vec![];
330    /// resp.copy_to(&mut buf)?;
331    /// assert_eq!(b"abcde", buf.as_slice());
332    /// # Ok(())
333    /// # }
334    /// ```
335    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> crate::Result<u64>
336    where
337        W: io::Write,
338    {
339        io::copy(self, w).map_err(crate::error::decode_io)
340    }
341
342    /// Turn a response into an error if the server returned an error.
343    ///
344    /// # Example
345    ///
346    /// ```rust,no_run
347    /// # extern crate reqwest;
348    /// # fn run() -> Result<(), Box<std::error::Error>> {
349    /// let res = reqwest::blocking::get("http://httpbin.org/status/400")?
350    ///     .error_for_status();
351    /// if let Err(err) = res {
352    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
353    /// }
354    /// # Ok(())
355    /// # }
356    /// # fn main() {}
357    /// ```
358    pub fn error_for_status(self) -> crate::Result<Self> {
359        let Response {
360            body,
361            inner,
362            timeout,
363            _thread_handle,
364        } = self;
365        inner.error_for_status().map(move |inner| Response {
366            inner,
367            body,
368            timeout,
369            _thread_handle,
370        })
371    }
372
373    /// Turn a reference to a response into an error if the server returned an error.
374    ///
375    /// # Example
376    ///
377    /// ```rust,no_run
378    /// # extern crate reqwest;
379    /// # fn run() -> Result<(), Box<std::error::Error>> {
380    /// let res = reqwest::blocking::get("http://httpbin.org/status/400")?;
381    /// let res = res.error_for_status_ref();
382    /// if let Err(err) = res {
383    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
384    /// }
385    /// # Ok(())
386    /// # }
387    /// # fn main() {}
388    /// ```
389    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
390        self.inner.error_for_status_ref().and_then(|_| Ok(self))
391    }
392
393    // private
394
395    fn body_mut(&mut self) -> Pin<&mut dyn futures_util::io::AsyncRead> {
396        use futures_util::TryStreamExt;
397        if self.body.is_none() {
398            let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty());
399
400            let body = body.map_err(crate::error::into_io).into_async_read();
401
402            self.body = Some(Box::pin(body));
403        }
404        self.body.as_mut().expect("body was init").as_mut()
405    }
406}
407
408impl Read for Response {
409    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
410        use futures_util::io::AsyncReadExt;
411
412        let timeout = self.timeout;
413        wait::timeout(self.body_mut().read(buf), timeout).map_err(|e| match e {
414            wait::Waited::TimedOut(e) => crate::error::decode(e).into_io(),
415            wait::Waited::Inner(e) => e,
416        })
417    }
418}
419
420impl<T: Into<async_impl::body::Body>> From<http::Response<T>> for Response {
421    fn from(r: http::Response<T>) -> Response {
422        let response = async_impl::Response::from(r);
423        Response::new(response, None, KeepCoreThreadAlive::empty())
424    }
425}