Skip to content
File

Blob: src/workerd/api/tests/request-signal-passthrough.js

javascript59 lines
1// Copyright (c) 2025 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { WorkerEntrypoint } from 'cloudflare:workers';
5import assert from 'node:assert';
6 
7let reqSignalReason = null;
8let rpcSignalReason = null;
9 
10export class OtherServer extends WorkerEntrypoint {
11 async echo(val) {
12 return val;
13 }
14 
15 async saveReqReason(req) {
16 rpcSignalReason = req.signal.reason?.message;
17 }
18}
19 
20export class Server extends WorkerEntrypoint {
21 async fetch(req) {
22 // req.signal is in the "this signal" slot. Because request_signal_passthrough is set, there is
23 // no special handling.
24 req.signal.onabort = () => {
25 // If we're here we know that the reason is filled in on req.signal
26 reqSignalReason = req.signal.reason.message;
27 
28 // Ensure that the incoming request signal is in the "signal" slot, which is the one we
29 // actually serialize currently.
30 const newReq = req.clone();
31 
32 // And if the signal really is included in serialization, OtherServer will be able to read
33 // the reason.
34 this.ctx.waitUntil(this.env.OtherServer.saveReqReason(newReq));
35 };
36 
37 // Just hang and wait for the client to give up
38 await scheduler.wait(86400);
39 }
40}
41 
42export const requestPassedOverRpcIncludesSignal = {
43 async test(ctrl, env, ctx) {
44 await assert.rejects(
45 () =>
46 env.Server.fetch('http://example.com', {
47 signal: AbortSignal.timeout(100),
48 }),
49 { message: 'The operation was aborted due to timeout' }
50 );
51 
52 // Yield so the workers can write the abort reasons
53 await scheduler.wait(0);
54 
55 assert.strictEqual(reqSignalReason, 'The client has disconnected');
56 assert.strictEqual(rpcSignalReason, 'The client has disconnected');
57 },
58};