diff --git a/docs/docs/api/ProxyAgent.md b/docs/docs/api/ProxyAgent.md index 932716ae795..49a0a643946 100644 --- a/docs/docs/api/ProxyAgent.md +++ b/docs/docs/api/ProxyAgent.md @@ -17,6 +17,8 @@ Returns: `ProxyAgent` Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions) > It ommits `AgentOptions#connect`. +> **Note:** When `AgentOptions#connections` is set, and different from `0`, the non-standard [`proxy-connection` header](https://udger.com/resources/http-request-headers-detail?header=Proxy-Connection) will be set to `keep-alive` in the request. + * **uri** `string | URL` (required) - The URI of the proxy server. This can be provided as a string, as an instance of the URL class, or as an object with a `uri` property of type string. If the `uri` is provided as a string or `uri` is an object with an `uri` property of type string, then it will be parsed into a `URL` object according to the [WHATWG URL Specification](https://url.spec.whatwg.org). For detailed information on the parsing process and potential validation errors, please refer to the ["Writing" section](https://url.spec.whatwg.org/#writing) of the WHATWG URL Specification. diff --git a/lib/dispatcher/proxy-agent.js b/lib/dispatcher/proxy-agent.js index c5b4d51babb..48a13f71267 100644 --- a/lib/dispatcher/proxy-agent.js +++ b/lib/dispatcher/proxy-agent.js @@ -75,7 +75,8 @@ class ProxyAgent extends DispatcherBase { signal: opts.signal, headers: { ...this[kProxyHeaders], - host: opts.host + host: opts.host, + ...(opts.connections == null || opts.connections > 0 ? { 'proxy-connection': 'keep-alive' } : {}) }, servername: this[kProxyTls]?.servername || proxyHostname }) diff --git a/test/proxy-agent.js b/test/proxy-agent.js index a412b4adebf..4a294d79cc4 100644 --- a/test/proxy-agent.js +++ b/test/proxy-agent.js @@ -119,6 +119,46 @@ test('should accept string, URL and object as options', (t) => { t.doesNotThrow(() => new ProxyAgent({ uri: 'http://example.com' })) }) +test('use proxy-agent to connect through proxy (keep alive)', async (t) => { + t = tspl(t, { plan: 6 }) + const server = await buildServer() + const proxy = await buildProxy() + delete proxy.authenticate + + const serverUrl = `http://localhost:${server.address().port}` + const proxyUrl = `http://localhost:${proxy.address().port}` + const proxyAgent = new ProxyAgent({ + uri: proxyUrl + }) + const parsedOrigin = new URL(serverUrl) + + proxy.on('connect', (msg) => { + t.strictEqual(msg.headers['proxy-connection'], 'keep-alive') + }) + + server.on('request', (req, res) => { + t.strictEqual(req.url, '/') + t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host') + res.setHeader('content-type', 'application/json') + res.end(JSON.stringify({ hello: 'world' })) + }) + + const { + statusCode, + headers, + body + } = await request(serverUrl, { dispatcher: proxyAgent }) + const json = await body.json() + + t.strictEqual(statusCode, 200) + t.deepStrictEqual(json, { hello: 'world' }) + t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open') + + server.close() + proxy.close() + proxyAgent.close() +}) + test('use proxy-agent to connect through proxy', async (t) => { t = tspl(t, { plan: 6 }) const server = await buildServer() @@ -519,6 +559,7 @@ test('ProxyAgent correctly sends headers when using fetch - #1355, #1623', async } const expectedProxyHeaders = { + 'proxy-connection': 'keep-alive', host: `localhost:${server.address().port}`, connection: 'close' }