Skip to content

Commit 032d200

Browse files
authored
feat(#4086): proxy keep alive (#4128)
1 parent f184975 commit 032d200

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

docs/docs/api/ProxyAgent.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Returns: `ProxyAgent`
1717
Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
1818
> It ommits `AgentOptions#connect`.
1919
20+
> **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.
21+
2022
* **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.
2123
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).
2224
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.

lib/dispatcher/proxy-agent.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ class ProxyAgent extends DispatcherBase {
144144
signal: opts.signal,
145145
headers: {
146146
...this[kProxyHeaders],
147-
host: opts.host
147+
host: opts.host,
148+
...(opts.connections == null || opts.connections > 0 ? { 'proxy-connection': 'keep-alive' } : {})
148149
},
149150
servername: this[kProxyTls]?.servername || proxyHostname
150151
})

test/proxy-agent.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,46 @@ test('should accept string, URL and object as options', (t) => {
119119
t.doesNotThrow(() => new ProxyAgent({ uri: 'http://example.com' }))
120120
})
121121

122+
test('use proxy-agent to connect through proxy (keep alive)', async (t) => {
123+
t = tspl(t, { plan: 6 })
124+
const server = await buildServer()
125+
const proxy = await buildProxy()
126+
delete proxy.authenticate
127+
128+
const serverUrl = `http://localhost:${server.address().port}`
129+
const proxyUrl = `http://localhost:${proxy.address().port}`
130+
const proxyAgent = new ProxyAgent({
131+
uri: proxyUrl
132+
})
133+
const parsedOrigin = new URL(serverUrl)
134+
135+
proxy.on('connect', (msg) => {
136+
t.strictEqual(msg.headers['proxy-connection'], 'keep-alive')
137+
})
138+
139+
server.on('request', (req, res) => {
140+
t.strictEqual(req.url, '/')
141+
t.strictEqual(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
142+
res.setHeader('content-type', 'application/json')
143+
res.end(JSON.stringify({ hello: 'world' }))
144+
})
145+
146+
const {
147+
statusCode,
148+
headers,
149+
body
150+
} = await request(serverUrl, { dispatcher: proxyAgent })
151+
const json = await body.json()
152+
153+
t.strictEqual(statusCode, 200)
154+
t.deepStrictEqual(json, { hello: 'world' })
155+
t.strictEqual(headers.connection, 'keep-alive', 'should remain the connection open')
156+
157+
server.close()
158+
proxy.close()
159+
proxyAgent.close()
160+
})
161+
122162
test('use proxy-agent to connect through proxy', async (t) => {
123163
t = tspl(t, { plan: 6 })
124164
const server = await buildServer()
@@ -519,6 +559,7 @@ test('ProxyAgent correctly sends headers when using fetch - #1355, #1623', async
519559
}
520560

521561
const expectedProxyHeaders = {
562+
'proxy-connection': 'keep-alive',
522563
host: `localhost:${server.address().port}`,
523564
connection: 'close'
524565
}

0 commit comments

Comments
 (0)