Skip to content

Commit

Permalink
Improve the Fetch API (#188)
Browse files Browse the repository at this point in the history
* WIP

* Implement wrapper around fetch API.

* Change directory structure

* Post merge fixes

* Fix some incorrect docs/examples

* Actually run CI

* Actually run CI part 2

Co-authored-by: Muhammad Hamza <[email protected]>
  • Loading branch information
richard-uk1 and ranile authored Mar 11, 2022
1 parent 07e7dfa commit 07ad0fc
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 37 deletions.
17 changes: 14 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
with:
toolchain: stable
profile: minimal
components: clippy
override: true
target: wasm32-unknown-unknown

- name: Install wasm-pack
Expand All @@ -106,16 +106,27 @@ jobs:
restore-keys: |
cargo-${{ runner.os }}-test-
cargo-${{ runner.os }}-
- name: Run browser tests
env:
HTTPBIN_URL: "http://localhost:8080"
ECHO_SERVER_URL: "ws://localhost:8081"
run: wasm-pack test --chrome --firefox --headless
run: |
cd crates/net
wasm-pack test --chrome --firefox --headless --all-features
- name: Run browser tests
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
target: wasm32-unknown-unknown

- name: Run native tests
env:
HTTPBIN_URL: "http://localhost:8080"
ECHO_SERVER_URL: "ws://localhost:8081"
uses: actions-rs/cargo@v1
with:
command: test
args: -p gloo-net --all-features
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ features = ["futures"]
members = [
"crates/timers",
"crates/events",
"crates/net",
"crates/file",
"crates/dialogs",
"crates/storage",
Expand Down
4 changes: 3 additions & 1 deletion crates/net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ version = "0.1.0"
authors = ["Rust and WebAssembly Working Group", "Muhammad Hamza <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
repository = "https://github.com/hamza1311/reqwasm"
repository = "https://github.com/rustwasm/gloo"
description = "HTTP requests library for WASM Apps"
readme = "README.md"
keywords = ["requests", "http", "wasm", "websockets"]
categories = ["wasm", "web-programming::http-client", "api-bindings"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
wasm-bindgen = "0.2"
Expand Down Expand Up @@ -62,6 +63,7 @@ http = [
'web-sys/RequestInit',
'web-sys/RequestMode',
'web-sys/Response',
'web-sys/ResponseType',
'web-sys/Window',
'web-sys/RequestCache',
'web-sys/RequestCredentials',
Expand Down
2 changes: 1 addition & 1 deletion crates/net/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ assert_eq!(resp.status(), 200);
### WebSocket

```rust
use reqwasm::websocket::{Message, futures::WebSocket};
use gloo_net::websocket::{Message, futures::WebSocket};
use wasm_bindgen_futures::spawn_local;
use futures::{SinkExt, StreamExt};

Expand Down
120 changes: 120 additions & 0 deletions crates/net/src/http/headers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use js_sys::{Array, Map};
use std::fmt;
use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};

// I experimented with using `js_sys::Object` for the headers, since this object is marked
// experimental in MDN. However it's in the fetch spec, and it's necessary for appending headers.
/// A wrapper around `web_sys::Headers`.
pub struct Headers {
raw: web_sys::Headers,
}

impl Default for Headers {
fn default() -> Self {
Self::new()
}
}

impl Headers {
/// Create a new empty headers object.
pub fn new() -> Self {
// pretty sure this will never throw.
Self {
raw: web_sys::Headers::new().unwrap_throw(),
}
}

/// Build [Headers] from [web_sys::Headers].
pub fn from_raw(raw: web_sys::Headers) -> Self {
Self { raw }
}

/// Covert [Headers] to [web_sys::Headers].
pub fn into_raw(self) -> web_sys::Headers {
self.raw
}

/// This method appends a new value onto an existing header, or adds the header if it does not
/// already exist.
pub fn append(&self, name: &str, value: &str) {
// XXX Can this throw? WEBIDL says yes, my experiments with forbidden headers and MDN say
// no.
self.raw.append(name, value).unwrap_throw()
}

/// Deletes a header if it is present.
pub fn delete(&self, name: &str) {
self.raw.delete(name).unwrap_throw()
}

/// Gets a header if it is present.
pub fn get(&self, name: &str) -> Option<String> {
self.raw.get(name).unwrap_throw()
}

/// Whether a header with the given name exists.
pub fn has(&self, name: &str) -> bool {
self.raw.has(name).unwrap_throw()
}

/// Overwrites a header with the given name.
pub fn set(&self, name: &str, value: &str) {
self.raw.set(name, value).unwrap_throw()
}

/// Iterate over (header name, header value) pairs.
pub fn entries(&self) -> impl Iterator<Item = (String, String)> {
// Here we cheat and cast to a map even though `self` isn't, because the method names match
// and everything works. Is there a better way? Should there be a `MapLike` or
// `MapIterator` type in `js_sys`?
let fake_map: &Map = self.raw.unchecked_ref();
UncheckedIter(fake_map.entries()).map(|entry| {
let entry: Array = entry.unchecked_into();
let key = entry.get(0);
let value = entry.get(1);
(
key.as_string().unwrap_throw(),
value.as_string().unwrap_throw(),
)
})
}

/// Iterate over the names of the headers.
pub fn keys(&self) -> impl Iterator<Item = String> {
let fake_map: &Map = self.raw.unchecked_ref();
UncheckedIter(fake_map.keys()).map(|key| key.as_string().unwrap_throw())
}

/// Iterate over the values of the headers.
pub fn values(&self) -> impl Iterator<Item = String> {
let fake_map: &Map = self.raw.unchecked_ref();
UncheckedIter(fake_map.values()).map(|v| v.as_string().unwrap_throw())
}
}

impl fmt::Debug for Headers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut dbg = f.debug_struct("Headers");
for (key, value) in self.entries() {
dbg.field(&key, &value);
}
dbg.finish()
}
}

struct UncheckedIter(js_sys::Iterator);

impl Iterator for UncheckedIter {
type Item = JsValue;

fn next(&mut self) -> Option<Self::Item> {
// we don't check for errors. Only use this type on things we know conform to the iterator
// interface.
let next = self.0.next().unwrap_throw();
if next.done() {
None
} else {
Some(next.value())
}
}
}
Loading

0 comments on commit 07ad0fc

Please sign in to comment.