feat: added https data protection via package

This commit is contained in:
Jay 2026-03-20 12:41:24 +02:00
parent 4d8931ca8a
commit 7320936afa
5 changed files with 430 additions and 61 deletions

View File

@ -8,6 +8,7 @@ import https from 'https';
import http2 from 'http2';
import util from 'util';
import followRedirects from 'follow-redirects';
import { HttpsProxyAgent } from 'https-proxy-agent';
import zlib from 'zlib';
import { VERSION } from '../env/data.js';
import transitionalDefaults from '../defaults/transitional.js';
@ -187,7 +188,7 @@ function dispatchBeforeRedirect(options, responseDetails) {
*
* @returns {http.ClientRequestArgs}
*/
function setProxy(options, configProxy, location) {
function setProxy(options, configProxy, location, agentOptions = {}) {
let proxy = configProxy;
if (!proxy && proxy !== false) {
const proxyUrl = getProxyForUrl(location);
@ -210,28 +211,65 @@ function setProxy(options, configProxy, location) {
} else if (typeof proxy.auth === 'object') {
throw new AxiosError('Invalid proxy authorization', AxiosError.ERR_BAD_OPTION, { proxy });
}
const base64 = Buffer.from(proxy.auth, 'utf8').toString('base64');
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
}
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
const targetIsHttps = isHttps.test(options.protocol);
const proxyProtocol = proxy.protocol
? proxy.protocol.includes(':')
? proxy.protocol
: `${proxy.protocol}:`
: 'http:';
const proxyHost = proxy.hostname || proxy.host;
options.hostname = proxyHost;
// Replace 'host' since options is not a URL object
options.host = proxyHost;
options.port = proxy.port;
options.path = location;
if (proxy.protocol) {
options.protocol = proxy.protocol.includes(':') ? proxy.protocol : `${proxy.protocol}:`;
if (targetIsHttps && proxyProtocol === 'http:') {
// HTTPS target over an HTTP proxy must use CONNECT tunneling.
if (proxy.auth) {
const [username, ...passwordParts] = String(proxy.auth).split(':');
const password = passwordParts.join(':');
const proxyUrl = `http://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${proxyHost}:${proxy.port}`;
options.agent = new HttpsProxyAgent(proxyUrl, agentOptions);
} else {
options.agent = new HttpsProxyAgent(`http://${proxyHost}:${proxy.port}`, agentOptions);
}
// The tunnel agent handles proxy auth during CONNECT. Do not forward it to origin.
delete options.headers['Proxy-Authorization'];
delete options.headers['proxy-authorization'];
if (options.agents) {
options.agents.https = options.agent;
}
// Preserve TLS settings from config.httpsAgent for the tunneled HTTPS request.
['rejectUnauthorized', 'ca', 'cert', 'key', 'passphrase', 'pfx', 'servername'].forEach(
(key) => {
if (!utils.isUndefined(agentOptions[key])) {
options[key] = agentOptions[key];
}
}
);
} else {
if (proxy.auth) {
const base64 = Buffer.from(proxy.auth, 'utf8').toString('base64');
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
}
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
options.hostname = proxyHost;
// Replace 'host' since options is not a URL object
options.host = proxyHost;
options.port = proxy.port;
options.path = location;
if (proxy.protocol) {
options.protocol = proxy.protocol.includes(':') ? proxy.protocol : `${proxy.protocol}:`;
}
}
}
options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
// Configure proxy for redirected request, passing the original config proxy to apply
// the exact same logic as if the redirected request was performed by axios directly.
setProxy(redirectOptions, configProxy, redirectOptions.href);
setProxy(redirectOptions, configProxy, redirectOptions.href, agentOptions);
};
}
@ -658,6 +696,9 @@ export default isHttpAdapterSupported &&
if (config.socketPath) {
options.socketPath = config.socketPath;
} else {
const httpsAgentOptions =
config.httpsAgent instanceof https.Agent ? config.httpsAgent.options || {} : {};
options.hostname = parsed.hostname.startsWith('[')
? parsed.hostname.slice(1, -1)
: parsed.hostname;
@ -665,13 +706,14 @@ export default isHttpAdapterSupported &&
setProxy(
options,
config.proxy,
protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path
protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path,
httpsAgentOptions
);
}
let transport;
const isHttpsRequest = isHttps.test(options.protocol);
options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
options.agent = options.agent || (isHttpsRequest ? config.httpsAgent : config.httpAgent);
if (isHttp2) {
transport = http2Transport;

37
package-lock.json generated
View File

@ -11,6 +11,7 @@
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"https-proxy-agent": "^8.0.0",
"proxy-from-env": "^2.1.0"
},
"devDependencies": {
@ -2373,6 +2374,20 @@
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/@npmcli/agent/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@npmcli/agent/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@ -5685,7 +5700,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@ -7999,19 +8013,27 @@
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-8.0.0.tgz",
"integrity": "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
"agent-base": "8.0.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent/node_modules/agent-base": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-8.0.0.tgz",
"integrity": "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
@ -10066,7 +10088,6 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/multer": {

View File

@ -151,6 +151,7 @@
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"https-proxy-agent": "^8.0.0",
"proxy-from-env": "^2.1.0"
},
"contributors": [

View File

@ -16,15 +16,9 @@ import util from 'util';
import NodeFormData from 'form-data';
const SERVER_PORT = 8010;
const LOCAL_SERVER_URL = `http://localhost:${SERVER_PORT}`;
const pipelineAsync = util.promisify(stream.pipeline);
const fetchAxios = axios.create({
baseURL: LOCAL_SERVER_URL,
adapter: 'fetch',
});
describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () => {
describe('responses', () => {
it('should support text response type', async () => {
@ -35,7 +29,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'text',
});
@ -53,7 +51,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'arraybuffer',
});
@ -74,7 +76,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'blob',
});
@ -92,7 +98,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'stream',
});
@ -123,7 +133,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
);
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'formdata',
});
@ -146,7 +160,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'json',
});
@ -188,7 +206,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const samples = [];
const { data } = await fetchAxios.post(
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.post(
`http://localhost:${server.address().port}/`,
readable,
{
@ -241,7 +263,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT });
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
onUploadProgress() {},
});
@ -284,7 +310,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const samples = [];
const { data } = await fetchAxios.post(
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.post(
`http://localhost:${server.address().port}/`,
readable,
{
@ -359,7 +389,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const server = await startHTTPServer(async (req, res) => res.end('OK'), { port: SERVER_PORT });
try {
const { data } = await fetchAxios.post(
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.post(
`http://localhost:${server.address().port}/`,
stream.Readable.from('OK')
);
@ -388,14 +422,14 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
}, 500);
await assert.rejects(async () => {
await fetchAxios.post(
`http://localhost:${server.address().port}/`,
makeReadableStream(),
{
responseType: 'stream',
signal: controller.signal,
}
);
const instance = axios.create({
adapter: 'fetch',
});
await instance.post(`http://localhost:${server.address().port}/`, makeReadableStream(), {
responseType: 'stream',
signal: controller.signal,
});
}, /CanceledError/);
} finally {
await stopHTTPServer(server);
@ -419,7 +453,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
controller.abort(new Error('test'));
}, 800);
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'stream',
signal: controller.signal,
});
@ -448,7 +486,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const ts = Date.now();
await assert.rejects(async () => {
await fetchAxios(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
await instance.get(`http://localhost:${server.address().port}/`, {
timeout,
});
}, /timeout/);
@ -464,9 +506,14 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
it('should combine baseURL and url', async () => {
const server = await startHTTPServer(async (req, res) => res.end('OK'), { port: SERVER_PORT });
try {
const res = await fetchAxios('/foo');
const instance = axios.create({
baseURL: `http://localhost:${server.address().port}`,
adapter: 'fetch',
});
assert.equal(res.config.baseURL, LOCAL_SERVER_URL);
const res = await instance.get(`/foo`);
assert.equal(res.config.baseURL, `http://localhost:${server.address().port}`);
assert.equal(res.config.url, '/foo');
} finally {
await stopHTTPServer(server);
@ -476,7 +523,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
it('should support params', async () => {
const server = await startHTTPServer((req, res) => res.end(req.url), { port: SERVER_PORT });
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/?test=1`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/?test=1`, {
params: {
foo: 1,
bar: 2,
@ -491,7 +542,12 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
it('should handle fetch failed error as an AxiosError with ERR_NETWORK code', async () => {
try {
await fetchAxios('http://notExistsUrl.in.nowhere');
const instance = axios.create({
adapter: 'fetch',
});
await instance.get('http://notExistsUrl.in.nowhere');
assert.fail('should fail');
} catch (err) {
assert.strictEqual(String(err), 'AxiosError: Network Error');
@ -509,7 +565,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
);
try {
const { headers } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { headers } = await instance.get(`http://localhost:${server.address().port}/`, {
responseType: 'stream',
});
@ -534,7 +594,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
);
try {
await fetchAxios.post(`http://localhost:${server.address().port}/form`, form);
const instance = axios.create({
adapter: 'fetch',
});
await instance.post(`http://localhost:${server.address().port}/form`, form);
} finally {
await stopHTTPServer(server);
}
@ -543,7 +607,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
describe('env config', () => {
it('should respect env fetch API configuration', async () => {
const { data, headers } = await fetchAxios.get('/', {
const instance = axios.create({
adapter: 'fetch',
});
const { data, headers } = await instance.get(`http://localhost:${SERVER_PORT}/`, {
env: {
fetch() {
return {
@ -565,7 +633,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
form.append('x', '1');
const { data, headers } = await fetchAxios.post('/', form, {
const instance = axios.create({
adapter: 'fetch',
});
const { data, headers } = await instance.post('/', form, {
onUploadProgress() {
// dummy listener to activate streaming
},
@ -587,7 +659,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
});
it('should be able to handle response with lack of Response object', async () => {
const { data, headers } = await fetchAxios.get('/', {
const instance = axios.create({
adapter: 'fetch',
});
const { data, headers } = await instance.get('/', {
onDownloadProgress() {
// dummy listener to activate streaming
},
@ -613,7 +689,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT });
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
env: {
fetch: undefined,
},
@ -640,7 +720,11 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT });
try {
const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, {
const instance = axios.create({
adapter: 'fetch',
});
const { data } = await instance.get(`http://localhost:${server.address().port}/`, {
env: {
fetch: undefined,
},

View File

@ -1253,6 +1253,10 @@ describe('supports http with nodejs', () => {
const closeServer = (server) =>
new Promise((resolve, reject) => {
if (typeof server.closeAllConnections === 'function') {
server.closeAllConnections();
}
server.close((error) => {
if (error) {
reject(error);
@ -1335,6 +1339,223 @@ describe('supports http with nodejs', () => {
}
});
it('should use CONNECT tunnel for HTTPS target via HTTP proxy', async () => {
const tlsOptions = {
key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')),
cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')),
};
const targetServer = await new Promise((resolve, reject) => {
const server = https
.createServer(tlsOptions, (req, res) => {
res.end('secure-data');
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
let connectSeen = false;
let plaintextForwardSeen = false;
const proxyServer = await new Promise((resolve, reject) => {
const server = http
.createServer((request, response) => {
plaintextForwardSeen = true;
response.statusCode = 500;
response.end('unexpected plaintext proxying');
})
.on('connect', (req, clientSocket, head) => {
connectSeen = true;
const serverSocket = net.connect(targetServer.address().port, 'localhost', () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
if (head && head.length) {
serverSocket.write(head);
}
serverSocket.pipe(clientSocket);
clientSocket.pipe(serverSocket);
});
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
try {
const response = await axios.get(`https://localhost:${targetServer.address().port}/`, {
proxy: {
host: 'localhost',
port: proxyServer.address().port,
protocol: 'http:',
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
assert.strictEqual(connectSeen, true, 'proxy should receive CONNECT request');
assert.strictEqual(
plaintextForwardSeen,
false,
'proxy should not receive plaintext HTTPS request data'
);
assert.strictEqual(String(response.data), 'secure-data');
} finally {
await Promise.all([stopHTTPServer(targetServer, 200), stopHTTPServer(proxyServer, 200)]);
}
});
it('should include Proxy-Authorization in CONNECT for authenticated HTTP proxy', async () => {
const tlsOptions = {
key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')),
cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')),
};
const targetServer = await new Promise((resolve, reject) => {
const server = https
.createServer(tlsOptions, (req, res) => {
res.end('ok');
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
let proxyAuthorization;
const proxyServer = await new Promise((resolve, reject) => {
const server = http
.createServer()
.on('connect', (req, clientSocket, head) => {
proxyAuthorization = req.headers['proxy-authorization'];
const serverSocket = net.connect(targetServer.address().port, 'localhost', () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
if (head && head.length) {
serverSocket.write(head);
}
serverSocket.pipe(clientSocket);
clientSocket.pipe(serverSocket);
});
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
try {
await axios.get(`https://localhost:${targetServer.address().port}/`, {
proxy: {
host: 'localhost',
port: proxyServer.address().port,
protocol: 'http:',
auth: {
username: 'user',
password: 'secret',
},
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
assert.ok(proxyAuthorization, 'Proxy-Authorization should be set on CONNECT request');
assert.strictEqual(
proxyAuthorization,
'Basic ' + Buffer.from('user:secret').toString('base64')
);
} finally {
await Promise.all([stopHTTPServer(targetServer, 200), stopHTTPServer(proxyServer, 200)]);
}
});
it('should use CONNECT tunnel for HTTPS redirect via HTTP proxy', async () => {
const tlsOptions = {
key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')),
cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')),
};
const httpsTargetServer = await new Promise((resolve, reject) => {
const server = https
.createServer(tlsOptions, (req, res) => {
res.end('redirected-data');
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
const redirectServer = await startHTTPServer(
(req, res) => {
res.writeHead(302, { Location: `https://localhost:${httpsTargetServer.address().port}/` });
res.end();
},
{ port: 0 }
);
let connectCount = 0;
const proxyServer = await new Promise((resolve, reject) => {
const server = http
.createServer((request, response) => {
const parsed = new URL(request.url);
const opts = {
host: parsed.hostname,
port: parsed.port,
path: `${parsed.pathname}${parsed.search}`,
method: request.method,
};
const proxyRequest = http.request(opts, (res) => {
response.writeHead(res.statusCode || 500, res.headers);
stream.pipeline(res, response, () => {});
});
proxyRequest.on('error', () => {
response.statusCode = 502;
response.end();
});
request.pipe(proxyRequest);
})
.on('connect', (req, clientSocket, head) => {
connectCount += 1;
const serverSocket = net.connect(httpsTargetServer.address().port, 'localhost', () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
if (head && head.length) {
serverSocket.write(head);
}
serverSocket.pipe(clientSocket);
clientSocket.pipe(serverSocket);
});
})
.listen(0, () => resolve(server));
server.on('error', reject);
});
try {
const response = await axios.get(`http://localhost:${redirectServer.address().port}/`, {
proxy: {
host: 'localhost',
port: proxyServer.address().port,
protocol: 'http:',
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
assert.ok(connectCount >= 1, 'CONNECT should be used for HTTPS redirect');
assert.strictEqual(String(response.data), 'redirected-data');
} finally {
await Promise.all([
stopHTTPServer(redirectServer, 200),
stopHTTPServer(httpsTargetServer, 200),
stopHTTPServer(proxyServer, 200),
]);
}
});
it('should not pass through disabled proxy', async () => {
const originalHttpProxy = process.env.http_proxy;
process.env.http_proxy = 'http://does-not-exists.example.com:4242/';