mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
feat(ext/http): abort signal when request is cancelled (#26761)
Closes https://github.com/denoland/deno/issues/21653
This commit is contained in:
parent
742744d498
commit
b9262130fe
5 changed files with 58 additions and 6 deletions
|
@ -269,12 +269,6 @@ class Request {
|
||||||
/** @type {AbortSignal} */
|
/** @type {AbortSignal} */
|
||||||
get [_signal]() {
|
get [_signal]() {
|
||||||
const signal = this[_signalCache];
|
const signal = this[_signalCache];
|
||||||
// This signal not been created yet, and the request is still in progress
|
|
||||||
if (signal === undefined) {
|
|
||||||
const signal = newSignal();
|
|
||||||
this[_signalCache] = signal;
|
|
||||||
return signal;
|
|
||||||
}
|
|
||||||
// This signal has not been created yet, but the request has already completed
|
// This signal has not been created yet, but the request has already completed
|
||||||
if (signal === false) {
|
if (signal === false) {
|
||||||
const signal = newSignal();
|
const signal = newSignal();
|
||||||
|
@ -282,6 +276,18 @@ class Request {
|
||||||
signal[signalAbort](signalAbortError);
|
signal[signalAbort](signalAbortError);
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This signal not been created yet, and the request is still in progress
|
||||||
|
if (signal === undefined) {
|
||||||
|
const signal = newSignal();
|
||||||
|
this[_signalCache] = signal;
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signal.aborted && this[_request].isCancelled) {
|
||||||
|
signal[signalAbort](signalAbortError);
|
||||||
|
}
|
||||||
|
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
get [_mimeType]() {
|
get [_mimeType]() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
op_http_cancel,
|
op_http_cancel,
|
||||||
op_http_close,
|
op_http_close,
|
||||||
op_http_close_after_finish,
|
op_http_close_after_finish,
|
||||||
|
op_http_get_request_cancelled,
|
||||||
op_http_get_request_headers,
|
op_http_get_request_headers,
|
||||||
op_http_get_request_method_and_url,
|
op_http_get_request_method_and_url,
|
||||||
op_http_read_request_body,
|
op_http_read_request_body,
|
||||||
|
@ -373,6 +374,13 @@ class InnerRequest {
|
||||||
get external() {
|
get external() {
|
||||||
return this.#external;
|
return this.#external;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCancelled() {
|
||||||
|
if (this.#external === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return op_http_get_request_cancelled(this.#external);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CallbackContext {
|
class CallbackContext {
|
||||||
|
|
|
@ -700,6 +700,14 @@ fn set_response(
|
||||||
http.complete();
|
http.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op2(fast)]
|
||||||
|
pub fn op_http_get_request_cancelled(external: *const c_void) -> bool {
|
||||||
|
let http =
|
||||||
|
// SAFETY: op is called with external.
|
||||||
|
unsafe { clone_external!(external, "op_http_get_request_cancelled") };
|
||||||
|
http.cancelled()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returned promise resolves when body streaming finishes.
|
/// Returned promise resolves when body streaming finishes.
|
||||||
/// Call [`op_http_close_after_finish`] when done with the external.
|
/// Call [`op_http_close_after_finish`] when done with the external.
|
||||||
#[op2(async)]
|
#[op2(async)]
|
||||||
|
|
|
@ -113,6 +113,7 @@ deno_core::extension!(
|
||||||
http_next::op_http_get_request_header,
|
http_next::op_http_get_request_header,
|
||||||
http_next::op_http_get_request_headers,
|
http_next::op_http_get_request_headers,
|
||||||
http_next::op_http_get_request_method_and_url<HTTP>,
|
http_next::op_http_get_request_method_and_url<HTTP>,
|
||||||
|
http_next::op_http_get_request_cancelled,
|
||||||
http_next::op_http_read_request_body,
|
http_next::op_http_read_request_body,
|
||||||
http_next::op_http_serve_on<HTTP>,
|
http_next::op_http_serve_on<HTTP>,
|
||||||
http_next::op_http_serve<HTTP>,
|
http_next::op_http_serve<HTTP>,
|
||||||
|
|
|
@ -4270,3 +4270,32 @@ Deno.test({
|
||||||
assertEquals(hostname, "0.0.0.0");
|
assertEquals(hostname, "0.0.0.0");
|
||||||
await server.shutdown();
|
await server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "AbortSignal aborted when request is cancelled",
|
||||||
|
}, async () => {
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
const server = Deno.serve({
|
||||||
|
hostname: "0.0.0.0",
|
||||||
|
port: servePort,
|
||||||
|
onListen: () => resolve(),
|
||||||
|
}, async (request) => {
|
||||||
|
request.signal.addEventListener("abort", () => cancelled = true);
|
||||||
|
assert(!request.signal.aborted);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000)); // abort during waiting
|
||||||
|
assert(request.signal.aborted);
|
||||||
|
return new Response("Ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
await fetch(`http://localhost:${servePort}/`, {
|
||||||
|
signal: AbortSignal.timeout(1000),
|
||||||
|
}).catch(() => {});
|
||||||
|
|
||||||
|
await server.shutdown();
|
||||||
|
|
||||||
|
assert(cancelled);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue