// This tests that when using a proxy with an agent with maxSockets: 1 and // keepAlive: true, after the first request finishes, a subsequent request // reuses the same socket connection. import * as common from '../common/index.mjs'; import assert from 'node:assert'; import { once } from 'events'; import { createProxyServer } from '../common/proxy-server.js'; import fixtures from '../common/fixtures.js'; if (!common.hasCrypto) common.skip('missing crypto'); // https must be dynamically imported so that builds without crypto support // can skip it. const { default: https } = await import('node:https'); // Start a server to handle requests const server = https.createServer({ cert: fixtures.readKey('agent8-cert.pem'), key: fixtures.readKey('agent8-key.pem'), }, common.mustCall((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(`Response for ${req.url}`); }, 2)); server.on('error', common.mustNotCall((err) => { console.error('Server error', err); })); server.listen(0); await once(server, 'listening'); // Start a minimal proxy server const { proxy, logs } = createProxyServer(); proxy.listen(0); await once(proxy, 'listening'); const serverHost = `localhost:${server.address().port}`; const proxyUrl = `http://localhost:${proxy.address().port}`; // Create an agent with maxSockets: 1, keepAlive: true, and proxy support const agent = new https.Agent({ maxSockets: 1, keepAlive: true, proxyEnv: { HTTPS_PROXY: proxyUrl, }, ca: fixtures.readKey('fake-startcom-root-cert.pem'), }); // Make first request const firstReq = https.request({ hostname: 'localhost', port: server.address().port, path: '/first', agent: agent, }, common.mustCall((res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', common.mustCall(() => { assert.strictEqual(data, `Response for /first`); })); })); firstReq.on('error', common.mustNotCall()); firstReq.end(); agent.once('free', common.mustCall((socket) => { // At this point, the first request has completed and the socket is returned // to the pool. process.nextTick(() => { const options = { hostname: 'localhost', port: server.address().port, path: '/second', agent: agent, }; // Check that the socket is still in the pool. // TODO(joyeecheung): we don't currently have a way to set the root certificate // dynamically and have to use the ca: option which affects name computation. // So we cannot check the pool here until we have that feature. // See https://github.com/nodejs/node/pull/58822 // Send second request when first request closes (socket returned to pool) const secondReq = https.request(options, common.mustCall((res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', common.mustCall(() => { assert.strictEqual(data, `Response for /second`); // The two shares the same proxy connection. assert.deepStrictEqual(logs, [{ method: 'CONNECT', url: serverHost, headers: { 'proxy-connection': 'keep-alive', 'host': serverHost }, }]); proxy.close(); server.close(); })); })); secondReq.on('error', common.mustNotCall()); secondReq.end(); }); }));