Skip to content

Commit 73d9a1e

Browse files
authored
fix: correctly set Dispatcher prototype for ProxyAgent (#451)
In an attempt to bundle only a subset of Undici code, we forgot to take some side-effect into account.
1 parent 07b58cc commit 73d9a1e

File tree

5 files changed

+72
-28
lines changed

5 files changed

+72
-28
lines changed

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
"v8-compile-cache": "^2.3.0",
5353
"which": "^4.0.0"
5454
},
55+
"resolutions": {
56+
"undici-types": "6.x"
57+
},
5558
"scripts": {
5659
"build": "rm -rf dist shims && run build:bundle && ts-node ./mkshims.ts",
5760
"build:bundle": "esbuild ./sources/_lib.ts --bundle --platform=node --target=node18.17.0 --external:corepack --outfile='./dist/lib/corepack.cjs' --resolve-extensions='.ts,.mjs,.js'",

sources/httpUtils.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ export async function fetchUrlStream(input: string | URL, init?: RequestInit) {
9292
return stream;
9393
}
9494

95+
let ProxyAgent: typeof import('undici').ProxyAgent;
96+
9597
async function getProxyAgent(input: string | URL) {
9698
const {getProxyForUrl} = await import(`proxy-from-env`);
9799

@@ -100,11 +102,20 @@ async function getProxyAgent(input: string | URL) {
100102

101103
if (!proxy) return undefined;
102104

103-
// Doing a deep import here since undici isn't tree-shakeable
104-
const {default: ProxyAgent} = (await import(
105-
// @ts-expect-error No types for this specific file
106-
`undici/lib/proxy-agent.js`
107-
)) as { default: typeof import('undici').ProxyAgent };
105+
if (ProxyAgent == null) {
106+
// Doing a deep import here since undici isn't tree-shakeable
107+
const [api, Dispatcher, _ProxyAgent] = await Promise.all([
108+
// @ts-expect-error internal module is untyped
109+
import(`undici/lib/api/index.js`),
110+
// @ts-expect-error internal module is untyped
111+
import(`undici/lib/dispatcher/dispatcher.js`),
112+
// @ts-expect-error internal module is untyped
113+
import(`undici/lib/dispatcher/proxy-agent.js`),
114+
]);
115+
116+
Object.assign(Dispatcher.default.prototype, api.default);
117+
ProxyAgent = _ProxyAgent.default;
118+
}
108119

109120
return new ProxyAgent(proxy);
110121
}

tests/_registryServer.mjs

+42-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {createHash} from 'node:crypto';
22
import {once} from 'node:events';
33
import {createServer} from 'node:http';
4+
import {connect} from 'node:net';
45
import {gzipSync} from 'node:zlib';
56

67
function createSimpleTarArchive(fileName, fileContent, mode = 0o644) {
@@ -110,12 +111,47 @@ const server = createServer((req, res) => {
110111
res.writeHead(500).end(`Internal Error`);
111112
throw new Error(`unsupported request`, {cause: {url: req.url, packageName}});
112113
}
113-
}).listen(0, `localhost`);
114+
});
115+
116+
if (process.env.AUTH_TYPE === `PROXY`) {
117+
const proxy = createServer((req, res) => {
118+
res.writeHead(200, {[`Content-Type`]: `text/plain`});
119+
res.end(`okay`);
120+
});
121+
proxy.on(`connect`, (req, clientSocket, head) => {
122+
if (req.url !== `example.com:80`) {
123+
// Reject all requests except those to `example.com`
124+
clientSocket.end(`HTTP/1.1 404 Not Found\r\n\r\n`);
125+
return;
126+
}
127+
const {address, port} = server.address();
128+
const serverSocket = connect(port, address, () => {
129+
clientSocket.write(`HTTP/1.1 200 Connection Established\r\n` +
130+
`Proxy-agent: Node.js-Proxy\r\n` +
131+
`\r\n`);
132+
serverSocket.write(head);
133+
serverSocket.pipe(clientSocket);
134+
clientSocket.pipe(serverSocket);
135+
});
136+
});
137+
proxy.listen(0, `localhost`);
138+
await once(proxy, `listening`);
139+
const {address, port} = proxy.address();
140+
process.env.ALL_PROXY = `http://${address.includes(`:`) ? `[${address}]` : address}:${port}`;
141+
142+
proxy.unref();
143+
}
114144

145+
server.listen(0, `localhost`);
115146
await once(server, `listening`);
116147

117148
const {address, port} = server.address();
118149
switch (process.env.AUTH_TYPE) {
150+
case `PROXY`:
151+
// The proxy set up above will redirect all requests to our custom registry,
152+
process.env.COREPACK_NPM_REGISTRY = `http://user:pass@example.com`;
153+
break;
154+
119155
case `COREPACK_NPM_REGISTRY`:
120156
process.env.COREPACK_NPM_REGISTRY = `http://user:pass@${address.includes(`:`) ? `[${address}]` : address}:${port}`;
121157
break;
@@ -137,8 +173,11 @@ switch (process.env.AUTH_TYPE) {
137173
if (process.env.NOCK_ENV === `replay`) {
138174
const originalFetch = globalThis.fetch;
139175
globalThis.fetch = function fetch(i) {
140-
if (!`${i}`.startsWith(`http://${address.includes(`:`) ? `[${address}]` : address}:${port}`))
141-
throw new Error;
176+
if (!`${i}`.startsWith(
177+
process.env.AUTH_TYPE === `PROXY` ?
178+
`http://example.com` :
179+
`http://${address.includes(`:`) ? `[${address}]` : address}:${port}`))
180+
throw new Error(`Unexpected request to ${i}`);
142181

143182
return Reflect.apply(originalFetch, this, arguments);
144183
};

tests/main.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ it(`should download yarn berry from custom registry`, async () => {
805805
});
806806
});
807807

808-
for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK_NPM_PASSWORD`]) {
808+
for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK_NPM_PASSWORD`, `PROXY`]) {
809809
describe(`custom registry with auth ${authType}`, () => {
810810
beforeEach(() => {
811811
process.env.AUTH_TYPE = authType; // See `_registryServer.mjs`

yarn.lock

+10-19
Original file line numberDiff line numberDiff line change
@@ -730,13 +730,6 @@ __metadata:
730730
languageName: node
731731
linkType: hard
732732

733-
"@fastify/busboy@npm:^2.0.0":
734-
version: 2.1.0
735-
resolution: "@fastify/busboy@npm:2.1.0"
736-
checksum: 10c0/7bb641080aac7cf01d88749ad331af10ba9ec3713ec07cabbe833908c75df21bd56249bb6173bdec07f5a41896b21e3689316f86684c06635da45f91ff4565a2
737-
languageName: node
738-
linkType: hard
739-
740733
"@humanwhocodes/config-array@npm:^0.11.13":
741734
version: 0.11.13
742735
resolution: "@humanwhocodes/config-array@npm:0.11.13"
@@ -1303,11 +1296,11 @@ __metadata:
13031296
linkType: hard
13041297

13051298
"@types/node@npm:*, @types/node@npm:^20.4.6":
1306-
version: 20.10.5
1307-
resolution: "@types/node@npm:20.10.5"
1299+
version: 20.12.6
1300+
resolution: "@types/node@npm:20.12.6"
13081301
dependencies:
13091302
undici-types: "npm:~5.26.4"
1310-
checksum: 10c0/be30609aae0bfe492097815f166ccc07f465220cb604647fa4e5ec05a1d16c012a41b82b5f11ecfe2485cbb479d4d20384b95b809ca0bcff6d94d5bbafa645bb
1303+
checksum: 10c0/48ce732162cd6c02656aa5f996f0e695b57fdeb1ae762fbaa966afac2dcdcf52cb56be5ce1efb4babf8f97c2de545889aebc7f43c2e86f033487245c41fa1e6b
13111304
languageName: node
13121305
linkType: hard
13131306

@@ -5991,19 +5984,17 @@ __metadata:
59915984
languageName: node
59925985
linkType: hard
59935986

5994-
"undici-types@npm:~5.26.4":
5995-
version: 5.26.5
5996-
resolution: "undici-types@npm:5.26.5"
5997-
checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501
5987+
"undici-types@npm:6.x":
5988+
version: 6.12.0
5989+
resolution: "undici-types@npm:6.12.0"
5990+
checksum: 10c0/439ad3a384b4b392ff7fbd98b50a29ea4ca0ca1899f29a967a043abb45f0e67a786b058f59a327b3007079cff35ed5337d20603f06b6bb6a5b27539ebecd9792
59985991
languageName: node
59995992
linkType: hard
60005993

60015994
"undici@npm:^6.6.1":
6002-
version: 6.6.2
6003-
resolution: "undici@npm:6.6.2"
6004-
dependencies:
6005-
"@fastify/busboy": "npm:^2.0.0"
6006-
checksum: 10c0/c8c8a436059b13603f67ed4d917b4ba6d9ef282ac55c932c4790ee1a1c8cad1369da3c11b6e0b9df5a95ed1849cb98fa2f2310f6d0f9331dd359286c912497d2
5995+
version: 6.12.0
5996+
resolution: "undici@npm:6.12.0"
5997+
checksum: 10c0/5bbfd261ea20c8ed6bb3a22703acdae80cb40b1de0595a1c4df46f6602db78d7a387174c292c5640e339422acb1bfd1d2e0987ec086c057ccc24806edfd4688b
60075998
languageName: node
60085999
linkType: hard
60096000

0 commit comments

Comments
 (0)