wasm: Add request body in the form of Bytes (#696)

* Add body bytes

* Add example and header creation code
This commit is contained in:
John Gallagher
2019-11-04 12:17:05 -05:00
committed by Sean McArthur
parent b24b0be461
commit f6f81f9cc1
11 changed files with 6306 additions and 9 deletions

5
examples/wasm_header/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules
pkg
target
Cargo.lock
*.swp

View 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"] }

View 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`.

View 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

File diff suppressed because it is too large Load Diff

View 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"
}
}

View 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))
}

View 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'
};

View File

@@ -1,3 +1,59 @@
/// dox
#[derive(Debug)]
pub struct Body(());
use bytes::Bytes;
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()
}
}

View File

@@ -1,9 +1,10 @@
use std::future::Future;
use http::Method;
use js_sys::Uint8Array;
use std::future::Future;
use wasm_bindgen::UnwrapThrowExt as _;
use crate::IntoUrl;
use super::{Request, RequestBuilder, Response};
use crate::IntoUrl;
/// dox
#[derive(Clone, Debug)]
@@ -91,7 +92,10 @@ impl Client {
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)
}
}
@@ -107,12 +111,21 @@ async fn fetch(req: Request) -> crate::Result<Response> {
for (name, value) in req.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::builder)?;
}
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)
.map_err(crate::error::wasm)
.map_err(crate::error::builder)?;
@@ -160,5 +173,4 @@ impl ClientBuilder {
pub fn build(self) -> Result<Client, crate::Error> {
Ok(Client(()))
}
}

View File

@@ -1,9 +1,11 @@
use http::HttpTryFrom;
use std::fmt;
use http::{Method, HeaderMap};
use http::Method;
use url::Url;
use super::{Body, Client, Response};
use crate::header::{HeaderMap, HeaderName, HeaderValue};
/// A request which can be executed with `Client::execute()`.
pub struct Request {
@@ -83,7 +85,6 @@ impl RequestBuilder {
RequestBuilder { client, request }
}
/// Set the request body.
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
if let Ok(ref mut req) = self.request {
@@ -92,6 +93,30 @@ impl RequestBuilder {
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
/// future Response.
///