1 | use std::fmt; |
2 | use std::io::{self, Read}; |
3 | use std::mem; |
4 | use std::net::SocketAddr; |
5 | use std::pin::Pin; |
6 | use std::time::Duration; |
7 | |
8 | use bytes::Bytes; |
9 | use http; |
10 | use hyper::header::HeaderMap; |
11 | #[cfg (feature = "json" )] |
12 | use serde::de::DeserializeOwned; |
13 | |
14 | use super::client::KeepCoreThreadAlive; |
15 | use super::wait; |
16 | #[cfg (feature = "cookies" )] |
17 | use crate::cookie; |
18 | use crate::{async_impl, StatusCode, Url, Version}; |
19 | |
20 | /// A Response to a submitted `Request`. |
21 | pub 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 | |
28 | impl fmt::Debug for Response { |
29 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
30 | fmt::Debug::fmt(&self.inner, f) |
31 | } |
32 | } |
33 | |
34 | impl 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 | |
408 | impl 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 | |
420 | impl<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 | |