reqwest_h3/wasm/
response.rs

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