package-rebuilds/pkgs/node-undici/node-undici-5.28.4+dfsg1+~cs23.12.11/test/proxy-agent.js
Fabian Grünbichler afee63929c add node-undici for libgit2 1.8.1
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2024-09-10 11:23:24 +02:00

558 lines
15 KiB
JavaScript

'use strict'
const { test, teardown } = require('tap')
const { request, fetch, setGlobalDispatcher, getGlobalDispatcher } = require('..')
const { InvalidArgumentError } = require('../lib/core/errors')
const { nodeMajor } = require('../lib/core/util')
const { readFileSync } = require('fs')
const { join } = require('path')
const ProxyAgent = require('../lib/proxy-agent')
const Pool = require('../lib/pool')
const { createServer } = require('http')
const https = require('https')
const proxy = require('proxy').createProxy
test('should throw error when no uri is provided', (t) => {
t.plan(2)
t.throws(() => new ProxyAgent(), InvalidArgumentError)
t.throws(() => new ProxyAgent({}), InvalidArgumentError)
})
test('using auth in combination with token should throw', (t) => {
t.plan(1)
t.throws(() => new ProxyAgent({
auth: 'foo',
token: 'Bearer bar',
uri: 'http://example.com'
}),
InvalidArgumentError
)
})
test('should accept string and object as options', (t) => {
t.plan(2)
t.doesNotThrow(() => new ProxyAgent('http://example.com'))
t.doesNotThrow(() => new ProxyAgent({ uri: 'http://example.com' }))
})
test('use proxy-agent to connect through proxy', async (t) => {
t.plan(6)
const server = await buildServer()
const proxy = await buildProxy()
delete proxy.authenticate
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
const parsedOrigin = new URL(serverUrl)
proxy.on('connect', () => {
t.pass('should connect to proxy')
})
server.on('request', (req, res) => {
t.equal(req.url, '/')
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const {
statusCode,
headers,
body
} = await request(serverUrl, { dispatcher: proxyAgent })
const json = await body.json()
t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy agent to connect through proxy using Pool', async (t) => {
t.plan(3)
const server = await buildServer()
const proxy = await buildProxy()
let resolveFirstConnect
let connectCount = 0
proxy.authenticate = async function (req, fn) {
if (++connectCount === 2) {
t.pass('second connect should arrive while first is still inflight')
resolveFirstConnect()
fn(null, true)
} else {
await new Promise((resolve) => {
resolveFirstConnect = resolve
})
fn(null, true)
}
}
server.on('request', (req, res) => {
res.end()
})
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const clientFactory = (url, options) => {
return new Pool(url, options)
}
const proxyAgent = new ProxyAgent({ auth: Buffer.from('user:pass').toString('base64'), uri: proxyUrl, clientFactory })
const firstRequest = request(`${serverUrl}`, { dispatcher: proxyAgent })
const secondRequest = await request(`${serverUrl}`, { dispatcher: proxyAgent })
t.equal((await firstRequest).statusCode, 200)
t.equal(secondRequest.statusCode, 200)
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy-agent to connect through proxy using path with params', async (t) => {
t.plan(6)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
const parsedOrigin = new URL(serverUrl)
proxy.on('connect', () => {
t.pass('should call proxy')
})
server.on('request', (req, res) => {
t.equal(req.url, '/hello?foo=bar')
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const {
statusCode,
headers,
body
} = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })
const json = await body.json()
t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy-agent with auth', async (t) => {
t.plan(7)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent({
auth: Buffer.from('user:pass').toString('base64'),
uri: proxyUrl
})
const parsedOrigin = new URL(serverUrl)
proxy.authenticate = function (req, fn) {
t.pass('authentication should be called')
fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`)
}
proxy.on('connect', () => {
t.pass('proxy should be called')
})
server.on('request', (req, res) => {
t.equal(req.url, '/hello?foo=bar')
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const {
statusCode,
headers,
body
} = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })
const json = await body.json()
t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy-agent with token', async (t) => {
t.plan(7)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent({
token: `Bearer ${Buffer.from('user:pass').toString('base64')}`,
uri: proxyUrl
})
const parsedOrigin = new URL(serverUrl)
proxy.authenticate = function (req, fn) {
t.pass('authentication should be called')
fn(null, req.headers['proxy-authorization'] === `Bearer ${Buffer.from('user:pass').toString('base64')}`)
}
proxy.on('connect', () => {
t.pass('proxy should be called')
})
server.on('request', (req, res) => {
t.equal(req.url, '/hello?foo=bar')
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const {
statusCode,
headers,
body
} = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })
const json = await body.json()
t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy-agent with custom headers', async (t) => {
t.plan(2)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent({
uri: proxyUrl,
headers: {
'User-Agent': 'Foobar/1.0.0'
}
})
proxy.on('connect', (req) => {
t.equal(req.headers['user-agent'], 'Foobar/1.0.0')
})
server.on('request', (req, res) => {
t.equal(req.headers['user-agent'], 'BarBaz/1.0.0')
res.end()
})
await request(serverUrl + '/hello?foo=bar', {
headers: { 'user-agent': 'BarBaz/1.0.0' },
dispatcher: proxyAgent
})
server.close()
proxy.close()
proxyAgent.close()
})
test('sending proxy-authorization in request headers should throw', async (t) => {
t.plan(3)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
server.on('request', (req, res) => {
res.end(JSON.stringify({ hello: 'world' }))
})
await t.rejects(
request(
serverUrl + '/hello?foo=bar',
{
dispatcher: proxyAgent,
headers: {
'proxy-authorization': Buffer.from('user:pass').toString('base64')
}
}
),
'Proxy-Authorization should be sent in ProxyAgent'
)
await t.rejects(
request(
serverUrl + '/hello?foo=bar',
{
dispatcher: proxyAgent,
headers: {
'PROXY-AUTHORIZATION': Buffer.from('user:pass').toString('base64')
}
}
),
'Proxy-Authorization should be sent in ProxyAgent'
)
await t.rejects(
request(
serverUrl + '/hello?foo=bar',
{
dispatcher: proxyAgent,
headers: {
'Proxy-Authorization': Buffer.from('user:pass').toString('base64')
}
}
),
'Proxy-Authorization should be sent in ProxyAgent'
)
server.close()
proxy.close()
proxyAgent.close()
})
test('use proxy-agent with setGlobalDispatcher', async (t) => {
t.plan(6)
const defaultDispatcher = getGlobalDispatcher()
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
const parsedOrigin = new URL(serverUrl)
setGlobalDispatcher(proxyAgent)
t.teardown(() => setGlobalDispatcher(defaultDispatcher))
proxy.on('connect', () => {
t.pass('should call proxy')
})
server.on('request', (req, res) => {
t.equal(req.url, '/hello?foo=bar')
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const {
statusCode,
headers,
body
} = await request(serverUrl + '/hello?foo=bar')
const json = await body.json()
t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
server.close()
proxy.close()
proxyAgent.close()
})
test('ProxyAgent correctly sends headers when using fetch - #1355, #1623', { skip: nodeMajor < 16 }, async (t) => {
t.plan(2)
const defaultDispatcher = getGlobalDispatcher()
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
setGlobalDispatcher(proxyAgent)
t.teardown(() => setGlobalDispatcher(defaultDispatcher))
const expectedHeaders = {
host: `localhost:${server.address().port}`,
connection: 'keep-alive',
'test-header': 'value',
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'gzip, deflate'
}
const expectedProxyHeaders = {
host: `localhost:${proxy.address().port}`,
connection: 'close'
}
proxy.on('connect', (req, res) => {
t.same(req.headers, expectedProxyHeaders)
})
server.on('request', (req, res) => {
t.same(req.headers, expectedHeaders)
res.end('goodbye')
})
await fetch(serverUrl, {
headers: { 'Test-header': 'value' }
})
server.close()
proxy.close()
proxyAgent.close()
t.end()
})
test('should throw when proxy does not return 200', async (t) => {
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
proxy.authenticate = function (req, fn) {
fn(null, false)
}
const proxyAgent = new ProxyAgent(proxyUrl)
try {
await request(serverUrl, { dispatcher: proxyAgent })
t.fail()
} catch (e) {
t.pass()
t.ok(e)
}
server.close()
proxy.close()
proxyAgent.close()
t.end()
})
test('pass ProxyAgent proxy status code error when using fetch - #2161', { skip: nodeMajor < 16 }, async (t) => {
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
proxy.authenticate = function (req, fn) {
fn(null, false)
}
const proxyAgent = new ProxyAgent(proxyUrl)
try {
await fetch(serverUrl, { dispatcher: proxyAgent })
} catch (e) {
t.hasProp(e, 'cause')
}
server.close()
proxy.close()
proxyAgent.close()
t.end()
})
test('Proxy via HTTP to HTTP endpoint', async (t) => {
t.plan(3)
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent(proxyUrl)
server.on('request', function (req, res) {
t.ok(!req.connection.encrypted)
res.end(JSON.stringify(req.headers))
})
server.on('secureConnection', () => {
t.fail('server is http')
})
proxy.on('secureConnection', () => {
t.fail('proxy is http')
})
proxy.on('connect', () => {
t.pass('connect to proxy')
})
proxy.on('request', function () {
t.fail('proxy should never receive requests')
})
const data = await request(serverUrl, { dispatcher: proxyAgent })
const json = await data.body.json()
t.strictSame(json, {
host: `localhost:${server.address().port}`,
connection: 'keep-alive'
})
server.close()
proxy.close()
proxyAgent.close()
})
function buildServer () {
return new Promise((resolve) => {
const server = createServer()
server.listen(0, () => resolve(server))
})
}
function buildSSLServer () {
const serverOptions = {
ca: [
readFileSync(join(__dirname, 'fixtures', 'client-ca-crt.pem'), 'utf8')
],
key: readFileSync(join(__dirname, 'fixtures', 'key.pem'), 'utf8'),
cert: readFileSync(join(__dirname, 'fixtures', 'cert.pem'), 'utf8')
}
return new Promise((resolve) => {
const server = https.createServer(serverOptions)
server.listen(0, () => resolve(server))
})
}
function buildProxy (listener) {
return new Promise((resolve) => {
const server = listener
? proxy(createServer(listener))
: proxy(createServer())
server.listen(0, () => resolve(server))
})
}
function buildSSLProxy () {
const serverOptions = {
ca: [
readFileSync(join(__dirname, 'fixtures', 'client-ca-crt.pem'), 'utf8')
],
key: readFileSync(join(__dirname, 'fixtures', 'key.pem'), 'utf8'),
cert: readFileSync(join(__dirname, 'fixtures', 'cert.pem'), 'utf8')
}
return new Promise((resolve) => {
const server = proxy(https.createServer(serverOptions))
server.listen(0, () => resolve(server))
})
}
teardown(() => process.exit())