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 determined 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: Option = self.timeout;
413 wait::timeout(self.body_mut().read(buf), timeout).map_err(|e: Waited| match e {
414 wait::Waited::TimedOut(e: TimedOut) => crate::error::decode(e).into_io(),
415 wait::Waited::Inner(e: Error) => 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: Response = async_impl::Response::from(r);
423 Response::new(res:response, timeout:None, thread:KeepCoreThreadAlive::empty())
424 }
425}
426