Skip to content

Commit 8d78f6d

Browse files
fix: apply cafile to proxied TLS connections
EnvHttpProxyAgent forwards opts to an internal ProxyAgent for proxied requests, which reads origin TLS config from requestTls rather than connect. Set connect/requestTls/proxyTls so the custom CA is honored on both direct and proxied paths. Adds a test covering https origin behind an HTTP CONNECT proxy with a custom CA.
1 parent ae72094 commit 8d78f6d

2 files changed

Lines changed: 56 additions & 1 deletion

File tree

lib/download.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,14 @@ async function createDispatcher (gyp) {
5353

5454
const opts = {}
5555
if (gyp.opts.cafile) {
56-
opts.connect = { ca: await readCAFile(gyp.opts.cafile) }
56+
const ca = await readCAFile(gyp.opts.cafile)
57+
// EnvHttpProxyAgent forwards opts to both its internal Agent (direct) and
58+
// ProxyAgent (proxied). Agent reads TLS config from `connect`; ProxyAgent
59+
// reads it from `requestTls` (origin) / `proxyTls` (proxy). Set all three
60+
// so the custom CA is applied regardless of which path a request takes.
61+
opts.connect = { ca }
62+
opts.requestTls = { ca }
63+
opts.proxyTls = { ca }
5764
}
5865
if (gyp.opts.proxy) {
5966
opts.httpProxy = gyp.opts.proxy

test/test-download.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,54 @@ describe('download', function () {
112112
assert.strictEqual(proxyUsed, true)
113113
})
114114

115+
it('download over https with proxy and custom ca', async function () {
116+
const cafile = path.join(__dirname, 'fixtures/ca-proxy.crt')
117+
await fs.writeFile(cafile, certs['ca.crt'], 'utf8')
118+
119+
const server = https.createServer({
120+
ca: await readCAFile(cafile),
121+
cert: certs['server.crt'],
122+
key: certs['server.key']
123+
}, (_, res) => res.end('ok'))
124+
125+
let proxyUsed = false
126+
const pserver = http.createServer()
127+
pserver.on('connect', (req, clientSocket, head) => {
128+
proxyUsed = true
129+
const [targetHost, targetPort] = req.url.split(':')
130+
const serverSocket = net.connect(targetPort, targetHost, () => {
131+
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n')
132+
serverSocket.write(head)
133+
serverSocket.pipe(clientSocket)
134+
clientSocket.pipe(serverSocket)
135+
})
136+
clientSocket.on('error', () => serverSocket.destroy())
137+
serverSocket.on('error', () => clientSocket.destroy())
138+
})
139+
140+
after(async () => {
141+
await new Promise((resolve) => server.close(resolve))
142+
await new Promise((resolve) => pserver.close(resolve))
143+
await fs.unlink(cafile)
144+
})
145+
146+
const host = 'localhost'
147+
await new Promise((resolve) => server.listen(0, host, resolve))
148+
const { port } = server.address()
149+
await new Promise((resolve) => pserver.listen(port + 1, host, resolve))
150+
const gyp = {
151+
opts: {
152+
cafile,
153+
proxy: `http://${host}:${port + 1}`,
154+
noproxy: 'bad'
155+
},
156+
version: '42'
157+
}
158+
const res = await download(gyp, `https://${host}:${port}`)
159+
assert.strictEqual(await res.text(), 'ok')
160+
assert.strictEqual(proxyUsed, true)
161+
})
162+
115163
it('download over http with noproxy', async function () {
116164
const server = http.createServer((req, res) => {
117165
assert.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)

0 commit comments

Comments
 (0)