1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 23:13:13 -04:00

Generalize http fetch (#488)

* Generalize http fetch

- allow bytes as request body
- expose request and response headers in API
- update http example to show response headers and allow POST requests

* clippy fixes

* add missing comment, pub

* doc comment fix

* fix: missing argument when feature syntect not enabled

* formatting fixes

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* remove commented out code

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* formatting fixes

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* cargo fmt

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
skuzins
2021-08-15 10:56:46 -04:00
committed by GitHub
parent eefc56c213
commit 6a8a93e120
4 changed files with 194 additions and 80 deletions

View File

@@ -7,67 +7,75 @@ pub use epi::http::{Request, Response};
pub async fn fetch_async(request: &Request) -> Result<Response, String> {
fetch_jsvalue(request)
.await
.map_err(|err| err.as_string().unwrap_or_default())
.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> {
let Request { method, url, body } = request;
// 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(method);
opts.method(&request.method);
opts.mode(web_sys::RequestMode::Cors);
if !body.is_empty() {
opts.body(Some(&JsValue::from_str(body)));
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 request = web_sys::Request::new_with_str_and_init(url, &opts)?;
request.headers().set("Accept", "*/*")?;
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(&request)).await?;
assert!(response.is_instance_of::<web_sys::Response>());
let response: web_sys::Response = response.dyn_into().unwrap();
// // TODO: support binary get
// let body = JsFuture::from(response.text()?).await?;
// let body = body.as_string().unwrap_or_default();
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();
let header_content_type = response
.headers()
.get("Content-Type")
.ok()
.flatten()
.unwrap_or_default();
// 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");
let text = if header_content_type.starts_with("text")
|| header_content_type == "application/javascript"
{
String::from_utf8(bytes.clone()).ok()
} else {
None
};
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 {
status_text: response.status_text(),
url: response.url(),
ok: response.ok(),
status: response.status(),
header_content_type,
status_text: response.status_text(),
bytes,
text,
headers,
})
}