Skip to content

Commit 0068aaf

Browse files
committed
fix: global dispatcher
1 parent b88c779 commit 0068aaf

4 files changed

Lines changed: 53 additions & 7 deletions

File tree

src/HttpClient.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import {
2222
request as undiciRequest,
2323
Dispatcher,
2424
Agent,
25-
getGlobalDispatcher,
2625
Pool,
26+
getGlobalDispatcher,
27+
MockAgent,
28+
setGlobalDispatcher,
2729
} from 'undici';
2830
import undiciSymbols from 'undici/lib/core/symbols.js';
2931
import { FormData as FormDataNode } from 'formdata-node';
@@ -207,7 +209,28 @@ export class HttpClient extends EventEmitter {
207209
}
208210

209211
getDispatcher() {
210-
return this.#dispatcher ?? getGlobalDispatcher();
212+
if (this.#dispatcher) {
213+
return this.#dispatcher;
214+
}
215+
return this.safeGetGlobalDispatcher();
216+
}
217+
218+
safeGetGlobalDispatcher() {
219+
// In a multi-version undici environment
220+
// the global dispatcher is the highest version of undici
221+
// which will conflict with the maxRedirects field and report an error
222+
// so we need to create it that use 5.x version
223+
const globalDispatcher = getGlobalDispatcher();
224+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
225+
// @ts-ignore
226+
// eslint-disable-next-line no-proto
227+
const proto = globalDispatcher.__proto__;
228+
if (proto !== Agent.prototype && proto !== MockAgent.prototype) {
229+
const dispatcher = proto.constructor.name === 'MockAgent' ? new MockAgent() : new Agent();
230+
setGlobalDispatcher(dispatcher);
231+
return dispatcher;
232+
}
233+
return globalDispatcher;
211234
}
212235

213236
setDispatcher(dispatcher: Dispatcher) {
@@ -415,7 +438,7 @@ export class HttpClient extends EventEmitter {
415438
headers,
416439
bodyTimeout,
417440
opaque: internalOpaque,
418-
dispatcher: args.dispatcher ?? this.#dispatcher,
441+
dispatcher: args.dispatcher ?? this.getDispatcher(),
419442
signal: args.signal,
420443
};
421444
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/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)