wasm: Add request body in the form of Bytes (#696)
* Add body bytes * Add example and header creation code
This commit is contained in:
committed by
Sean McArthur
parent
b24b0be461
commit
f6f81f9cc1
5
examples/wasm_header/.gitignore
vendored
Normal file
5
examples/wasm_header/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
pkg
|
||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
*.swp
|
||||||
16
examples/wasm_header/Cargo.toml
Normal file
16
examples/wasm_header/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["John Gallagher <john.willis.gallagher@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# Config mostly pulled from: https://github.com/rustwasm/wasm-bindgen/blob/master/examples/fetch/Cargo.toml
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = {path = "../../"}
|
||||||
|
wasm-bindgen-futures = "0.4.1"
|
||||||
|
wasm-bindgen = { version = "0.2.51", features = ["serde-serialize"] }
|
||||||
|
|
||||||
11
examples/wasm_header/README.md
Normal file
11
examples/wasm_header/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
## Example usage of Reqwest from WASM
|
||||||
|
|
||||||
|
You can build the example locally with:
|
||||||
|
|
||||||
|
|
||||||
|
npm run serve
|
||||||
|
|
||||||
|
and then visiting http://localhost:8080 in a browser should run the example!
|
||||||
|
|
||||||
|
|
||||||
|
This example is loosely based off of [this example](https://github.com/rustwasm/wasm-bindgen/blob/master/examples/fetch/src/lib.rs), an example usage of `fetch` from `wasm-bindgen`.
|
||||||
9
examples/wasm_header/index.js
Normal file
9
examples/wasm_header/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const rust = import('./pkg');
|
||||||
|
|
||||||
|
rust
|
||||||
|
.then(m => {
|
||||||
|
return m.run().then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
6101
examples/wasm_header/package-lock.json
generated
Normal file
6101
examples/wasm_header/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
examples/wasm_header/package.json
Normal file
14
examples/wasm_header/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack",
|
||||||
|
"serve": "webpack-dev-server"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@wasm-tool/wasm-pack-plugin": "1.0.1",
|
||||||
|
"text-encoding": "^0.7.0",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"webpack": "^4.29.4",
|
||||||
|
"webpack-cli": "^3.1.1",
|
||||||
|
"webpack-dev-server": "^3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
examples/wasm_header/src/lib.rs
Normal file
23
examples/wasm_header/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
// NOTE: This test is a clone of https://github.com/rustwasm/wasm-bindgen/blob/master/examples/fetch/src/lib.rs
|
||||||
|
// but uses Reqwest instead of the web_sys fetch api directly
|
||||||
|
|
||||||
|
/**
|
||||||
|
* curl --location --request POST "https://postman-echo.com/post" \
|
||||||
|
--data "This is expected to be sent back as part of response body."
|
||||||
|
*/
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn run() -> Result<JsValue, JsValue> {
|
||||||
|
let res = reqwest::Client::new()
|
||||||
|
.post("https://postman-echo.com/post")
|
||||||
|
.body("This is expected to be sent back as part of response body.")
|
||||||
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
// .header("Access-Control-Allow-Origin", "*")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let text = res.text().await?;
|
||||||
|
|
||||||
|
Ok(JsValue::from_str(&text))
|
||||||
|
}
|
||||||
25
examples/wasm_header/webpack.config.js
Normal file
25
examples/wasm_header/webpack.config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './index.js',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'index.js',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin(),
|
||||||
|
new WasmPackPlugin({
|
||||||
|
crateDirectory: path.resolve(__dirname, ".")
|
||||||
|
}),
|
||||||
|
// Have this example work in Edge which doesn't ship `TextEncoder` or
|
||||||
|
// `TextDecoder` at this time.
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
TextDecoder: ['text-encoding', 'TextDecoder'],
|
||||||
|
TextEncoder: ['text-encoding', 'TextEncoder']
|
||||||
|
})
|
||||||
|
],
|
||||||
|
mode: 'development'
|
||||||
|
};
|
||||||
@@ -1,3 +1,59 @@
|
|||||||
/// dox
|
/// dox
|
||||||
#[derive(Debug)]
|
use bytes::Bytes;
|
||||||
pub struct Body(());
|
use std::fmt;
|
||||||
|
|
||||||
|
/// The body of a `Request`.
|
||||||
|
///
|
||||||
|
/// In most cases, this is not needed directly, as the
|
||||||
|
/// [`RequestBuilder.body`][builder] method uses `Into<Body>`, which allows
|
||||||
|
/// passing many things (like a string or vector of bytes).
|
||||||
|
///
|
||||||
|
/// [builder]: ./struct.RequestBuilder.html#method.body
|
||||||
|
pub struct Body(Bytes);
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub(crate) fn bytes(&self) -> &Bytes {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Bytes> for Body {
|
||||||
|
#[inline]
|
||||||
|
fn from(bytes: Bytes) -> Body {
|
||||||
|
Body(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Body {
|
||||||
|
#[inline]
|
||||||
|
fn from(vec: Vec<u8>) -> Body {
|
||||||
|
Body(vec.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static [u8]> for Body {
|
||||||
|
#[inline]
|
||||||
|
fn from(s: &'static [u8]) -> Body {
|
||||||
|
Body(Bytes::from_static(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Body {
|
||||||
|
#[inline]
|
||||||
|
fn from(s: String) -> Body {
|
||||||
|
Body(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Body {
|
||||||
|
#[inline]
|
||||||
|
fn from(s: &'static str) -> Body {
|
||||||
|
s.as_bytes().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Body {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("Body").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::future::Future;
|
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
use js_sys::Uint8Array;
|
||||||
|
use std::future::Future;
|
||||||
use wasm_bindgen::UnwrapThrowExt as _;
|
use wasm_bindgen::UnwrapThrowExt as _;
|
||||||
|
|
||||||
use crate::IntoUrl;
|
|
||||||
use super::{Request, RequestBuilder, Response};
|
use super::{Request, RequestBuilder, Response};
|
||||||
|
use crate::IntoUrl;
|
||||||
|
|
||||||
/// dox
|
/// dox
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -91,7 +92,10 @@ impl Client {
|
|||||||
RequestBuilder::new(self.clone(), req)
|
RequestBuilder::new(self.clone(), req)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn execute_request(&self, req: Request) -> impl Future<Output = crate::Result<Response>> {
|
pub(super) fn execute_request(
|
||||||
|
&self,
|
||||||
|
req: Request,
|
||||||
|
) -> impl Future<Output = crate::Result<Response>> {
|
||||||
fetch(req)
|
fetch(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,12 +111,21 @@ async fn fetch(req: Request) -> crate::Result<Response> {
|
|||||||
|
|
||||||
for (name, value) in req.headers() {
|
for (name, value) in req.headers() {
|
||||||
js_headers
|
js_headers
|
||||||
.append(name.as_str(), value.to_str().map_err(crate::error::builder)?)
|
.append(
|
||||||
|
name.as_str(),
|
||||||
|
value.to_str().map_err(crate::error::builder)?,
|
||||||
|
)
|
||||||
.map_err(crate::error::wasm)
|
.map_err(crate::error::wasm)
|
||||||
.map_err(crate::error::builder)?;
|
.map_err(crate::error::builder)?;
|
||||||
}
|
}
|
||||||
init.headers(&js_headers.into());
|
init.headers(&js_headers.into());
|
||||||
|
|
||||||
|
if let Some(body) = req.body() {
|
||||||
|
let body_bytes: &[u8] = body.bytes();
|
||||||
|
let body_array: Uint8Array = body_bytes.into();
|
||||||
|
init.body(Some(&body_array.into()));
|
||||||
|
}
|
||||||
|
|
||||||
let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
|
let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
|
||||||
.map_err(crate::error::wasm)
|
.map_err(crate::error::wasm)
|
||||||
.map_err(crate::error::builder)?;
|
.map_err(crate::error::builder)?;
|
||||||
@@ -160,5 +173,4 @@ impl ClientBuilder {
|
|||||||
pub fn build(self) -> Result<Client, crate::Error> {
|
pub fn build(self) -> Result<Client, crate::Error> {
|
||||||
Ok(Client(()))
|
Ok(Client(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use http::HttpTryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use http::{Method, HeaderMap};
|
use http::Method;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use super::{Body, Client, Response};
|
use super::{Body, Client, Response};
|
||||||
|
use crate::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
|
|
||||||
/// A request which can be executed with `Client::execute()`.
|
/// A request which can be executed with `Client::execute()`.
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
@@ -83,7 +85,6 @@ impl RequestBuilder {
|
|||||||
RequestBuilder { client, request }
|
RequestBuilder { client, request }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Set the request body.
|
/// Set the request body.
|
||||||
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
|
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
|
||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
@@ -92,6 +93,30 @@ impl RequestBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a `Header` to this Request.
|
||||||
|
pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
|
||||||
|
where
|
||||||
|
HeaderName: HttpTryFrom<K>,
|
||||||
|
HeaderValue: HttpTryFrom<V>,
|
||||||
|
{
|
||||||
|
let mut error = None;
|
||||||
|
if let Ok(ref mut req) = self.request {
|
||||||
|
match <HeaderName as HttpTryFrom<K>>::try_from(key) {
|
||||||
|
Ok(key) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
|
||||||
|
Ok(value) => {
|
||||||
|
req.headers_mut().append(key, value);
|
||||||
|
}
|
||||||
|
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||||
|
},
|
||||||
|
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if let Some(err) = error {
|
||||||
|
self.request = Err(err);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs the Request and sends it to the target URL, returning a
|
/// Constructs the Request and sends it to the target URL, returning a
|
||||||
/// future Response.
|
/// future Response.
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user