diff --git a/README.md b/README.md index 6a5a98e17c..b650a36b82 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ Some additional resources for Node.js native addons and writing `gyp` configurat | `--devdir=$path` | SDK download directory (default is OS cache directory) | `--ensure` | Don't reinstall headers if already present | `--dist-url=$url` | Download header tarball from custom URL +| `--dist-url-auth-token=$token` | Use a Bearer token when downloading from the custom distribution URL | `--proxy=$url` | Set HTTP(S) proxy for downloading header tarball | `--noproxy=$urls` | Set urls to ignore proxies when downloading header tarball | `--cafile=$cafile` | Override default CA chain (to download tarball) diff --git a/lib/download.js b/lib/download.js index dfaca798cf..3b97b9dad1 100644 --- a/lib/download.js +++ b/lib/download.js @@ -6,11 +6,16 @@ const log = require('./log') async function download (gyp, url) { log.http('GET', url) + const headers = { + 'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`, + Connection: 'keep-alive' + } + if (gyp.opts['dist-url-auth-token'] && isDistUrl(gyp, url)) { + headers.Authorization = `Bearer ${gyp.opts['dist-url-auth-token']}` + } + const requestOpts = { - headers: { - 'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`, - Connection: 'keep-alive' - }, + headers, dispatcher: await createDispatcher(gyp) } @@ -44,6 +49,19 @@ async function download (gyp, url) { } } +function isDistUrl (gyp, url) { + const distUrl = gyp.opts['dist-url'] || gyp.opts.disturl + if (!distUrl) { + return false + } + + const base = new URL(distUrl) + const target = new URL(url) + const basePath = base.pathname.endsWith('/') ? base.pathname : base.pathname + '/' + + return base.origin === target.origin && target.pathname.startsWith(basePath) +} + async function createDispatcher (gyp) { const env = process.env const hasProxyEnv = env.http_proxy || env.HTTP_PROXY || env.https_proxy || env.HTTPS_PROXY diff --git a/lib/node-gyp.js b/lib/node-gyp.js index f60ea51c35..2e743c17da 100644 --- a/lib/node-gyp.js +++ b/lib/node-gyp.js @@ -44,6 +44,7 @@ class Gyp extends EventEmitter { loglevel: String, // everywhere python: String, // 'configure' 'dist-url': String, // 'install' + 'dist-url-auth-token': String, // 'install' tarball: String, // 'install' jobs: String, // 'build' thin: String, // 'configure' diff --git a/test/test-download.js b/test/test-download.js index 24372afeec..88758fd511 100644 --- a/test/test-download.js +++ b/test/test-download.js @@ -34,6 +34,50 @@ describe('download', function () { assert.strictEqual(await res.text(), 'ok') }) + it('download from a custom distribution URL with an auth token', async function () { + const server = http.createServer((req, res) => { + assert.strictEqual(req.headers.authorization, 'Bearer secret') + res.end('ok') + }) + + after(() => new Promise((resolve) => server.close(resolve))) + + const host = 'localhost' + await new Promise((resolve) => server.listen(0, host, resolve)) + const { port } = server.address() + const gyp = { + opts: { + 'dist-url': `http://${host}:${port}/mirror`, + 'dist-url-auth-token': 'secret' + }, + version: '42' + } + const res = await download(gyp, `http://${host}:${port}/mirror/v42/headers.tar.gz`) + assert.strictEqual(await res.text(), 'ok') + }) + + it('does not send a distribution auth token to another URL', async function () { + const server = http.createServer((req, res) => { + assert.strictEqual(req.headers.authorization, undefined) + res.end('ok') + }) + + after(() => new Promise((resolve) => server.close(resolve))) + + const host = 'localhost' + await new Promise((resolve) => server.listen(0, host, resolve)) + const { port } = server.address() + const gyp = { + opts: { + 'dist-url': `http://${host}:${port}/mirror`, + 'dist-url-auth-token': 'secret' + }, + version: '42' + } + const res = await download(gyp, `http://${host}:${port}/other/file`) + assert.strictEqual(await res.text(), 'ok') + }) + it('download over https with custom ca', async function () { const cafile = path.join(__dirname, 'fixtures/ca.crt') const cacontents = certs['ca.crt']