Skip to content

Commit

Permalink
Add gRPC-web support in WASM #701 (BCHD API for SLP). (#1127)
Browse files Browse the repository at this point in the history
* WIP. Adding RequestBody::Bytes.

* WIP. gRPC-web doesn't work under WASM yet.

* WIP. Catching up with dev. WASM doesn't compile.

* Fixed compilation after merging with dev. Some tests fail.

* WIP. Added x-grpc-web header to make CORS working. Tests still fail for other reason.

* Got gRPC-web working in WASM. Added Provider::Nomics.
  • Loading branch information
artemii235 committed Nov 11, 2021
1 parent 72ff703 commit fd8ad70
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
16 changes: 16 additions & 0 deletions mm2src/coins/utxo/bchd_grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,19 @@ mod bchd_grpc_tests {
}
}
}

#[cfg(target_arch = "wasm32")]
mod wasm_tests {
use super::*;
use wasm_bindgen_test::*;

wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
async fn test_check_slp_transaction_valid() {
let url = "https://bchd-testnet.greyh.at:18335";
// https://testnet.simpleledger.info/tx/c5f46ccc5431687154335d5b6526f1b9cfa961c44b97956b7bec77f884f56c73
let tx = hex::decode("010000000232809631da50999813c96996d587ceda2829db5e16247477a0eafcbb1ab9a10b020000006a473044022057c88d815fa563eda8ef7d0dd5c522f4501ffa6110df455b151b31609f149c22022048fecfc9b16e983fbfd05b0d2b7c011c3dbec542577fa00cd9bd192b81961f8e4121036879df230663db4cd083c8eeb0f293f46abc460ad3c299b0089b72e6d472202cffffffff32809631da50999813c96996d587ceda2829db5e16247477a0eafcbb1ab9a10b030000006a4730440220539e1204d2805c0474111a1f233ff82c0ab06e6e2bfc0cbe4975eacae64a0b1f02200ec83d32c2180f5567d0f760e85f1efc99d9341cfebd86c9a334310f6d4381494121036879df230663db4cd083c8eeb0f293f46abc460ad3c299b0089b72e6d472202cffffffff040000000000000000406a04534c500001010453454e4420bb309e48930671582bea508f9a1d9b491e49b69be3d6f372dc08da2ac6e90eb7080000000000000001080000000000002326e8030000000000001976a914ca1e04745e8ca0c60d8c5881531d51bec470743f88ace8030000000000001976a9148cfffc2409d063437d6aa8b75a009b9ba51b71fc88ac9f694801000000001976a9148cfffc2409d063437d6aa8b75a009b9ba51b71fc88ac8983d460").unwrap();
check_slp_transaction(&[url], tx).await.unwrap();
}
}
17 changes: 14 additions & 3 deletions mm2src/common/grpc_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,22 @@ where
}

#[cfg(target_arch = "wasm32")]
pub async fn post_grpc_web<Req, Res>(url: &str, _req: &Req) -> Result<Res, MmError<PostGrpcWebErr>>
pub async fn post_grpc_web<Req, Res>(url: &str, req: &Req) -> Result<Res, MmError<PostGrpcWebErr>>
where
Req: prost::Message + Send + 'static,
Res: prost::Message + Default + Send + 'static,
{
let _request = FetchRequest::post(url);
unimplemented!()
let body = encode_body(req)?;
let request = FetchRequest::post(url)
.body_bytes(body)
.header("content-type", "application/grpc-web+proto")
.header("accept", "application/grpc-web+proto")
// https://github.com/grpc/grpc-web/issues/85#issue-217223001
.header("x-grpc-web", "1");

let response = request.request_array().await?;

let reply = decode_body(response.1.into())?;

Ok(reply)
}
78 changes: 71 additions & 7 deletions mm2src/common/transport/wasm_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::stringify_js_error;
use crate::transport::{SlurpError, SlurpResult};
use futures::channel::oneshot;
use http::{HeaderMap, StatusCode};
use js_sys::Uint8Array;
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
Expand Down Expand Up @@ -67,6 +68,11 @@ impl FetchRequest {
self
}

pub fn body_bytes(mut self, body: Vec<u8>) -> FetchRequest {
self.body = Some(RequestBody::Bytes(body));
self
}

/// Set the mode to [`RequestMode::Cors`].
/// The request is no-cors by default.
pub fn cors(mut self) -> FetchRequest {
Expand All @@ -88,6 +94,15 @@ impl FetchRequest {
}
}

pub async fn request_array(self) -> FetchResult<Vec<u8>> {
let (tx, rx) = oneshot::channel();
Self::spawn_fetch_array(self, tx);
match rx.await {
Ok(res) => res,
Err(_e) => MmError::err(SlurpError::Internal("Spawned future has been canceled".to_owned())),
}
}

fn spawn_fetch_str(request: Self, tx: oneshot::Sender<FetchResult<String>>) {
let fut = async move {
let result = Self::fetch_str(request).await;
Expand All @@ -96,8 +111,15 @@ impl FetchRequest {
spawn_local(fut);
}

/// The private non-Send method that is called in a spawned future.
async fn fetch_str(request: Self) -> FetchResult<String> {
fn spawn_fetch_array(request: Self, tx: oneshot::Sender<FetchResult<Vec<u8>>>) {
let fut = async move {
let result = Self::fetch_array(request).await;
tx.send(result).ok();
};
spawn_local(fut);
}

async fn fetch(request: Self) -> FetchResult<JsResponse> {
let window = web_sys::window().expect("!window");
let uri = request.uri;

Expand Down Expand Up @@ -133,6 +155,23 @@ impl FetchRequest {
},
};

let status_code = js_response.status();
let status_code = match StatusCode::from_u16(status_code) {
Ok(code) => code,
Err(e) => {
let error = format!("Unexpected HTTP status code, found {}: {}", status_code, e);
return MmError::err(SlurpError::ErrorDeserializing { uri, error });
},
};

Ok((status_code, js_response))
}

/// The private non-Send method that is called in a spawned future.
async fn fetch_str(request: Self) -> FetchResult<String> {
let uri = request.uri.clone();
let (status_code, js_response) = Self::fetch(request).await?;

let resp_txt_fut = match js_response.text() {
Ok(txt) => txt,
Err(e) => {
Expand All @@ -155,15 +194,35 @@ impl FetchRequest {
},
};

let status_code = js_response.status();
let status_code = match StatusCode::from_u16(status_code) {
Ok(code) => code,
Ok((status_code, resp_str))
}

/// The private non-Send method that is called in a spawned future.
async fn fetch_array(request: Self) -> FetchResult<Vec<u8>> {
let uri = request.uri.clone();
let (status_code, js_response) = Self::fetch(request).await?;

let resp_array_fut = match js_response.array_buffer() {
Ok(blob) => blob,
Err(e) => {
let error = format!("Unexpected HTTP status code, found {}: {}", status_code, e);
let error = format!(
"Expected blob, found {:?}: {}",
js_response,
crate::stringify_js_error(&e)
);
return MmError::err(SlurpError::ErrorDeserializing { uri, error });
},
};
Ok((status_code, resp_str))
let resp_array = JsFuture::from(resp_array_fut)
.await
.map_to_mm(|e| SlurpError::ErrorDeserializing {
uri: uri.clone(),
error: stringify_js_error(&e),
})?;

let array = Uint8Array::new(&resp_array);

Ok((status_code, array.to_vec()))
}
}

Expand All @@ -183,12 +242,17 @@ impl FetchMethod {

enum RequestBody {
Utf8(String),
Bytes(Vec<u8>),
}

impl RequestBody {
fn into_js_value(self) -> JsValue {
match self {
RequestBody::Utf8(string) => JsValue::from_str(&string),
RequestBody::Bytes(bytes) => {
let js_array = Uint8Array::from(bytes.as_slice());
js_array.into()
},
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions mm2src/lp_ordermatch/lp_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub enum Provider {
Coingecko,
#[serde(rename = "coinpaprika")]
Coinpaprika,
#[serde(rename = "nomics")]
Nomics,
#[serde(rename = "unknown")]
Unknown,
}
Expand Down
5 changes: 1 addition & 4 deletions mm2src/mm2_tests/lp_bot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ mod tests {

#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_process_price_request() {
let resp = block_on(process_price_request(KMD_PRICE_ENDPOINT));
assert_ne!(resp.is_err(), true);
}
fn test_process_price_request() { let _resp = block_on(process_price_request(KMD_PRICE_ENDPOINT)).unwrap(); }

#[test]
#[cfg(not(target_arch = "wasm32"))]
Expand Down

0 comments on commit fd8ad70

Please sign in to comment.