From f7ae260b4dbbccb0b51ebc96e0937742b0c320eb Mon Sep 17 00:00:00 2001 From: Nicholas Paun Date: Fri, 14 Mar 2025 11:51:19 -0700 Subject: [PATCH 1/5] Document Request.signal --- ...2025-03-14-handle-request-cancellation.mdx | 43 +++++++++++++++++++ .../request-signal-passthrough.md | 15 +++++++ .../docs/workers/runtime-apis/request.mdx | 5 +++ 3 files changed, 63 insertions(+) create mode 100644 src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx create mode 100644 src/content/compatibility-flags/request-signal-passthrough.md diff --git a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx new file mode 100644 index 000000000000000..79ffbafc7aab329 --- /dev/null +++ b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx @@ -0,0 +1,43 @@ +--- +title: Handle request cancellation with Request.signal +description: Workers can now add event listeners on Request.signal and perform tasks when the request is cancelled +products: + - workers +date: 2025-03-14T01:00:00Z +--- + +import { Render, TypeScriptExample } from "~/components"; + +`Request` objects now have a `signal` property. You can now attach an event listener to the `signal` and perform tasks when the request to your Worker is canceled by the client. + +This lets you do things like clean up after the request or write to logs before your Worker is aborted. + + +```ts +import { WorkerEntrypoint } from 'cloudflare:workers'; + +class Example extends WorkerEntrypoint { + async fetch(req: Request): Response { + req.signal.onabort = () => { + console.log('The request was aborted!'); + }; + + const { readable, writable } = new IdentityTransformStream(); + this.ctx.waitUntil(this.sendPing(writable)); + return new Response(readable); + } + + async sendPing(writable: WritableStream): void { + const writer = writable.getWriter(); + const enc = new TextEncoder(); + + for (;;) { + // Send 'ping' every second to keep the connection alive + await writer.write(enc.encode('ping')); + scheduler.wait(1000); + } + } +} + + +For more information see the [`Request` documentation](/workers/runtime-apis/request). diff --git a/src/content/compatibility-flags/request-signal-passthrough.md b/src/content/compatibility-flags/request-signal-passthrough.md new file mode 100644 index 000000000000000..510aba63feec990 --- /dev/null +++ b/src/content/compatibility-flags/request-signal-passthrough.md @@ -0,0 +1,15 @@ +--- +_build: + publishResources: false + render: never + list: never + +name: "Request.signal is passed through to subrequests" +sort_date: "2025-03-14" +enable_date: "request_signal_passthrough" +disable_date: "no_request_signal_passthrough" +--- + +When this flag is enabled, the `signal` property of the incoming request to a Worker follows Web-standard behaviour, and will be passed through when the request is cloned, or a new request is created from it. As a result, if the incoming request is canceled by the client, all outgoing subrequests will also be canceled, unless the `signal` property is explicitly modified or cleared. + +Because support for Request.signal was recently added to Cloudflare Workers, this behaviour will not apply to existing Workers, and can also be disabled by setting `no_request_signal_passthrough`. diff --git a/src/content/docs/workers/runtime-apis/request.mdx b/src/content/docs/workers/runtime-apis/request.mdx index 93f4cb620f56af7..ae3d82716350367 100644 --- a/src/content/docs/workers/runtime-apis/request.mdx +++ b/src/content/docs/workers/runtime-apis/request.mdx @@ -88,6 +88,8 @@ An object containing properties that you want to apply to the request. * The redirect mode to use: `follow`, `error`, or `manual`. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. +* `signal` AbortSignal optional + * If provided, the request can be canceled by triggering an abort on the corresponding `AbortController`. #### The `cf` property (`RequestInitCfProperties`) @@ -194,6 +196,9 @@ All properties of an incoming `Request` object (the request you receive from the * The redirect mode to use: `follow`, `error`, or `manual`. The `fetch` method will automatically follow redirects if the redirect mode is set to `follow`. If set to `manual`, the `3xx` redirect response will be returned to the caller as-is. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. +* `signal` AbortSignal read-only + * The `AbortSignal` corresponding to this request. By attaching an event listener to the signal, a Worker can take action when the request made to it is canceled by the client. For example, the Worker may write log entries or perform cleanup tasks before being aborted. + * `url` string read-only * Contains the URL of the request. From 08ac85e92b02fbbec066eddd58f4a934b9700ab0 Mon Sep 17 00:00:00 2001 From: Nicholas Paun Date: Fri, 14 Mar 2025 13:11:20 -0700 Subject: [PATCH 2/5] Apply improved wording from @irvinebroque Co-authored-by: Brendan Irvine-Broque --- .../workers/2025-03-14-handle-request-cancellation.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx index 79ffbafc7aab329..56605781bef4f0f 100644 --- a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx +++ b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx @@ -1,5 +1,5 @@ --- -title: Handle request cancellation with Request.signal +title: Handle incoming request cancellation in Workers with Request.signal description: Workers can now add event listeners on Request.signal and perform tasks when the request is cancelled products: - workers @@ -8,9 +8,9 @@ date: 2025-03-14T01:00:00Z import { Render, TypeScriptExample } from "~/components"; -`Request` objects now have a `signal` property. You can now attach an event listener to the `signal` and perform tasks when the request to your Worker is canceled by the client. +In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. -This lets you do things like clean up after the request or write to logs before your Worker is aborted. +This allows you to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: ```ts From ec9f8ce2c59e87a461f15f06df8945a7bdd330ff Mon Sep 17 00:00:00 2001 From: Nicholas Paun Date: Fri, 14 Mar 2025 14:43:47 -0700 Subject: [PATCH 3/5] Improve example and include it in the documentation of Request --- ...2025-03-14-handle-request-cancellation.mdx | 30 ++-------------- .../request-signal-passthrough.md | 5 +-- .../docs/workers/runtime-apis/request.mdx | 6 ++-- .../workers/request-signal-example.mdx | 34 +++++++++++++++++++ 4 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 src/content/partials/workers/request-signal-example.mdx diff --git a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx index 56605781bef4f0f..33155d7a77cd67f 100644 --- a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx +++ b/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx @@ -6,38 +6,12 @@ products: date: 2025-03-14T01:00:00Z --- -import { Render, TypeScriptExample } from "~/components"; +import { Render } from "~/components"; In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. This allows you to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: - -```ts -import { WorkerEntrypoint } from 'cloudflare:workers'; - -class Example extends WorkerEntrypoint { - async fetch(req: Request): Response { - req.signal.onabort = () => { - console.log('The request was aborted!'); - }; - - const { readable, writable } = new IdentityTransformStream(); - this.ctx.waitUntil(this.sendPing(writable)); - return new Response(readable); - } - - async sendPing(writable: WritableStream): void { - const writer = writable.getWriter(); - const enc = new TextEncoder(); - - for (;;) { - // Send 'ping' every second to keep the connection alive - await writer.write(enc.encode('ping')); - scheduler.wait(1000); - } - } -} - + For more information see the [`Request` documentation](/workers/runtime-apis/request). diff --git a/src/content/compatibility-flags/request-signal-passthrough.md b/src/content/compatibility-flags/request-signal-passthrough.md index 510aba63feec990..617609b2b6aebe2 100644 --- a/src/content/compatibility-flags/request-signal-passthrough.md +++ b/src/content/compatibility-flags/request-signal-passthrough.md @@ -6,8 +6,9 @@ _build: name: "Request.signal is passed through to subrequests" sort_date: "2025-03-14" -enable_date: "request_signal_passthrough" -disable_date: "no_request_signal_passthrough" +enable_date: "2025-04-14" +enable_flag: "request_signal_passthrough" +disable_flag: "no_request_signal_passthrough" --- When this flag is enabled, the `signal` property of the incoming request to a Worker follows Web-standard behaviour, and will be passed through when the request is cloned, or a new request is created from it. As a result, if the incoming request is canceled by the client, all outgoing subrequests will also be canceled, unless the `signal` property is explicitly modified or cleared. diff --git a/src/content/docs/workers/runtime-apis/request.mdx b/src/content/docs/workers/runtime-apis/request.mdx index ae3d82716350367..35d57a2177e6266 100644 --- a/src/content/docs/workers/runtime-apis/request.mdx +++ b/src/content/docs/workers/runtime-apis/request.mdx @@ -6,7 +6,7 @@ description: Interface that represents an HTTP request. --- -import { Type, MetaInfo } from "~/components"; +import { Type, MetaInfo, Render } from "~/components"; The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request) interface represents an HTTP request and is part of the [Fetch API](/workers/runtime-apis/fetch/). @@ -197,7 +197,9 @@ All properties of an incoming `Request` object (the request you receive from the * The redirect mode to use: `follow`, `error`, or `manual`. The `fetch` method will automatically follow redirects if the redirect mode is set to `follow`. If set to `manual`, the `3xx` redirect response will be returned to the caller as-is. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. * `signal` AbortSignal read-only - * The `AbortSignal` corresponding to this request. By attaching an event listener to the signal, a Worker can take action when the request made to it is canceled by the client. For example, the Worker may write log entries or perform cleanup tasks before being aborted. + * The `AbortSignal` corresponding to this request. By attaching an event listener to the signal, you can perform cleanup tasks or write to logs before your Worker's invocation ends. + For example, if you run the Worker below, and then abort the request from the client, a log will be written: + * `url` string read-only diff --git a/src/content/partials/workers/request-signal-example.mdx b/src/content/partials/workers/request-signal-example.mdx new file mode 100644 index 000000000000000..be69879912d7382 --- /dev/null +++ b/src/content/partials/workers/request-signal-example.mdx @@ -0,0 +1,34 @@ +--- +{} + +--- + +import { TypeScriptExample } from "~/components"; + + +```ts +export default { + async fetch(request, env, ctx): Promise { + request.signal.onabort = () => { + console.log('The request was aborted!'); + }; + + const { readable, writable } = new IdentityTransformStream(); + ctx.waitUntil(sendPing(writable)); + return new Response(readable); + }, +} satisfies ExportedHandler; + + +async function sendPing(writable: WritableStream): Promise { + const writer = writable.getWriter(); + const enc = new TextEncoder(); + + for (;;) { + // Send 'ping' every second to keep the connection alive + await writer.write(enc.encode('ping\r\n')); + await scheduler.wait(1000); + } + } +``` + From 0e1e297ec7db05f2a1ab65ae4ba9543d6196c667 Mon Sep 17 00:00:00 2001 From: Nicholas Paun Date: Thu, 22 May 2025 10:09:40 -0700 Subject: [PATCH 4/5] Update docs to indicate a compat flag is required --- ...025-05-22-handle-request-cancellation.mdx} | 5 ++-- .../request-signal-passthrough.md | 16 ----------- .../compatibility-flags/request-signal.md | 8 ++++++ .../docs/workers/runtime-apis/request.mdx | 4 +-- .../workers/request-signal-example.mdx | 27 +++++++++---------- 5 files changed, 26 insertions(+), 34 deletions(-) rename src/content/changelog/workers/{2025-03-14-handle-request-cancellation.mdx => 2025-05-22-handle-request-cancellation.mdx} (85%) delete mode 100644 src/content/compatibility-flags/request-signal-passthrough.md create mode 100644 src/content/compatibility-flags/request-signal.md diff --git a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx b/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx similarity index 85% rename from src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx rename to src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx index 33155d7a77cd67f..19e2e03d3ba6050 100644 --- a/src/content/changelog/workers/2025-03-14-handle-request-cancellation.mdx +++ b/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx @@ -3,15 +3,16 @@ title: Handle incoming request cancellation in Workers with Request.signal description: Workers can now add event listeners on Request.signal and perform tasks when the request is cancelled products: - workers -date: 2025-03-14T01:00:00Z +date: 2025-05-22T01:00:00Z --- import { Render } from "~/components"; -In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. +In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. To use this feature, you must set the `enable_request_signal` compatibility flag. This allows you to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: + For more information see the [`Request` documentation](/workers/runtime-apis/request). diff --git a/src/content/compatibility-flags/request-signal-passthrough.md b/src/content/compatibility-flags/request-signal-passthrough.md deleted file mode 100644 index 617609b2b6aebe2..000000000000000 --- a/src/content/compatibility-flags/request-signal-passthrough.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -_build: - publishResources: false - render: never - list: never - -name: "Request.signal is passed through to subrequests" -sort_date: "2025-03-14" -enable_date: "2025-04-14" -enable_flag: "request_signal_passthrough" -disable_flag: "no_request_signal_passthrough" ---- - -When this flag is enabled, the `signal` property of the incoming request to a Worker follows Web-standard behaviour, and will be passed through when the request is cloned, or a new request is created from it. As a result, if the incoming request is canceled by the client, all outgoing subrequests will also be canceled, unless the `signal` property is explicitly modified or cleared. - -Because support for Request.signal was recently added to Cloudflare Workers, this behaviour will not apply to existing Workers, and can also be disabled by setting `no_request_signal_passthrough`. diff --git a/src/content/compatibility-flags/request-signal.md b/src/content/compatibility-flags/request-signal.md new file mode 100644 index 000000000000000..58285968f582bc7 --- /dev/null +++ b/src/content/compatibility-flags/request-signal.md @@ -0,0 +1,8 @@ +--- +name: "Enable `Request.signal` for incoming requests" +sort_date: "2025-05-22" +enable_flag: "enable_request_signal" +disable_flag: "disable_request_signal" +--- + +When you use the `enable_request_signal` compatibility flag, you can attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. \ No newline at end of file diff --git a/src/content/docs/workers/runtime-apis/request.mdx b/src/content/docs/workers/runtime-apis/request.mdx index 35d57a2177e6266..a3a2a2f2400d8be 100644 --- a/src/content/docs/workers/runtime-apis/request.mdx +++ b/src/content/docs/workers/runtime-apis/request.mdx @@ -197,8 +197,8 @@ All properties of an incoming `Request` object (the request you receive from the * The redirect mode to use: `follow`, `error`, or `manual`. The `fetch` method will automatically follow redirects if the redirect mode is set to `follow`. If set to `manual`, the `3xx` redirect response will be returned to the caller as-is. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. * `signal` AbortSignal read-only - * The `AbortSignal` corresponding to this request. By attaching an event listener to the signal, you can perform cleanup tasks or write to logs before your Worker's invocation ends. - For example, if you run the Worker below, and then abort the request from the client, a log will be written: + * The `AbortSignal` corresponding to this request. If you use the `enable_request_signal` compatibility flag, you can attach an event listener to the signal, and perform cleanup tasks or write to logs before your Worker's invocation ends. + For example, if you run the Worker below, and then abort the request from the client, a log will be written: * `url` string read-only diff --git a/src/content/partials/workers/request-signal-example.mdx b/src/content/partials/workers/request-signal-example.mdx index be69879912d7382..59e67c1b4b8e8bf 100644 --- a/src/content/partials/workers/request-signal-example.mdx +++ b/src/content/partials/workers/request-signal-example.mdx @@ -9,26 +9,25 @@ import { TypeScriptExample } from "~/components"; ```ts export default { async fetch(request, env, ctx): Promise { - request.signal.onabort = () => { + request.signal.addEventListener('abort', () => { console.log('The request was aborted!'); - }; - + }); + const { readable, writable } = new IdentityTransformStream(); - ctx.waitUntil(sendPing(writable)); - return new Response(readable); + sendPing(writable); + return new Response(readable, { headers: { 'Content-Type': 'text/plain' } }); }, } satisfies ExportedHandler; - async function sendPing(writable: WritableStream): Promise { - const writer = writable.getWriter(); - const enc = new TextEncoder(); + const writer = writable.getWriter(); + const enc = new TextEncoder(); - for (;;) { - // Send 'ping' every second to keep the connection alive - await writer.write(enc.encode('ping\r\n')); - await scheduler.wait(1000); - } - } + for (;;) { + // Send 'ping' every second to keep the connection alive + await writer.write(enc.encode('ping\r\n')); + await scheduler.wait(1000); + } +} ``` From 50c33fcb25ce1a6e403f5576aeb7104273064b19 Mon Sep 17 00:00:00 2001 From: Nicholas Paun Date: Tue, 27 May 2025 09:50:43 -0700 Subject: [PATCH 5/5] Wording improvements suggested by @mikenomitch --- .../workers/2025-05-22-handle-request-cancellation.mdx | 6 +++--- src/content/compatibility-flags/request-signal.md | 2 +- src/content/docs/workers/runtime-apis/request.mdx | 2 +- src/content/partials/workers/request-signal-example.mdx | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx b/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx index 19e2e03d3ba6050..89205f2b639ab61 100644 --- a/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx +++ b/src/content/changelog/workers/2025-05-22-handle-request-cancellation.mdx @@ -1,6 +1,6 @@ --- title: Handle incoming request cancellation in Workers with Request.signal -description: Workers can now add event listeners on Request.signal and perform tasks when the request is cancelled +description: Workers can now add event listeners on Request.signal and perform tasks when the request is cancelled by the client products: - workers date: 2025-05-22T01:00:00Z @@ -8,9 +8,9 @@ date: 2025-05-22T01:00:00Z import { Render } from "~/components"; -In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. To use this feature, you must set the `enable_request_signal` compatibility flag. +In Cloudflare Workers, you can now attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal). This allows you to perform tasks when the request to your Worker is canceled by the client. To use this feature, you must set the [`enable_request_signal`](/workers/configuration/compatibility-flags/#enable-requestsignal-for-incoming-requests) compatibility flag. -This allows you to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: +You can use a listener to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: diff --git a/src/content/compatibility-flags/request-signal.md b/src/content/compatibility-flags/request-signal.md index 58285968f582bc7..e60e43e66403e2f 100644 --- a/src/content/compatibility-flags/request-signal.md +++ b/src/content/compatibility-flags/request-signal.md @@ -5,4 +5,4 @@ enable_flag: "enable_request_signal" disable_flag: "disable_request_signal" --- -When you use the `enable_request_signal` compatibility flag, you can attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal), and perform tasks when the request to your Worker is canceled by the client. \ No newline at end of file +When you use the `enable_request_signal` compatibility flag, you can attach an event listener to [`Request`](/workers/runtime-apis/request/) objects, using the [`signal` property](https://developer.mozilla.org/en-US/docs/Web/API/Request/signal). This allows you to perform tasks when the request to your Worker is canceled by the client. \ No newline at end of file diff --git a/src/content/docs/workers/runtime-apis/request.mdx b/src/content/docs/workers/runtime-apis/request.mdx index a3a2a2f2400d8be..0571113efb32b4a 100644 --- a/src/content/docs/workers/runtime-apis/request.mdx +++ b/src/content/docs/workers/runtime-apis/request.mdx @@ -197,7 +197,7 @@ All properties of an incoming `Request` object (the request you receive from the * The redirect mode to use: `follow`, `error`, or `manual`. The `fetch` method will automatically follow redirects if the redirect mode is set to `follow`. If set to `manual`, the `3xx` redirect response will be returned to the caller as-is. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. * `signal` AbortSignal read-only - * The `AbortSignal` corresponding to this request. If you use the `enable_request_signal` compatibility flag, you can attach an event listener to the signal, and perform cleanup tasks or write to logs before your Worker's invocation ends. + * The `AbortSignal` corresponding to this request. If you use the [`enable_request_signal`](/workers/configuration/compatibility-flags/#enable-requestsignal-for-incoming-requests) compatibility flag, you can attach an event listener to the signal. This allows you to perform cleanup tasks or write to logs before your Worker's invocation ends. For example, if you run the Worker below, and then abort the request from the client, a log will be written: diff --git a/src/content/partials/workers/request-signal-example.mdx b/src/content/partials/workers/request-signal-example.mdx index 59e67c1b4b8e8bf..e07caed0427765e 100644 --- a/src/content/partials/workers/request-signal-example.mdx +++ b/src/content/partials/workers/request-signal-example.mdx @@ -9,6 +9,8 @@ import { TypeScriptExample } from "~/components"; ```ts export default { async fetch(request, env, ctx): Promise { + // This sets up an event listener that will be called if the client disconnects from your + // worker. request.signal.addEventListener('abort', () => { console.log('The request was aborted!'); });