wasi_http_client/
response.rs

1use anyhow::{anyhow, Result};
2#[cfg(feature = "json")]
3use serde::de::DeserializeOwned;
4use std::collections::HashMap;
5use wasi::http::types::{IncomingBody, IncomingResponse};
6use wasi::io::streams::{InputStream, StreamError};
7
8pub struct Response {
9    status: u16,
10    headers: HashMap<String, String>,
11    // input-stream resource is a child: it must be dropped before the parent incoming-body is dropped
12    input_stream: InputStream,
13    _incoming_body: IncomingBody,
14}
15
16impl Response {
17    pub(crate) fn new(incoming_response: IncomingResponse) -> Result<Self> {
18        let status = incoming_response.status();
19
20        let mut headers: HashMap<String, String> = HashMap::new();
21        let headers_handle = incoming_response.headers();
22        for (key, value) in headers_handle.entries() {
23            headers.insert(key, String::from_utf8(value)?);
24        }
25        drop(headers_handle);
26
27        // The consume() method can only be called once
28        let incoming_body = incoming_response.consume().unwrap();
29        drop(incoming_response);
30
31        // The stream() method can only be called once
32        let input_stream = incoming_body.stream().unwrap();
33        Ok(Self {
34            status,
35            headers,
36            input_stream,
37            _incoming_body: incoming_body,
38        })
39    }
40
41    /// Get the status code of the response.
42    pub fn status(&self) -> u16 {
43        self.status
44    }
45
46    /// Get the headers of the response.
47    pub fn headers(&self) -> &HashMap<String, String> {
48        &self.headers
49    }
50
51    /// Get a chunk of the response body.
52    ///
53    /// It will block until at least one byte can be read or the stream is closed.
54    pub fn chunk(&self, len: u64) -> Result<Option<Vec<u8>>> {
55        match self.input_stream.blocking_read(len) {
56            Ok(c) => Ok(Some(c)),
57            Err(StreamError::Closed) => Ok(None),
58            Err(e) => Err(anyhow!("input_stream read failed: {e:?}"))?,
59        }
60    }
61
62    /// Get the full response body.
63    ///
64    /// It will block until the stream is closed.
65    pub fn body(self) -> Result<Vec<u8>> {
66        let mut body = Vec::new();
67        while let Some(mut chunk) = self.chunk(1024 * 1024)? {
68            body.append(&mut chunk);
69        }
70        Ok(body)
71    }
72
73    /// Deserialize the response body as JSON.
74    ///
75    /// # Optional
76    ///
77    /// This requires the `json` feature enabled.
78    ///
79    /// ```
80    /// # use anyhow::Result;
81    /// # use serde::Deserialize;
82    /// # use wasi_http_client::Client;
83    /// # fn run() -> Result<()> {
84    /// #[derive(Deserialize)]
85    /// struct Data {
86    ///     origin: String,
87    ///     url: String,
88    /// }
89    ///
90    /// let resp = Client::new().get("https://httpbin.org/get").send()?;
91    /// let json_data = resp.json::<Data>()?;
92    /// # Ok(())
93    /// # }
94    /// ```
95    #[cfg(feature = "json")]
96    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
97    pub fn json<T: DeserializeOwned>(self) -> Result<T> {
98        Ok(serde_json::from_slice(self.body()?.as_ref())?)
99    }
100}