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