surf/response.rs
1use crate::http::{
2 self,
3 headers::{self, HeaderName, HeaderValues, ToHeaderValues},
4 Body, Error, Mime, StatusCode, Version,
5};
6
7use async_std::io::BufRead;
8use futures_util::io::AsyncRead;
9use serde::de::DeserializeOwned;
10
11use std::fmt;
12use std::io;
13use std::ops::Index;
14use std::pin::Pin;
15use std::task::{Context, Poll};
16
17pin_project_lite::pin_project! {
18 /// An HTTP response, returned by `Request`.
19 pub struct Response {
20 #[pin]
21 res: http_client::Response,
22 }
23}
24
25impl Response {
26 /// Create a new instance.
27 pub(crate) fn new(res: http_client::Response) -> Self {
28 Self { res }
29 }
30
31 /// Get the HTTP status code.
32 ///
33 /// # Examples
34 ///
35 /// ```no_run
36 /// # #[async_std::main]
37 /// # async fn main() -> surf::Result<()> {
38 /// let res = surf::get("https://httpbin.org/get").await?;
39 /// assert_eq!(res.status(), 200);
40 /// # Ok(()) }
41 /// ```
42 pub fn status(&self) -> StatusCode {
43 self.res.status()
44 }
45
46 /// Get the HTTP protocol version.
47 ///
48 /// # Examples
49 ///
50 /// ```no_run
51 /// # #[async_std::main]
52 /// # async fn main() -> surf::Result<()> {
53 /// use surf::http::Version;
54 ///
55 /// let res = surf::get("https://httpbin.org/get").await?;
56 /// assert_eq!(res.version(), Some(Version::Http1_1));
57 /// # Ok(()) }
58 /// ```
59 pub fn version(&self) -> Option<Version> {
60 self.res.version()
61 }
62
63 /// Get a header.
64 ///
65 /// # Examples
66 ///
67 /// ```no_run
68 /// # #[async_std::main]
69 /// # async fn main() -> surf::Result<()> {
70 /// let res = surf::get("https://httpbin.org/get").await?;
71 /// assert!(res.header("Content-Length").is_some());
72 /// # Ok(()) }
73 /// ```
74 pub fn header(&self, name: impl Into<HeaderName>) -> Option<&HeaderValues> {
75 self.res.header(name)
76 }
77
78 /// Get an HTTP header mutably.
79 pub fn header_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
80 self.res.header_mut(name)
81 }
82
83 /// Remove a header.
84 pub fn remove_header(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
85 self.res.remove_header(name)
86 }
87
88 /// Insert an HTTP header.
89 pub fn insert_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
90 self.res.insert_header(key, value);
91 }
92
93 /// Append an HTTP header.
94 pub fn append_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
95 self.res.append_header(key, value);
96 }
97
98 /// An iterator visiting all header pairs in arbitrary order.
99 #[must_use]
100 pub fn iter(&self) -> headers::Iter<'_> {
101 self.res.iter()
102 }
103
104 /// An iterator visiting all header pairs in arbitrary order, with mutable references to the
105 /// values.
106 #[must_use]
107 pub fn iter_mut(&mut self) -> headers::IterMut<'_> {
108 self.res.iter_mut()
109 }
110
111 /// An iterator visiting all header names in arbitrary order.
112 #[must_use]
113 pub fn header_names(&self) -> headers::Names<'_> {
114 self.res.header_names()
115 }
116
117 /// An iterator visiting all header values in arbitrary order.
118 #[must_use]
119 pub fn header_values(&self) -> headers::Values<'_> {
120 self.res.header_values()
121 }
122
123 /// Get a response scoped extension value.
124 #[must_use]
125 pub fn ext<T: Send + Sync + 'static>(&self) -> Option<&T> {
126 self.res.ext().get()
127 }
128
129 /// Set a response scoped extension value.
130 pub fn insert_ext<T: Send + Sync + 'static>(&mut self, val: T) {
131 self.res.ext_mut().insert(val);
132 }
133
134 /// Get the response content type as a `Mime`.
135 ///
136 /// Gets the `Content-Type` header and parses it to a `Mime` type.
137 ///
138 /// [Read more on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)
139 ///
140 /// # Panics
141 ///
142 /// This method will panic if an invalid MIME type was set as a header.
143 ///
144 /// # Examples
145 ///
146 /// ```no_run
147 /// # #[async_std::main]
148 /// # async fn main() -> surf::Result<()> {
149 /// use surf::http::mime;
150 /// let res = surf::get("https://httpbin.org/json").await?;
151 /// assert_eq!(res.content_type(), Some(mime::JSON));
152 /// # Ok(()) }
153 /// ```
154 pub fn content_type(&self) -> Option<Mime> {
155 self.res.content_type()
156 }
157
158 /// Get the length of the body stream, if it has been set.
159 ///
160 /// This value is set when passing a fixed-size object into as the body.
161 /// E.g. a string, or a buffer. Consumers of this API should check this
162 /// value to decide whether to use `Chunked` encoding, or set the
163 /// response length.
164 #[allow(clippy::len_without_is_empty)]
165 pub fn len(&self) -> Option<usize> {
166 self.res.len()
167 }
168
169 /// Returns `true` if the set length of the body stream is zero, `false`
170 /// otherwise.
171 pub fn is_empty(&self) -> Option<bool> {
172 self.res.is_empty()
173 }
174
175 /// Set the body reader.
176 pub fn set_body(&mut self, body: impl Into<Body>) {
177 self.res.set_body(body);
178 }
179
180 /// Take the response body as a `Body`.
181 ///
182 /// This method can be called after the body has already been taken or read,
183 /// but will return an empty `Body`.
184 ///
185 /// Useful for adjusting the whole body, such as in middleware.
186 pub fn take_body(&mut self) -> Body {
187 self.res.take_body()
188 }
189
190 /// Swaps the value of the body with another body, without deinitializing
191 /// either one.
192 pub fn swap_body(&mut self, body: &mut Body) {
193 self.res.swap_body(body)
194 }
195
196 /// Reads the entire request body into a byte buffer.
197 ///
198 /// This method can be called after the body has already been read, but will
199 /// produce an empty buffer.
200 ///
201 /// # Errors
202 ///
203 /// Any I/O error encountered while reading the body is immediately returned
204 /// as an `Err`.
205 ///
206 /// # Examples
207 ///
208 /// ```no_run
209 /// # #[async_std::main]
210 /// # async fn main() -> surf::Result<()> {
211 /// let mut res = surf::get("https://httpbin.org/get").await?;
212 /// let bytes: Vec<u8> = res.body_bytes().await?;
213 /// # Ok(()) }
214 /// ```
215 pub async fn body_bytes(&mut self) -> crate::Result<Vec<u8>> {
216 self.res.body_bytes().await
217 }
218
219 /// Reads the entire response body into a string.
220 ///
221 /// This method can be called after the body has already been read, but will
222 /// produce an empty buffer.
223 ///
224 /// # Encodings
225 ///
226 /// If the "encoding" feature is enabled, this method tries to decode the body
227 /// with the encoding that is specified in the Content-Type header. If the header
228 /// does not specify an encoding, UTF-8 is assumed. If the "encoding" feature is
229 /// disabled, Surf only supports reading UTF-8 response bodies. The "encoding"
230 /// feature is enabled by default.
231 ///
232 /// # Errors
233 ///
234 /// Any I/O error encountered while reading the body is immediately returned
235 /// as an `Err`.
236 ///
237 /// If the body cannot be interpreted because the encoding is unsupported or
238 /// incorrect, an `Err` is returned.
239 ///
240 /// # Examples
241 ///
242 /// ```no_run
243 /// # #[async_std::main]
244 /// # async fn main() -> surf::Result<()> {
245 /// let mut res = surf::get("https://httpbin.org/get").await?;
246 /// let string: String = res.body_string().await?;
247 /// # Ok(()) }
248 /// ```
249 pub async fn body_string(&mut self) -> crate::Result<String> {
250 let bytes = self.body_bytes().await?;
251 let mime = self.content_type();
252 let claimed_encoding = mime
253 .as_ref()
254 .and_then(|mime| mime.param("charset"))
255 .map(|name| name.to_string());
256 decode_body(bytes, claimed_encoding.as_deref())
257 }
258
259 /// Reads and deserialized the entire request body from json.
260 ///
261 /// # Errors
262 ///
263 /// Any I/O error encountered while reading the body is immediately returned
264 /// as an `Err`.
265 ///
266 /// If the body cannot be interpreted as valid json for the target type `T`,
267 /// an `Err` is returned.
268 ///
269 /// # Examples
270 ///
271 /// ```no_run
272 /// # use serde::{Deserialize, Serialize};
273 /// # #[async_std::main]
274 /// # async fn main() -> surf::Result<()> {
275 /// #[derive(Deserialize, Serialize)]
276 /// struct Ip {
277 /// ip: String
278 /// }
279 ///
280 /// let mut res = surf::get("https://api.ipify.org?format=json").await?;
281 /// let Ip { ip } = res.body_json().await?;
282 /// # Ok(()) }
283 /// ```
284 pub async fn body_json<T: DeserializeOwned>(&mut self) -> crate::Result<T> {
285 let body_bytes = self.body_bytes().await?;
286 Ok(serde_json::from_slice(&body_bytes).map_err(crate::Error::from)?)
287 }
288
289 /// Reads and deserialized the entire request body from form encoding.
290 ///
291 /// # Errors
292 ///
293 /// Any I/O error encountered while reading the body is immediately returned
294 /// as an `Err`.
295 ///
296 /// If the body cannot be interpreted as valid json for the target type `T`,
297 /// an `Err` is returned.
298 ///
299 /// # Examples
300 ///
301 /// ```no_run
302 /// # use serde::{Deserialize, Serialize};
303 /// # #[async_std::main]
304 /// # async fn main() -> surf::Result<()> {
305 /// #[derive(Deserialize, Serialize)]
306 /// struct Body {
307 /// apples: u32
308 /// }
309 ///
310 /// let mut res = surf::get("https://api.example.com/v1/response").await?;
311 /// let Body { apples } = res.body_form().await?;
312 /// # Ok(()) }
313 /// ```
314 pub async fn body_form<T: serde::de::DeserializeOwned>(&mut self) -> crate::Result<T> {
315 self.res.body_form().await
316 }
317}
318
319impl From<http::Response> for Response {
320 fn from(response: http::Response) -> Self {
321 Self::new(response)
322 }
323}
324
325#[allow(clippy::from_over_into)]
326impl Into<http::Response> for Response {
327 fn into(self) -> http::Response {
328 self.res
329 }
330}
331
332impl AsRef<http::Headers> for Response {
333 fn as_ref(&self) -> &http::Headers {
334 self.res.as_ref()
335 }
336}
337
338impl AsMut<http::Headers> for Response {
339 fn as_mut(&mut self) -> &mut http::Headers {
340 self.res.as_mut()
341 }
342}
343
344impl AsRef<http::Response> for Response {
345 fn as_ref(&self) -> &http::Response {
346 &self.res
347 }
348}
349
350impl AsMut<http::Response> for Response {
351 fn as_mut(&mut self) -> &mut http::Response {
352 &mut self.res
353 }
354}
355
356impl AsyncRead for Response {
357 #[allow(missing_doc_code_examples)]
358 fn poll_read(
359 mut self: Pin<&mut Self>,
360 cx: &mut Context<'_>,
361 buf: &mut [u8],
362 ) -> Poll<Result<usize, io::Error>> {
363 Pin::new(&mut self.res).poll_read(cx, buf)
364 }
365}
366
367impl BufRead for Response {
368 #[allow(missing_doc_code_examples)]
369 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
370 let this = self.project();
371 this.res.poll_fill_buf(cx)
372 }
373
374 fn consume(mut self: Pin<&mut Self>, amt: usize) {
375 Pin::new(&mut self.res).consume(amt)
376 }
377}
378
379impl fmt::Debug for Response {
380 #[allow(missing_doc_code_examples)]
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 f.debug_struct("Response")
383 .field("response", &self.res)
384 .finish()
385 }
386}
387
388impl Index<HeaderName> for Response {
389 type Output = HeaderValues;
390
391 /// Returns a reference to the value corresponding to the supplied name.
392 ///
393 /// # Panics
394 ///
395 /// Panics if the name is not present in `Response`.
396 #[inline]
397 fn index(&self, name: HeaderName) -> &HeaderValues {
398 &self.res[name]
399 }
400}
401
402impl Index<&str> for Response {
403 type Output = HeaderValues;
404
405 /// Returns a reference to the value corresponding to the supplied name.
406 ///
407 /// # Panics
408 ///
409 /// Panics if the name is not present in `Response`.
410 #[inline]
411 fn index(&self, name: &str) -> &HeaderValues {
412 &self.res[name]
413 }
414}
415
416/// An error occurred while decoding a response body to a string.
417///
418/// The error carries the encoding that was used to attempt to decode the body, and the raw byte
419/// contents of the body. This can be used to treat un-decodable bodies specially or to implement a
420/// fallback parsing strategy.
421#[derive(Clone)]
422pub struct DecodeError {
423 /// The name of the encoding that was used to try to decode the input.
424 pub encoding: String,
425 /// The input data as bytes.
426 pub data: Vec<u8>,
427}
428
429// Override debug output so you don't get each individual byte in `data` printed out separately,
430// because it can be many megabytes large. The actual content is not that interesting anyways
431// and can be accessed manually if it is required.
432impl fmt::Debug for DecodeError {
433 #[allow(missing_doc_code_examples)]
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 f.debug_struct("DecodeError")
436 .field("encoding", &self.encoding)
437 // Perhaps we can output the first N bytes of the response in the future
438 .field("data", &format!("{} bytes", self.data.len()))
439 .finish()
440 }
441}
442
443impl fmt::Display for DecodeError {
444 #[allow(missing_doc_code_examples)]
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 write!(f, "could not decode body as {}", &self.encoding)
447 }
448}
449
450impl std::error::Error for DecodeError {}
451
452/// Check if an encoding label refers to the UTF-8 encoding.
453#[allow(dead_code)]
454fn is_utf8_encoding(encoding_label: &str) -> bool {
455 encoding_label.eq_ignore_ascii_case("utf-8")
456 || encoding_label.eq_ignore_ascii_case("utf8")
457 || encoding_label.eq_ignore_ascii_case("unicode-1-1-utf-8")
458}
459
460/// Decode a response body as utf-8.
461///
462/// # Errors
463///
464/// If the body cannot be decoded as utf-8, this function returns an `std::io::Error` of kind
465/// `std::io::ErrorKind::InvalidData`, carrying a `DecodeError` struct.
466#[cfg(not(feature = "encoding"))]
467fn decode_body(bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
468 if is_utf8_encoding(content_encoding.unwrap_or("utf-8")) {
469 Ok(String::from_utf8(bytes).map_err(|err| {
470 let err = DecodeError {
471 encoding: "utf-8".to_string(),
472 data: err.into_bytes(),
473 };
474 io::Error::new(io::ErrorKind::InvalidData, err)
475 })?)
476 } else {
477 let err = DecodeError {
478 encoding: "utf-8".to_string(),
479 data: bytes,
480 };
481 Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
482 }
483}
484
485/// Decode a response body as the given content type.
486///
487/// If the input bytes are valid utf-8, this does not make a copy.
488///
489/// # Errors
490///
491/// If an unsupported encoding is requested, or the body does not conform to the requested
492/// encoding, this function returns an `std::io::Error` of kind `std::io::ErrorKind::InvalidData`,
493/// carrying a `DecodeError` struct.
494#[cfg(all(feature = "encoding", not(target_arch = "wasm32")))]
495fn decode_body(bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
496 use encoding_rs::Encoding;
497 use std::borrow::Cow;
498
499 let content_encoding = content_encoding.unwrap_or("utf-8");
500 if let Some(encoding) = Encoding::for_label(content_encoding.as_bytes()) {
501 let (decoded, encoding_used, failed) = encoding.decode(&bytes);
502 if failed {
503 let err = DecodeError {
504 encoding: encoding_used.name().into(),
505 data: bytes,
506 };
507 Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
508 } else {
509 Ok(match decoded {
510 // If encoding_rs returned a `Cow::Borrowed`, the bytes are guaranteed to be valid
511 // UTF-8, by virtue of being UTF-8 or being in the subset of ASCII that is the same
512 // in UTF-8.
513 Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
514 Cow::Owned(string) => string,
515 })
516 }
517 } else {
518 let err = DecodeError {
519 encoding: content_encoding.to_string(),
520 data: bytes,
521 };
522 Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
523 }
524}
525
526/// Decode a response body as the given content type.
527///
528/// This always makes a copy. (It could be optimized to avoid the copy if the encoding is utf-8.)
529///
530/// # Errors
531///
532/// If an unsupported encoding is requested, or the body does not conform to the requested
533/// encoding, this function returns an `std::io::Error` of kind `std::io::ErrorKind::InvalidData`,
534/// carrying a `DecodeError` struct.
535#[cfg(all(feature = "encoding", target_arch = "wasm32"))]
536fn decode_body(mut bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
537 use web_sys::TextDecoder;
538
539 // Encoding names are always valid ASCII, so we can avoid including casing mapping tables
540 let content_encoding = content_encoding.unwrap_or("utf-8").to_ascii_lowercase();
541 if is_utf8_encoding(&content_encoding) {
542 return String::from_utf8(bytes)
543 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err).into());
544 }
545
546 let decoder = TextDecoder::new_with_label(&content_encoding).unwrap();
547
548 Ok(decoder.decode_with_u8_array(&mut bytes).map_err(|_| {
549 let err = DecodeError {
550 encoding: content_encoding.to_string(),
551 data: bytes,
552 };
553 io::Error::new(io::ErrorKind::InvalidData, err)
554 })?)
555}
556
557#[cfg(test)]
558mod decode_tests {
559 use super::decode_body;
560
561 #[test]
562 fn utf8() {
563 let input = "Rød grød med fløde";
564 assert_eq!(
565 decode_body(input.as_bytes().to_vec(), Some("utf-8")).unwrap(),
566 input,
567 "Parses utf-8"
568 );
569 }
570
571 #[test]
572 fn default_utf8() {
573 let input = "Rød grød med fløde";
574 assert_eq!(
575 decode_body(input.as_bytes().to_vec(), None).unwrap(),
576 input,
577 "Defaults to utf-8"
578 );
579 }
580
581 #[test]
582 fn euc_kr() {
583 let input = vec![
584 0xb3, 0xbb, 0x20, 0xc7, 0xb0, 0xc0, 0xb8, 0xb7, 0xce, 0x20, 0xb5, 0xb9, 0xbe, 0xc6,
585 0xbf, 0xc0, 0xb6, 0xf3, 0x2c, 0x20, 0xb3, 0xbb, 0x20, 0xbe, 0xc8, 0xbf, 0xa1, 0xbc,
586 0xad, 0x20, 0xc0, 0xe1, 0xb5, 0xe9, 0xb0, 0xc5, 0xb6, 0xf3,
587 ];
588
589 let result = decode_body(input, Some("euc-kr"));
590 if cfg!(feature = "encoding") {
591 assert_eq!(result.unwrap(), "내 품으로 돌아오라, 내 안에서 잠들거라");
592 } else {
593 assert!(result.is_err(), "Only utf-8 is supported");
594 }
595 }
596}