Skip to content

Commit a8baa38

Browse files
authored
DX-1938: update signal parameter to accept function (#1379)
* feat: update signal parameter to accept function * fix: add timeout test
1 parent 09f2eba commit a8baa38

File tree

4 files changed

+56
-10
lines changed

4 files changed

+56
-10
lines changed

pkg/http.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, test, expect } from "bun:test";
22
import { Redis } from "../platforms/nodejs";
3+
import { serve } from "bun";
4+
5+
const MOCK_SERVER_PORT = 8080;
6+
const SERVER_URL = `http://localhost:${MOCK_SERVER_PORT}`;
37

48
describe("http", () => {
59
test("should terminate after sleeping 5 times", async () => {
@@ -20,4 +24,41 @@ describe("http", () => {
2024
// if the Promise.race doesn't throw, that means the retries took longer than 4.5s
2125
expect(throws).toThrow("fetch() URL is invalid");
2226
});
27+
28+
test("should throw on request timeouts", async () => {
29+
const server = serve({
30+
async fetch(request) {
31+
const body = await request.text();
32+
33+
if (body.includes("zed")) {
34+
return new Response(JSON.stringify({ result: '"zed-result"' }), { status: 200 });
35+
}
36+
37+
await new Promise((resolve) => setTimeout(resolve, 5000));
38+
return new Response("Hello World");
39+
},
40+
port: MOCK_SERVER_PORT,
41+
});
42+
43+
const redis = new Redis({
44+
url: SERVER_URL,
45+
token: "non-existent",
46+
signal: () => AbortSignal.timeout(1000), // set a timeout of 1 second
47+
// set to false since mock server doesn't return a response
48+
// for a pipeline. If you want to test pipelining, you can set it to true
49+
// and make the mock server return a pipeline response.
50+
enableAutoPipelining: false,
51+
});
52+
53+
try {
54+
expect(redis.get("foo")).rejects.toThrow("The operation timed out.");
55+
expect(redis.get("bar")).rejects.toThrow("The operation timed out.");
56+
expect(redis.get("zed")).resolves.toBe("zed-result");
57+
} catch (error) {
58+
server.stop(true);
59+
throw error;
60+
} finally {
61+
server.stop(true);
62+
}
63+
});
2364
});

pkg/http.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export type HttpClientConfig = {
125125
options?: Options;
126126
retry?: RetryConfig;
127127
agent?: any;
128-
signal?: AbortSignal;
128+
signal?: AbortSignal | (() => AbortSignal);
129129
keepAlive?: boolean;
130130

131131
/**
@@ -141,7 +141,7 @@ export class HttpClient implements Requester {
141141
public readonly options: {
142142
backend?: string;
143143
agent: any;
144-
signal?: AbortSignal;
144+
signal?: HttpClientConfig["signal"];
145145
responseEncoding?: false | "base64";
146146
cache?: CacheSetting;
147147
keepAlive: boolean;
@@ -218,6 +218,9 @@ export class HttpClient implements Requester {
218218
const requestUrl = [this.baseUrl, ...(req.path ?? [])].join("/");
219219
const isEventStream = requestHeaders.Accept === "text/event-stream";
220220

221+
const signal = req.signal ?? this.options.signal;
222+
const isSignalFunction = typeof signal === "function";
223+
221224
const requestOptions: RequestInit & { backend?: string; agent?: any } = {
222225
//@ts-expect-error this should throw due to bun regression
223226
cache: this.options.cache,
@@ -226,7 +229,7 @@ export class HttpClient implements Requester {
226229
body: JSON.stringify(req.body),
227230
keepalive: this.options.keepAlive,
228231
agent: this.options.agent,
229-
signal: req.signal ?? this.options.signal,
232+
signal: isSignalFunction ? signal() : signal,
230233

231234
/**
232235
* Fastly specific
@@ -256,13 +259,15 @@ export class HttpClient implements Requester {
256259
res = await fetch(requestUrl, requestOptions);
257260
break;
258261
} catch (error_) {
259-
if (this.options.signal?.aborted) {
262+
if (requestOptions.signal?.aborted && isSignalFunction) {
263+
throw error_;
264+
} else if (requestOptions.signal?.aborted) {
260265
const myBlob = new Blob([
261-
JSON.stringify({ result: this.options.signal.reason ?? "Aborted" }),
266+
JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" }),
262267
]);
263268
const myOptions = {
264269
status: 200,
265-
statusText: this.options.signal.reason ?? "Aborted",
270+
statusText: requestOptions.signal.reason ?? "Aborted",
266271
};
267272
res = new Response(myBlob, myOptions);
268273
break;

platforms/cloudflare.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RequesterConfig } from "../pkg/http";
1+
import type { HttpClientConfig, RequesterConfig } from "../pkg/http";
22
import { HttpClient } from "../pkg/http";
33
import * as core from "../pkg/redis";
44
import { VERSION } from "../version";
@@ -27,7 +27,7 @@ export type RedisConfigCloudflare = {
2727
* The signal will allow aborting requests on the fly.
2828
* For more check: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
2929
*/
30-
signal?: AbortSignal;
30+
signal?: HttpClientConfig["signal"];
3131
keepAlive?: boolean;
3232

3333
/**

platforms/nodejs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/ban-ts-comment */
22
// deno-lint-ignore-file
33

4-
import type { Requester, RequesterConfig } from "../pkg/http";
4+
import type { HttpClientConfig, Requester, RequesterConfig } from "../pkg/http";
55
import { HttpClient } from "../pkg/http";
66

77
import * as core from "../pkg/redis";
@@ -49,7 +49,7 @@ export type RedisConfigNodejs = {
4949
* The signal will allow aborting requests on the fly.
5050
* For more check: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
5151
*/
52-
signal?: AbortSignal;
52+
signal?: HttpClientConfig["signal"];
5353
latencyLogging?: boolean;
5454
agent?: unknown;
5555
keepAlive?: boolean;

0 commit comments

Comments
 (0)