Skip to content

Commit 1a701f6

Browse files
committed
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 <swigger@gmail.com>
1 parent 7a11a9b commit 1a701f6

4 files changed

Lines changed: 121 additions & 3 deletions

File tree

doc/api/http.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@ changes:
208208
* `timeout` {number} Socket timeout in milliseconds.
209209
This will set the timeout when the socket is created.
210210
* `proxyEnv` {Object|undefined} Environment variables for proxy configuration.
211-
See [Built-in Proxy Support][] for details. **Default:** `undefined`
211+
See [Built-in Proxy Support][] for details. **Default:** `undefined`; when
212+
Node.js is configured to use the environment proxy, defaults to `process.env`
213+
unless a falsy value (such as `null`) is passed to opt out.
212214
* `HTTP_PROXY` {string|undefined} URL for the proxy server that HTTP requests should use.
213215
If undefined, no proxy is used for HTTP requests.
214216
* `HTTPS_PROXY` {string|undefined} URL for the proxy server that HTTPS requests should use.
@@ -4554,12 +4556,22 @@ When Node.js creates the global agent, if the `NODE_USE_ENV_PROXY` environment v
45544556
set to `1` or `--use-env-proxy` is enabled, the global agent will be constructed
45554557
with `proxyEnv: process.env`, enabling proxy support based on the environment variables.
45564558
4559+
The same fallback applies to any agent constructed without an explicit `proxyEnv`
4560+
option, not just the global agent. To opt a specific agent out, pass a falsy
4561+
`proxyEnv` value such as `null`:
4562+
4563+
```js
4564+
// Never uses the environment proxy, regardless of --use-env-proxy or NODE_USE_ENV_PROXY.
4565+
const agent = new Agent({ proxyEnv: null });
4566+
```
4567+
45574568
To enable proxy support dynamically and globally, use [`http.setGlobalProxyFromEnv()`][].
45584569
45594570
Custom agents can also be created with proxy support by passing a
45604571
`proxyEnv` option when constructing the agent. The value can be `process.env`
45614572
if they just want to inherit the configuration from the environment variables,
4562-
or an object with specific setting overriding the environment.
4573+
or an object with specific setting overriding the environment, or null
4574+
to explicitly disable proxy support.
45634575
45644576
The following properties of the `proxyEnv` are checked to configure proxy
45654577
support.

lib/_http_agent.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
const {
2525
NumberIsFinite,
2626
NumberParseInt,
27+
ObjectHasOwn,
2728
ObjectKeys,
2829
ObjectSetPrototypeOf,
2930
ObjectValues,
@@ -184,7 +185,9 @@ function Agent(options) {
184185
this.options.agentKeepAliveTimeoutBuffer :
185186
1000;
186187

187-
const proxyEnv = this.options.proxyEnv;
188+
const proxyEnv = ObjectHasOwn(this.options, 'proxyEnv') ?
189+
this.options.proxyEnv :
190+
(getOptionValue('--use-env-proxy') ? process.env : undefined);
188191
if (typeof proxyEnv === 'object' && proxyEnv !== null) {
189192
this[kProxyConfig] = parseProxyConfigFromEnv(proxyEnv, this.protocol, this.keepAlive);
190193
debug(`new ${this.protocol} agent with proxy config`, this[kProxyConfig]);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// This tests that when Node.js is configured to use the environment proxy
2+
// (via --use-env-proxy or NODE_USE_ENV_PROXY), a custom agent created without
3+
// a `proxyEnv` option falls back to process.env and goes through the proxy,
4+
// while an agent created with an explicit `proxyEnv: null` bypasses it.
5+
6+
import * as common from '../common/index.mjs';
7+
import assert from 'node:assert';
8+
import http from 'node:http';
9+
import { once } from 'events';
10+
import fixtures from '../common/fixtures.js';
11+
import { createProxyServer } from '../common/proxy-server.js';
12+
13+
// Start a minimal proxy server.
14+
const { proxy, logs } = createProxyServer();
15+
proxy.listen(0);
16+
await once(proxy, 'listening');
17+
18+
delete process.env.NODE_USE_ENV_PROXY; // Ensure the environment variable is not set.
19+
20+
// Start a HTTP server to process the final request.
21+
const server = http.createServer(common.mustCall((req, res) => {
22+
res.end('Hello world');
23+
}, 2));
24+
server.on('error', common.mustNotCall((err) => { console.error('Server error', err); }));
25+
server.listen(0);
26+
await once(server, 'listening');
27+
28+
const serverHost = `localhost:${server.address().port}`;
29+
const requestUrl = `http://${serverHost}/test`;
30+
const script = fixtures.path('agent-request-and-log.js');
31+
32+
function runAgentRequest(env, cliArgs = []) {
33+
return common.spawnPromisified(process.execPath, [...cliArgs, script], {
34+
env: {
35+
...process.env,
36+
REQUEST_URL: requestUrl,
37+
HTTP_PROXY: `http://localhost:${proxy.address().port}`,
38+
...env,
39+
},
40+
});
41+
}
42+
43+
// A plain agent without a `proxyEnv` option should use the environment proxy
44+
// when Node.js is started with --use-env-proxy.
45+
{
46+
const { code, signal, stderr, stdout } = await runAgentRequest({}, ['--use-env-proxy']);
47+
assert.strictEqual(stderr.trim(), '');
48+
assert.match(stdout, /Hello world/);
49+
assert.strictEqual(code, 0);
50+
assert.strictEqual(signal, null);
51+
// The request should go through the proxy.
52+
assert.strictEqual(logs.length, 1);
53+
assert.strictEqual(logs[0].method, 'GET');
54+
assert.strictEqual(logs[0].url, requestUrl);
55+
}
56+
57+
// An agent created with an explicit `proxyEnv: null` should bypass the proxy
58+
// even when Node.js is configured to use the environment proxy. The same must
59+
// hold whether the proxy is enabled via NODE_USE_ENV_PROXY or --use-env-proxy.
60+
{
61+
logs.splice(0, logs.length);
62+
const { code, signal, stderr, stdout } = await runAgentRequest({
63+
NODE_USE_ENV_PROXY: '1',
64+
PROXY_ENV_NULL: '1',
65+
});
66+
assert.strictEqual(stderr.trim(), '');
67+
assert.match(stdout, /Hello world/);
68+
assert.strictEqual(code, 0);
69+
assert.strictEqual(signal, null);
70+
// The request should reach the server directly, so the proxy sees nothing.
71+
assert.strictEqual(logs.length, 0);
72+
}
73+
74+
server.close();
75+
proxy.close();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
// A minimal fixture that issues a single HTTP GET request using a
4+
// user-provided agent. It is used to exercise how an agent picks up the
5+
// environment proxy configuration when Node.js is started with
6+
// --use-env-proxy or NODE_USE_ENV_PROXY.
7+
//
8+
// When PROXY_ENV_NULL is set, the agent is created with an explicit
9+
// `proxyEnv: null`, which should disable proxying even when Node.js is
10+
// configured to use the environment proxy. Otherwise a plain agent is
11+
// created without any `proxyEnv` option, which should fall back to
12+
// process.env when the environment proxy is enabled.
13+
14+
const http = require('http');
15+
16+
const url = process.env.REQUEST_URL;
17+
18+
const agent = process.env.PROXY_ENV_NULL ?
19+
new http.Agent({ proxyEnv: null }) :
20+
new http.Agent();
21+
22+
const req = http.get(url, { agent }, (res) => {
23+
res.pipe(process.stdout);
24+
});
25+
26+
req.on('error', (e) => {
27+
console.error('Request Error', e);
28+
});

0 commit comments

Comments
 (0)