Skip to content

Commit 6714f9d

Browse files
oooshimaoshimaharuna
and
oshimaharuna
authored
feat: add parameter to ProxyAgent options, which is type of authentication (nodejs#1705)
* feat: add parameter to ProxyAgent options, whitch is type of authentication. * fix: docs and comments * fix: docs * add: throw error when using auth in combination with token * add: test for the types Co-authored-by: oshimaharuna <[email protected]>
1 parent f2e18aa commit 6714f9d

File tree

5 files changed

+88
-1
lines changed

5 files changed

+88
-1
lines changed

docs/api/ProxyAgent.md

+22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Returns: `ProxyAgent`
1717
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
1818

1919
* **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
20+
* **token** `string` (optional) - It can be passed by a string of token for authentication.
21+
* **auth** `string` (**deprecated**) - Use token.
2022

2123
Examples:
2224

@@ -74,6 +76,26 @@ for await (const data of body) {
7476
}
7577
```
7678

79+
#### Example - Basic Proxy Request with authentication
80+
81+
```js
82+
import { setGlobalDispatcher, request, ProxyAgent } from 'undici';
83+
84+
const proxyAgent = new ProxyAgent({
85+
uri: 'my.proxy.server',
86+
token: 'Bearer xxxx'
87+
});
88+
setGlobalDispatcher(proxyAgent);
89+
90+
const { statusCode, body } = await request('http://localhost:3000/foo');
91+
92+
console.log('response received', statusCode); // response received 200
93+
94+
for await (const data of body) {
95+
console.log('data', data.toString('utf8')); // data foo
96+
}
97+
```
98+
7799
### `ProxyAgent.close()`
78100

79101
Closes the proxy agent and waits for registered pools and clients to also close before resolving.

lib/proxy-agent.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,13 @@ class ProxyAgent extends DispatcherBase {
5555
this[kProxyTls] = opts.proxyTls
5656
this[kProxyHeaders] = {}
5757

58-
if (opts.auth) {
58+
if (opts.auth && opts.token) {
59+
throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')
60+
} else if (opts.auth) {
61+
/* @deprecated in favour of opts.token */
5962
this[kProxyHeaders]['proxy-authorization'] = `Basic ${opts.auth}`
63+
} else if (opts.token) {
64+
this[kProxyHeaders]['proxy-authorization'] = opts.token
6065
}
6166

6267
const resolvedUrl = new URL(opts.uri)

test/proxy-agent.js

+55
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ test('should throw error when no uri is provided', (t) => {
1818
t.throws(() => new ProxyAgent({}), InvalidArgumentError)
1919
})
2020

21+
test('using auth in combination with token should throw', (t) => {
22+
t.plan(1)
23+
t.throws(() => new ProxyAgent({
24+
auth: 'foo',
25+
token: 'Bearer bar',
26+
uri: 'http://example.com'
27+
}),
28+
InvalidArgumentError
29+
)
30+
})
31+
2132
test('should accept string and object as options', (t) => {
2233
t.plan(2)
2334
t.doesNotThrow(() => new ProxyAgent('http://example.com'))
@@ -142,6 +153,50 @@ test('use proxy-agent with auth', async (t) => {
142153
proxyAgent.close()
143154
})
144155

156+
test('use proxy-agent with token', async (t) => {
157+
t.plan(7)
158+
const server = await buildServer()
159+
const proxy = await buildProxy()
160+
161+
const serverUrl = `http://localhost:${server.address().port}`
162+
const proxyUrl = `http://localhost:${proxy.address().port}`
163+
const proxyAgent = new ProxyAgent({
164+
token: `Bearer ${Buffer.from('user:pass').toString('base64')}`,
165+
uri: proxyUrl
166+
})
167+
const parsedOrigin = new URL(serverUrl)
168+
169+
proxy.authenticate = function (req, fn) {
170+
t.pass('authentication should be called')
171+
fn(null, req.headers['proxy-authorization'] === `Bearer ${Buffer.from('user:pass').toString('base64')}`)
172+
}
173+
proxy.on('connect', () => {
174+
t.pass('proxy should be called')
175+
})
176+
177+
server.on('request', (req, res) => {
178+
t.equal(req.url, '/hello?foo=bar')
179+
t.equal(req.headers.host, parsedOrigin.host, 'should not use proxyUrl as host')
180+
res.setHeader('content-type', 'application/json')
181+
res.end(JSON.stringify({ hello: 'world' }))
182+
})
183+
184+
const {
185+
statusCode,
186+
headers,
187+
body
188+
} = await request(serverUrl + '/hello?foo=bar', { dispatcher: proxyAgent })
189+
const json = await body.json()
190+
191+
t.equal(statusCode, 200)
192+
t.same(json, { hello: 'world' })
193+
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')
194+
195+
server.close()
196+
proxy.close()
197+
proxyAgent.close()
198+
})
199+
145200
test('sending proxy-authorization in request headers should throw', async (t) => {
146201
t.plan(3)
147202
const server = await buildServer()

test/types/proxy-agent.test-d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ expectAssignable<ProxyAgent>(
99
connections: 1,
1010
uri: '',
1111
auth: '',
12+
token: '',
1213
maxRedirections: 1,
1314
factory: (_origin: URL, opts: Object) => new Agent(opts),
1415
requestTls: {

types/proxy-agent.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ declare class ProxyAgent extends Dispatcher {
1414
declare namespace ProxyAgent {
1515
export interface Options extends Agent.Options {
1616
uri: string;
17+
/**
18+
* @deprecated use opts.token
19+
*/
1720
auth?: string;
21+
token?: string;
1822
requestTls?: TlsOptions & { servername?: string };
1923
proxyTls?: TlsOptions & { servername?: string };
2024
}

0 commit comments

Comments
 (0)