use super::{Body, HttpClient, Request, Response};
use futures::future::BoxFuture;
use futures::prelude::*;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
#[derive(Debug)]
pub struct WasmClient {
_priv: (),
}
impl WasmClient {
pub fn new() -> Self {
Self { _priv: () }
}
}
impl Clone for WasmClient {
fn clone(&self) -> Self {
Self { _priv: () }
}
}
impl HttpClient for WasmClient {
type Error = std::io::Error;
fn send(&self, req: Request) -> BoxFuture<'static, Result<Response, Self::Error>> {
let fut = Box::pin(async move {
let url = format!("{}", req.uri());
let req = fetch::new(req.method().as_str(), &url);
let mut res = req.send().await?;
let body = res.body_bytes();
let mut response = Response::new(Body::from(body));
*response.status_mut() = http::StatusCode::from_u16(res.status()).unwrap();
for (name, value) in res.headers() {
let name: http::header::HeaderName = name.parse().unwrap();
response.headers_mut().insert(name, value.parse().unwrap());
}
Ok(response)
});
Box::pin(InnerFuture { fut })
}
}
struct InnerFuture {
fut: Pin<Box<dyn Future<Output = Result<Response, io::Error>> + 'static>>,
}
unsafe impl Send for InnerFuture {}
impl Future for InnerFuture {
type Output = Result<Response, io::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { Pin::new_unchecked(&mut self.fut).poll(cx) }
}
}
mod fetch {
use js_sys::{Array, ArrayBuffer, Reflect, Uint8Array};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::window;
use web_sys::RequestInit;
use std::io;
use std::iter::{IntoIterator, Iterator};
pub(crate) fn new(method: impl AsRef<str>, url: impl AsRef<str>) -> Request {
Request::new(method, url)
}
pub(crate) struct Request {
init: RequestInit,
url: String,
}
impl Request {
pub(crate) fn new(method: impl AsRef<str>, url: impl AsRef<str>) -> Self {
let mut init = web_sys::RequestInit::new();
init.method(method.as_ref());
Self {
init,
url: url.as_ref().to_owned(),
}
}
pub(crate) async fn send(self) -> Result<Response, io::Error> {
let window = window().expect("A global window object could not be found");
let request = web_sys::Request::new_with_str_and_init(&self.url, &self.init).unwrap();
let promise = window.fetch_with_request(&request);
let resp = JsFuture::from(promise).await.unwrap();
debug_assert!(resp.is_instance_of::<web_sys::Response>());
let res: web_sys::Response = resp.dyn_into().unwrap();
let promise = res.array_buffer().unwrap();
let resp = JsFuture::from(promise).await.unwrap();
debug_assert!(resp.is_instance_of::<js_sys::ArrayBuffer>());
let buf: ArrayBuffer = resp.dyn_into().unwrap();
let slice = Uint8Array::new(&buf);
let mut body: Vec<u8> = vec![0; slice.length() as usize];
slice.copy_to(&mut body);
Ok(Response::new(res, body))
}
}
pub(crate) struct Response {
res: web_sys::Response,
body: Option<Vec<u8>>,
}
impl Response {
fn new(res: web_sys::Response, body: Vec<u8>) -> Self {
Self {
res,
body: Some(body),
}
}
pub(crate) fn headers(&self) -> Headers {
Headers {
headers: self.res.headers(),
}
}
pub(crate) fn body_bytes(&mut self) -> Vec<u8> {
self.body.take().unwrap_or_else(|| vec![])
}
pub(crate) fn status(&self) -> u16 {
self.res.status()
}
}
pub(crate) struct Headers {
headers: web_sys::Headers,
}
impl IntoIterator for Headers {
type Item = (String, String);
type IntoIter = HeadersIter;
fn into_iter(self) -> Self::IntoIter {
HeadersIter {
iter: js_sys::try_iter(&self.headers).unwrap().unwrap(),
}
}
}
pub(crate) struct HeadersIter {
iter: js_sys::IntoIter,
}
impl Iterator for HeadersIter {
type Item = (String, String);
fn next(&mut self) -> Option<Self::Item> {
let pair = self.iter.next()?;
let array: Array = pair.unwrap().into();
let vals = array.values();
let prop = String::from("value").into();
let key = Reflect::get(&vals.next().unwrap(), &prop).unwrap();
let value = Reflect::get(&vals.next().unwrap(), &prop).unwrap();
Some((
key.as_string().to_owned().unwrap(),
value.as_string().to_owned().unwrap(),
))
}
}
}