Skip to content

Commit

Permalink
feat: add parameter to ProxyAgent options, which is type of authentic…
Browse files Browse the repository at this point in the history
…ation (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 <ooooooshim115@gmail.com>
  • Loading branch information
oooshima and oshimaharuna authored Oct 17, 2022
1 parent f2e18aa commit 6714f9d
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 1 deletion.
22 changes: 22 additions & 0 deletions docs/api/ProxyAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Returns: `ProxyAgent`
Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)

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

Examples:

Expand Down Expand Up @@ -74,6 +76,26 @@ for await (const data of body) {
}
```

#### Example - Basic Proxy Request with authentication

```js
import { setGlobalDispatcher, request, ProxyAgent } from 'undici';

const proxyAgent = new ProxyAgent({
uri: 'my.proxy.server',
token: 'Bearer xxxx'
});
setGlobalDispatcher(proxyAgent);

const { statusCode, body } = await request('http://localhost:3000/foo');

console.log('response received', statusCode); // response received 200

for await (const data of body) {
console.log('data', data.toString('utf8')); // data foo
}
```

### `ProxyAgent.close()`

Closes the proxy agent and waits for registered pools and clients to also close before resolving.
Expand Down
7 changes: 6 additions & 1 deletion lib/proxy-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@ class ProxyAgent extends DispatcherBase {
this[kProxyTls] = opts.proxyTls
this[kProxyHeaders] = {}

if (opts.auth) {
if (opts.auth && opts.token) {
throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')
} else if (opts.auth) {
/* @deprecated in favour of opts.token */
this[kProxyHeaders]['proxy-authorization'] = `Basic ${opts.auth}`
} else if (opts.token) {
this[kProxyHeaders]['proxy-authorization'] = opts.token
}

const resolvedUrl = new URL(opts.uri)
Expand Down
55 changes: 55 additions & 0 deletions test/proxy-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ test('should throw error when no uri is provided', (t) => {
t.throws(() => new ProxyAgent({}), InvalidArgumentError)
})

test('using auth in combination with token should throw', (t) => {
t.plan(1)
t.throws(() => new ProxyAgent({
auth: 'foo',
token: 'Bearer bar',
uri: 'http://example.com'
}),
InvalidArgumentError
)
})

test('should accept string and object as options', (t) => {
t.plan(2)
t.doesNotThrow(() => new ProxyAgent('http://example.com'))
Expand Down Expand Up @@ -142,6 +153,50 @@ test('use proxy-agent with auth', async (t) => {
proxyAgent.close()
})

test('use proxy-agent with token', async (t) => {
t.plan(7)
const server = await buildServer()
const proxy = await buildProxy()

const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
const proxyAgent = new ProxyAgent({
token: `Bearer ${Buffer.from('user:pass').toString('base64')}`,
uri: proxyUrl
})
const parsedOrigin = new URL(serverUrl)

proxy.authenticate = function (req, fn) {
t.pass('authentication should be called')
fn(null, req.headers['proxy-authorization'] === `Bearer ${Buffer.from('user:pass').toString('base64')}`)
}
proxy.on('connect', () => {
t.pass('proxy should be called')
})

server.on('request', (req, res) => {
t.equal(req.url, '/hello?foo=bar')
t.equal(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 + '/hello?foo=bar', { dispatcher: proxyAgent })
const json = await body.json()

t.equal(statusCode, 200)
t.same(json, { hello: 'world' })
t.equal(headers.connection, 'keep-alive', 'should remain the connection open')

server.close()
proxy.close()
proxyAgent.close()
})

test('sending proxy-authorization in request headers should throw', async (t) => {
t.plan(3)
const server = await buildServer()
Expand Down
1 change: 1 addition & 0 deletions test/types/proxy-agent.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ expectAssignable<ProxyAgent>(
connections: 1,
uri: '',
auth: '',
token: '',
maxRedirections: 1,
factory: (_origin: URL, opts: Object) => new Agent(opts),
requestTls: {
Expand Down
4 changes: 4 additions & 0 deletions types/proxy-agent.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ declare class ProxyAgent extends Dispatcher {
declare namespace ProxyAgent {
export interface Options extends Agent.Options {
uri: string;
/**
* @deprecated use opts.token
*/
auth?: string;
token?: string;
requestTls?: TlsOptions & { servername?: string };
proxyTls?: TlsOptions & { servername?: string };
}
Expand Down

0 comments on commit 6714f9d

Please sign in to comment.