mirror of https://github.com/emilk/egui.git
Browse Source
I've extracted all the http request code and turned it into its own crate at <https://github.com/emilk/ehttp>. There was never a reason for the HTTP request library to be part of `eframe`. Much better to have it as its own crate!pull/698/head
Emil Ernerfeldt
3 years ago
committed by
GitHub
22 changed files with 49 additions and 330 deletions
@ -1,70 +0,0 @@ |
|||
use std::collections::BTreeMap; |
|||
|
|||
pub use epi::http::{Request, Response}; |
|||
|
|||
/// NOTE: Ok(..) is returned on network error.
|
|||
/// Err is only for failure to use the fetch api.
|
|||
pub fn fetch_blocking(request: &Request) -> Result<Response, String> { |
|||
let mut req = ureq::request(&request.method, &request.url); |
|||
|
|||
for header in &request.headers { |
|||
req = req.set(header.0, header.1); |
|||
} |
|||
|
|||
let resp = if request.body.is_empty() { |
|||
req.call() |
|||
} else { |
|||
req.send_bytes(&request.body) |
|||
}; |
|||
|
|||
let (ok, resp) = match resp { |
|||
Ok(resp) => (true, resp), |
|||
Err(ureq::Error::Status(_, resp)) => (false, resp), // Still read the body on e.g. 404
|
|||
Err(ureq::Error::Transport(error)) => return Err(error.to_string()), |
|||
}; |
|||
|
|||
let url = resp.get_url().to_owned(); |
|||
let status = resp.status(); |
|||
let status_text = resp.status_text().to_owned(); |
|||
let mut headers = BTreeMap::new(); |
|||
for key in &resp.headers_names() { |
|||
if let Some(value) = resp.header(key) { |
|||
// lowercase for easy lookup
|
|||
headers.insert(key.to_ascii_lowercase(), value.to_owned()); |
|||
} |
|||
} |
|||
|
|||
let mut reader = resp.into_reader(); |
|||
let mut bytes = vec![]; |
|||
use std::io::Read; |
|||
reader |
|||
.read_to_end(&mut bytes) |
|||
.map_err(|err| err.to_string())?; |
|||
|
|||
let response = Response { |
|||
url, |
|||
ok, |
|||
status, |
|||
status_text, |
|||
bytes, |
|||
headers, |
|||
}; |
|||
Ok(response) |
|||
} |
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
pub(crate) struct GliumHttp {} |
|||
|
|||
impl epi::backend::Http for GliumHttp { |
|||
fn fetch_dyn( |
|||
&self, |
|||
request: Request, |
|||
on_done: Box<dyn FnOnce(Result<Response, String>) + Send>, |
|||
) { |
|||
std::thread::spawn(move || { |
|||
let result = crate::http::fetch_blocking(&request); |
|||
on_done(result) |
|||
}); |
|||
} |
|||
} |
@ -1,97 +0,0 @@ |
|||
use wasm_bindgen::prelude::*; |
|||
|
|||
pub use epi::http::{Request, Response}; |
|||
|
|||
/// NOTE: Ok(..) is returned on network error.
|
|||
/// Err is only for failure to use the fetch api.
|
|||
pub async fn fetch_async(request: &Request) -> Result<Response, String> { |
|||
fetch_jsvalue(request) |
|||
.await |
|||
.map_err(|err| err.as_string().unwrap_or(format!("{:#?}", err))) |
|||
} |
|||
|
|||
/// NOTE: Ok(..) is returned on network error.
|
|||
/// Err is only for failure to use the fetch api.
|
|||
async fn fetch_jsvalue(request: &Request) -> Result<Response, JsValue> { |
|||
// https://rustwasm.github.io/wasm-bindgen/examples/fetch.html
|
|||
// https://github.com/seanmonstar/reqwest/blob/master/src/wasm/client.rs
|
|||
|
|||
use wasm_bindgen::JsCast; |
|||
use wasm_bindgen_futures::JsFuture; |
|||
|
|||
let mut opts = web_sys::RequestInit::new(); |
|||
opts.method(&request.method); |
|||
opts.mode(web_sys::RequestMode::Cors); |
|||
|
|||
if !request.body.is_empty() { |
|||
let body_bytes: &[u8] = &request.body; |
|||
let body_array: js_sys::Uint8Array = body_bytes.into(); |
|||
let js_value: &JsValue = body_array.as_ref(); |
|||
opts.body(Some(js_value)); |
|||
} |
|||
|
|||
let js_request = web_sys::Request::new_with_str_and_init(&request.url, &opts)?; |
|||
|
|||
for h in &request.headers { |
|||
js_request.headers().set(h.0, h.1)?; |
|||
} |
|||
|
|||
let window = web_sys::window().unwrap(); |
|||
let response = JsFuture::from(window.fetch_with_request(&js_request)).await?; |
|||
let response: web_sys::Response = response.dyn_into()?; |
|||
|
|||
let array_buffer = JsFuture::from(response.array_buffer()?).await?; |
|||
let uint8_array = js_sys::Uint8Array::new(&array_buffer); |
|||
let bytes = uint8_array.to_vec(); |
|||
|
|||
// https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
|||
// "Note: When Header values are iterated over, [...] values from duplicate header names are combined."
|
|||
let mut headers = std::collections::BTreeMap::<String, String>::new(); |
|||
let js_headers: web_sys::Headers = response.headers(); |
|||
let js_iter = js_sys::try_iter(&js_headers) |
|||
.expect("headers try_iter") |
|||
.expect("headers have an iterator"); |
|||
|
|||
for item in js_iter { |
|||
let item = item.expect("headers iterator"); |
|||
let array: js_sys::Array = item.into(); |
|||
let v: Vec<JsValue> = array.to_vec(); |
|||
|
|||
let mut key = v[0] |
|||
.as_string() |
|||
.ok_or_else(|| JsValue::from_str("headers name"))?; |
|||
let value = v[1] |
|||
.as_string() |
|||
.ok_or_else(|| JsValue::from_str("headers value"))?; |
|||
|
|||
// for easy lookup
|
|||
key.make_ascii_lowercase(); |
|||
headers.insert(key, value); |
|||
} |
|||
|
|||
Ok(Response { |
|||
url: response.url(), |
|||
ok: response.ok(), |
|||
status: response.status(), |
|||
status_text: response.status_text(), |
|||
bytes, |
|||
headers, |
|||
}) |
|||
} |
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
pub(crate) struct WebHttp {} |
|||
|
|||
impl epi::backend::Http for WebHttp { |
|||
fn fetch_dyn( |
|||
&self, |
|||
request: Request, |
|||
on_done: Box<dyn FnOnce(Result<Response, String>) + Send>, |
|||
) { |
|||
crate::spawn_future(async move { |
|||
let result = crate::http::fetch_async(&request).await; |
|||
on_done(result) |
|||
}); |
|||
} |
|||
} |
Loading…
Reference in new issue