wasm: fix standalone/multipart body conversion to JsValue (#1364)

This commit is contained in:
Niklas Wolber
2021-11-19 01:13:59 +01:00
committed by GitHub
parent ab49de875e
commit 0ef1a2ea78
3 changed files with 209 additions and 16 deletions

View File

@@ -166,7 +166,8 @@ features = [
"Blob",
"BlobPropertyBag",
"ServiceWorkerGlobalScope",
"RequestCredentials"
"RequestCredentials",
"File"
]
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]

View File

@@ -19,8 +19,12 @@ pub struct Body {
enum Inner {
Bytes(Bytes),
/// MultipartForm holds a multipart/form-data body.
#[cfg(feature = "multipart")]
Multipart(Form),
MultipartForm(Form),
/// MultipartPart holds the body of a multipart/form-data part.
#[cfg(feature = "multipart")]
MultipartPart(Bytes),
}
impl Body {
@@ -32,7 +36,9 @@ impl Body {
match &self.inner {
Inner::Bytes(bytes) => Some(bytes.as_ref()),
#[cfg(feature = "multipart")]
Inner::Multipart(_) => None,
Inner::MultipartForm(_) => None,
#[cfg(feature = "multipart")]
Inner::MultipartPart(bytes) => Some(bytes.as_ref()),
}
}
pub(crate) fn to_js_value(&self) -> crate::Result<JsValue> {
@@ -44,11 +50,20 @@ impl Body {
Ok(js_value.to_owned())
}
#[cfg(feature = "multipart")]
Inner::Multipart(form) => {
Inner::MultipartForm(form) => {
let form_data = form.to_form_data()?;
let js_value: &JsValue = form_data.as_ref();
Ok(js_value.to_owned())
}
#[cfg(feature = "multipart")]
Inner::MultipartPart(body_bytes) => {
let body_bytes: &[u8] = body_bytes.as_ref();
let body_uint8_array: Uint8Array = body_bytes.into();
let body_array = js_sys::Array::new();
body_array.push(&body_uint8_array);
let js_value: &JsValue = body_array.as_ref();
Ok(js_value.to_owned())
}
}
}
@@ -56,7 +71,23 @@ impl Body {
#[cfg(feature = "multipart")]
pub(crate) fn from_form(f: Form) -> Body {
Self {
inner: Inner::Multipart(f),
inner: Inner::MultipartForm(f),
}
}
/// into_part turns a regular body into the body of a mutlipart/form-data part.
#[cfg(feature = "multipart")]
pub(crate) fn into_part(self) -> Body {
match self.inner {
Inner::Bytes(bytes) => Self {
inner: Inner::MultipartPart(bytes),
},
Inner::MultipartForm(form) => Self {
inner: Inner::MultipartForm(form),
},
Inner::MultipartPart(bytes) => Self {
inner: Inner::MultipartPart(bytes),
},
}
}
@@ -64,7 +95,9 @@ impl Body {
match &self.inner {
Inner::Bytes(bytes) => bytes.is_empty(),
#[cfg(feature = "multipart")]
Inner::Multipart(form) => form.is_empty(),
Inner::MultipartForm(form) => form.is_empty(),
#[cfg(feature = "multipart")]
Inner::MultipartPart(bytes) => bytes.is_empty(),
}
}
@@ -74,7 +107,11 @@ impl Body {
inner: Inner::Bytes(bytes.clone()),
}),
#[cfg(feature = "multipart")]
Inner::Multipart(_) => None,
Inner::MultipartForm(_) => None,
#[cfg(feature = "multipart")]
Inner::MultipartPart(bytes) => Some(Self {
inner: Inner::MultipartPart(bytes.clone()),
}),
}
}
}
@@ -130,7 +167,8 @@ impl fmt::Debug for Body {
#[cfg(test)]
mod tests {
// use js_sys::{Array, Uint8Array};
use crate::Body;
use js_sys::Uint8Array;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
@@ -146,16 +184,12 @@ mod tests {
#[wasm_bindgen_test]
async fn test_body() {
use crate::Body;
let body = Body::from("TEST");
assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
}
#[wasm_bindgen_test]
async fn test_body_js() {
use crate::Body;
async fn test_body_js_static_str() {
let body_value = "TEST";
let body = Body::from(body_value);
@@ -176,4 +210,82 @@ mod tests {
assert_eq!(text.as_string().expect("text is not a string"), body_value);
}
#[wasm_bindgen_test]
async fn test_body_js_string() {
let body_value = "TEST".to_string();
let body = Body::from(body_value.clone());
let mut init = web_sys::RequestInit::new();
init.method("POST");
init.body(Some(
body.to_js_value()
.expect("could not convert body to JsValue")
.as_ref(),
));
let js_req = web_sys::Request::new_with_str_and_init("", &init)
.expect("could not create JS request");
let text_promise = js_req.text().expect("could not get text promise");
let text = crate::wasm::promise::<JsValue>(text_promise)
.await
.expect("could not get request body as text");
assert_eq!(text.as_string().expect("text is not a string"), body_value);
}
#[wasm_bindgen_test]
async fn test_body_js_static_u8_slice() {
let body_value: &'static [u8] = b"\x00\x42";
let body = Body::from(body_value);
let mut init = web_sys::RequestInit::new();
init.method("POST");
init.body(Some(
body.to_js_value()
.expect("could not convert body to JsValue")
.as_ref(),
));
let js_req = web_sys::Request::new_with_str_and_init("", &init)
.expect("could not create JS request");
let array_buffer_promise = js_req
.array_buffer()
.expect("could not get array_buffer promise");
let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
.await
.expect("could not get request body as array buffer");
let v = Uint8Array::new(&array_buffer).to_vec();
assert_eq!(v, body_value);
}
#[wasm_bindgen_test]
async fn test_body_js_vec_u8() {
let body_value = vec![0u8, 42];
let body = Body::from(body_value.clone());
let mut init = web_sys::RequestInit::new();
init.method("POST");
init.body(Some(
body.to_js_value()
.expect("could not convert body to JsValue")
.as_ref(),
));
let js_req = web_sys::Request::new_with_str_and_init("", &init)
.expect("could not create JS request");
let array_buffer_promise = js_req
.array_buffer()
.expect("could not get array_buffer promise");
let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
.await
.expect("could not get request body as array buffer");
let v = Uint8Array::new(&array_buffer).to_vec();
assert_eq!(v, body_value);
}
}

View File

@@ -150,7 +150,7 @@ impl Part {
fn new(value: Body) -> Part {
Part {
meta: PartMetadata::new(),
value,
value: value.into_part(),
}
}
@@ -191,7 +191,7 @@ impl Part {
}
// BUG: the return value of to_js_value() is not valid if
// it is a Multipart variant.
// it is a MultipartForm variant.
let js_value = self.value.to_js_value()?;
Blob::new_with_u8_array_sequence_and_options(&js_value, &properties)
.map_err(crate::error::wasm)
@@ -277,4 +277,84 @@ impl PartMetadata {
}
#[cfg(test)]
mod tests {}
mod tests {
use wasm_bindgen_test::*;
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
async fn test_multipart_js() {
use super::{Form, Part};
use js_sys::Uint8Array;
use wasm_bindgen::JsValue;
use web_sys::{File, FormData};
let text_file_name = "test.txt";
let text_file_type = "text/plain";
let text_content = "TEST";
let text_part = Part::text(text_content)
.file_name(text_file_name)
.mime_str(text_file_type)
.expect("invalid mime type");
let binary_file_name = "binary.bin";
let binary_file_type = "application/octet-stream";
let binary_content = vec![0u8, 42];
let binary_part = Part::bytes(binary_content.clone())
.file_name(binary_file_name)
.mime_str(binary_file_type)
.expect("invalid mime type");
let text_name = "text part";
let binary_name = "binary part";
let form = Form::new()
.part(text_name, text_part)
.part(binary_name, binary_part);
let mut init = web_sys::RequestInit::new();
init.method("POST");
init.body(Some(
form.to_form_data()
.expect("could not convert to FormData")
.as_ref(),
));
let js_req = web_sys::Request::new_with_str_and_init("", &init)
.expect("could not create JS request");
let form_data_promise = js_req.form_data().expect("could not get form_data promise");
let form_data = crate::wasm::promise::<FormData>(form_data_promise)
.await
.expect("could not get body as form data");
// check text part
let text_file = File::from(form_data.get(text_name));
assert_eq!(text_file.name(), text_file_name);
assert_eq!(text_file.type_(), text_file_type);
let text_promise = text_file.text();
let text = crate::wasm::promise::<JsValue>(text_promise)
.await
.expect("could not get text body as text");
assert_eq!(
text.as_string().expect("text is not a string"),
text_content
);
// check binary part
let binary_file = File::from(form_data.get(binary_name));
assert_eq!(binary_file.name(), binary_file_name);
assert_eq!(binary_file.type_(), binary_file_type);
let binary_array_buffer_promise = binary_file.array_buffer();
let array_buffer = crate::wasm::promise::<JsValue>(binary_array_buffer_promise)
.await
.expect("could not get request body as array buffer");
let binary = Uint8Array::new(&array_buffer).to_vec();
assert_eq!(binary, binary_content);
}
}