wasm: fix standalone/multipart body conversion to JsValue (#1364)
This commit is contained in:
@@ -166,7 +166,8 @@ features = [
|
|||||||
"Blob",
|
"Blob",
|
||||||
"BlobPropertyBag",
|
"BlobPropertyBag",
|
||||||
"ServiceWorkerGlobalScope",
|
"ServiceWorkerGlobalScope",
|
||||||
"RequestCredentials"
|
"RequestCredentials",
|
||||||
|
"File"
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
|
|||||||
136
src/wasm/body.rs
136
src/wasm/body.rs
@@ -19,8 +19,12 @@ pub struct Body {
|
|||||||
|
|
||||||
enum Inner {
|
enum Inner {
|
||||||
Bytes(Bytes),
|
Bytes(Bytes),
|
||||||
|
/// MultipartForm holds a multipart/form-data body.
|
||||||
#[cfg(feature = "multipart")]
|
#[cfg(feature = "multipart")]
|
||||||
Multipart(Form),
|
MultipartForm(Form),
|
||||||
|
/// MultipartPart holds the body of a multipart/form-data part.
|
||||||
|
#[cfg(feature = "multipart")]
|
||||||
|
MultipartPart(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
@@ -32,7 +36,9 @@ impl Body {
|
|||||||
match &self.inner {
|
match &self.inner {
|
||||||
Inner::Bytes(bytes) => Some(bytes.as_ref()),
|
Inner::Bytes(bytes) => Some(bytes.as_ref()),
|
||||||
#[cfg(feature = "multipart")]
|
#[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> {
|
pub(crate) fn to_js_value(&self) -> crate::Result<JsValue> {
|
||||||
@@ -44,11 +50,20 @@ impl Body {
|
|||||||
Ok(js_value.to_owned())
|
Ok(js_value.to_owned())
|
||||||
}
|
}
|
||||||
#[cfg(feature = "multipart")]
|
#[cfg(feature = "multipart")]
|
||||||
Inner::Multipart(form) => {
|
Inner::MultipartForm(form) => {
|
||||||
let form_data = form.to_form_data()?;
|
let form_data = form.to_form_data()?;
|
||||||
let js_value: &JsValue = form_data.as_ref();
|
let js_value: &JsValue = form_data.as_ref();
|
||||||
Ok(js_value.to_owned())
|
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")]
|
#[cfg(feature = "multipart")]
|
||||||
pub(crate) fn from_form(f: Form) -> Body {
|
pub(crate) fn from_form(f: Form) -> Body {
|
||||||
Self {
|
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 {
|
match &self.inner {
|
||||||
Inner::Bytes(bytes) => bytes.is_empty(),
|
Inner::Bytes(bytes) => bytes.is_empty(),
|
||||||
#[cfg(feature = "multipart")]
|
#[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()),
|
inner: Inner::Bytes(bytes.clone()),
|
||||||
}),
|
}),
|
||||||
#[cfg(feature = "multipart")]
|
#[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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// use js_sys::{Array, Uint8Array};
|
use crate::Body;
|
||||||
|
use js_sys::Uint8Array;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_test::*;
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
@@ -146,16 +184,12 @@ mod tests {
|
|||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
async fn test_body() {
|
async fn test_body() {
|
||||||
use crate::Body;
|
|
||||||
|
|
||||||
let body = Body::from("TEST");
|
let body = Body::from("TEST");
|
||||||
assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
|
assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
async fn test_body_js() {
|
async fn test_body_js_static_str() {
|
||||||
use crate::Body;
|
|
||||||
|
|
||||||
let body_value = "TEST";
|
let body_value = "TEST";
|
||||||
let body = Body::from(body_value);
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ impl Part {
|
|||||||
fn new(value: Body) -> Part {
|
fn new(value: Body) -> Part {
|
||||||
Part {
|
Part {
|
||||||
meta: PartMetadata::new(),
|
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
|
// 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()?;
|
let js_value = self.value.to_js_value()?;
|
||||||
Blob::new_with_u8_array_sequence_and_options(&js_value, &properties)
|
Blob::new_with_u8_array_sequence_and_options(&js_value, &properties)
|
||||||
.map_err(crate::error::wasm)
|
.map_err(crate::error::wasm)
|
||||||
@@ -277,4 +277,84 @@ impl PartMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user