[go: up one dir, main page]

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}