wasm_framework/
response.rs

1use crate::Error;
2use bytes::Bytes;
3use serde::Serialize;
4use std::fmt;
5use wasm_bindgen::JsValue;
6
7/// Worker response for HTTP requests.
8/// The Response is created/accessed from `ctx.response()` and has a builder-like api.
9#[derive(Debug)]
10pub struct Response {
11    status: u16,
12    headers: Option<web_sys::Headers>,
13    body: Body,
14    unset: bool,
15}
16
17impl Default for Response {
18    fn default() -> Self {
19        Self {
20            status: 200,
21            headers: None,
22            body: Body::from(Bytes::new()),
23            unset: true,
24        }
25    }
26}
27
28impl Response {
29    /// Sets response status
30    pub fn status(&mut self, status: u16) -> &mut Self {
31        self.status = status;
32        self.unset = false;
33        self
34    }
35
36    /// Sets response body to the binary data
37    pub fn body<T: Into<Body>>(&mut self, body: T) -> &mut Self {
38        self.body = body.into();
39        self.unset = false;
40        self
41    }
42
43    /// Sets response body to value serialized as json, and sets content-type to application/json
44    pub fn json<T: Serialize>(&mut self, value: &T) -> Result<&mut Self, Error> {
45        use mime::APPLICATION_JSON;
46        self.body = serde_json::to_vec(value)?.into();
47        self.content_type(APPLICATION_JSON).unwrap();
48        self.unset = false;
49        Ok(self)
50    }
51
52    /// Sets response body to the text string, encoded as utf-8
53    pub fn text<T: Into<String>>(&mut self, text: T) -> &mut Self {
54        let str_val = text.into();
55        self.body = str_val.into();
56        self.unset = false;
57        self
58    }
59
60    /// Sets a header for this response
61    pub fn header<K: AsRef<str>, V: AsRef<str>>(
62        &mut self,
63        key: K,
64        val: V,
65    ) -> Result<&mut Self, Error> {
66        if self.headers.is_none() {
67            self.headers = Some(web_sys::Headers::new().unwrap());
68        }
69        if let Some(ref mut headers) = self.headers {
70            headers.set(key.as_ref(), val.as_ref())?;
71        }
72        Ok(self)
73    }
74
75    /// Sets response content type
76    pub fn content_type<T: AsRef<str>>(&mut self, ctype: T) -> Result<&mut Self, Error> {
77        self.header(reqwest::header::CONTENT_TYPE, ctype)?;
78        Ok(self)
79    }
80
81    /// Returns the status of this response
82    pub fn get_status(&self) -> u16 {
83        self.status
84    }
85
86    /// Returns body of this response.
87    pub fn get_body(&self) -> &[u8] {
88        &self.body.inner.as_ref()
89    }
90
91    /// Returns headers for this response, or None if no headers have been set
92    pub fn get_headers(&self) -> Option<&web_sys::Headers> {
93        self.headers.as_ref()
94    }
95
96    /// Returns true if the body is empty
97    pub fn is_empty(&self) -> bool {
98        self.body.is_empty()
99    }
100
101    /// Converts Response to JsValue
102    /// This is destructive to self (removes headers) and is used after
103    /// application request handling has completed.
104    pub(crate) fn into_js(mut self) -> JsValue {
105        let map = js_sys::Map::new();
106        map.set(
107            &JsValue::from_str("status"),
108            &JsValue::from_f64(self.status as f64),
109        );
110        map.set(
111            &JsValue::from_str("body"),
112            &js_sys::Uint8Array::from(self.body.inner.as_ref()),
113        );
114        if self.headers.is_some() {
115            let headers = std::mem::take(&mut self.headers).unwrap();
116            map.set(&JsValue::from_str("headers"), &JsValue::from(headers));
117        } else {
118            map.set(
119                &JsValue::from_str("headers"),
120                &JsValue::from(web_sys::Headers::new().unwrap()),
121            );
122        }
123        JsValue::from(map)
124    }
125
126    /// True if the response has not been filled in (none of status(), text() or body() has been
127    /// called). (even if status() is called with 200 status or body is set to empty)
128    /// This could be used as a flag for chained handlers to determine whether a previous
129    /// handler has filled in the response yet.
130    /// Setting headers (including content_type or user_agent) does not mark the request "set"
131    /// This is so that headers can be set at the top of a handler, before errors may occur
132    pub fn is_unset(&self) -> bool {
133        self.unset
134    }
135}
136
137/// The body of a `Response`.
138// this is adapted from reqwest::wasm::Body, which is used in requests
139pub struct Body {
140    inner: Bytes,
141}
142
143impl Body {
144    /// True if the body is empty
145    pub fn is_empty(&self) -> bool {
146        self.inner.is_empty()
147    }
148}
149
150impl From<Bytes> for Body {
151    #[inline]
152    fn from(bytes: Bytes) -> Body {
153        Body { inner: bytes }
154    }
155}
156
157impl From<Vec<u8>> for Body {
158    #[inline]
159    fn from(vec: Vec<u8>) -> Body {
160        Body { inner: vec.into() }
161    }
162}
163
164impl From<&'static [u8]> for Body {
165    #[inline]
166    fn from(s: &'static [u8]) -> Body {
167        Body {
168            inner: Bytes::from_static(s),
169        }
170    }
171}
172
173impl From<String> for Body {
174    #[inline]
175    fn from(s: String) -> Body {
176        Body { inner: s.into() }
177    }
178}
179
180impl From<&'static str> for Body {
181    #[inline]
182    fn from(s: &'static str) -> Body {
183        s.as_bytes().into()
184    }
185}
186
187impl fmt::Debug for Body {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        f.debug_struct("Body").finish()
190    }
191}