undici 4.8.1 → 4.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api/ProxyAgent.md +100 -0
- package/docs/best-practices/proxy.md +4 -1
- package/index.d.ts +2 -1
- package/index.js +2 -0
- package/lib/client.js +147 -127
- package/lib/core/symbols.js +2 -1
- package/lib/proxy-agent.js +59 -0
- package/package.json +1 -1
- package/types/proxy-agent.d.ts +17 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Class: ProxyAgent
|
|
2
|
+
|
|
3
|
+
Extends: `undici.Dispatcher`
|
|
4
|
+
|
|
5
|
+
A Proxy Agent class that implements the Agent API. It allows the connection through proxy in a simple way.
|
|
6
|
+
|
|
7
|
+
## `new ProxyAgent([options])`
|
|
8
|
+
|
|
9
|
+
Arguments:
|
|
10
|
+
|
|
11
|
+
* **options** `ProxyAgentOptions` (required) - It extends the `Agent` options.
|
|
12
|
+
|
|
13
|
+
Returns: `ProxyAgent`
|
|
14
|
+
|
|
15
|
+
### Parameter: `ProxyAgentOptions`
|
|
16
|
+
|
|
17
|
+
Extends: [`AgentOptions`](docs/api/Agent.md#parameter-agentoptions)
|
|
18
|
+
|
|
19
|
+
* **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { ProxyAgent } from 'undici'
|
|
25
|
+
|
|
26
|
+
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
27
|
+
// or
|
|
28
|
+
const proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Example - Basic ProxyAgent instantiation
|
|
32
|
+
|
|
33
|
+
This will instantiate the ProxyAgent. It will not do anything until registered as the agent to use with requests.
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import { ProxyAgent } from 'undici'
|
|
37
|
+
|
|
38
|
+
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Example - Basic Proxy Request with global agent dispatcher
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { setGlobalDispatcher, request, ProxyAgent } from 'undici'
|
|
45
|
+
|
|
46
|
+
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
47
|
+
setGlobalDispatcher(proxyAgent)
|
|
48
|
+
|
|
49
|
+
const { statusCode, body } = await request('http://localhost:3000/foo')
|
|
50
|
+
|
|
51
|
+
console.log('response received', statusCode) // response received 200
|
|
52
|
+
|
|
53
|
+
for await (const data of body) {
|
|
54
|
+
console.log('data', data.toString('utf8')) // data foo
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Example - Basic Proxy Request with local agent dispatcher
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import { ProxyAgent, request } from 'undici'
|
|
62
|
+
|
|
63
|
+
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
statusCode,
|
|
67
|
+
body
|
|
68
|
+
} = await request('http://localhost:3000/foo', { dispatcher: proxyAgent })
|
|
69
|
+
|
|
70
|
+
console.log('response received', statusCode) // response received 200
|
|
71
|
+
|
|
72
|
+
for await (const data of body) {
|
|
73
|
+
console.log('data', data.toString('utf8')) // data foo
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `ProxyAgent.close()`
|
|
78
|
+
|
|
79
|
+
Closes the proxy agent and waits for registered pools and clients to also close before resolving.
|
|
80
|
+
|
|
81
|
+
Returns: `Promise<void>`
|
|
82
|
+
|
|
83
|
+
#### Example - clean up after tests are complete
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import { ProxyAgent, setGlobalDispatcher } from 'undici'
|
|
87
|
+
|
|
88
|
+
const proxyAgent = new ProxyAgent('my.proxy.server')
|
|
89
|
+
setGlobalDispatcher(proxyAgent)
|
|
90
|
+
|
|
91
|
+
await proxyAgent.close()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `ProxyAgent.dispatch(options, handlers)`
|
|
95
|
+
|
|
96
|
+
Implements [`Agent.dispatch(options, handlers)`](docs/api/Agent.md#parameter-agentdispatchoptions).
|
|
97
|
+
|
|
98
|
+
### `ProxyAgent.request(options[, callback])`
|
|
99
|
+
|
|
100
|
+
See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Connecting through a proxy
|
|
2
2
|
|
|
3
|
-
Connecting through a proxy is possible by
|
|
3
|
+
Connecting through a proxy is possible by:
|
|
4
|
+
|
|
5
|
+
- Using [AgentProxy](docs/api/ProxyAgent.md).
|
|
6
|
+
- Configuring `Client` or `Pool` constructor.
|
|
4
7
|
|
|
5
8
|
The proxy url should be passed to the `Client` or `Pool` constructor, while the upstream server url
|
|
6
9
|
should be added to every request call in the `path`.
|
package/index.d.ts
CHANGED
|
@@ -10,13 +10,14 @@ import MockClient = require('./types/mock-client')
|
|
|
10
10
|
import MockPool = require('./types/mock-pool')
|
|
11
11
|
import MockAgent = require('./types/mock-agent')
|
|
12
12
|
import mockErrors = require('./types/mock-errors')
|
|
13
|
+
import ProxyAgent from './types/proxy-agent'
|
|
13
14
|
import { request, pipeline, stream, connect, upgrade } from './types/api'
|
|
14
15
|
|
|
15
16
|
export * from './types/fetch'
|
|
16
17
|
export * from './types/file'
|
|
17
18
|
export * from './types/formdata'
|
|
18
19
|
|
|
19
|
-
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, MockClient, MockPool, MockAgent, mockErrors }
|
|
20
|
+
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent }
|
|
20
21
|
export default Undici
|
|
21
22
|
|
|
22
23
|
declare function Undici(url: string, opts: Pool.Options): Pool
|
package/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const MockClient = require('./lib/mock/mock-client')
|
|
|
14
14
|
const MockAgent = require('./lib/mock/mock-agent')
|
|
15
15
|
const MockPool = require('./lib/mock/mock-pool')
|
|
16
16
|
const mockErrors = require('./lib/mock/mock-errors')
|
|
17
|
+
const ProxyAgent = require('./lib/proxy-agent')
|
|
17
18
|
|
|
18
19
|
const nodeVersion = process.versions.node.split('.')
|
|
19
20
|
const nodeMajor = Number(nodeVersion[0])
|
|
@@ -26,6 +27,7 @@ module.exports.Client = Client
|
|
|
26
27
|
module.exports.Pool = Pool
|
|
27
28
|
module.exports.BalancedPool = BalancedPool
|
|
28
29
|
module.exports.Agent = Agent
|
|
30
|
+
module.exports.ProxyAgent = ProxyAgent
|
|
29
31
|
|
|
30
32
|
module.exports.buildConnector = buildConnector
|
|
31
33
|
module.exports.errors = errors
|
package/lib/client.js
CHANGED
|
@@ -397,76 +397,77 @@ class Client extends Dispatcher {
|
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
const { resolve } = require('path')
|
|
401
|
-
const { readFileSync } = require('fs')
|
|
402
400
|
const constants = require('./llhttp/constants')
|
|
403
401
|
const EMPTY_BUF = Buffer.alloc(0)
|
|
404
402
|
|
|
403
|
+
let llhttpPromise
|
|
405
404
|
let llhttpInstance
|
|
406
|
-
function lazyllhttp () {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
mod = new WebAssembly.Module(readFileSync(resolve(__dirname, './llhttp/llhttp_simd.wasm')))
|
|
411
|
-
} catch (e) {
|
|
412
|
-
/* istanbul ignore next */
|
|
405
|
+
async function lazyllhttp () {
|
|
406
|
+
const { resolve } = require('path')
|
|
407
|
+
const { readFile } = require('fs').promises
|
|
413
408
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
409
|
+
let mod
|
|
410
|
+
try {
|
|
411
|
+
mod = await WebAssembly.compile(await readFile(resolve(__dirname, './llhttp/llhttp_simd.wasm')))
|
|
412
|
+
} catch (e) {
|
|
413
|
+
/* istanbul ignore next */
|
|
420
414
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
415
|
+
// We could check if the error was caused by the simd option not
|
|
416
|
+
// being enabled, but the occurring of this other error
|
|
417
|
+
// * https://github.com/emscripten-core/emscripten/issues/11495
|
|
418
|
+
// got me to remove that check to avoid breaking Node 12.
|
|
419
|
+
mod = await WebAssembly.compile(await readFile(resolve(__dirname, './llhttp/llhttp.wasm')))
|
|
420
|
+
}
|
|
424
421
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
},
|
|
429
|
-
wasm_on_status: (p, at, len) => {
|
|
430
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
431
|
-
const start = at - currentBufferPtr
|
|
432
|
-
const end = start + len
|
|
433
|
-
return currentParser.onStatus(currentBufferRef.slice(start, end)) || 0
|
|
434
|
-
},
|
|
435
|
-
wasm_on_message_begin: (p) => {
|
|
436
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
437
|
-
return currentParser.onMessageBegin() || 0
|
|
438
|
-
},
|
|
439
|
-
wasm_on_header_field: (p, at, len) => {
|
|
440
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
441
|
-
const start = at - currentBufferPtr
|
|
442
|
-
const end = start + len
|
|
443
|
-
return currentParser.onHeaderField(currentBufferRef.slice(start, end)) || 0
|
|
444
|
-
},
|
|
445
|
-
wasm_on_header_value: (p, at, len) => {
|
|
446
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
447
|
-
const start = at - currentBufferPtr
|
|
448
|
-
const end = start + len
|
|
449
|
-
return currentParser.onHeaderValue(currentBufferRef.slice(start, end)) || 0
|
|
450
|
-
},
|
|
451
|
-
wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {
|
|
452
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
453
|
-
return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0
|
|
454
|
-
},
|
|
455
|
-
wasm_on_body: (p, at, len) => {
|
|
456
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
457
|
-
const start = at - currentBufferPtr
|
|
458
|
-
const end = start + len
|
|
459
|
-
return currentParser.onBody(currentBufferRef.slice(start, end)) || 0
|
|
460
|
-
},
|
|
461
|
-
wasm_on_message_complete: (p) => {
|
|
462
|
-
assert.strictEqual(currentParser.ptr, p)
|
|
463
|
-
return currentParser.onMessageComplete() || 0
|
|
464
|
-
}
|
|
422
|
+
llhttpInstance = new WebAssembly.Instance(mod, {
|
|
423
|
+
env: {
|
|
424
|
+
/* eslint-disable camelcase */
|
|
465
425
|
|
|
466
|
-
|
|
426
|
+
wasm_on_url: (p, at, len) => {
|
|
427
|
+
/* istanbul ignore next */
|
|
428
|
+
return 0
|
|
429
|
+
},
|
|
430
|
+
wasm_on_status: (p, at, len) => {
|
|
431
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
432
|
+
const start = at - currentBufferPtr
|
|
433
|
+
const end = start + len
|
|
434
|
+
return currentParser.onStatus(currentBufferRef.slice(start, end)) || 0
|
|
435
|
+
},
|
|
436
|
+
wasm_on_message_begin: (p) => {
|
|
437
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
438
|
+
return currentParser.onMessageBegin() || 0
|
|
439
|
+
},
|
|
440
|
+
wasm_on_header_field: (p, at, len) => {
|
|
441
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
442
|
+
const start = at - currentBufferPtr
|
|
443
|
+
const end = start + len
|
|
444
|
+
return currentParser.onHeaderField(currentBufferRef.slice(start, end)) || 0
|
|
445
|
+
},
|
|
446
|
+
wasm_on_header_value: (p, at, len) => {
|
|
447
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
448
|
+
const start = at - currentBufferPtr
|
|
449
|
+
const end = start + len
|
|
450
|
+
return currentParser.onHeaderValue(currentBufferRef.slice(start, end)) || 0
|
|
451
|
+
},
|
|
452
|
+
wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {
|
|
453
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
454
|
+
return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0
|
|
455
|
+
},
|
|
456
|
+
wasm_on_body: (p, at, len) => {
|
|
457
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
458
|
+
const start = at - currentBufferPtr
|
|
459
|
+
const end = start + len
|
|
460
|
+
return currentParser.onBody(currentBufferRef.slice(start, end)) || 0
|
|
461
|
+
},
|
|
462
|
+
wasm_on_message_complete: (p) => {
|
|
463
|
+
assert.strictEqual(currentParser.ptr, p)
|
|
464
|
+
return currentParser.onMessageComplete() || 0
|
|
467
465
|
}
|
|
468
|
-
|
|
469
|
-
|
|
466
|
+
|
|
467
|
+
/* eslint-enable camelcase */
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
|
|
470
471
|
return llhttpInstance
|
|
471
472
|
}
|
|
472
473
|
|
|
@@ -480,10 +481,10 @@ const TIMEOUT_BODY = 2
|
|
|
480
481
|
const TIMEOUT_IDLE = 3
|
|
481
482
|
|
|
482
483
|
class Parser {
|
|
483
|
-
constructor (client, socket) {
|
|
484
|
+
constructor (client, socket, { exports }) {
|
|
484
485
|
assert(Number.isFinite(client[kMaxHeadersSize]) && client[kMaxHeadersSize] > 0)
|
|
485
486
|
|
|
486
|
-
this.llhttp =
|
|
487
|
+
this.llhttp = exports
|
|
487
488
|
this.ptr = this.llhttp.llhttp_alloc(constants.TYPE.RESPONSE)
|
|
488
489
|
this.client = client
|
|
489
490
|
this.socket = socket
|
|
@@ -1096,7 +1097,7 @@ function onSocketClose () {
|
|
|
1096
1097
|
resume(client)
|
|
1097
1098
|
}
|
|
1098
1099
|
|
|
1099
|
-
function connect (client) {
|
|
1100
|
+
async function connect (client) {
|
|
1100
1101
|
assert(!client[kConnecting])
|
|
1101
1102
|
assert(!client[kSocket])
|
|
1102
1103
|
|
|
@@ -1128,78 +1129,97 @@ function connect (client) {
|
|
|
1128
1129
|
})
|
|
1129
1130
|
}
|
|
1130
1131
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
port,
|
|
1136
|
-
servername: client[kServerName]
|
|
1137
|
-
}, function (err, socket) {
|
|
1138
|
-
client[kConnecting] = false
|
|
1139
|
-
|
|
1140
|
-
if (err) {
|
|
1141
|
-
if (channels.connectError.hasSubscribers) {
|
|
1142
|
-
channels.connectError.publish({
|
|
1143
|
-
connectParams: {
|
|
1144
|
-
host,
|
|
1145
|
-
hostname,
|
|
1146
|
-
protocol,
|
|
1147
|
-
port,
|
|
1148
|
-
servername: client[kServerName]
|
|
1149
|
-
},
|
|
1150
|
-
connector: client[kConnector],
|
|
1151
|
-
error: err
|
|
1152
|
-
})
|
|
1132
|
+
try {
|
|
1133
|
+
if (!llhttpInstance) {
|
|
1134
|
+
if (!llhttpPromise) {
|
|
1135
|
+
llhttpPromise = lazyllhttp()
|
|
1153
1136
|
}
|
|
1137
|
+
await llhttpPromise
|
|
1138
|
+
assert(llhttpInstance)
|
|
1139
|
+
llhttpPromise = null
|
|
1140
|
+
}
|
|
1154
1141
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1142
|
+
const socket = await new Promise((resolve, reject) => {
|
|
1143
|
+
client[kConnector]({
|
|
1144
|
+
host,
|
|
1145
|
+
hostname,
|
|
1146
|
+
protocol,
|
|
1147
|
+
port,
|
|
1148
|
+
servername: client[kServerName]
|
|
1149
|
+
}, (err, socket) => {
|
|
1150
|
+
if (err) {
|
|
1151
|
+
reject(err)
|
|
1152
|
+
} else {
|
|
1153
|
+
resolve(socket)
|
|
1160
1154
|
}
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1155
|
+
})
|
|
1156
|
+
})
|
|
1164
1157
|
|
|
1165
|
-
|
|
1166
|
-
} else {
|
|
1167
|
-
assert(socket)
|
|
1158
|
+
client[kConnecting] = false
|
|
1168
1159
|
|
|
1169
|
-
|
|
1160
|
+
assert(socket)
|
|
1170
1161
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1162
|
+
client[kSocket] = socket
|
|
1163
|
+
|
|
1164
|
+
socket[kNoRef] = false
|
|
1165
|
+
socket[kWriting] = false
|
|
1166
|
+
socket[kReset] = false
|
|
1167
|
+
socket[kBlocking] = false
|
|
1168
|
+
socket[kError] = null
|
|
1169
|
+
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
|
1170
|
+
socket[kClient] = client
|
|
1171
|
+
socket
|
|
1172
|
+
.on('error', onSocketError)
|
|
1173
|
+
.on('readable', onSocketReadable)
|
|
1174
|
+
.on('end', onSocketEnd)
|
|
1175
|
+
.on('close', onSocketClose)
|
|
1176
|
+
|
|
1177
|
+
if (channels.connected.hasSubscribers) {
|
|
1178
|
+
channels.connected.publish({
|
|
1179
|
+
connectParams: {
|
|
1180
|
+
host,
|
|
1181
|
+
hostname,
|
|
1182
|
+
protocol,
|
|
1183
|
+
port,
|
|
1184
|
+
servername: client[kServerName]
|
|
1185
|
+
},
|
|
1186
|
+
connector: client[kConnector],
|
|
1187
|
+
socket
|
|
1188
|
+
})
|
|
1189
|
+
}
|
|
1197
1190
|
|
|
1198
|
-
|
|
1191
|
+
client.emit('connect', client[kUrl], [client])
|
|
1192
|
+
} catch (err) {
|
|
1193
|
+
client[kConnecting] = false
|
|
1194
|
+
|
|
1195
|
+
if (channels.connectError.hasSubscribers) {
|
|
1196
|
+
channels.connectError.publish({
|
|
1197
|
+
connectParams: {
|
|
1198
|
+
host,
|
|
1199
|
+
hostname,
|
|
1200
|
+
protocol,
|
|
1201
|
+
port,
|
|
1202
|
+
servername: client[kServerName]
|
|
1203
|
+
},
|
|
1204
|
+
connector: client[kConnector],
|
|
1205
|
+
error: err
|
|
1206
|
+
})
|
|
1199
1207
|
}
|
|
1200
1208
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1209
|
+
if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
|
|
1210
|
+
assert(client[kRunning] === 0)
|
|
1211
|
+
while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) {
|
|
1212
|
+
const request = client[kQueue][client[kPendingIdx]++]
|
|
1213
|
+
errorRequest(client, request, err)
|
|
1214
|
+
}
|
|
1215
|
+
} else {
|
|
1216
|
+
onError(client, err)
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
client.emit('connectionError', client[kUrl], [client], err)
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
resume(client)
|
|
1203
1223
|
}
|
|
1204
1224
|
|
|
1205
1225
|
function emitDrain (client) {
|
package/lib/core/symbols.js
CHANGED
|
@@ -40,5 +40,6 @@ module.exports = {
|
|
|
40
40
|
kHostHeader: Symbol('host header'),
|
|
41
41
|
kConnector: Symbol('connector'),
|
|
42
42
|
kStrictContentLength: Symbol('strict content length'),
|
|
43
|
-
kMaxRedirections: Symbol('maxRedirections')
|
|
43
|
+
kMaxRedirections: Symbol('maxRedirections'),
|
|
44
|
+
kProxy: Symbol('proxy agent options')
|
|
44
45
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { kClients, kProxy } = require('./core/symbols')
|
|
4
|
+
const url = require('url')
|
|
5
|
+
const Agent = require('./agent')
|
|
6
|
+
const Dispatcher = require('./dispatcher')
|
|
7
|
+
const { InvalidArgumentError } = require('./core/errors')
|
|
8
|
+
|
|
9
|
+
const kAgent = Symbol('proxy agent')
|
|
10
|
+
|
|
11
|
+
class ProxyAgent extends Dispatcher {
|
|
12
|
+
constructor (opts) {
|
|
13
|
+
super(opts)
|
|
14
|
+
this[kProxy] = buildProxyOptions(opts)
|
|
15
|
+
|
|
16
|
+
const agent = new Agent(opts)
|
|
17
|
+
this[kAgent] = agent
|
|
18
|
+
|
|
19
|
+
this[kClients] = agent[kClients]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dispatch (opts, handler) {
|
|
23
|
+
const { host } = url.parse(opts.origin)
|
|
24
|
+
return this[kAgent].dispatch(
|
|
25
|
+
{
|
|
26
|
+
...opts,
|
|
27
|
+
origin: this[kProxy].uri,
|
|
28
|
+
path: opts.origin + opts.path,
|
|
29
|
+
headers: {
|
|
30
|
+
...opts.headers,
|
|
31
|
+
host
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
handler
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async close () {
|
|
39
|
+
await this[kAgent].close()
|
|
40
|
+
this[kClients].clear()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildProxyOptions(opts) {
|
|
45
|
+
if (typeof opts === 'string') {
|
|
46
|
+
opts = { uri: opts }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!opts || !opts.uri) {
|
|
50
|
+
throw new InvalidArgumentError('Proxy opts.uri is mandatory')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
uri: opts.uri,
|
|
55
|
+
protocol: opts.protocol || 'https'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = ProxyAgent
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Agent = require('./agent')
|
|
2
|
+
import Dispatcher = require('./dispatcher')
|
|
3
|
+
|
|
4
|
+
export = ProxyAgent
|
|
5
|
+
|
|
6
|
+
declare class ProxyAgent extends Dispatcher {
|
|
7
|
+
constructor(options: ProxyAgent.Options | string)
|
|
8
|
+
|
|
9
|
+
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): void;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare namespace ProxyAgent {
|
|
14
|
+
export interface Options extends Agent.Options {
|
|
15
|
+
uri: string;
|
|
16
|
+
}
|
|
17
|
+
}
|