From 7d636e2d1f03581108cd61e9bdb87d4f1a040e43 Mon Sep 17 00:00:00 2001 From: Brooks Townsend Date: Thu, 15 Aug 2024 15:27:58 -0400 Subject: [PATCH] refactor(wasm): remove extra component implementations Signed-off-by: Brooks Townsend --- src/lib.rs | 2 +- src/wasm/component/body.rs | 174 +------- src/wasm/component/client/future.rs | 11 +- .../component/{client.rs => client/mod.rs} | 75 +--- src/wasm/component/mod.rs | 2 - src/wasm/component/multipart.rs | 419 ------------------ src/wasm/component/request.rs | 86 +--- src/wasm/component/response.rs | 21 +- 8 files changed, 35 insertions(+), 755 deletions(-) rename src/wasm/component/{client.rs => client/mod.rs} (84%) delete mode 100644 src/wasm/component/multipart.rs diff --git a/src/lib.rs b/src/lib.rs index cf3d39d0f..73b40b331 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -372,6 +372,6 @@ if_wasm! { mod util; pub use self::wasm::{Body, Client, ClientBuilder, Request, RequestBuilder, Response}; - #[cfg(feature = "multipart")] + #[cfg(all(not(all(target_os = "wasi", target_env = "p2")), feature = "multipart"))] pub use self::wasm::multipart; } diff --git a/src/wasm/component/body.rs b/src/wasm/component/body.rs index 14315a83a..3bfabadba 100644 --- a/src/wasm/component/body.rs +++ b/src/wasm/component/body.rs @@ -1,25 +1,13 @@ -#[cfg(feature = "multipart")] -use super::multipart::Form; -/// dox use bytes::Bytes; use std::{borrow::Cow, fmt}; -/// The body of a `Request`. -/// -/// In most cases, this is not needed directly, as the -/// [`RequestBuilder.body`][builder] method uses `Into`, which allows -/// passing many things (like a string or vector of bytes). -/// -/// [builder]: ./struct.RequestBuilder.html#method.body +/// The body of a [`super::Request`]. pub struct Body { inner: Inner, } enum Inner { Single(Single), - /// MultipartForm holds a multipart/form-data body. - #[cfg(feature = "multipart")] - MultipartForm(Form), } #[derive(Clone)] @@ -52,37 +40,6 @@ impl Body { pub fn as_bytes(&self) -> Option<&[u8]> { match &self.inner { Inner::Single(single) => Some(single.as_bytes()), - #[cfg(feature = "multipart")] - Inner::MultipartForm(_) => None, - } - } - - #[cfg(feature = "multipart")] - pub(crate) fn as_single(&self) -> Option<&Single> { - match &self.inner { - Inner::Single(single) => Some(single), - Inner::MultipartForm(_) => None, - } - } - - #[inline] - #[cfg(feature = "multipart")] - pub(crate) fn from_form(f: Form) -> Body { - Self { - inner: Inner::MultipartForm(f), - } - } - - /// into_part turns a regular body into the body of a multipart/form-data part. - #[cfg(feature = "multipart")] - pub(crate) fn into_part(self) -> Body { - match self.inner { - Inner::Single(single) => Self { - inner: Inner::Single(single), - }, - Inner::MultipartForm(form) => Self { - inner: Inner::MultipartForm(form), - }, } } @@ -90,8 +47,6 @@ impl Body { pub(crate) fn is_empty(&self) -> bool { match &self.inner { Inner::Single(single) => single.is_empty(), - #[cfg(feature = "multipart")] - Inner::MultipartForm(form) => form.is_empty(), } } @@ -100,8 +55,6 @@ impl Body { Inner::Single(single) => Some(Self { inner: Inner::Single(single.clone()), }), - #[cfg(feature = "multipart")] - Inner::MultipartForm(_) => None, } } } @@ -156,128 +109,3 @@ impl fmt::Debug for Body { f.debug_struct("Body").finish() } } - -#[cfg(test)] -mod tests { - // use crate::Body; - // use js_sys::Uint8Array; - // use wasm_bindgen::prelude::*; - // use wasm_bindgen_test::*; - - // wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - - // #[wasm_bindgen] - // extern "C" { - // // Use `js_namespace` here to bind `console.log(..)` instead of just - // // `log(..)` - // #[wasm_bindgen(js_namespace = console)] - // fn log(s: String); - // } - - // #[wasm_bindgen_test] - // async fn test_body() { - // let body = Body::from("TEST"); - // assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap()); - // } - - // #[wasm_bindgen_test] - // async fn test_body_js_static_str() { - // let body_value = "TEST"; - // 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 text_promise = js_req.text().expect("could not get text promise"); - // let text = crate::wasm::promise::(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_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::(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::(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::(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); - // } -} diff --git a/src/wasm/component/client/future.rs b/src/wasm/component/client/future.rs index 8fb67ee02..73b92263e 100644 --- a/src/wasm/component/client/future.rs +++ b/src/wasm/component/client/future.rs @@ -4,16 +4,15 @@ use std::{ }; use futures_core::Future; -use wasi::{ - self, - http::{ - outgoing_handler::{FutureIncomingResponse, OutgoingRequest}, - types::{OutgoingBody, OutputStream}, - }, +use wasi::http::{ + outgoing_handler::{FutureIncomingResponse, OutgoingRequest}, + types::{OutgoingBody, OutputStream}, }; use crate::{Body, Request, Response}; +/// A [`Future`] implementation for a [`Response`] that uses the [`wasi::io::poll`] +/// primitives to poll receipt of the HTTP response. #[derive(Debug)] pub struct ResponseFuture { request: Request, diff --git a/src/wasm/component/client.rs b/src/wasm/component/client/mod.rs similarity index 84% rename from src/wasm/component/client.rs rename to src/wasm/component/client/mod.rs index 2e62dfdff..c0ce95d59 100644 --- a/src/wasm/component/client.rs +++ b/src/wasm/component/client/mod.rs @@ -1,6 +1,6 @@ #![allow(warnings)] -use http::header::{CONTENT_LENGTH, USER_AGENT}; +use http::header::{Entry, CONTENT_LENGTH, USER_AGENT}; use http::{HeaderMap, HeaderValue, Method}; use std::any::Any; use std::convert::TryInto; @@ -8,34 +8,33 @@ use std::pin::Pin; use std::task::{ready, Context, Poll}; use std::{fmt, future::Future, sync::Arc}; -use self::future::ResponseFuture; - -use super::{Request, RequestBuilder, Response}; -use crate::Body; -use crate::IntoUrl; -use wasi::http::outgoing_handler::{self, OutgoingRequest}; +use crate::wasm::component::{Request, RequestBuilder, Response}; +use crate::{Body, IntoUrl}; +use wasi::http::outgoing_handler::OutgoingRequest; use wasi::http::types::{FutureIncomingResponse, OutgoingBody, OutputStream, Pollable}; mod future; +use future::ResponseFuture; -/// dox -#[derive(Clone)] +/// A client for making HTTP requests. +#[derive(Default, Debug, Clone)] pub struct Client { config: Arc, } -/// dox +/// A builder to configure a [`Client`]. +#[derive(Default, Debug)] pub struct ClientBuilder { config: Config, } impl Client { - /// Constructs a new `Client`. + /// Constructs a new [`Client`]. pub fn new() -> Self { Client::builder().build().expect("Client::new()") } - /// dox + /// Constructs a new [`ClientBuilder`]. pub fn builder() -> ClientBuilder { ClientBuilder::new() } @@ -123,12 +122,10 @@ impl Client { self.execute_request(request) } - // merge request headers with Client default_headers, prior to external http fetch - fn merge_headers(&self, req: &mut Request) { - use http::header::Entry; + /// Merge [`Request`] headers with default headers set in [`Config`] + fn merge_default_headers(&self, req: &mut Request) { let headers: &mut HeaderMap = req.headers_mut(); - // insert default headers in the request headers - // without overwriting already appended headers. + // Insert without overwriting existing headers for (key, value) in self.config.headers.iter() { if let Entry::Vacant(entry) = headers.entry(key) { entry.insert(value.clone()); @@ -137,37 +134,14 @@ impl Client { } pub(super) fn execute_request(&self, mut req: Request) -> crate::Result { - self.merge_headers(&mut req); + self.merge_default_headers(&mut req); fetch(req) } } -impl Default for Client { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for Client { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut builder = f.debug_struct("Client"); - self.config.fmt_fields(&mut builder); - builder.finish() - } -} - -impl fmt::Debug for ClientBuilder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut builder = f.debug_struct("ClientBuilder"); - self.config.fmt_fields(&mut builder); - builder.finish() - } -} - fn fetch(req: Request) -> crate::Result { let headers = wasi::http::types::Fields::new(); for (name, value) in req.headers() { - // TODO: see if we can avoid the extra allocation headers .append(&name.to_string(), &value.as_bytes().to_vec()) .map_err(crate::error::builder)?; @@ -232,8 +206,6 @@ fn fetch(req: Request) -> crate::Result { ResponseFuture::new(req, outgoing_request) } -// ===== impl ClientBuilder ===== - impl ClientBuilder { /// Return a new `ClientBuilder`. pub fn new() -> Self { @@ -280,27 +252,12 @@ impl ClientBuilder { } } -impl Default for ClientBuilder { - fn default() -> Self { - Self::new() - } -} - -#[derive(Debug)] +#[derive(Default, Debug)] struct Config { headers: HeaderMap, error: Option, } -impl Default for Config { - fn default() -> Config { - Config { - headers: HeaderMap::new(), - error: None, - } - } -} - impl Config { fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) { f.field("default_headers", &self.headers); diff --git a/src/wasm/component/mod.rs b/src/wasm/component/mod.rs index 3ddeadc64..e46333cde 100644 --- a/src/wasm/component/mod.rs +++ b/src/wasm/component/mod.rs @@ -1,7 +1,5 @@ mod body; mod client; -#[cfg(feature = "multipart")] -pub mod multipart; mod request; mod response; diff --git a/src/wasm/component/multipart.rs b/src/wasm/component/multipart.rs deleted file mode 100644 index 9b5b4c951..000000000 --- a/src/wasm/component/multipart.rs +++ /dev/null @@ -1,419 +0,0 @@ -//! multipart/form-data -use std::borrow::Cow; -use std::fmt; - -use http::HeaderMap; -use mime_guess::Mime; -use web_sys::FormData; - -use super::Body; - -/// An async multipart/form-data request. -pub struct Form { - inner: FormParts, -} - -impl Form { - pub(crate) fn is_empty(&self) -> bool { - self.inner.fields.is_empty() - } -} - -/// A field in a multipart form. -pub struct Part { - meta: PartMetadata, - value: Body, -} - -pub(crate) struct FormParts

{ - pub(crate) fields: Vec<(Cow<'static, str>, P)>, -} - -pub(crate) struct PartMetadata { - mime: Option, - file_name: Option>, - pub(crate) headers: HeaderMap, -} - -pub(crate) trait PartProps { - fn metadata(&self) -> &PartMetadata; -} - -// ===== impl Form ===== - -impl Default for Form { - fn default() -> Self { - Self::new() - } -} - -impl Form { - /// Creates a new async Form without any content. - pub fn new() -> Form { - Form { - inner: FormParts::new(), - } - } - - /// Add a data field with supplied name and value. - /// - /// # Examples - /// - /// ``` - /// let form = reqwest::multipart::Form::new() - /// .text("username", "seanmonstar") - /// .text("password", "secret"); - /// ``` - pub fn text(self, name: T, value: U) -> Form - where - T: Into>, - U: Into>, - { - self.part(name, Part::text(value)) - } - - /// Adds a customized Part. - pub fn part(self, name: T, part: Part) -> Form - where - T: Into>, - { - self.with_inner(move |inner| inner.part(name, part)) - } - - fn with_inner(self, func: F) -> Self - where - F: FnOnce(FormParts) -> FormParts, - { - Form { - inner: func(self.inner), - } - } - - pub(crate) fn to_form_data(&self) -> crate::Result { - let form = FormData::new() - .map_err(crate::error::wasm) - .map_err(crate::error::builder)?; - - for (name, part) in self.inner.fields.iter() { - part.append_to_form(name, &form) - .map_err(crate::error::wasm) - .map_err(crate::error::builder)?; - } - Ok(form) - } -} - -impl fmt::Debug for Form { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt_fields("Form", f) - } -} - -// ===== impl Part ===== - -impl Part { - /// Makes a text parameter. - pub fn text(value: T) -> Part - where - T: Into>, - { - let body = match value.into() { - Cow::Borrowed(slice) => Body::from(slice), - Cow::Owned(string) => Body::from(string), - }; - Part::new(body) - } - - /// Makes a new parameter from arbitrary bytes. - pub fn bytes(value: T) -> Part - where - T: Into>, - { - let body = match value.into() { - Cow::Borrowed(slice) => Body::from(slice), - Cow::Owned(vec) => Body::from(vec), - }; - Part::new(body) - } - - /// Makes a new parameter from an arbitrary stream. - pub fn stream>(value: T) -> Part { - Part::new(value.into()) - } - - fn new(value: Body) -> Part { - Part { - meta: PartMetadata::new(), - value: value.into_part(), - } - } - - /// Tries to set the mime of this part. - pub fn mime_str(self, mime: &str) -> crate::Result { - Ok(self.mime(mime.parse().map_err(crate::error::builder)?)) - } - - // Re-export when mime 0.4 is available, with split MediaType/MediaRange. - fn mime(self, mime: Mime) -> Part { - self.with_inner(move |inner| inner.mime(mime)) - } - - /// Sets the filename, builder style. - pub fn file_name(self, filename: T) -> Part - where - T: Into>, - { - self.with_inner(move |inner| inner.file_name(filename)) - } - - /// Sets custom headers for the part. - pub fn headers(self, headers: HeaderMap) -> Part { - self.with_inner(move |inner| inner.headers(headers)) - } - - fn with_inner(self, func: F) -> Self - where - F: FnOnce(PartMetadata) -> PartMetadata, - { - Part { - meta: func(self.meta), - value: self.value, - } - } - - fn append_to_form( - &self, - name: &str, - form: &web_sys::FormData, - ) -> Result<(), wasm_bindgen::JsValue> { - let single = self - .value - .as_single() - .expect("A part's body can't be multipart itself"); - - let mut mime_type = self.metadata().mime.as_ref(); - - // The JS fetch API doesn't support file names and mime types for strings. So we do our best - // effort to use `append_with_str` and fallback to `append_with_blob_*` if that's not - // possible. - if let super::body::Single::Text(text) = single { - if mime_type.is_none() || mime_type == Some(&mime_guess::mime::TEXT_PLAIN) { - if self.metadata().file_name.is_none() { - return form.append_with_str(name, text); - } - } else { - mime_type = Some(&mime_guess::mime::TEXT_PLAIN); - } - } - - let blob = self.blob(mime_type)?; - - if let Some(file_name) = &self.metadata().file_name { - form.append_with_blob_and_filename(name, &blob, file_name) - } else { - form.append_with_blob(name, &blob) - } - } - - fn blob(&self, mime_type: Option<&Mime>) -> crate::Result { - use web_sys::Blob; - use web_sys::BlobPropertyBag; - let mut properties = BlobPropertyBag::new(); - if let Some(mime) = mime_type { - properties.type_(mime.as_ref()); - } - - let js_value = self - .value - .as_single() - .expect("A part's body can't be set to a multipart body") - .to_js_value(); - - let body_array = js_sys::Array::new(); - body_array.push(&js_value); - - Blob::new_with_u8_array_sequence_and_options(body_array.as_ref(), &properties) - .map_err(crate::error::wasm) - .map_err(crate::error::builder) - } -} - -impl fmt::Debug for Part { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut dbg = f.debug_struct("Part"); - dbg.field("value", &self.value); - self.meta.fmt_fields(&mut dbg); - dbg.finish() - } -} - -impl PartProps for Part { - fn metadata(&self) -> &PartMetadata { - &self.meta - } -} - -// ===== impl FormParts ===== - -impl FormParts

{ - pub(crate) fn new() -> Self { - FormParts { fields: Vec::new() } - } - - /// Adds a customized Part. - pub(crate) fn part(mut self, name: T, part: P) -> Self - where - T: Into>, - { - self.fields.push((name.into(), part)); - self - } -} - -impl FormParts

{ - pub(crate) fn fmt_fields(&self, ty_name: &'static str, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct(ty_name) - .field("parts", &self.fields) - .finish() - } -} - -// ===== impl PartMetadata ===== - -impl PartMetadata { - pub(crate) fn new() -> Self { - PartMetadata { - mime: None, - file_name: None, - headers: HeaderMap::default(), - } - } - - pub(crate) fn mime(mut self, mime: Mime) -> Self { - self.mime = Some(mime); - self - } - - pub(crate) fn file_name(mut self, filename: T) -> Self - where - T: Into>, - { - self.file_name = Some(filename.into()); - self - } - - pub(crate) fn headers(mut self, headers: T) -> Self - where - T: Into, - { - self.headers = headers.into(); - self - } -} - -impl PartMetadata { - pub(crate) fn fmt_fields<'f, 'fa, 'fb>( - &self, - debug_struct: &'f mut fmt::DebugStruct<'fa, 'fb>, - ) -> &'f mut fmt::DebugStruct<'fa, 'fb> { - debug_struct - .field("mime", &self.mime) - .field("file_name", &self.file_name) - .field("headers", &self.headers) - } -} - -#[cfg(test)] -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 string_name = "string"; - let string_content = "CONTENT"; - let string_part = Part::text(string_content); - - let text_name = "text part"; - let binary_name = "binary part"; - let form = Form::new() - .part(text_name, text_part) - .part(binary_name, binary_part) - .part(string_name, string_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::(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::(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); - - // check string part - let string = form_data - .get(string_name) - .as_string() - .expect("content is not a string"); - assert_eq!(string, string_content); - - let binary_array_buffer_promise = binary_file.array_buffer(); - let array_buffer = crate::wasm::promise::(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); - } -} diff --git a/src/wasm/component/request.rs b/src/wasm/component/request.rs index d122fdae0..1c1e442ae 100644 --- a/src/wasm/component/request.rs +++ b/src/wasm/component/request.rs @@ -7,10 +7,10 @@ use serde::Serialize; #[cfg(feature = "json")] use serde_json; use url::Url; -use web_sys::RequestCredentials; -use super::{Body, Client, Response}; +use super::{Client, Response}; use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; +use crate::Body; /// A request which can be executed with `Client::execute()`. pub struct Request { @@ -18,8 +18,6 @@ pub struct Request { url: Url, headers: HeaderMap, body: Option, - pub(super) cors: bool, - pub(super) credentials: Option, } /// A builder to construct the properties of a `Request`. @@ -37,8 +35,6 @@ impl Request { url, headers: HeaderMap::new(), body: None, - cors: true, - credentials: None, } } @@ -104,8 +100,6 @@ impl Request { url: self.url.clone(), headers: self.headers.clone(), body, - cors: self.cors, - credentials: self.credentials, }) } } @@ -241,16 +235,6 @@ impl RequestBuilder { self } - /// TODO - #[cfg(feature = "multipart")] - #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))] - pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder { - if let Ok(ref mut req) = self.request { - *req.body_mut() = Some(Body::from_form(multipart)) - } - self - } - /// Add a `Header` to this Request. pub fn header(mut self, key: K, value: V) -> RequestBuilder where @@ -287,70 +271,6 @@ impl RequestBuilder { self } - /// Disable CORS on fetching the request. - /// - /// # WASM - /// - /// This option is only effective with WebAssembly target. - /// - /// The [request mode][mdn] will be set to 'no-cors'. - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/mode - pub fn fetch_mode_no_cors(mut self) -> RequestBuilder { - if let Ok(ref mut req) = self.request { - req.cors = false; - } - self - } - - /// Set fetch credentials to 'same-origin' - /// - /// # WASM - /// - /// This option is only effective with WebAssembly target. - /// - /// The [request credentials][mdn] will be set to 'same-origin'. - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials - pub fn fetch_credentials_same_origin(mut self) -> RequestBuilder { - if let Ok(ref mut req) = self.request { - req.credentials = Some(RequestCredentials::SameOrigin); - } - self - } - - /// Set fetch credentials to 'include' - /// - /// # WASM - /// - /// This option is only effective with WebAssembly target. - /// - /// The [request credentials][mdn] will be set to 'include'. - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials - pub fn fetch_credentials_include(mut self) -> RequestBuilder { - if let Ok(ref mut req) = self.request { - req.credentials = Some(RequestCredentials::Include); - } - self - } - - /// Set fetch credentials to 'omit' - /// - /// # WASM - /// - /// This option is only effective with WebAssembly target. - /// - /// The [request credentials][mdn] will be set to 'omit'. - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials - pub fn fetch_credentials_omit(mut self) -> RequestBuilder { - if let Ok(ref mut req) = self.request { - req.credentials = Some(RequestCredentials::Omit); - } - self - } - /// Build a `Request`, which can be inspected, modified and executed with /// `Client::execute()`. pub fn build(self) -> crate::Result { @@ -466,8 +386,6 @@ where url, headers, body: Some(body.into()), - cors: true, - credentials: None, }) } } diff --git a/src/wasm/component/response.rs b/src/wasm/component/response.rs index 7cb47392c..8c9b6d2ed 100644 --- a/src/wasm/component/response.rs +++ b/src/wasm/component/response.rs @@ -2,10 +2,9 @@ use std::{fmt, io::Read as _}; use bytes::Bytes; use http::{HeaderMap, StatusCode, Version}; -use url::Url; - #[cfg(feature = "json")] use serde::de::DeserializeOwned; +use url::Url; /// A Response to a submitted `Request`. pub struct Response { @@ -53,7 +52,7 @@ impl Response { /// /// - The server didn't send a `content-length` header. /// - The response is compressed and automatically decoded (thus changing - /// the actual decoded length). + /// the actual decoded length). pub fn content_length(&self) -> Option { self.headers() .get(http::header::CONTENT_LENGTH)? @@ -84,11 +83,11 @@ impl Response { serde_json::from_slice(&full).map_err(crate::error::decode) } - /// Get the response text. + /// Get the response as text pub async fn text(self) -> crate::Result { self.bytes() .await - .map(|s| String::from_utf8(s.to_vec()).map_err(crate::error::decode))? + .map(|s| String::from_utf8_lossy(&s).to_string()) } /// Get the response as bytes @@ -116,20 +115,20 @@ impl Response { /// then be used to stream the body. #[cfg(feature = "stream")] pub fn bytes_stream(&mut self) -> crate::Result { - let body = self + let response_body = self .http .body() .consume() .map_err(|_| crate::error::decode("failed to consume response body"))?; - let stream = body + let stream = response_body .stream() .map_err(|_| crate::error::decode("failed to stream response body")); - self.incoming_body = Some(body); + // Dropping the incoming body when the stream is present will trap as the + // stream is a child resource of the incoming body. + self.incoming_body = Some(response_body); stream } - // util methods - /// Turn a response into an error if the server returned an error. pub fn error_for_status(self) -> crate::Result { let status = self.status(); @@ -154,7 +153,7 @@ impl Response { impl fmt::Debug for Response { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Response") - //.field("url", self.url()) + .field("url", self.url()) .field("status", &self.status()) .field("headers", self.headers()) .finish()