From 19d192429a2719bd954b68c4b4a36dc38ed35cd9 Mon Sep 17 00:00:00 2001 From: swigger Date: Thu, 16 Jul 2026 11:12:55 +0800 Subject: [PATCH] http: use env proxy when agent has no proxyEnv option When a user creates a custom agent without specifying proxyEnv but Node.js is configured to use a proxy from the environment (via --use-env-proxy or NODE_USE_ENV_PROXY), fall back to process.env. A developer can still explicitly disable proxying for an agent even when Node.js is configured to use a proxy at runtime, by passing a falsy proxyEnv explicitly. For example: const agent = new https.Agent({ proxyEnv: null }); Signed-off-by: swigger --- lib/_http_agent.js | 5 +- .../client-proxy/test-use-env-proxy-agent.mjs | 75 +++++++++++++++++++ test/fixtures/agent-request-and-log.js | 28 +++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 test/client-proxy/test-use-env-proxy-agent.mjs create mode 100644 test/fixtures/agent-request-and-log.js diff --git a/lib/_http_agent.js b/lib/_http_agent.js index eb58650f885105..7f95a2167d2dbd 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -25,6 +25,7 @@ const { NumberIsFinite, NumberParseInt, ObjectKeys, + ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, ObjectValues, Symbol, @@ -184,7 +185,9 @@ function Agent(options) { this.options.agentKeepAliveTimeoutBuffer : 1000; - const proxyEnv = this.options.proxyEnv; + const proxyEnv = ObjectPrototypeHasOwnProperty(this.options, 'proxyEnv') ? + this.options.proxyEnv : + (getOptionValue('--use-env-proxy') ? process.env : undefined); if (typeof proxyEnv === 'object' && proxyEnv !== null) { this[kProxyConfig] = parseProxyConfigFromEnv(proxyEnv, this.protocol, this.keepAlive); debug(`new ${this.protocol} agent with proxy config`, this[kProxyConfig]); diff --git a/test/client-proxy/test-use-env-proxy-agent.mjs b/test/client-proxy/test-use-env-proxy-agent.mjs new file mode 100644 index 00000000000000..5e02028f63dd03 --- /dev/null +++ b/test/client-proxy/test-use-env-proxy-agent.mjs @@ -0,0 +1,75 @@ +// This tests that when Node.js is configured to use the environment proxy +// (via --use-env-proxy or NODE_USE_ENV_PROXY), a custom agent created without +// a `proxyEnv` option falls back to process.env and goes through the proxy, +// while an agent created with an explicit `proxyEnv: null` bypasses it. + +import * as common from '../common/index.mjs'; +import assert from 'node:assert'; +import http from 'node:http'; +import { once } from 'events'; +import fixtures from '../common/fixtures.js'; +import { createProxyServer } from '../common/proxy-server.js'; + +// Start a minimal proxy server. +const { proxy, logs } = createProxyServer(); +proxy.listen(0); +await once(proxy, 'listening'); + +delete process.env.NODE_USE_ENV_PROXY; // Ensure the environment variable is not set. + +// Start a HTTP server to process the final request. +const server = http.createServer(common.mustCall((req, res) => { + res.end('Hello world'); +}, 2)); +server.on('error', common.mustNotCall((err) => { console.error('Server error', err); })); +server.listen(0); +await once(server, 'listening'); + +const serverHost = `localhost:${server.address().port}`; +const requestUrl = `http://${serverHost}/test`; +const script = fixtures.path('agent-request-and-log.js'); + +function runAgentRequest(env, cliArgs = []) { + return common.spawnPromisified(process.execPath, [...cliArgs, script], { + env: { + ...process.env, + REQUEST_URL: requestUrl, + HTTP_PROXY: `http://localhost:${proxy.address().port}`, + ...env, + }, + }); +} + +// A plain agent without a `proxyEnv` option should use the environment proxy +// when Node.js is started with --use-env-proxy. +{ + const { code, signal, stderr, stdout } = await runAgentRequest({}, ['--use-env-proxy']); + assert.strictEqual(stderr.trim(), ''); + assert.match(stdout, /Hello world/); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + // The request should go through the proxy. + assert.strictEqual(logs.length, 1); + assert.strictEqual(logs[0].method, 'GET'); + assert.strictEqual(logs[0].url, requestUrl); +} + +// An agent created with an explicit `proxyEnv: null` should bypass the proxy +// even when Node.js is configured to use the environment proxy. The same must +// hold whether the proxy is enabled via NODE_USE_ENV_PROXY or --use-env-proxy. +{ + logs.splice(0, logs.length); + const { code, signal, stderr, stdout } = await runAgentRequest({ + NODE_USE_ENV_PROXY: '1', + PROXY_ENV_NULL: '1', + }); + assert.strictEqual(stderr.trim(), ''); + assert.match(stdout, /Hello world/); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + // The request should reach the server directly, so the proxy sees nothing. + assert.strictEqual(logs.length, 0); +} + +server.close(); +proxy.close(); diff --git a/test/fixtures/agent-request-and-log.js b/test/fixtures/agent-request-and-log.js new file mode 100644 index 00000000000000..61aa21ba24adc6 --- /dev/null +++ b/test/fixtures/agent-request-and-log.js @@ -0,0 +1,28 @@ +'use strict'; + +// A minimal fixture that issues a single HTTP GET request using a +// user-provided agent. It is used to exercise how an agent picks up the +// environment proxy configuration when Node.js is started with +// --use-env-proxy or NODE_USE_ENV_PROXY. +// +// When PROXY_ENV_NULL is set, the agent is created with an explicit +// `proxyEnv: null`, which should disable proxying even when Node.js is +// configured to use the environment proxy. Otherwise a plain agent is +// created without any `proxyEnv` option, which should fall back to +// process.env when the environment proxy is enabled. + +const http = require('http'); + +const url = process.env.REQUEST_URL; + +const agent = process.env.PROXY_ENV_NULL ? + new http.Agent({ proxyEnv: null }) : + new http.Agent(); + +const req = http.get(url, { agent }, (res) => { + res.pipe(process.stdout); +}); + +req.on('error', (e) => { + console.error('Request Error', e); +});