Skip to content
Open
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
16 changes: 14 additions & 2 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ changes:
* `timeout` {number} Socket timeout in milliseconds.
This will set the timeout when the socket is created.
* `proxyEnv` {Object|undefined} Environment variables for proxy configuration.
See [Built-in Proxy Support][] for details. **Default:** `undefined`
See [Built-in Proxy Support][] for details. **Default:** `undefined`; when
Node.js is configured to use the environment proxy, defaults to `process.env`
unless a falsy value (such as `null`) is passed to opt out.
* `HTTP_PROXY` {string|undefined} URL for the proxy server that HTTP requests should use.
If undefined, no proxy is used for HTTP requests.
* `HTTPS_PROXY` {string|undefined} URL for the proxy server that HTTPS requests should use.
Expand Down Expand Up @@ -4554,12 +4556,22 @@ When Node.js creates the global agent, if the `NODE_USE_ENV_PROXY` environment v
set to `1` or `--use-env-proxy` is enabled, the global agent will be constructed
with `proxyEnv: process.env`, enabling proxy support based on the environment variables.

The same fallback applies to any agent constructed without an explicit `proxyEnv`
option, not just the global agent. To opt a specific agent out, pass a falsy
`proxyEnv` value such as `null`:

```js
// Never uses the environment proxy, regardless of --use-env-proxy or NODE_USE_ENV_PROXY.
const agent = new Agent({ proxyEnv: null });
```

To enable proxy support dynamically and globally, use [`http.setGlobalProxyFromEnv()`][].

Custom agents can also be created with proxy support by passing a
`proxyEnv` option when constructing the agent. The value can be `process.env`
if they just want to inherit the configuration from the environment variables,
or an object with specific setting overriding the environment.
or an object with specific setting overriding the environment, or null
to explicitly disable proxy support.

The following properties of the `proxyEnv` are checked to configure proxy
support.
Expand Down
5 changes: 4 additions & 1 deletion lib/_http_agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
const {
NumberIsFinite,
NumberParseInt,
ObjectHasOwn,
ObjectKeys,
ObjectSetPrototypeOf,
ObjectValues,
Expand Down Expand Up @@ -184,7 +185,9 @@ function Agent(options) {
this.options.agentKeepAliveTimeoutBuffer :
1000;

const proxyEnv = this.options.proxyEnv;
const proxyEnv = ObjectHasOwn(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]);
Expand Down
75 changes: 75 additions & 0 deletions test/client-proxy/test-use-env-proxy-agent.mjs
Original file line number Diff line number Diff line change
@@ -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();
28 changes: 28 additions & 0 deletions test/fixtures/agent-request-and-log.js
Original file line number Diff line number Diff line change
@@ -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);
});