reqwest_wasm/wasm/
response.rs

1use std::fmt;
2
3use bytes::Bytes;
4use http::{HeaderMap, StatusCode};
5use js_sys::Uint8Array;
6use url::Url;
7
8#[cfg(feature = "stream")]
9use futures_util::stream::StreamExt;
10#[cfg(feature = "json")]
11use serde::de::DeserializeOwned;
12#[cfg(feature = "stream")]
13use wasm_bindgen::JsCast;
14#[cfg(feature = "stream")]
15use wasm_streams::ReadableStream;
16
17/// A Response to a submitted `Request`.
18pub struct Response {
19    http: http::Response<web_sys::Response>,
20    // Boxed to save space (11 words to 1 word), and it's not accessed
21    // frequently internally.
22    url: Box<Url>,
23}
24
25impl Response {
26    pub(super) fn new(res: http::Response<web_sys::Response>, url: Url) -> Response {
27        Response {
28            http: res,
29            url: Box::new(url),
30        }
31    }
32
33    /// Get the `StatusCode` of this `Response`.
34    #[inline]
35    pub fn status(&self) -> StatusCode {
36        self.http.status()
37    }
38
39    /// Get the `Headers` of this `Response`.
40    #[inline]
41    pub fn headers(&self) -> &HeaderMap {
42        self.http.headers()
43    }
44
45    /// Get a mutable reference to the `Headers` of this `Response`.
46    #[inline]
47    pub fn headers_mut(&mut self) -> &mut HeaderMap {
48        self.http.headers_mut()
49    }
50
51    /// Get the content-length of this response, if known.
52    ///
53    /// Reasons it may not be known:
54    ///
55    /// - The server didn't send a `content-length` header.
56    /// - The response is compressed and automatically decoded (thus changing
57    ///  the actual decoded length).
58    pub fn content_length(&self) -> Option<u64> {
59        self.headers()
60            .get(http::header::CONTENT_LENGTH)?
61            .to_str()
62            .ok()?
63            .parse()
64            .ok()
65    }
66
67    /// Get the final `Url` of this `Response`.
68    #[inline]
69    pub fn url(&self) -> &Url {
70        &self.url
71    }
72
73    /* It might not be possible to detect this in JS?
74    /// Get the HTTP `Version` of this `Response`.
75    #[inline]
76    pub fn version(&self) -> Version {
77        self.http.version()
78    }
79    */
80
81    /// Try to deserialize the response body as JSON.
82    #[cfg(feature = "json")]
83    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
84    pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
85        let full = self.bytes().await?;
86
87        serde_json::from_slice(&full).map_err(crate::error::decode)
88    }
89
90    /// Get the response text.
91    pub async fn text(self) -> crate::Result<String> {
92        let p = self
93            .http
94            .body()
95            .text()
96            .map_err(crate::error::wasm)
97            .map_err(crate::error::decode)?;
98        let js_val = super::promise::<wasm_bindgen::JsValue>(p)
99            .await
100            .map_err(crate::error::decode)?;
101        if let Some(s) = js_val.as_string() {
102            Ok(s)
103        } else {
104            Err(crate::error::decode("response.text isn't string"))
105        }
106    }
107
108    /// Get the response as bytes
109    pub async fn bytes(self) -> crate::Result<Bytes> {
110        let p = self
111            .http
112            .body()
113            .array_buffer()
114            .map_err(crate::error::wasm)
115            .map_err(crate::error::decode)?;
116
117        let buf_js = super::promise::<wasm_bindgen::JsValue>(p)
118            .await
119            .map_err(crate::error::decode)?;
120
121        let buffer = Uint8Array::new(&buf_js);
122        let mut bytes = vec![0; buffer.length() as usize];
123        buffer.copy_to(&mut bytes);
124        Ok(bytes.into())
125    }
126
127    /// Get the web_sys::ReadableStream of the response body.
128    #[cfg(feature = "stream")]
129    pub fn js_stream(self) -> web_sys::ReadableStream {
130        let web_response = self.http.into_body();
131
132        web_response
133            .body()
134            .expect("could not create wasm byte stream")
135    }
136
137    /// Convert the response into a `Stream` of `Bytes` from the body.
138    #[cfg(feature = "stream")]
139    pub fn bytes_stream(self) -> impl futures_core::Stream<Item = crate::Result<Bytes>> {
140        let body = self.js_stream();
141        let body = ReadableStream::from_raw(body.unchecked_into());
142        body.into_stream().map(|buf_js| {
143            let buffer = Uint8Array::new(
144                &buf_js
145                    .map_err(crate::error::wasm)
146                    .map_err(crate::error::decode)?,
147            );
148            let mut bytes = vec![0; buffer.length() as usize];
149            buffer.copy_to(&mut bytes);
150            Ok(bytes.into())
151        })
152    }
153
154    // util methods
155
156    /// Turn a response into an error if the server returned an error.
157    pub fn error_for_status(self) -> crate::Result<Self> {
158        let status = self.status();
159        if status.is_client_error() || status.is_server_error() {
160            Err(crate::error::status_code(*self.url, status))
161        } else {
162            Ok(self)
163        }
164    }
165
166    /// Turn a reference to a response into an error if the server returned an error.
167    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
168        let status = self.status();
169        if status.is_client_error() || status.is_server_error() {
170            Err(crate::error::status_code(*self.url.clone(), status))
171        } else {
172            Ok(self)
173        }
174    }
175}
176
177impl fmt::Debug for Response {
178    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179        f.debug_struct("Response")
180            //.field("url", self.url())
181            .field("status", &self.status())
182            .field("headers", self.headers())
183            .finish()
184    }
185}