Skip to content

Commit e063ad2

Browse files
committed
feat(wasm): support request timeout
fixes #1135 #1274
1 parent bb8fec4 commit e063ad2

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ winreg = "0.10"
150150
# wasm
151151

152152
[target.'cfg(target_arch = "wasm32")'.dependencies]
153+
gloo-timers = "0.2.6"
153154
js-sys = "0.3.45"
154155
serde_json = "1.0"
155156
wasm-bindgen = "0.2.68"

src/wasm/client.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ async fn fetch(req: Request) -> crate::Result<Response> {
216216
}
217217
}
218218

219-
let abort = AbortGuard::new()?;
219+
let mut abort = AbortGuard::new()?;
220+
if let Some(timeout) = req.timeout() {
221+
abort.timeout(*timeout);
222+
}
220223
init.signal(Some(&abort.signal()));
221224

222225
let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)

src/wasm/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::convert::TryInto;
2+
use std::time::Duration;
3+
4+
use gloo_timers::callback::Timeout;
15
use wasm_bindgen::JsCast;
26
use web_sys::{AbortController, AbortSignal};
37

@@ -30,6 +34,7 @@ where
3034
/// A guard that cancels a fetch request when dropped.
3135
struct AbortGuard {
3236
ctrl: AbortController,
37+
_timeout: Option<Timeout>,
3338
}
3439

3540
impl AbortGuard {
@@ -38,12 +43,22 @@ impl AbortGuard {
3843
ctrl: AbortController::new()
3944
.map_err(crate::error::wasm)
4045
.map_err(crate::error::builder)?,
46+
_timeout: None,
4147
})
4248
}
4349

4450
fn signal(&self) -> AbortSignal {
4551
self.ctrl.signal()
4652
}
53+
54+
fn timeout(&mut self, timeout: Duration) {
55+
let millis = timeout.as_millis().try_into().expect("timeout");
56+
let ctrl = self.ctrl.clone();
57+
let timeout = Timeout::new(millis, move || ctrl.abort());
58+
if let Some(old) = self._timeout.replace(timeout) {
59+
old.cancel();
60+
}
61+
}
4762
}
4863

4964
impl Drop for AbortGuard {

src/wasm/request.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::convert::TryFrom;
22
use std::fmt;
3+
use std::time::Duration;
34

45
use bytes::Bytes;
56
use http::{request::Parts, Method, Request as HttpRequest};
@@ -18,6 +19,7 @@ pub struct Request {
1819
url: Url,
1920
headers: HeaderMap,
2021
body: Option<Body>,
22+
timeout: Option<Duration>,
2123
pub(super) cors: bool,
2224
pub(super) credentials: Option<RequestCredentials>,
2325
}
@@ -37,6 +39,7 @@ impl Request {
3739
url,
3840
headers: HeaderMap::new(),
3941
body: None,
42+
timeout: None,
4043
cors: true,
4144
credentials: None,
4245
}
@@ -90,6 +93,18 @@ impl Request {
9093
&mut self.body
9194
}
9295

96+
/// Get the timeout.
97+
#[inline]
98+
pub fn timeout(&self) -> Option<&Duration> {
99+
self.timeout.as_ref()
100+
}
101+
102+
/// Get a mutable reference to the timeout.
103+
#[inline]
104+
pub fn timeout_mut(&mut self) -> &mut Option<Duration> {
105+
&mut self.timeout
106+
}
107+
93108
/// Attempts to clone the `Request`.
94109
///
95110
/// None is returned if a body is which can not be cloned.
@@ -104,6 +119,7 @@ impl Request {
104119
url: self.url.clone(),
105120
headers: self.headers.clone(),
106121
body,
122+
timeout: self.timeout.clone(),
107123
cors: self.cors,
108124
credentials: self.credentials,
109125
})
@@ -233,6 +249,14 @@ impl RequestBuilder {
233249
self
234250
}
235251

252+
/// Enables a request timeout.
253+
pub fn timeout(mut self, timeout: Duration) -> RequestBuilder {
254+
if let Ok(ref mut req) = self.request {
255+
*req.timeout_mut() = Some(timeout);
256+
}
257+
self
258+
}
259+
236260
/// TODO
237261
#[cfg(feature = "multipart")]
238262
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
@@ -449,6 +473,7 @@ where
449473
url,
450474
headers,
451475
body: Some(body.into()),
476+
timeout: None,
452477
cors: true,
453478
credentials: None,
454479
})

0 commit comments

Comments
 (0)