Skip to content

Commit 25e90ab

Browse files
authored
fix: global dispatcher (#583)
1 parent b88c779 commit 25e90ab

File tree

6 files changed

+51
-10
lines changed

6 files changed

+51
-10
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"@types/qs": "^6.9.7",
7171
"@types/selfsigned": "^2.0.1",
7272
"@types/tar-stream": "^2.2.2",
73-
"@vitest/coverage-v8": "^1.3.1",
73+
"@vitest/coverage-v8": "^3.0.0",
7474
"busboy": "^1.6.0",
7575
"cross-env": "^7.0.3",
7676
"eslint": "^8.25.0",
@@ -83,7 +83,7 @@
8383
"tshy": "^1.0.0",
8484
"tshy-after": "^1.0.0",
8585
"typescript": "^5.0.4",
86-
"vitest": "^1.3.1"
86+
"vitest": "^3.0.0"
8787
},
8888
"engines": {
8989
"node": ">= 14.19.3"

src/HttpClient.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import {
2222
request as undiciRequest,
2323
Dispatcher,
2424
Agent,
25-
getGlobalDispatcher,
2625
Pool,
26+
getGlobalDispatcher,
27+
MockAgent,
2728
} from 'undici';
2829
import undiciSymbols from 'undici/lib/core/symbols.js';
2930
import { FormData as FormDataNode } from 'formdata-node';
@@ -207,10 +208,23 @@ export class HttpClient extends EventEmitter {
207208
}
208209

209210
getDispatcher() {
210-
return this.#dispatcher ?? getGlobalDispatcher();
211+
if (this.#dispatcher) {
212+
return this.#dispatcher;
213+
}
214+
// In a multi-version undici environment
215+
// the global dispatcher is the highest version of undici
216+
// which will conflict with the maxRedirects field and report an error
217+
// so we need to create it that use 5.x version
218+
const globalDispatcher = getGlobalDispatcher();
219+
if (!(globalDispatcher instanceof Agent) && !(globalDispatcher instanceof MockAgent)) {
220+
const dispatcher = globalDispatcher.constructor.name === 'MockAgent' ? new MockAgent() : new Agent();
221+
this.setDispatcher(dispatcher);
222+
return dispatcher;
223+
}
224+
return globalDispatcher;
211225
}
212226

213-
setDispatcher(dispatcher: Dispatcher) {
227+
setDispatcher(dispatcher?: Dispatcher) {
214228
this.#dispatcher = dispatcher;
215229
}
216230

@@ -415,7 +429,7 @@ export class HttpClient extends EventEmitter {
415429
headers,
416430
bodyTimeout,
417431
opaque: internalOpaque,
418-
dispatcher: args.dispatcher ?? this.#dispatcher,
432+
dispatcher: args.dispatcher ?? this.getDispatcher(),
419433
signal: args.signal,
420434
};
421435
if (typeof args.highWaterMark === 'number') {

test/diagnostics_channel.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { strict as assert } from 'node:assert';
22
import diagnosticsChannel from 'node:diagnostics_channel';
33
import { describe, it, beforeEach, afterEach } from 'vitest';
4-
import urllib from '../src';
5-
import type {
4+
import urllib, { getGlobalDispatcher, setGlobalDispatcher } from '../src';
5+
import {
6+
MockAgent,
67
RequestDiagnosticsMessage,
78
ResponseDiagnosticsMessage,
89
} from '../src';
@@ -13,14 +14,22 @@ import { sleep } from './utils';
1314
describe('diagnostics_channel.test.ts', () => {
1415
let close: any;
1516
let _url: string;
17+
18+
let mockAgent: MockAgent;
19+
const globalAgent = getGlobalDispatcher();
20+
1621
beforeEach(async () => {
1722
const { closeServer, url } = await startServer();
1823
close = closeServer;
1924
_url = url;
25+
mockAgent = new MockAgent();
26+
setGlobalDispatcher(mockAgent);
2027
});
2128

2229
afterEach(async () => {
2330
await close();
31+
setGlobalDispatcher(globalAgent);
32+
await mockAgent.close();
2433
});
2534

2635
it('should support trace socket info by undici:client:sendHeaders and undici:request:trailers', async () => {

test/index.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,15 @@ describe('index.test.ts', () => {
180180
const globalAgent = getGlobalDispatcher();
181181
beforeEach(() => {
182182
mockAgent = new MockAgent();
183+
const httpClient = getDefaultHttpClient();
184+
httpClient.setDispatcher(mockAgent);
183185
setGlobalDispatcher(mockAgent);
184186
});
185187

186188
afterEach(async () => {
187189
setGlobalDispatcher(globalAgent);
190+
const httpClient = getDefaultHttpClient();
191+
httpClient.setDispatcher();
188192
await mockAgent.close();
189193
});
190194

test/options.retry.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { strict as assert } from 'node:assert';
22
import { createWriteStream, createReadStream } from 'node:fs';
33
import { describe, it, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
4-
import urllib from '../src';
4+
import urllib, { getGlobalDispatcher, MockAgent, setGlobalDispatcher } from '../src';
55
import { startServer } from './fixtures/server';
66
import { readableToString, createTempfile } from './utils';
77

@@ -11,6 +11,9 @@ describe('options.retry.test.ts', () => {
1111
let tmpfile: string;
1212
let cleanup: any;
1313

14+
let mockAgent: MockAgent;
15+
const globalAgent = getGlobalDispatcher();
16+
1417
beforeAll(async () => {
1518
const { closeServer, url } = await startServer();
1619
close = closeServer;
@@ -24,9 +27,13 @@ describe('options.retry.test.ts', () => {
2427
const item = await createTempfile();
2528
tmpfile = item.tmpfile;
2629
cleanup = item.cleanup;
30+
mockAgent = new MockAgent();
31+
setGlobalDispatcher(mockAgent);
2732
});
2833
afterEach(async () => {
2934
await cleanup();
35+
setGlobalDispatcher(globalAgent);
36+
await mockAgent.close();
3037
});
3138

3239
it('should not retry on 400', async () => {

test/options.timing.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
import { strict as assert } from 'node:assert';
22
import { describe, it, beforeAll, afterAll } from 'vitest';
3-
import urllib from '../src';
3+
import urllib, { getGlobalDispatcher, MockAgent, setGlobalDispatcher } from '../src';
44
import { RawResponseWithMeta } from '../src/Response';
55
import { startServer } from './fixtures/server';
66
import { sleep } from './utils';
77

88
describe('options.timing.test.ts', () => {
99
let close: any;
1010
let _url: string;
11+
12+
let mockAgent: MockAgent;
13+
const globalAgent = getGlobalDispatcher();
1114
beforeAll(async () => {
1215
const { closeServer, url } = await startServer();
1316
close = closeServer;
1417
_url = url;
18+
mockAgent = new MockAgent();
19+
setGlobalDispatcher(mockAgent);
1520
});
1621

1722
afterAll(async () => {
1823
await close();
24+
setGlobalDispatcher(globalAgent);
25+
await mockAgent.close();
1926
});
2027

2128
it('should timing = true work', async () => {

0 commit comments

Comments
 (0)