Skip to content

Commit

Permalink
Make wasm requests cancel when the future drops. (seanmonstar#1755)
Browse files Browse the repository at this point in the history
  • Loading branch information
jneem authored and Nutomic committed Nov 7, 2024
1 parent 87daa0d commit b204825
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ wasm-streams = { version = "0.2", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
version = "0.3.25"
features = [
"AbortController",
"AbortSignal",
"Headers",
"Request",
"RequestInit",
Expand Down
7 changes: 5 additions & 2 deletions src/wasm/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fmt, future::Future, sync::Arc};
use url::Url;
use wasm_bindgen::prelude::{wasm_bindgen, UnwrapThrowExt as _};

use super::{Request, RequestBuilder, Response};
use super::{AbortGuard, Request, RequestBuilder, Response};
use crate::IntoUrl;

#[wasm_bindgen]
Expand Down Expand Up @@ -216,6 +216,9 @@ async fn fetch(req: Request) -> crate::Result<Response> {
}
}

let abort = AbortGuard::new()?;
init.signal(Some(&abort.signal()));

let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
.map_err(crate::error::wasm)
.map_err(crate::error::builder)?;
Expand Down Expand Up @@ -247,7 +250,7 @@ async fn fetch(req: Request) -> crate::Result<Response> {
}

resp.body(js_resp)
.map(|resp| Response::new(resp, url))
.map(|resp| Response::new(resp, url, abort))
.map_err(crate::error::request)
}

Expand Down
26 changes: 26 additions & 0 deletions src/wasm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use wasm_bindgen::JsCast;
use web_sys::{AbortController, AbortSignal};

mod body;
mod client;
Expand All @@ -25,3 +26,28 @@ where
.dyn_into::<T>()
.map_err(|_js_val| "promise resolved to unexpected type".into())
}

/// A guard that cancels a fetch request when dropped.
struct AbortGuard {
ctrl: AbortController,
}

impl AbortGuard {
fn new() -> crate::Result<Self> {
Ok(AbortGuard {
ctrl: AbortController::new()
.map_err(crate::error::wasm)
.map_err(crate::error::builder)?,
})
}

fn signal(&self) -> AbortSignal {
self.ctrl.signal()
}
}

impl Drop for AbortGuard {
fn drop(&mut self) {
self.ctrl.abort();
}
}
10 changes: 9 additions & 1 deletion src/wasm/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use http::{HeaderMap, StatusCode};
use js_sys::Uint8Array;
use url::Url;

use crate::wasm::AbortGuard;

#[cfg(feature = "stream")]
use wasm_bindgen::JsCast;

Expand All @@ -17,16 +19,22 @@ use serde::de::DeserializeOwned;
/// A Response to a submitted `Request`.
pub struct Response {
http: http::Response<web_sys::Response>,
_abort: AbortGuard,
// Boxed to save space (11 words to 1 word), and it's not accessed
// frequently internally.
url: Box<Url>,
}

impl Response {
pub(super) fn new(res: http::Response<web_sys::Response>, url: Url) -> Response {
pub(super) fn new(
res: http::Response<web_sys::Response>,
url: Url,
abort: AbortGuard,
) -> Response {
Response {
http: res,
url: Box::new(url),
_abort: abort,
}
}

Expand Down

0 comments on commit b204825

Please sign in to comment.