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;
12use tokio::time::Sleep;
13use url::Url;
14
15use super::body::Body;
16use crate::async_impl::body::ResponseBody;
17#[cfg(feature = "cookies")]
18use crate::cookie;
19
20#[cfg(feature = "charset")]
21use encoding_rs::{Encoding, UTF_8};
22#[cfg(feature = "charset")]
23use mime::Mime;
24
25/// A Response to a submitted `Request`.
26pub struct Response {
27 pub(super) res: hyper::Response<ResponseBody>,
28 // Boxed to save space (11 words to 1 word), and it's not accessed
29 // frequently internally.
30 url: Box<Url>,
31}
32
33impl Response {
34 pub(super) fn new(
35 res: hyper::Response<ResponseBody>,
36 url: Url,
37 total_timeout: Option<Pin<Box<Sleep>>>,
38 read_timeout: Option<Duration>,
39 ) -> Response {
40 let (parts, body) = res.into_parts();
41 let res = hyper::Response::from_parts(
42 parts,
43 super::body::response(body, total_timeout, read_timeout),
44 );
45
46 Response {
47 res,
48 url: Box::new(url),
49 }
50 }
51
52 /// Get the `StatusCode` of this `Response`.
53 #[inline]
54 pub fn status(&self) -> StatusCode {
55 self.res.status()
56 }
57
58 /// Get the HTTP `Version` of this `Response`.
59 #[inline]
60 pub fn version(&self) -> Version {
61 self.res.version()
62 }
63
64 /// Get the `Headers` of this `Response`.
65 #[inline]
66 pub fn headers(&self) -> &HeaderMap {
67 self.res.headers()
68 }
69
70 /// Get a mutable reference to the `Headers` of this `Response`.
71 #[inline]
72 pub fn headers_mut(&mut self) -> &mut HeaderMap {
73 self.res.headers_mut()
74 }
75
76 /// Get the content length of the response, if it is known.
77 ///
78 /// This value does not directly represents the value of the `Content-Length`
79 /// header, but rather the size of the response's body. To read the header's
80 /// value, please use the [`Response::headers`] method instead.
81 ///
82 /// Reasons it may not be known:
83 ///
84 /// - The response does not include a body (e.g. it responds to a `HEAD`
85 /// request).
86 /// - The response is gzipped and automatically decoded (thus changing the
87 /// actual decoded length).
88 pub fn content_length(&self) -> Option<u64> {
89 use hyper::body::Body;
90
91 Body::size_hint(self.res.body()).exact()
92 }
93
94 /// Retrieve the cookies contained in the response.
95 ///
96 /// Note that invalid 'Set-Cookie' headers will be ignored.
97 ///
98 /// # Optional
99 ///
100 /// This requires the optional `cookies` feature to be enabled.
101 #[cfg(feature = "cookies")]
102 #[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
103 pub fn cookies(&self) -> impl Iterator<Item = cookie::Cookie<'_>> + '_ {
104 cookie::extract_response_cookies(self.res.headers()).filter_map(Result::ok)
105 }
106
107 /// Get the final `Url` of this `Response`.
108 #[inline]
109 pub fn url(&self) -> &Url {
110 &self.url
111 }
112
113 /// Get the remote address used to get this `Response`.
114 pub fn remote_addr(&self) -> Option<SocketAddr> {
115 self.res
116 .extensions()
117 .get::<HttpInfo>()
118 .map(|info| info.remote_addr())
119 }
120
121 /// Returns a reference to the associated extensions.
122 pub fn extensions(&self) -> &http::Extensions {
123 self.res.extensions()
124 }
125
126 /// Returns a mutable reference to the associated extensions.
127 pub fn extensions_mut(&mut self) -> &mut http::Extensions {
128 self.res.extensions_mut()
129 }
130
131 // body methods
132
133 /// Get the full response text.
134 ///
135 /// This method decodes the response body with BOM sniffing
136 /// and with malformed sequences replaced with the
137 /// [`char::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 [`char::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, url) = self.do_bytes().await?;
269
270 serde_json::from_slice(&full).map_err(|err| crate::error::decode(err).with_url(*url))
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 self.do_bytes().await.map(|(bytes, _)| bytes)
290 }
291
292 /// Stream a chunk of the response body.
293 ///
294 /// When the response body has been exhausted, this will return `None`.
295 ///
296 /// # Example
297 ///
298 /// ```
299 /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
300 /// let mut res = reqwest::get("https://hyper.rs").await?;
301 ///
302 /// while let Some(chunk) = res.chunk().await? {
303 /// println!("Chunk: {chunk:?}");
304 /// }
305 /// # Ok(())
306 /// # }
307 /// ```
308 pub async fn chunk(&mut self) -> crate::Result<Option<Bytes>> {
309 use http_body_util::BodyExt;
310
311 // loop to ignore unrecognized frames
312 loop {
313 if let Some(res) = self.res.body_mut().frame().await {
314 let frame = res.map_err(crate::error::decode)?;
315 if let Ok(buf) = frame.into_data() {
316 return Ok(Some(buf));
317 }
318 // else continue
319 } else {
320 return Ok(None);
321 }
322 }
323 }
324
325 /// Convert the response into a `Stream` of `Bytes` from the body.
326 ///
327 /// # Example
328 ///
329 /// ```
330 /// use futures_util::StreamExt;
331 ///
332 /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
333 /// let mut stream = reqwest::get("http://httpbin.org/ip")
334 /// .await?
335 /// .bytes_stream();
336 ///
337 /// while let Some(item) = stream.next().await {
338 /// println!("Chunk: {:?}", item?);
339 /// }
340 /// # Ok(())
341 /// # }
342 /// ```
343 ///
344 /// # Optional
345 ///
346 /// This requires the optional `stream` feature to be enabled.
347 #[cfg(feature = "stream")]
348 #[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
349 pub fn bytes_stream(self) -> impl futures_core::Stream<Item = crate::Result<Bytes>> {
350 http_body_util::BodyDataStream::new(self.res.into_body().map_err(crate::error::decode))
351 }
352
353 // util methods
354
355 /// Turn a response into an error if the server returned an error.
356 ///
357 /// # Example
358 ///
359 /// ```
360 /// # use reqwest::Response;
361 /// fn on_response(res: Response) {
362 /// match res.error_for_status() {
363 /// Ok(_res) => (),
364 /// Err(err) => {
365 /// // asserting a 400 as an example
366 /// // it could be any status between 400...599
367 /// assert_eq!(
368 /// err.status(),
369 /// Some(reqwest::StatusCode::BAD_REQUEST)
370 /// );
371 /// }
372 /// }
373 /// }
374 /// # fn main() {}
375 /// ```
376 pub fn error_for_status(self) -> crate::Result<Self> {
377 let status = self.status();
378 let reason = self.extensions().get::<hyper::ext::ReasonPhrase>().cloned();
379 if status.is_client_error() || status.is_server_error() {
380 Err(crate::error::status_code(*self.url, status, reason))
381 } else {
382 Ok(self)
383 }
384 }
385
386 /// Turn a reference to a response into an error if the server returned an error.
387 ///
388 /// # Example
389 ///
390 /// ```
391 /// # use reqwest::Response;
392 /// fn on_response(res: &Response) {
393 /// match res.error_for_status_ref() {
394 /// Ok(_res) => (),
395 /// Err(err) => {
396 /// // asserting a 400 as an example
397 /// // it could be any status between 400...599
398 /// assert_eq!(
399 /// err.status(),
400 /// Some(reqwest::StatusCode::BAD_REQUEST)
401 /// );
402 /// }
403 /// }
404 /// }
405 /// # fn main() {}
406 /// ```
407 pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
408 let status = self.status();
409 let reason = self.extensions().get::<hyper::ext::ReasonPhrase>().cloned();
410 if status.is_client_error() || status.is_server_error() {
411 Err(crate::error::status_code(*self.url.clone(), status, reason))
412 } else {
413 Ok(self)
414 }
415 }
416
417 // private
418
419 // The Response's body is an implementation detail.
420 // You no longer need to get a reference to it, there are async methods
421 // on the `Response` itself.
422 //
423 // This method is just used by the blocking API.
424 #[cfg(feature = "blocking")]
425 pub(crate) fn body_mut(&mut self) -> &mut ResponseBody {
426 self.res.body_mut()
427 }
428 async fn do_bytes(self) -> crate::Result<(Bytes, Box<Url>)> {
429 use http_body_util::BodyExt;
430
431 match BodyExt::collect(self.res.into_body()).await {
432 Ok(buf) => Ok((buf.to_bytes(), self.url)),
433 Err(err) => Err(crate::error::decode(err).with_url(*self.url)),
434 }
435 }
436}
437
438impl fmt::Debug for Response {
439 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440 f.debug_struct("Response")
441 .field("url", &self.url().as_str())
442 .field("status", &self.status())
443 .field("headers", self.headers())
444 .finish()
445 }
446}
447
448/// A `Response` can be piped as the `Body` of another request.
449impl From<Response> for Body {
450 fn from(r: Response) -> Body {
451 Body::wrap(r.res.into_body())
452 }
453}
454
455// I'm not sure this conversion is that useful... People should be encouraged
456// to use `http::Response`, not `reqwest::Response`.
457impl<T: Into<Body>> From<http::Response<T>> for Response {
458 fn from(r: http::Response<T>) -> Response {
459 use crate::response::ResponseUrl;
460
461 let (mut parts, body) = r.into_parts();
462 let body: crate::async_impl::body::Body = body.into();
463 let url = parts
464 .extensions
465 .remove::<ResponseUrl>()
466 .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap()));
467 let url = url.0;
468 let res = hyper::Response::from_parts(parts, ResponseBody::new(body.map_err(Into::into)));
469 Response {
470 res,
471 url: Box::new(url),
472 }
473 }
474}
475
476/// A `Response` can be converted into a `http::Response`.
477// It's supposed to be the inverse of the conversion above.
478impl From<Response> for http::Response<Body> {
479 fn from(r: Response) -> http::Response<Body> {
480 let (parts, body) = r.res.into_parts();
481 let body = Body::wrap(body);
482 http::Response::from_parts(parts, body)
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use super::Response;
489 use crate::ResponseBuilderExt;
490 use http::response::Builder;
491 use url::Url;
492
493 #[test]
494 fn test_from_http_response() {
495 let url = Url::parse("http://example.com").unwrap();
496 let response = Builder::new()
497 .status(200)
498 .url(url.clone())
499 .body("foo")
500 .unwrap();
501 let response = Response::from(response);
502
503 assert_eq!(response.status(), 200);
504 assert_eq!(*response.url(), url);
505 }
506}