Skip to content

Commit 9dac053

Browse files
committed
Assert SEND_PROXY_VALUE echoed; fix agent so proxy headers are honored
- When SEND_PROXY_HEADER and SEND_PROXY_VALUE are set and we check the same header, test now requires the proxy response value to equal SEND_PROXY_VALUE. - ProxyHeadersAgent: stop returning proxySocket from createConnection so the agent uses only the callback stream (tlsSocket). Returning the raw socket let the agent write the request before CONNECT completed, so the proxy sometimes ignored our X-ProxyMesh-IP and assigned a different IP. - utils.buildConnectRequest: support proxyHeaders as Map for consistency. Made-with: Cursor
1 parent 3a7d9a7 commit 9dac053

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

lib/core/proxy-headers-agent.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export class ProxyHeadersAgent extends Agent {
9494
this.proxyAuth,
9595
this.proxyHeaders
9696
);
97-
9897
proxySocket.write(connectRequest);
9998
});
10099

@@ -153,7 +152,8 @@ export class ProxyHeadersAgent extends Agent {
153152
});
154153
});
155154

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

lib/core/utils.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ export function buildConnectRequest(targetHost, targetPort, proxyAuth, proxyHead
5656
lines.push(`Proxy-Authorization: Basic ${proxyAuth}`);
5757
}
5858

59-
for (const [key, value] of Object.entries(proxyHeaders)) {
59+
const entries = proxyHeaders instanceof Map
60+
? [...proxyHeaders.entries()]
61+
: Object.entries(proxyHeaders || {});
62+
for (const [key, value] of entries) {
6063
lines.push(`${key}: ${value}`);
6164
}
6265

test/test_proxy_headers.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,20 @@ function checkHeader(headers, headerName) {
128128
return null;
129129
}
130130

131+
/**
132+
* When SEND_PROXY_HEADER and SEND_PROXY_VALUE are set and we're checking the same header,
133+
* the proxy response must echo that value (e.g. X-ProxyMesh-IP).
134+
* @returns {string|null} Error message if expectation not met, null if OK or N/A.
135+
*/
136+
function validateSentHeaderValue(config, headerValue) {
137+
if (!config.sendProxyHeader || !config.sendProxyValue) return null;
138+
if (config.proxyHeader.toLowerCase() !== config.sendProxyHeader.toLowerCase()) return null;
139+
const expected = String(config.sendProxyValue).trim();
140+
const actual = headerValue ? String(headerValue).trim() : '';
141+
if (actual === expected) return null;
142+
return `Expected ${config.proxyHeader} to equal ${expected} (sent in request) but got ${actual}`;
143+
}
144+
131145
const AVAILABLE_TESTS = {
132146
async core(config) {
133147
try {
@@ -159,7 +173,10 @@ const AVAILABLE_TESTS = {
159173
? checkHeader(capturedHeaders, config.proxyHeader)
160174
: null;
161175

162-
if (headerValue) {
176+
const sentErr = validateSentHeaderValue(config, headerValue);
177+
if (sentErr) {
178+
resolve(new TestResult('core', false, null, sentErr, res.statusCode));
179+
} else if (headerValue) {
163180
resolve(new TestResult('core', true, headerValue, null, res.statusCode));
164181
} else {
165182
resolve(new TestResult('core', false, null,
@@ -194,6 +211,8 @@ const AVAILABLE_TESTS = {
194211
});
195212
const headerValue = checkHeader(response.headers, config.proxyHeader);
196213

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

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

238+
const sentErr = validateSentHeaderValue(config, headerValue);
239+
if (sentErr) return new TestResult('node-fetch', false, null, sentErr);
219240
if (headerValue) {
220241
return new TestResult('node-fetch', true, headerValue, null, response.status);
221242
}
@@ -240,6 +261,8 @@ const AVAILABLE_TESTS = {
240261
const response = await client(config.testUrl);
241262
const headerValue = checkHeader(response.headers, config.proxyHeader);
242263

264+
const sentErr = validateSentHeaderValue(config, headerValue);
265+
if (sentErr) return new TestResult('got', false, null, sentErr);
243266
if (headerValue) {
244267
return new TestResult('got', true, headerValue, null, response.statusCode);
245268
}
@@ -262,6 +285,8 @@ const AVAILABLE_TESTS = {
262285

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

288+
const sentErr = validateSentHeaderValue(config, headerValue);
289+
if (sentErr) return new TestResult('undici', false, null, sentErr);
265290
if (headerValue) {
266291
return new TestResult('undici', true, headerValue, null, statusCode);
267292
}
@@ -285,6 +310,8 @@ const AVAILABLE_TESTS = {
285310
const response = await client.get(config.testUrl).ok(() => true);
286311
const headerValue = checkHeader(response.headers, config.proxyHeader);
287312

313+
const sentErr = validateSentHeaderValue(config, headerValue);
314+
if (sentErr) return new TestResult('superagent', false, null, sentErr);
288315
if (headerValue) {
289316
return new TestResult('superagent', true, headerValue, null, response.status);
290317
}

0 commit comments

Comments
 (0)