Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions lib/core/proxy-headers-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ export class ProxyHeadersAgent extends Agent {
this.proxyAuth,
this.proxyHeaders
);

proxySocket.write(connectRequest);
});

Expand Down Expand Up @@ -153,7 +152,8 @@ export class ProxyHeadersAgent extends Agent {
});
});

return proxySocket;
// Do not return proxySocket - the agent must use the stream from the callback
// (tlsSocket) so the CONNECT handshake completes before any request is written.
}
}

Expand Down
5 changes: 4 additions & 1 deletion lib/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ export function buildConnectRequest(targetHost, targetPort, proxyAuth, proxyHead
lines.push(`Proxy-Authorization: Basic ${proxyAuth}`);
}

for (const [key, value] of Object.entries(proxyHeaders)) {
const entries = proxyHeaders instanceof Map
? [...proxyHeaders.entries()]
: Object.entries(proxyHeaders || {});
for (const [key, value] of entries) {
lines.push(`${key}: ${value}`);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"LICENSE"
],
"scripts": {
"test": "node test/test_proxy_headers.js",
"test": "node test/test_proxy_headers.js core axios node-fetch got undici superagent",
"test:verbose": "node test/test_proxy_headers.js -v",
"lint": "eslint lib test",
"prepublishOnly": "npm test"
Expand Down
36 changes: 33 additions & 3 deletions test/test_proxy_headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ function checkHeader(headers, headerName) {
return null;
}

/**
* When SEND_PROXY_HEADER and SEND_PROXY_VALUE are set and we're checking the same header,
* the proxy response must echo that value (e.g. X-ProxyMesh-IP).
* @returns {string|null} Error message if expectation not met, null if OK or N/A.
*/
function validateSentHeaderValue(config, headerValue) {
if (!config.sendProxyHeader || !config.sendProxyValue) return null;
if (config.proxyHeader.toLowerCase() !== config.sendProxyHeader.toLowerCase()) return null;
const expected = String(config.sendProxyValue).trim();
const actual = headerValue ? String(headerValue).trim() : '';
if (actual === expected) return null;
return `Expected ${config.proxyHeader} to equal ${expected} (sent in request) but got ${actual}`;
}

const AVAILABLE_TESTS = {
async core(config) {
try {
Expand Down Expand Up @@ -159,7 +173,10 @@ const AVAILABLE_TESTS = {
? checkHeader(capturedHeaders, config.proxyHeader)
: null;

if (headerValue) {
const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) {
resolve(new TestResult('core', false, null, sentErr, res.statusCode));
} else if (headerValue) {
resolve(new TestResult('core', true, headerValue, null, res.statusCode));
} else {
resolve(new TestResult('core', false, null,
Expand Down Expand Up @@ -189,9 +206,13 @@ const AVAILABLE_TESTS = {
proxyHeaders: config.proxyHeadersToSend,
});

const response = await client.get(config.testUrl);
const response = await client.get(config.testUrl, {
validateStatus: () => true,
});
const headerValue = checkHeader(response.headers, config.proxyHeader);

const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) return new TestResult('axios', false, null, sentErr);
if (headerValue) {
return new TestResult('axios', true, headerValue, null, response.status);
}
Expand All @@ -214,6 +235,8 @@ const AVAILABLE_TESTS = {

const headerValue = checkHeader(response.proxyHeaders, config.proxyHeader);

const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) return new TestResult('node-fetch', false, null, sentErr);
if (headerValue) {
return new TestResult('node-fetch', true, headerValue, null, response.status);
}
Expand All @@ -232,11 +255,14 @@ const AVAILABLE_TESTS = {
const client = await createProxyGot({
proxy: config.proxyUrl,
proxyHeaders: config.proxyHeadersToSend,
gotOptions: { throwHttpErrors: false },
});

const response = await client(config.testUrl);
const headerValue = checkHeader(response.headers, config.proxyHeader);

const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) return new TestResult('got', false, null, sentErr);
if (headerValue) {
return new TestResult('got', true, headerValue, null, response.statusCode);
}
Expand All @@ -259,6 +285,8 @@ const AVAILABLE_TESTS = {

const headerValue = checkHeader(proxyHeaders, config.proxyHeader);

const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) return new TestResult('undici', false, null, sentErr);
if (headerValue) {
return new TestResult('undici', true, headerValue, null, statusCode);
}
Expand All @@ -279,9 +307,11 @@ const AVAILABLE_TESTS = {
proxyHeaders: config.proxyHeadersToSend,
});

const response = await client.get(config.testUrl);
const response = await client.get(config.testUrl).ok(() => true);
const headerValue = checkHeader(response.headers, config.proxyHeader);

const sentErr = validateSentHeaderValue(config, headerValue);
if (sentErr) return new TestResult('superagent', false, null, sentErr);
if (headerValue) {
return new TestResult('superagent', true, headerValue, null, response.status);
}
Expand Down