Created
December 24, 2019 10:50
-
-
Save izissise/dcfe5325826e716f98223c8087942e15 to your computer and use it in GitHub Desktop.
Ruma stdweb XmlHttpRequest client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Implement ruma_client::HttpRequester with XmlHttpRequest | |
//! so it works in a browser context | |
use futures::channel::oneshot::channel; | |
use futures_core::future::Future; | |
use http::{Request as HttpRequest, Response as HttpResponse, StatusCode}; | |
use ruma_client::{Client, HttpRequester, HttpRequesterError}; | |
use std::pin::Pin; | |
use stdweb::traits::IEvent; | |
use stdweb::web::{event::ReadyStateChangeEvent, IEventTarget, XhrReadyState, XmlHttpRequest}; | |
use stdweb::Reference; | |
#[derive(Debug)] | |
pub struct XmlHttpRequester; | |
/// Wrapper type for ruma client using XmlHttpRequest | |
pub type XmlHttpClient = Client<XmlHttpRequester>; | |
impl HttpRequester<Vec<u8>> for XmlHttpRequester { | |
fn request( | |
&self, | |
req: HttpRequest<Vec<u8>>, | |
) -> Pin<Box<dyn Future<Output = Result<HttpResponse<Vec<u8>>, HttpRequesterError>> + Send + '_>> | |
{ | |
let xhr = XmlHttpRequest::new(); | |
let body = req.body(); | |
let method = req.method().as_str(); | |
let endpoint = req.uri().to_string(); | |
match xhr.open(method, &endpoint) { | |
Ok(_) => {} | |
Err(_) => return Box::pin(async move { Err(HttpRequesterError) }), | |
}; | |
let (sender, fut) = channel::<HttpResponse<Vec<u8>>>(); | |
// https://users.rust-lang.org/t/moving-a-sender-into-a-fn-closure/33875/5 | |
let mut sender = Some(sender); | |
xhr.add_event_listener(move |e: ReadyStateChangeEvent| { | |
// Magically retrieve the request object through js and stdweb | |
let xhr = Reference::from(e.target().unwrap()) | |
.downcast::<XmlHttpRequest>() | |
.unwrap(); | |
match xhr.ready_state() { | |
XhrReadyState::Done => { | |
// FIXME Waiting merge of https://github.com/koute/stdweb/pull/381 so headers can be put into the hyper response | |
let raw_headers: Option<String> = None /*xhr.get_all_response_headers()*/; | |
let headers = match raw_headers { | |
Some(rh) => { | |
let mut headers = http::header::HeaderMap::new(); | |
for h in rh.split("\r\n") { | |
if h.len() == 0 { | |
continue; | |
} | |
let parts: Vec<&str> = h.split(": ").collect(); | |
let k = parts[0]; | |
let k = http::header::HeaderName::from_bytes(k.as_bytes()).unwrap(); | |
let v = parts[1..].join(": "); | |
let v = http::header::HeaderValue::from_str(&v).unwrap(); | |
headers.append(k, v); | |
} | |
headers | |
} | |
None => http::header::HeaderMap::new(), | |
}; | |
let _type = xhr.response_type(); | |
let tmp_response = HttpResponse::new(""); | |
let (mut parts, _body) = tmp_response.into_parts(); | |
let body = match xhr.raw_response().into_string() { | |
Some(b) => b, | |
None => "".to_owned(), | |
}; | |
let body = body.as_bytes().to_owned(); | |
parts.status = StatusCode::from_u16(xhr.status()).unwrap(); | |
parts.version = http::version::Version::HTTP_11; | |
parts.headers = headers; | |
let rep = HttpResponse::from_parts(parts, body); | |
if let Some(tx) = sender.take() { | |
tx.send(rep).unwrap(); | |
} else { | |
panic!("Unreachable"); | |
} | |
} | |
_ => { /*debug!("Request on-going");*/ } | |
}; | |
}); | |
match xhr.send_with_bytes(body) { | |
Ok(_) => {} | |
Err(_) => return Box::pin(async move { Err(HttpRequesterError) }), | |
}; | |
Box::pin(async move { fut.await.map_err(|_e| HttpRequesterError) }) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment