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