Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make wasm requests cancel when the future drops. #1755

Merged
merged 1 commit into from Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
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
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
@@ -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
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