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