diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 1c4a9407552829..c2afb2cd46048e 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -119,6 +119,7 @@ function prepareExecution(options) { initializeConfigFileSupport(); require('internal/dns/utils').initializeDns(); + setupHttpProxy(); if (isMainThread) { assert(internalBinding('worker').isMainThread); @@ -154,6 +155,17 @@ function prepareExecution(options) { return mainEntry; } +function setupHttpProxy() { + if (process.env.NODE_USE_ENV_PROXY && + (process.env.HTTP_PROXY || process.env.HTTPS_PROXY)) { + const { setGlobalDispatcher, EnvHttpProxyAgent } = require('internal/deps/undici/undici'); + const envHttpProxyAgent = new EnvHttpProxyAgent(); + setGlobalDispatcher(envHttpProxyAgent); + // TODO(joyeecheung): handle http/https global agents and perhaps Agent constructor + // behaviors. + } +} + function setupUserModules(forceDefaultLoader = false) { initializeCJSLoader(); initializeESMLoader(forceDefaultLoader); diff --git a/test/fixtures/fetch-and-log.mjs b/test/fixtures/fetch-and-log.mjs new file mode 100644 index 00000000000000..6907ea74832437 --- /dev/null +++ b/test/fixtures/fetch-and-log.mjs @@ -0,0 +1,4 @@ +const address = process.env.SERVER_ADDRESS; +const response = await fetch(address); +const body = await response.text(); +console.log(body); diff --git a/test/fixtures/proxy-handler.js b/test/fixtures/proxy-handler.js new file mode 100644 index 00000000000000..44c41f54f4129f --- /dev/null +++ b/test/fixtures/proxy-handler.js @@ -0,0 +1,22 @@ +const net = require('net'); + +exports.onConnect = function (req, clientSocket, head) { + const [hostname, port] = req.url.split(':'); + + const serverSocket = net.connect(port, hostname, () => { + clientSocket.write( + 'HTTP/1.1 200 Connection Established\r\n' + + 'Proxy-agent: Node.js-Proxy\r\n' + + '\r\n' + ); + serverSocket.write(head); + clientSocket.pipe(serverSocket); + serverSocket.pipe(clientSocket); + }); + + serverSocket.on('error', (err) => { + console.error('Error on CONNECT tunnel:', err.message); + clientSocket.write('HTTP/1.1 500 Connection Error\r\n\r\n'); + clientSocket.end(); + }); +}; diff --git a/test/parallel/test-http-proxy-fetch.js b/test/parallel/test-http-proxy-fetch.js new file mode 100644 index 00000000000000..e60c75c55a8b1a --- /dev/null +++ b/test/parallel/test-http-proxy-fetch.js @@ -0,0 +1,64 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { spawn } = require('child_process'); +const http = require('http'); +const { onConnect } = require('../fixtures/proxy-handler'); + +// Start a server to process the final request. +const server = http.createServer((req, res) => { + res.end('Hello world'); +}); +server.on('error', (err) => { console.log('Server error', err); }); + +server.listen(0, common.mustCall(() => { + // Start a proxy server to tunnel the request. + const proxy = http.createServer(); + // If the request does not go through the proxy server, common.mustCall fails. + proxy.on('connect', common.mustCall((req, clientSocket, head) => { + console.log('Proxying CONNECT', req.url, req.headers); + assert.strictEqual(req.url, `localhost:${server.address().port}`); + onConnect(req, clientSocket, head); + })); + proxy.on('error', (err) => { console.log('Proxy error', err); }); + + proxy.listen(0, common.mustCall(() => { + const proxyAddress = `http://localhost:${proxy.address().port}`; + const serverAddress = `http://localhost:${server.address().port}`; + const child = spawn(process.execPath, + [fixtures.path('fetch-and-log.mjs')], + { + env: { + ...process.env, + HTTP_PROXY: proxyAddress, + NODE_USE_ENV_PROXY: true, + SERVER_ADDRESS: serverAddress, + }, + }); + + const stderr = []; + const stdout = []; + child.stderr.on('data', (chunk) => { + stderr.push(chunk); + }); + child.stdout.on('data', (chunk) => { + stdout.push(chunk); + }); + + child.on('exit', common.mustCall(function(code, signal) { + proxy.close(); + server.close(); + + console.log('--- stderr ---'); + console.log(Buffer.concat(stderr).toString()); + console.log('--- stdout ---'); + const stdoutStr = Buffer.concat(stdout).toString(); + console.log(stdoutStr); + assert.strictEqual(stdoutStr.trim(), 'Hello world'); + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + })); +}));