golem_wasi_http/wasi/
response.rs

1use crate::Body;
2use bytes::Bytes;
3use encoding_rs::{Encoding, UTF_8};
4use http::header::CONTENT_LENGTH;
5use http::{Extensions, HeaderMap, StatusCode, Version};
6use mime::Mime;
7#[cfg(feature = "json")]
8use serde::de::DeserializeOwned;
9#[cfg(feature = "json")]
10use serde_json;
11use std::borrow::Cow;
12use std::io;
13use std::io::Read;
14use std::net::SocketAddr;
15use url::Url;
16
17use wasi::http::types::IncomingBody;
18use wasi::http::*;
19use wasi::io::streams::InputStream;
20
21/// A Response to a submitted `Request`.
22#[derive(Debug)]
23pub struct Response {
24    status: StatusCode,
25    headers: HeaderMap,
26    body: Option<Body>,
27    #[allow(dead_code)]
28    incoming_response: types::IncomingResponse,
29    url: Url,
30    extensions: Extensions,
31}
32
33impl Response {
34    pub(crate) fn new(
35        status: StatusCode,
36        headers: HeaderMap,
37        body: Body,
38        incoming_response: types::IncomingResponse,
39        url: Url,
40    ) -> Response {
41        Response {
42            status,
43            headers,
44            body: Some(body),
45            incoming_response,
46            url,
47            extensions: Extensions::default(),
48        }
49    }
50
51    /// Get the `StatusCode` of this `Response`.
52    #[inline]
53    pub fn status(&self) -> StatusCode {
54        self.status
55    }
56
57    /// Get the `Headers` of this `Response`.
58    #[inline]
59    pub fn headers(&self) -> &HeaderMap {
60        &self.headers
61    }
62
63    /// Get a mutable reference to the `Headers` of this `Response`.
64    #[inline]
65    pub fn headers_mut(&mut self) -> &mut HeaderMap {
66        &mut self.headers
67    }
68
69    /// Get the HTTP `Version` of this `Response`.
70    #[inline]
71    pub fn version(&self) -> Version {
72        Version::HTTP_11
73    }
74
75    /// Get the final `Url` of this `Response`.
76    #[inline]
77    pub fn url(&self) -> &Url {
78        &self.url
79    }
80
81    /// Get the remote address used to get this `Response`.
82    pub fn remote_addr(&self) -> Option<SocketAddr> {
83        None
84    }
85
86    /// Returns a reference to the associated extensions.
87    pub fn extensions(&self) -> &http::Extensions {
88        &self.extensions
89    }
90
91    /// Returns a mutable reference to the associated extensions.
92    pub fn extensions_mut(&mut self) -> &mut http::Extensions {
93        &mut self.extensions
94    }
95
96    /// Get the content-length of the response, if it is known.
97    ///
98    /// Reasons it may not be known:
99    ///
100    /// - The server didn't send a `content-length` header.
101    /// - The response is gzipped and automatically decoded (thus changing
102    ///   the actual decoded length).
103    pub fn content_length(&self) -> Option<u64> {
104        self.headers
105            .get(CONTENT_LENGTH)
106            .and_then(|ct_len| ct_len.to_str().ok())
107            .and_then(|ct_len| ct_len.parse().ok())
108    }
109
110    /// Try and deserialize the response body as JSON using `serde`.
111    ///
112    /// # Optional
113    ///
114    /// This requires the optional `json` feature enabled.
115    /// # Errors
116    ///
117    /// This method fails whenever the response body is not in JSON format
118    /// or it cannot be properly deserialized to target type `T`. For more
119    /// details please see [`serde_json::from_reader`].
120    ///
121    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
122    #[cfg(feature = "json")]
123    #[cfg(not(feature = "async"))]
124    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
125    pub fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
126        let full = self.bytes()?;
127        serde_json::from_slice(&full).map_err(crate::error::decode)
128    }
129
130    /// Try and deserialize the response body as JSON using `serde`.
131    ///
132    /// # Optional
133    ///
134    /// This requires the optional `json` feature enabled.
135    /// # Errors
136    ///
137    /// This method fails whenever the response body is not in JSON format
138    /// or it cannot be properly deserialized to target type `T`. For more
139    /// details please see [`serde_json::from_reader`].
140    ///
141    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
142    #[cfg(feature = "json")]
143    #[cfg(feature = "async")]
144    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
145    pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
146        let full = self.bytes().await?;
147        serde_json::from_slice(&full).map_err(crate::error::decode)
148    }
149
150    /// Get the full response body as `Bytes`.
151    #[cfg(not(feature = "async"))]
152    pub fn bytes(mut self) -> crate::Result<Bytes> {
153        let bytes: Bytes = Bytes::copy_from_slice(self.body.take().unwrap().buffer()?);
154        Ok(bytes)
155    }
156
157    /// Get the full response body as `Bytes`.
158    #[cfg(feature = "async")]
159    pub async fn bytes(mut self) -> crate::Result<Bytes> {
160        let bytes: Bytes = Bytes::copy_from_slice(self.body.take().unwrap().buffer().await?);
161        Ok(bytes)
162    }
163
164    /// Gets an async iterator yielding the response body in chunks.
165    #[cfg(feature = "async")]
166    pub fn async_chunks(
167        mut self,
168    ) -> impl async_iterator::Iterator<Item = Result<Vec<u8>, crate::Error>> {
169        let sync_reader = self.body.take().unwrap().into_reader();
170        let async_reader = sync_reader.into_async();
171        async_reader
172    }
173
174    /// Gets the underlying WASI response body stream. If the body was already consumed and/or buffered,
175    /// it fails with a panic.
176    pub fn get_raw_input_stream(&mut self) -> (InputStream, IncomingBody) {
177        let body = self.body.take().unwrap();
178        body.into_raw_input_stream()
179    }
180
181    /// Get the response text.
182    ///
183    /// This method decodes the response body with BOM sniffing
184    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
185    /// Encoding is determined from the `charset` parameter of `Content-Type` header,
186    /// and defaults to `utf-8` if not presented.
187    #[cfg(not(feature = "async"))]
188    pub fn text(self) -> crate::Result<String> {
189        self.text_with_charset("utf-8")
190    }
191
192    /// Get the response text.
193    ///
194    /// This method decodes the response body with BOM sniffing
195    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
196    /// Encoding is determined from the `charset` parameter of `Content-Type` header,
197    /// and defaults to `utf-8` if not presented.
198    #[cfg(feature = "async")]
199    pub async fn text(self) -> crate::Result<String> {
200        self.text_with_charset("utf-8").await
201    }
202
203    /// Get the response text given a specific encoding.
204    ///
205    /// This method decodes the response body with BOM sniffing
206    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
207    /// You can provide a default encoding for decoding the raw message, while the
208    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
209    /// about the possible encoding name, please go to [`encoding_rs`] docs.
210    ///
211    /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages
212    #[cfg(not(feature = "async"))]
213    pub fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
214        let content_type = self
215            .headers()
216            .get(crate::header::CONTENT_TYPE)
217            .and_then(|value| value.to_str().ok())
218            .and_then(|value| value.parse::<Mime>().ok());
219        let encoding_name = content_type
220            .as_ref()
221            .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
222            .unwrap_or(default_encoding);
223        let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
224
225        let full = self.bytes()?;
226
227        let (text, _, _) = encoding.decode(&full);
228        if let Cow::Owned(s) = text {
229            return Ok(s);
230        }
231        unsafe {
232            // decoding returned Cow::Borrowed, meaning these bytes
233            // are already valid utf8
234            Ok(String::from_utf8_unchecked(full.to_vec()))
235        }
236    }
237
238    /// Get the response text given a specific encoding.
239    ///
240    /// This method decodes the response body with BOM sniffing
241    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
242    /// You can provide a default encoding for decoding the raw message, while the
243    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
244    /// about the possible encoding name, please go to [`encoding_rs`] docs.
245    ///
246    /// [`encoding_rs`]: https://docs.rs/encoding_rs/0.8/encoding_rs/#relationship-with-windows-code-pages
247    #[cfg(feature = "async")]
248    pub async fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
249        let content_type = self
250            .headers()
251            .get(crate::header::CONTENT_TYPE)
252            .and_then(|value| value.to_str().ok())
253            .and_then(|value| value.parse::<Mime>().ok());
254        let encoding_name = content_type
255            .as_ref()
256            .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
257            .unwrap_or(default_encoding);
258        let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
259
260        let full = self.bytes().await?;
261
262        let (text, _, _) = encoding.decode(&full);
263        if let Cow::Owned(s) = text {
264            return Ok(s);
265        }
266        unsafe {
267            // decoding returned Cow::Borrowed, meaning these bytes
268            // are already valid utf8
269            Ok(String::from_utf8_unchecked(full.to_vec()))
270        }
271    }
272
273    /// Copy the response body into a writer.
274    ///
275    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
276    /// the body and then write it into writer in a streaming fashion until EOF is met.
277    ///
278    /// On success, the total number of bytes that were copied to `writer` is returned.
279    ///
280    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
281    pub fn copy_to<W: io::Write + ?Sized>(&mut self, w: &mut W) -> crate::Result<u64> {
282        io::copy(self, w).map_err(crate::error::decode_io)
283    }
284
285    /// Turn a response into an error if the server returned an error.
286    pub fn error_for_status(self) -> crate::Result<Self> {
287        let status = self.status();
288        if status.is_client_error() || status.is_server_error() {
289            Err(crate::error::status_code(self.url.clone(), status))
290        } else {
291            Ok(self)
292        }
293    }
294
295    /// Turn a reference to a response into an error if the server returned an error.
296    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
297        let status = self.status();
298        if status.is_client_error() || status.is_server_error() {
299            Err(crate::error::status_code(self.url.clone(), status))
300        } else {
301            Ok(self)
302        }
303    }
304}
305
306impl Read for Response {
307    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
308        self.body.take().unwrap().into_reader().read(buf)
309    }
310}