Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions lib/download.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const fetch = require('make-fetch-happen')
const { Readable } = require('stream')
const { EnvHttpProxyAgent } = require('undici')
const { promises: fs } = require('graceful-fs')
const log = require('./log')

Expand All @@ -10,19 +11,58 @@ async function download (gyp, url) {
'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`,
Connection: 'keep-alive'
},
proxy: gyp.opts.proxy,
noProxy: gyp.opts.noproxy
dispatcher: await createDispatcher(gyp)
}

const cafile = gyp.opts.cafile
if (cafile) {
requestOpts.ca = await readCAFile(cafile)
let res
try {
res = await fetch(url, requestOpts)
} catch (err) {
// Built-in fetch wraps low-level errors in "TypeError: fetch failed" with
// the underlying error on .cause. Callers inspect .code (e.g. ENOTFOUND).
if (err.cause) {
throw err.cause
}
throw err
}

const res = await fetch(url, requestOpts)
log.http(res.status, res.url)

return res
const body = Readable.fromWeb(res.body)
return {
status: res.status,
url: res.url,
body,
text: async () => {
Comment thread
MarshallOfSound marked this conversation as resolved.
Outdated
let data = ''
body.setEncoding('utf8')
for await (const chunk of body) {
data += chunk
}
return data
}
}
}

async function createDispatcher (gyp) {
Comment thread
gengjiawen marked this conversation as resolved.
const env = process.env
const hasProxyEnv = env.http_proxy || env.HTTP_PROXY || env.https_proxy || env.HTTPS_PROXY
if (!gyp.opts.proxy && !gyp.opts.cafile && !hasProxyEnv) {
return undefined
}

const opts = {}
if (gyp.opts.cafile) {
opts.connect = { ca: await readCAFile(gyp.opts.cafile) }
}
if (gyp.opts.proxy) {
opts.httpProxy = gyp.opts.proxy
opts.httpsProxy = gyp.opts.proxy
}
if (gyp.opts.noproxy) {
opts.noProxy = gyp.opts.noproxy
}
return new EnvHttpProxyAgent(opts)
}

async function readCAFile (filename) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
"env-paths": "^2.2.0",
"exponential-backoff": "^3.1.1",
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^15.0.0",
"nopt": "^9.0.0",
"proc-log": "^6.0.0",
"semver": "^7.3.5",
"tar": "^7.5.4",
"tinyglobby": "^0.2.12",
"undici": "^6.25.0",
"which": "^6.0.0"
},
"engines": {
Expand Down
28 changes: 20 additions & 8 deletions test/test-download.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fs = require('fs/promises')
const path = require('path')
const http = require('http')
const https = require('https')
const net = require('net')
const install = require('../lib/install')
const { download, readCAFile } = require('../lib/download')
const { FULL_TEST, devDir, platformTimeout } = require('./common')
Expand Down Expand Up @@ -69,13 +70,22 @@ describe('download', function () {
})

it('download over http with proxy', async function () {
const server = http.createServer((_, res) => {
const server = http.createServer((req, res) => {
assert.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
res.end('ok')
})

const pserver = http.createServer((req, res) => {
assert.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
res.end('proxy ok')
let proxyUsed = false
const pserver = http.createServer()
pserver.on('connect', (req, clientSocket, head) => {
proxyUsed = true
const [targetHost, targetPort] = req.url.split(':')
const serverSocket = net.connect(targetPort, targetHost, () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n')
serverSocket.write(head)
serverSocket.pipe(clientSocket)
clientSocket.pipe(serverSocket)
})
Comment thread
MarshallOfSound marked this conversation as resolved.
})

after(() => Promise.all([
Expand All @@ -96,7 +106,8 @@ describe('download', function () {
}
const url = `http://${host}:${port}`
const res = await download(gyp, url)
assert.strictEqual(await res.text(), 'proxy ok')
assert.strictEqual(await res.text(), 'ok')
assert.strictEqual(proxyUsed, true)
})

it('download over http with noproxy', async function () {
Expand All @@ -105,9 +116,9 @@ describe('download', function () {
res.end('ok')
})

const pserver = http.createServer((_, res) => {
res.end('proxy ok')
})
let proxyUsed = false
const pserver = http.createServer()
pserver.on('connect', () => { proxyUsed = true })
Comment thread
cclauss marked this conversation as resolved.
Outdated

after(() => Promise.all([
new Promise((resolve) => server.close(resolve)),
Expand All @@ -128,6 +139,7 @@ describe('download', function () {
const url = `http://${host}:${port}`
const res = await download(gyp, url)
assert.strictEqual(await res.text(), 'ok')
assert.strictEqual(proxyUsed, false)
})

it('download with missing cafile', async function () {
Expand Down
Loading