Skip to content

Commit 2811301

Browse files
wip: feat(wasm): add wasip2 component support
Signed-off-by: Brooks Townsend <[email protected]> wip: feat(wasm): add component feature Signed-off-by: Brooks Townsend <[email protected]>
1 parent 91131bf commit 2811301

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4639
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ features = [
2929
[features]
3030
default = ["default-tls", "charset", "http2", "macos-system-configuration"]
3131

32+
# TODO: use better target usage to remove need for feature
33+
wasm-component = []
3234
# Note: this doesn't enable the 'native-tls' feature, which adds specific
3335
# functionality for it.
3436
default-tls = ["dep:hyper-tls", "dep:native-tls-crate", "__tls", "dep:tokio-native-tls"]
@@ -109,6 +111,7 @@ sync_wrapper = "0.1.2"
109111
serde_json = { version = "1.0", optional = true }
110112
## multipart
111113
mime_guess = { version = "2.0", default-features = false, optional = true }
114+
wit-bindgen = "0.24.0"
112115

113116
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
114117
encoding_rs = { version = "0.8", optional = true }

src/wasm/component/body.rs

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

0 commit comments

Comments
 (0)