-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
155 lines (137 loc) · 3.55 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
'use strict'
const { Pool } = require('undici')
const {
kHostsMap,
kCloseTimeout,
kMaxHosts,
kDefaultConnectionOptions,
kSetupConnection,
kRemove,
kClose,
kRefresh,
kStore,
kTimersMap
} = require('./symbols')
const noop = () => {}
const URL_REG = /^(http[s]*:)?\/?\/?([^:/]+):?([0-9]+)?/
function onTimeout (key, pool, client) {
client[kRemove](key, pool)
pool.close(noop)
}
class Agent11 {
constructor ({
closeTimeout = 6e4,
maxHosts = Infinity,
connectionOptions = {}
} = {}) {
if (typeof closeTimeout !== 'number' || closeTimeout <= 0) {
throw new Error(
'closeTimeout must be a number > 0'
)
}
if (typeof maxHosts !== 'number' || maxHosts <= 0) {
throw new Error('maxHosts must be a number > 0')
}
this[kHostsMap] = new Map()
this[kCloseTimeout] = closeTimeout
this[kMaxHosts] = maxHosts
this[kDefaultConnectionOptions] = connectionOptions
this[kTimersMap] = new Map()
}
static urlToObject (url) {
if (typeof url === 'string' && url.length) {
const match = URL_REG.exec(url)
return {
protocol: match && match[1],
hostname: match && match[2],
port: match && match[3]
}
// perf: this branch has polymorphic inline caches
} else if (typeof url === 'object' && url !== null) {
return {
protocol: url.protocol,
hostname: url.hostname,
port: url.port
}
}
throw new TypeError(`Invalid url, received: ${url}`)
}
static getKey (url, options) {
let key = url.protocol || 'http:'
// perf: this part has polymorphic inline caches
if (key.charAt(key.length - 1) !== ':') {
key += ':'
}
key += url.hostname
if ((typeof url.port === 'string' && url.port.length) || typeof url.port === 'number') {
key += ':'
key += url.port
}
if (options && options.socketPath) {
key += ':'
key += options.socketPath
}
return key
}
[kSetupConnection] (url, options) {
if (this.size === this[kMaxHosts]) {
throw new Error(`Maximum number of ${this[kMaxHosts]} hosts reached`)
}
return new Pool(url, Object.assign({}, this[kDefaultConnectionOptions], options))
}
[kRefresh] (pool) {
const timer = this[kTimersMap].get(pool)
timer.refresh()
}
[kStore] (key, pool) {
this[kHostsMap].set(key, pool)
this[kTimersMap].set(
pool,
setTimeout(onTimeout, this[kCloseTimeout], key, pool, this)
)
}
[kRemove] (key, pool) {
this[kHostsMap].delete(key)
this[kTimersMap].delete(pool)
}
[kClose] (key, pool) {
clearTimeout(this[kTimersMap].get(pool))
this[kRemove](key, pool)
}
get size () {
return this[kHostsMap].size
}
connection (key, url, options) {
if (this[kHostsMap].has(key)) {
const pool = this[kHostsMap].get(key)
this[kRefresh](pool)
return pool
} else {
const pool = this[kSetupConnection](url, options)
this[kStore](key, pool)
return pool
}
}
getConnection (url, options) {
url = Agent11.urlToObject(url)
const key = Agent11.getKey(url, options)
return this.connection(key, url, options)
}
close () {
const closing = []
for (const [key, pool] of this[kHostsMap]) {
closing.push(pool.close())
this[kClose](key, pool)
}
return Promise.all(closing)
}
destroy (err) {
const closing = []
for (const [key, pool] of this[kHostsMap]) {
closing.push(pool.destroy(err))
this[kClose](key, pool)
}
return Promise.all(closing)
}
}
module.exports = Agent11