Skip to content

Commit

Permalink
feat(ext/http): abort signal when request is cancelled (#26761)
Browse files Browse the repository at this point in the history
Closes #21653
  • Loading branch information
littledivy committed Nov 10, 2024
1 parent 6f73b98 commit 12c48f9
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 5 deletions.
16 changes: 11 additions & 5 deletions ext/fetch/23_request.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,19 +269,25 @@ class Request {
/** @type {AbortSignal} */
get [_signal]() {
const signal = this[_signalCache];
// This signal not been created yet, and the request is still in progress
if (signal === undefined) {
// This signal has not been created yet, but the request has already completed
if (signal === false) {
const signal = newSignal();
this[_signalCache] = signal;
signal[signalAbort](signalAbortError);
return signal;
}
// This signal has not been created yet, but the request has already completed
if (signal === false) {

// This signal not been created yet, and the request is still in progress
if (signal === undefined) {
const signal = newSignal();
this[_signalCache] = signal;
signal[signalAbort](signalAbortError);
return signal;
}

if (!signal.aborted && this[_request].isCancelled) {
signal[signalAbort](signalAbortError);
}

return signal;
}
get [_mimeType]() {
Expand Down
8 changes: 8 additions & 0 deletions ext/http/00_serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
op_http_cancel,
op_http_close,
op_http_close_after_finish,
op_http_get_request_cancelled,
op_http_get_request_headers,
op_http_get_request_method_and_url,
op_http_read_request_body,
Expand Down Expand Up @@ -373,6 +374,13 @@ class InnerRequest {
get external() {
return this.#external;
}

get isCancelled() {
if (this.#external === null) {
return true;
}
return op_http_get_request_cancelled(this.#external);
}
}

class CallbackContext {
Expand Down
8 changes: 8 additions & 0 deletions ext/http/http_next.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,14 @@ fn set_response(
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.
/// Call [`op_http_close_after_finish`] when done with the external.
#[op2(async)]
Expand Down
1 change: 1 addition & 0 deletions ext/http/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ deno_core::extension!(
http_next::op_http_get_request_header,
http_next::op_http_get_request_headers,
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_serve_on<HTTP>,
http_next::op_http_serve<HTTP>,
Expand Down
29 changes: 29 additions & 0 deletions tests/unit/serve_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4270,3 +4270,32 @@ Deno.test({
assertEquals(hostname, "0.0.0.0");
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);
});

0 comments on commit 12c48f9

Please sign in to comment.