reqwest_wasm/wasm/
body.rs

1#[cfg(feature = "multipart")]
2use super::multipart::Form;
3/// dox
4use bytes::Bytes;
5use js_sys::Uint8Array;
6use std::fmt;
7use wasm_bindgen::JsValue;
8
9/// The body of a `Request`.
10///
11/// In most cases, this is not needed directly, as the
12/// [`RequestBuilder.body`][builder] method uses `Into<Body>`, which allows
13/// passing many things (like a string or vector of bytes).
14///
15/// [builder]: ./struct.RequestBuilder.html#method.body
16pub struct Body {
17    inner: Inner,
18}
19
20enum Inner {
21    Bytes(Bytes),
22    /// MultipartForm holds a multipart/form-data body.
23    #[cfg(feature = "multipart")]
24    MultipartForm(Form),
25    /// MultipartPart holds the body of a multipart/form-data part.
26    #[cfg(feature = "multipart")]
27    MultipartPart(Bytes),
28}
29
30impl Body {
31    /// Returns a reference to the internal data of the `Body`.
32    ///
33    /// `None` is returned, if the underlying data is a multipart form.
34    #[inline]
35    pub fn as_bytes(&self) -> Option<&[u8]> {
36        match &self.inner {
37            Inner::Bytes(bytes) => Some(bytes.as_ref()),
38            #[cfg(feature = "multipart")]
39            Inner::MultipartForm(_) => None,
40            #[cfg(feature = "multipart")]
41            Inner::MultipartPart(bytes) => Some(bytes.as_ref()),
42        }
43    }
44    pub(crate) fn to_js_value(&self) -> crate::Result<JsValue> {
45        match &self.inner {
46            Inner::Bytes(body_bytes) => {
47                let body_bytes: &[u8] = body_bytes.as_ref();
48                let body_uint8_array: Uint8Array = body_bytes.into();
49                let js_value: &JsValue = body_uint8_array.as_ref();
50                Ok(js_value.to_owned())
51            }
52            #[cfg(feature = "multipart")]
53            Inner::MultipartForm(form) => {
54                let form_data = form.to_form_data()?;
55                let js_value: &JsValue = form_data.as_ref();
56                Ok(js_value.to_owned())
57            }
58            #[cfg(feature = "multipart")]
59            Inner::MultipartPart(body_bytes) => {
60                let body_bytes: &[u8] = body_bytes.as_ref();
61                let body_uint8_array: Uint8Array = body_bytes.into();
62                let body_array = js_sys::Array::new();
63                body_array.push(&body_uint8_array);
64                let js_value: &JsValue = body_array.as_ref();
65                Ok(js_value.to_owned())
66            }
67        }
68    }
69
70    #[inline]
71    #[cfg(feature = "multipart")]
72    pub(crate) fn from_form(f: Form) -> Body {
73        Self {
74            inner: Inner::MultipartForm(f),
75        }
76    }
77
78    /// into_part turns a regular body into the body of a mutlipart/form-data part.
79    #[cfg(feature = "multipart")]
80    pub(crate) fn into_part(self) -> Body {
81        match self.inner {
82            Inner::Bytes(bytes) => Self {
83                inner: Inner::MultipartPart(bytes),
84            },
85            Inner::MultipartForm(form) => Self {
86                inner: Inner::MultipartForm(form),
87            },
88            Inner::MultipartPart(bytes) => Self {
89                inner: Inner::MultipartPart(bytes),
90            },
91        }
92    }
93
94    pub(crate) fn is_empty(&self) -> bool {
95        match &self.inner {
96            Inner::Bytes(bytes) => bytes.is_empty(),
97            #[cfg(feature = "multipart")]
98            Inner::MultipartForm(form) => form.is_empty(),
99            #[cfg(feature = "multipart")]
100            Inner::MultipartPart(bytes) => bytes.is_empty(),
101        }
102    }
103
104    pub(crate) fn try_clone(&self) -> Option<Body> {
105        match &self.inner {
106            Inner::Bytes(bytes) => Some(Self {
107                inner: Inner::Bytes(bytes.clone()),
108            }),
109            #[cfg(feature = "multipart")]
110            Inner::MultipartForm(_) => None,
111            #[cfg(feature = "multipart")]
112            Inner::MultipartPart(bytes) => Some(Self {
113                inner: Inner::MultipartPart(bytes.clone()),
114            }),
115        }
116    }
117}
118
119impl From<Bytes> for Body {
120    #[inline]
121    fn from(bytes: Bytes) -> Body {
122        Body {
123            inner: Inner::Bytes(bytes),
124        }
125    }
126}
127
128impl From<Vec<u8>> for Body {
129    #[inline]
130    fn from(vec: Vec<u8>) -> Body {
131        Body {
132            inner: Inner::Bytes(vec.into()),
133        }
134    }
135}
136
137impl From<&'static [u8]> for Body {
138    #[inline]
139    fn from(s: &'static [u8]) -> Body {
140        Body {
141            inner: Inner::Bytes(Bytes::from_static(s)),
142        }
143    }
144}
145
146impl From<String> for Body {
147    #[inline]
148    fn from(s: String) -> Body {
149        Body {
150            inner: Inner::Bytes(s.into()),
151        }
152    }
153}
154
155impl From<&'static str> for Body {
156    #[inline]
157    fn from(s: &'static str) -> Body {
158        s.as_bytes().into()
159    }
160}
161
162impl fmt::Debug for Body {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        f.debug_struct("Body").finish()
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use crate::Body;
171    use js_sys::Uint8Array;
172    use wasm_bindgen::prelude::*;
173    use wasm_bindgen_test::*;
174
175    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
176
177    #[wasm_bindgen]
178    extern "C" {
179        // Use `js_namespace` here to bind `console.log(..)` instead of just
180        // `log(..)`
181        #[wasm_bindgen(js_namespace = console)]
182        fn log(s: String);
183    }
184
185    #[wasm_bindgen_test]
186    async fn test_body() {
187        let body = Body::from("TEST");
188        assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
189    }
190
191    #[wasm_bindgen_test]
192    async fn test_body_js_static_str() {
193        let body_value = "TEST";
194        let body = Body::from(body_value);
195
196        let mut init = web_sys::RequestInit::new();
197        init.method("POST");
198        init.body(Some(
199            body.to_js_value()
200                .expect("could not convert body to JsValue")
201                .as_ref(),
202        ));
203
204        let js_req = web_sys::Request::new_with_str_and_init("", &init)
205            .expect("could not create JS request");
206        let text_promise = js_req.text().expect("could not get text promise");
207        let text = crate::wasm::promise::<JsValue>(text_promise)
208            .await
209            .expect("could not get request body as text");
210
211        assert_eq!(text.as_string().expect("text is not a string"), body_value);
212    }
213    #[wasm_bindgen_test]
214    async fn test_body_js_string() {
215        let body_value = "TEST".to_string();
216        let body = Body::from(body_value.clone());
217
218        let mut init = web_sys::RequestInit::new();
219        init.method("POST");
220        init.body(Some(
221            body.to_js_value()
222                .expect("could not convert body to JsValue")
223                .as_ref(),
224        ));
225
226        let js_req = web_sys::Request::new_with_str_and_init("", &init)
227            .expect("could not create JS request");
228        let text_promise = js_req.text().expect("could not get text promise");
229        let text = crate::wasm::promise::<JsValue>(text_promise)
230            .await
231            .expect("could not get request body as text");
232
233        assert_eq!(text.as_string().expect("text is not a string"), body_value);
234    }
235
236    #[wasm_bindgen_test]
237    async fn test_body_js_static_u8_slice() {
238        let body_value: &'static [u8] = b"\x00\x42";
239        let body = Body::from(body_value);
240
241        let mut init = web_sys::RequestInit::new();
242        init.method("POST");
243        init.body(Some(
244            body.to_js_value()
245                .expect("could not convert body to JsValue")
246                .as_ref(),
247        ));
248
249        let js_req = web_sys::Request::new_with_str_and_init("", &init)
250            .expect("could not create JS request");
251
252        let array_buffer_promise = js_req
253            .array_buffer()
254            .expect("could not get array_buffer promise");
255        let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
256            .await
257            .expect("could not get request body as array buffer");
258
259        let v = Uint8Array::new(&array_buffer).to_vec();
260
261        assert_eq!(v, body_value);
262    }
263
264    #[wasm_bindgen_test]
265    async fn test_body_js_vec_u8() {
266        let body_value = vec![0u8, 42];
267        let body = Body::from(body_value.clone());
268
269        let mut init = web_sys::RequestInit::new();
270        init.method("POST");
271        init.body(Some(
272            body.to_js_value()
273                .expect("could not convert body to JsValue")
274                .as_ref(),
275        ));
276
277        let js_req = web_sys::Request::new_with_str_and_init("", &init)
278            .expect("could not create JS request");
279
280        let array_buffer_promise = js_req
281            .array_buffer()
282            .expect("could not get array_buffer promise");
283        let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
284            .await
285            .expect("could not get request body as array buffer");
286
287        let v = Uint8Array::new(&array_buffer).to_vec();
288
289        assert_eq!(v, body_value);
290    }
291}