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