libp2p 0.37.2 → 0.37.3-3c0fb13

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.
Files changed (165) hide show
  1. package/README.md +1 -2
  2. package/dist/src/address-manager/index.d.ts +2 -2
  3. package/dist/src/address-manager/index.d.ts.map +1 -1
  4. package/dist/src/circuit/auto-relay.d.ts +4 -4
  5. package/dist/src/circuit/auto-relay.d.ts.map +1 -1
  6. package/dist/src/circuit/circuit/hop.d.ts +5 -4
  7. package/dist/src/circuit/circuit/hop.d.ts.map +1 -1
  8. package/dist/src/circuit/circuit/hop.js +8 -4
  9. package/dist/src/circuit/circuit/hop.js.map +1 -1
  10. package/dist/src/circuit/circuit/stop.d.ts +3 -2
  11. package/dist/src/circuit/circuit/stop.d.ts.map +1 -1
  12. package/dist/src/circuit/circuit/stop.js +4 -2
  13. package/dist/src/circuit/circuit/stop.js.map +1 -1
  14. package/dist/src/circuit/circuit/stream-handler.d.ts +1 -1
  15. package/dist/src/circuit/circuit/stream-handler.d.ts.map +1 -1
  16. package/dist/src/circuit/index.d.ts +4 -12
  17. package/dist/src/circuit/index.d.ts.map +1 -1
  18. package/dist/src/circuit/index.js.map +1 -1
  19. package/dist/src/circuit/listener.d.ts +3 -3
  20. package/dist/src/circuit/listener.d.ts.map +1 -1
  21. package/dist/src/circuit/transport.d.ts +8 -5
  22. package/dist/src/circuit/transport.d.ts.map +1 -1
  23. package/dist/src/circuit/transport.js +81 -59
  24. package/dist/src/circuit/transport.js.map +1 -1
  25. package/dist/src/config.d.ts.map +1 -1
  26. package/dist/src/config.js +27 -4
  27. package/dist/src/config.js.map +1 -1
  28. package/dist/src/connection-manager/auto-dialler.d.ts +1 -1
  29. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  30. package/dist/src/connection-manager/dialer/auto-dialer.d.ts +2 -2
  31. package/dist/src/connection-manager/dialer/auto-dialer.d.ts.map +1 -1
  32. package/dist/src/connection-manager/dialer/auto-dialer.js +6 -0
  33. package/dist/src/connection-manager/dialer/auto-dialer.js.map +1 -1
  34. package/dist/src/connection-manager/dialer/dial-request.d.ts +1 -1
  35. package/dist/src/connection-manager/dialer/dial-request.d.ts.map +1 -1
  36. package/dist/src/connection-manager/dialer/index.d.ts +9 -9
  37. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  38. package/dist/src/connection-manager/dialer/index.js +11 -11
  39. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  40. package/dist/src/connection-manager/index.d.ts +24 -20
  41. package/dist/src/connection-manager/index.d.ts.map +1 -1
  42. package/dist/src/connection-manager/index.js +209 -82
  43. package/dist/src/connection-manager/index.js.map +1 -1
  44. package/dist/src/constants.d.ts +4 -0
  45. package/dist/src/constants.d.ts.map +1 -1
  46. package/dist/src/constants.js +4 -0
  47. package/dist/src/constants.js.map +1 -1
  48. package/dist/src/content-routing/index.d.ts +4 -4
  49. package/dist/src/content-routing/index.d.ts.map +1 -1
  50. package/dist/src/content-routing/utils.d.ts +2 -2
  51. package/dist/src/content-routing/utils.d.ts.map +1 -1
  52. package/dist/src/dht/dht-content-routing.d.ts +3 -3
  53. package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
  54. package/dist/src/dht/dht-peer-routing.d.ts +5 -5
  55. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  56. package/dist/src/dht/dummy-dht.d.ts +3 -3
  57. package/dist/src/dht/dummy-dht.d.ts.map +1 -1
  58. package/dist/src/dht/dummy-dht.js +1 -1
  59. package/dist/src/dht/dummy-dht.js.map +1 -1
  60. package/dist/src/errors.d.ts +4 -1
  61. package/dist/src/errors.d.ts.map +1 -1
  62. package/dist/src/errors.js +3 -0
  63. package/dist/src/errors.js.map +1 -1
  64. package/dist/src/fetch/index.d.ts +11 -4
  65. package/dist/src/fetch/index.d.ts.map +1 -1
  66. package/dist/src/fetch/index.js +82 -41
  67. package/dist/src/fetch/index.js.map +1 -1
  68. package/dist/src/get-peer.d.ts +2 -2
  69. package/dist/src/get-peer.d.ts.map +1 -1
  70. package/dist/src/get-peer.js +1 -1
  71. package/dist/src/get-peer.js.map +1 -1
  72. package/dist/src/identify/index.d.ts +8 -8
  73. package/dist/src/identify/index.d.ts.map +1 -1
  74. package/dist/src/identify/index.js +48 -33
  75. package/dist/src/identify/index.js.map +1 -1
  76. package/dist/src/index.d.ts +18 -17
  77. package/dist/src/index.d.ts.map +1 -1
  78. package/dist/src/index.js.map +1 -1
  79. package/dist/src/insecure/index.d.ts +2 -2
  80. package/dist/src/insecure/index.d.ts.map +1 -1
  81. package/dist/src/insecure/index.js +1 -1
  82. package/dist/src/insecure/index.js.map +1 -1
  83. package/dist/src/keychain/index.d.ts +2 -2
  84. package/dist/src/keychain/index.d.ts.map +1 -1
  85. package/dist/src/libp2p.d.ts +14 -14
  86. package/dist/src/libp2p.d.ts.map +1 -1
  87. package/dist/src/libp2p.js +17 -17
  88. package/dist/src/libp2p.js.map +1 -1
  89. package/dist/src/metrics/index.d.ts +3 -3
  90. package/dist/src/metrics/index.d.ts.map +1 -1
  91. package/dist/src/metrics/index.js +6 -2
  92. package/dist/src/metrics/index.js.map +1 -1
  93. package/dist/src/metrics/moving-average.d.ts +1 -1
  94. package/dist/src/metrics/moving-average.d.ts.map +1 -1
  95. package/dist/src/metrics/stats.d.ts +1 -1
  96. package/dist/src/metrics/stats.d.ts.map +1 -1
  97. package/dist/src/nat-manager.d.ts +1 -1
  98. package/dist/src/nat-manager.d.ts.map +1 -1
  99. package/dist/src/peer-record-updater.d.ts +1 -1
  100. package/dist/src/peer-record-updater.d.ts.map +1 -1
  101. package/dist/src/peer-routing.d.ts +4 -4
  102. package/dist/src/peer-routing.d.ts.map +1 -1
  103. package/dist/src/ping/index.d.ts +10 -3
  104. package/dist/src/ping/index.d.ts.map +1 -1
  105. package/dist/src/ping/index.js +31 -8
  106. package/dist/src/ping/index.js.map +1 -1
  107. package/dist/src/pnet/index.d.ts +1 -2
  108. package/dist/src/pnet/index.d.ts.map +1 -1
  109. package/dist/src/pnet/index.js.map +1 -1
  110. package/dist/src/pubsub/dummy-pubsub.d.ts +2 -2
  111. package/dist/src/pubsub/dummy-pubsub.d.ts.map +1 -1
  112. package/dist/src/registrar.d.ts +10 -8
  113. package/dist/src/registrar.d.ts.map +1 -1
  114. package/dist/src/registrar.js +63 -44
  115. package/dist/src/registrar.js.map +1 -1
  116. package/dist/src/transport-manager.d.ts +3 -3
  117. package/dist/src/transport-manager.d.ts.map +1 -1
  118. package/dist/src/upgrader.d.ts +12 -6
  119. package/dist/src/upgrader.d.ts.map +1 -1
  120. package/dist/src/upgrader.js +164 -65
  121. package/dist/src/upgrader.js.map +1 -1
  122. package/dist/src/version.d.ts.map +1 -1
  123. package/dist/src/version.js.map +1 -1
  124. package/package.json +56 -32
  125. package/src/address-manager/index.ts +2 -2
  126. package/src/circuit/auto-relay.ts +4 -4
  127. package/src/circuit/circuit/hop.ts +15 -8
  128. package/src/circuit/circuit/stop.ts +8 -4
  129. package/src/circuit/circuit/stream-handler.ts +1 -1
  130. package/src/circuit/index.ts +4 -13
  131. package/src/circuit/listener.ts +3 -3
  132. package/src/circuit/transport.ts +90 -63
  133. package/src/config.ts +27 -4
  134. package/src/connection-manager/auto-dialler.ts +1 -1
  135. package/src/connection-manager/dialer/auto-dialer.ts +8 -2
  136. package/src/connection-manager/dialer/dial-request.ts +1 -1
  137. package/src/connection-manager/dialer/index.ts +15 -15
  138. package/src/connection-manager/index.ts +267 -97
  139. package/src/constants.ts +5 -0
  140. package/src/content-routing/index.ts +2 -2
  141. package/src/content-routing/utils.ts +2 -2
  142. package/src/dht/dht-content-routing.ts +2 -2
  143. package/src/dht/dht-peer-routing.ts +3 -3
  144. package/src/dht/dummy-dht.ts +3 -3
  145. package/src/errors.ts +4 -1
  146. package/src/fetch/index.ts +120 -53
  147. package/src/get-peer.ts +3 -3
  148. package/src/identify/index.ts +60 -42
  149. package/src/index.ts +18 -17
  150. package/src/insecure/index.ts +3 -3
  151. package/src/keychain/index.ts +2 -2
  152. package/src/libp2p.ts +34 -29
  153. package/src/metrics/index.ts +9 -5
  154. package/src/metrics/moving-average.ts +1 -1
  155. package/src/metrics/stats.ts +1 -1
  156. package/src/nat-manager.ts +1 -1
  157. package/src/peer-record-updater.ts +1 -1
  158. package/src/peer-routing.ts +4 -4
  159. package/src/ping/index.ts +47 -14
  160. package/src/pnet/index.ts +1 -2
  161. package/src/pubsub/dummy-pubsub.ts +2 -2
  162. package/src/registrar.ts +79 -55
  163. package/src/transport-manager.ts +3 -3
  164. package/src/upgrader.ts +204 -76
  165. package/src/version.ts +1 -2
@@ -7,17 +7,19 @@ import retimer from 'retimer'
7
7
  import type { AbortOptions } from '@libp2p/interfaces'
8
8
  import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
9
9
  import type { Startable } from '@libp2p/interfaces/startable'
10
- import { trackedMap } from '@libp2p/tracked-map'
11
10
  import { codes } from '../errors.js'
12
- import { isPeerId, PeerId } from '@libp2p/interfaces/peer-id'
11
+ import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
13
12
  import { setMaxListeners } from 'events'
14
- import type { Connection } from '@libp2p/interfaces/connection'
15
- import type { ConnectionManager } from '@libp2p/interfaces/connection-manager'
16
- import { Components, Initializable } from '@libp2p/interfaces/components'
17
- import * as STATUS from '@libp2p/interfaces/connection/status'
13
+ import type { Connection } from '@libp2p/interface-connection'
14
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
15
+ import { Components, Initializable } from '@libp2p/components'
16
+ import * as STATUS from '@libp2p/interface-connection/status'
18
17
  import { Dialer } from './dialer/index.js'
19
- import type { AddressSorter } from '@libp2p/interfaces/peer-store'
18
+ import type { AddressSorter } from '@libp2p/interface-peer-store'
20
19
  import type { Resolver } from '@multiformats/multiaddr'
20
+ import { PeerMap } from '@libp2p/peer-collections'
21
+ import { TimeoutController } from 'timeout-abort-controller'
22
+ import { KEEP_ALIVE } from '@libp2p/interface-peer-store/tags'
21
23
 
22
24
  const log = logger('libp2p:connection-manager')
23
25
 
@@ -30,13 +32,12 @@ const defaultOptions: Partial<ConnectionManagerInit> = {
30
32
  maxEventLoopDelay: Infinity,
31
33
  pollInterval: 2000,
32
34
  autoDialInterval: 10000,
33
- movingAverageInterval: 60000,
34
- defaultPeerValue: 1
35
+ movingAverageInterval: 60000
35
36
  }
36
37
 
38
+ const METRICS_SYSTEM = 'libp2p'
37
39
  const METRICS_COMPONENT = 'connection-manager'
38
- const METRICS_PEER_CONNECTIONS = 'peer-connections'
39
- const METRICS_PEER_VALUES = 'peer-values'
40
+ const STARTUP_RECONNECT_TIMEOUT = 60000
40
41
 
41
42
  export interface ConnectionManagerInit {
42
43
  /**
@@ -79,11 +80,6 @@ export interface ConnectionManagerInit {
79
80
  */
80
81
  movingAverageInterval?: number
81
82
 
82
- /**
83
- * The value of the peer
84
- */
85
- defaultPeerValue?: number
86
-
87
83
  /**
88
84
  * If true, try to connect to all discovered peers up to the connection manager limit
89
85
  */
@@ -111,10 +107,17 @@ export interface ConnectionManagerInit {
111
107
  maxAddrsToDial?: number
112
108
 
113
109
  /**
114
- * How long a dial attempt is allowed to take
110
+ * How long a dial attempt is allowed to take, including DNS resolution
111
+ * of the multiaddr, opening a socket and upgrading it to a Connection.
115
112
  */
116
113
  dialTimeout?: number
117
114
 
115
+ /**
116
+ * When a new inbound connection is opened, the upgrade process (e.g. protect,
117
+ * encrypt, multiplex etc) must complete within this number of ms.
118
+ */
119
+ inboundUpgradeTimeout: number
120
+
118
121
  /**
119
122
  * Number of max concurrent dials per peer
120
123
  */
@@ -124,6 +127,12 @@ export interface ConnectionManagerInit {
124
127
  * Multiaddr resolvers to use when dialing
125
128
  */
126
129
  resolvers?: Record<string, Resolver>
130
+
131
+ /**
132
+ * On startup we try to dial any peer that has previously been
133
+ * tagged with KEEP_ALIVE up to this timeout in ms. (default: 60000)
134
+ */
135
+ startupReconnectTimeout?: number
127
136
  }
128
137
 
129
138
  export interface ConnectionManagerEvents {
@@ -138,11 +147,13 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
138
147
  public readonly dialer: Dialer
139
148
  private components = new Components()
140
149
  private readonly opts: Required<ConnectionManagerInit>
141
- private readonly peerValues: Map<string, number>
142
150
  private readonly connections: Map<string, Connection[]>
143
151
  private started: boolean
144
152
  private timer?: ReturnType<retimer>
145
153
  private readonly latencyMonitor: LatencyMonitor
154
+ private readonly startupReconnectTimeout: number
155
+ private connectOnStartupController?: TimeoutController
156
+ private readonly dialTimeout: number
146
157
 
147
158
  constructor (init: ConnectionManagerInit) {
148
159
  super()
@@ -155,25 +166,10 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
155
166
 
156
167
  log('options: %o', this.opts)
157
168
 
158
- /**
159
- * Map of peer identifiers to their peer value for pruning connections.
160
- *
161
- * @type {Map<string, number>}
162
- */
163
- this.peerValues = trackedMap({
164
- component: METRICS_COMPONENT,
165
- metric: METRICS_PEER_VALUES,
166
- metrics: this.components.getMetrics()
167
- })
168
-
169
169
  /**
170
170
  * Map of connections per peer
171
171
  */
172
- this.connections = trackedMap({
173
- component: METRICS_COMPONENT,
174
- metric: METRICS_PEER_CONNECTIONS,
175
- metrics: this.components.getMetrics()
176
- })
172
+ this.connections = new Map()
177
173
 
178
174
  this.started = false
179
175
  this._checkMetrics = this._checkMetrics.bind(this)
@@ -192,12 +188,103 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
192
188
 
193
189
  this.onConnect = this.onConnect.bind(this)
194
190
  this.onDisconnect = this.onDisconnect.bind(this)
191
+
192
+ this.startupReconnectTimeout = init.startupReconnectTimeout ?? STARTUP_RECONNECT_TIMEOUT
193
+ this.dialTimeout = init.dialTimeout ?? 30000
195
194
  }
196
195
 
197
196
  init (components: Components): void {
198
197
  this.components = components
199
198
 
200
199
  this.dialer.init(components)
200
+
201
+ // track inbound/outbound connections
202
+ this.components.getMetrics()?.updateComponentMetric({
203
+ system: METRICS_SYSTEM,
204
+ component: METRICS_COMPONENT,
205
+ metric: 'connections',
206
+ label: 'direction',
207
+ value: () => {
208
+ const metric = {
209
+ inbound: 0,
210
+ outbound: 0
211
+ }
212
+
213
+ for (const conns of this.connections.values()) {
214
+ for (const conn of conns) {
215
+ if (conn.stat.direction === 'inbound') {
216
+ metric.inbound++
217
+ } else {
218
+ metric.outbound++
219
+ }
220
+ }
221
+ }
222
+
223
+ return metric
224
+ }
225
+ })
226
+
227
+ // track total number of streams per protocol
228
+ this.components.getMetrics()?.updateComponentMetric({
229
+ system: METRICS_SYSTEM,
230
+ component: METRICS_COMPONENT,
231
+ metric: 'protocol-streams-total',
232
+ label: 'protocol',
233
+ value: () => {
234
+ const metric: Record<string, number> = {}
235
+
236
+ for (const conns of this.connections.values()) {
237
+ for (const conn of conns) {
238
+ for (const stream of conn.streams) {
239
+ const key = `${stream.stat.direction} ${stream.stat.protocol ?? 'unnegotiated'}`
240
+
241
+ metric[key] = (metric[key] ?? 0) + 1
242
+ }
243
+ }
244
+ }
245
+
246
+ return metric
247
+ }
248
+ })
249
+
250
+ // track 90th percentile of streams per protocol
251
+ this.components.getMetrics()?.updateComponentMetric({
252
+ system: METRICS_SYSTEM,
253
+ component: METRICS_COMPONENT,
254
+ metric: 'protocol-streams-per-connection-90th-percentile',
255
+ label: 'protocol',
256
+ value: () => {
257
+ const allStreams: Record<string, number[]> = {}
258
+
259
+ for (const conns of this.connections.values()) {
260
+ for (const conn of conns) {
261
+ const streams: Record<string, number> = {}
262
+
263
+ for (const stream of conn.streams) {
264
+ const key = `${stream.stat.direction} ${stream.stat.protocol ?? 'unnegotiated'}`
265
+
266
+ streams[key] = (streams[key] ?? 0) + 1
267
+ }
268
+
269
+ for (const [protocol, count] of Object.entries(streams)) {
270
+ allStreams[protocol] = allStreams[protocol] ?? []
271
+ allStreams[protocol].push(count)
272
+ }
273
+ }
274
+ }
275
+
276
+ const metric: Record<string, number> = {}
277
+
278
+ for (let [protocol, counts] of Object.entries(allStreams)) {
279
+ counts = counts.sort((a, b) => a - b)
280
+
281
+ const index = Math.floor(counts.length * 0.9)
282
+ metric[protocol] = counts[index]
283
+ }
284
+
285
+ return metric
286
+ }
287
+ })
201
288
  }
202
289
 
203
290
  isStarted () {
@@ -226,9 +313,48 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
226
313
  async afterStart () {
227
314
  this.components.getUpgrader().addEventListener('connection', this.onConnect)
228
315
  this.components.getUpgrader().addEventListener('connectionEnd', this.onDisconnect)
316
+
317
+ // re-connect to any peers with the KEEP_ALIVE tag
318
+ void Promise.resolve()
319
+ .then(async () => {
320
+ const keepAlivePeers: PeerId[] = []
321
+
322
+ for (const peer of await this.components.getPeerStore().all()) {
323
+ const tags = await this.components.getPeerStore().getTags(peer.id)
324
+ const hasKeepAlive = tags.filter(tag => tag.name === KEEP_ALIVE).length > 0
325
+
326
+ if (hasKeepAlive) {
327
+ keepAlivePeers.push(peer.id)
328
+ }
329
+ }
330
+
331
+ this.connectOnStartupController?.clear()
332
+ this.connectOnStartupController = new TimeoutController(this.startupReconnectTimeout)
333
+
334
+ try {
335
+ // fails on node < 15.4
336
+ setMaxListeners?.(Infinity, this.connectOnStartupController.signal)
337
+ } catch {}
338
+
339
+ await Promise.all(
340
+ keepAlivePeers.map(async peer => {
341
+ await this.openConnection(peer, {
342
+ signal: this.connectOnStartupController?.signal
343
+ })
344
+ .catch(err => {
345
+ log.error(err)
346
+ })
347
+ })
348
+ )
349
+ })
350
+ .finally(() => {
351
+ this.connectOnStartupController?.clear()
352
+ })
229
353
  }
230
354
 
231
355
  async beforeStop () {
356
+ // if we are still dialing KEEP_ALIVE peers, abort those dials
357
+ this.connectOnStartupController?.abort()
232
358
  this.components.getUpgrader().removeEventListener('connection', this.onConnect)
233
359
  this.components.getUpgrader().removeEventListener('connectionEnd', this.onDisconnect)
234
360
  }
@@ -271,18 +397,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
271
397
  this.connections.clear()
272
398
  }
273
399
 
274
- /**
275
- * Sets the value of the given peer. Peers with lower values
276
- * will be disconnected first.
277
- */
278
- setPeerValue (peerId: PeerId, value: number) {
279
- if (value < 0 || value > 1) {
280
- throw new Error('value should be a number between 0 and 1')
281
- }
282
-
283
- this.peerValues.set(peerId.toString(), value)
284
- }
285
-
286
400
  /**
287
401
  * Checks the libp2p metrics to determine if any values have exceeded
288
402
  * the configured maximums.
@@ -340,11 +454,10 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
340
454
  await this.components.getPeerStore().keyBook.set(peerId, peerId.publicKey)
341
455
  }
342
456
 
343
- if (!this.peerValues.has(peerIdStr)) {
344
- this.peerValues.set(peerIdStr, this.opts.defaultPeerValue)
345
- }
457
+ const numConnections = this.getConnections().length
458
+ const toPrune = numConnections - this.opts.maxConnections
346
459
 
347
- await this._checkMaxLimit('maxConnections', this.getConnections().length)
460
+ await this._checkMaxLimit('maxConnections', numConnections, toPrune)
348
461
  this.dispatchEvent(new CustomEvent<Connection>('peer:connect', { detail: connection }))
349
462
  }
350
463
 
@@ -367,7 +480,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
367
480
  this.connections.set(peerId, storedConn)
368
481
  } else if (storedConn != null) {
369
482
  this.connections.delete(peerId)
370
- this.peerValues.delete(connection.remotePeer.toString())
371
483
  this.dispatchEvent(new CustomEvent<Connection>('peer:disconnect', { detail: connection }))
372
484
 
373
485
  this.components.getMetrics()?.onPeerDisconnected(connection.remotePeer)
@@ -388,7 +500,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
388
500
  return conns
389
501
  }
390
502
 
391
- async openConnection (peerId: PeerId, options?: AbortOptions): Promise<Connection> {
503
+ async openConnection (peerId: PeerId, options: AbortOptions = {}): Promise<Connection> {
392
504
  log('dial to %p', peerId)
393
505
  const existingConnections = this.getConnections(peerId)
394
506
 
@@ -398,30 +510,48 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
398
510
  return existingConnections[0]
399
511
  }
400
512
 
401
- const connection = await this.dialer.dial(peerId, options)
402
- let peerConnections = this.connections.get(peerId.toString())
513
+ let timeoutController: TimeoutController | undefined
403
514
 
404
- if (peerConnections == null) {
405
- peerConnections = []
406
- this.connections.set(peerId.toString(), peerConnections)
515
+ if (options?.signal == null) {
516
+ timeoutController = new TimeoutController(this.dialTimeout)
517
+ options.signal = timeoutController.signal
518
+
519
+ try {
520
+ // fails on node < 15.4
521
+ setMaxListeners?.(Infinity, timeoutController.signal)
522
+ } catch {}
407
523
  }
408
524
 
409
- // we get notified of connections via the Upgrader emitting "connection"
410
- // events, double check we aren't already tracking this connection before
411
- // storing it
412
- let trackedConnection = false
525
+ try {
526
+ const connection = await this.dialer.dial(peerId, options)
527
+ let peerConnections = this.connections.get(peerId.toString())
413
528
 
414
- for (const conn of peerConnections) {
415
- if (conn.id === connection.id) {
416
- trackedConnection = true
529
+ if (peerConnections == null) {
530
+ peerConnections = []
531
+ this.connections.set(peerId.toString(), peerConnections)
417
532
  }
418
- }
419
533
 
420
- if (!trackedConnection) {
421
- peerConnections.push(connection)
422
- }
534
+ // we get notified of connections via the Upgrader emitting "connection"
535
+ // events, double check we aren't already tracking this connection before
536
+ // storing it
537
+ let trackedConnection = false
423
538
 
424
- return connection
539
+ for (const conn of peerConnections) {
540
+ if (conn.id === connection.id) {
541
+ trackedConnection = true
542
+ }
543
+ }
544
+
545
+ if (!trackedConnection) {
546
+ peerConnections.push(connection)
547
+ }
548
+
549
+ return connection
550
+ } finally {
551
+ if (timeoutController != null) {
552
+ timeoutController.clear()
553
+ }
554
+ }
425
555
  }
426
556
 
427
557
  async closeConnections (peerId: PeerId): Promise<void> {
@@ -459,7 +589,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
459
589
  _onLatencyMeasure (evt: CustomEvent<SummaryObject>) {
460
590
  const { detail: summary } = evt
461
591
 
462
- this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
592
+ this._checkMaxLimit('maxEventLoopDelay', summary.avgMs, 1)
463
593
  .catch(err => {
464
594
  log.error(err)
465
595
  })
@@ -468,46 +598,86 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
468
598
  /**
469
599
  * If the `value` of `name` has exceeded its limit, maybe close a connection
470
600
  */
471
- async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number) {
601
+ async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number, toPrune: number = 1) {
472
602
  const limit = this.opts[name]
473
603
  log.trace('checking limit of %s. current value: %d of %d', name, value, limit)
474
604
  if (value > limit) {
475
- log('%s: limit exceeded: %p, %d', this.components.getPeerId(), name, value)
476
- await this._maybeDisconnectOne()
605
+ log('%s: limit exceeded: %p, %d/%d, pruning %d connection(s)', this.components.getPeerId(), name, value, limit, toPrune)
606
+ await this._maybePruneConnections(toPrune)
477
607
  }
478
608
  }
479
609
 
480
610
  /**
481
- * If we have more connections than our maximum, close a connection
482
- * to the lowest valued peer.
611
+ * If we have more connections than our maximum, select some excess connections
612
+ * to prune based on peer value
483
613
  */
484
- async _maybeDisconnectOne () {
485
- if (this.opts.minConnections < this.connections.size) {
486
- const peerValues = Array.from(new Map([...this.peerValues.entries()].sort((a, b) => a[1] - b[1])))
614
+ async _maybePruneConnections (toPrune: number) {
615
+ const connections = this.getConnections()
487
616
 
488
- log('%p: sorted peer values: %j', this.components.getPeerId(), peerValues)
489
- const disconnectPeer = peerValues[0]
617
+ if (connections.length <= this.opts.minConnections || toPrune < 1) {
618
+ return
619
+ }
490
620
 
491
- if (disconnectPeer != null) {
492
- const peerId = disconnectPeer[0]
493
- log('%p: lowest value peer is %s', this.components.getPeerId(), peerId)
494
- log('%p: closing a connection to %j', this.components.getPeerId(), peerId)
621
+ const peerValues = new PeerMap<number>()
495
622
 
496
- for (const connections of this.connections.values()) {
497
- if (connections[0].remotePeer.toString() === peerId) {
498
- void connections[0].close()
499
- .catch(err => {
500
- log.error(err)
501
- })
623
+ // work out peer values
624
+ for (const connection of connections) {
625
+ const remotePeer = connection.remotePeer
502
626
 
503
- // TODO: should not need to invoke this manually
504
- this.onDisconnect(new CustomEvent<Connection>('connectionEnd', {
505
- detail: connections[0]
506
- }))
507
- break
508
- }
509
- }
627
+ if (peerValues.has(remotePeer)) {
628
+ continue
629
+ }
630
+
631
+ const tags = await this.components.getPeerStore().getTags(remotePeer)
632
+
633
+ // sum all tag values
634
+ peerValues.set(remotePeer, tags.reduce((acc, curr) => {
635
+ return acc + curr.value
636
+ }, 0))
637
+ }
638
+
639
+ // sort by value, lowest to highest
640
+ const sortedConnections = connections.sort((a, b) => {
641
+ const peerAValue = peerValues.get(a.remotePeer) ?? 0
642
+ const peerBValue = peerValues.get(b.remotePeer) ?? 0
643
+
644
+ if (peerAValue > peerBValue) {
645
+ return 1
646
+ }
647
+
648
+ if (peerAValue < peerBValue) {
649
+ return -1
650
+ }
651
+
652
+ return 0
653
+ })
654
+
655
+ // close some connections
656
+ const toClose = []
657
+
658
+ for (const connection of sortedConnections) {
659
+ log('too many connections open - closing a connection to %p', connection.remotePeer)
660
+ toClose.push(connection)
661
+
662
+ if (toClose.length === toPrune) {
663
+ break
510
664
  }
511
665
  }
666
+
667
+ // close connections
668
+ await Promise.all(
669
+ toClose.map(async connection => {
670
+ try {
671
+ await connection.close()
672
+ } catch (err) {
673
+ log.error(err)
674
+ }
675
+
676
+ // TODO: should not need to invoke this manually
677
+ this.onDisconnect(new CustomEvent<Connection>('connectionEnd', {
678
+ detail: connection
679
+ }))
680
+ })
681
+ )
512
682
  }
513
683
  }
package/src/constants.ts CHANGED
@@ -4,6 +4,11 @@
4
4
  */
5
5
  export const DIAL_TIMEOUT = 30e3
6
6
 
7
+ /**
8
+ * How long in ms an inbound connection upgrade is allowed to take
9
+ */
10
+ export const INBOUND_UPGRADE_TIMEOUT = 30e3
11
+
7
12
  /**
8
13
  * Maximum allowed concurrent dials
9
14
  */
@@ -8,11 +8,11 @@ import {
8
8
  import drain from 'it-drain'
9
9
  import merge from 'it-merge'
10
10
  import { pipe } from 'it-pipe'
11
- import type { ContentRouting } from '@libp2p/interfaces/content-routing'
11
+ import type { ContentRouting } from '@libp2p/interface-content-routing'
12
12
  import type { AbortOptions } from '@libp2p/interfaces'
13
13
  import type { Startable } from '@libp2p/interfaces/startable'
14
14
  import type { CID } from 'multiformats/cid'
15
- import type { Components } from '@libp2p/interfaces/components'
15
+ import type { Components } from '@libp2p/components'
16
16
 
17
17
  export interface CompoundContentRoutingInit {
18
18
  routers: ContentRouting[]
@@ -2,8 +2,8 @@ import errCode from 'err-code'
2
2
  import filter from 'it-filter'
3
3
  import map from 'it-map'
4
4
  import type { Source } from 'it-stream-types'
5
- import type { PeerInfo } from '@libp2p/interfaces/peer-info'
6
- import type { PeerStore } from '@libp2p/interfaces/peer-store'
5
+ import type { PeerInfo } from '@libp2p/interface-peer-info'
6
+ import type { PeerStore } from '@libp2p/interface-peer-store'
7
7
 
8
8
  /**
9
9
  * Store the multiaddrs from every peer in the passed peer store
@@ -1,7 +1,7 @@
1
1
  import drain from 'it-drain'
2
2
  import errCode from 'err-code'
3
- import type { DHT } from '@libp2p/interfaces/dht'
4
- import type { ContentRouting } from '@libp2p/interfaces/content-routing'
3
+ import type { DHT } from '@libp2p/interface-dht'
4
+ import type { ContentRouting } from '@libp2p/interface-content-routing'
5
5
  import type { CID } from 'multiformats/cid'
6
6
  import type { AbortOptions } from '@libp2p/interfaces'
7
7
 
@@ -1,8 +1,8 @@
1
1
  import errCode from 'err-code'
2
2
  import { messages, codes } from '../errors.js'
3
- import type { PeerRouting } from '@libp2p/interfaces/peer-routing'
4
- import type { DHT } from '@libp2p/interfaces/dht'
5
- import type { PeerId } from '@libp2p/interfaces/peer-id'
3
+ import type { PeerRouting } from '@libp2p/interface-peer-routing'
4
+ import type { DHT } from '@libp2p/interface-dht'
5
+ import type { PeerId } from '@libp2p/interface-peer-id'
6
6
  import type { AbortOptions } from '@libp2p/interfaces'
7
7
 
8
8
  /**
@@ -1,9 +1,9 @@
1
- import type { DualDHT, QueryEvent, SingleDHT } from '@libp2p/interfaces/dht'
2
- import type { PeerDiscoveryEvents } from '@libp2p/interfaces/peer-discovery'
1
+ import type { DualDHT, QueryEvent, SingleDHT } from '@libp2p/interface-dht'
2
+ import type { PeerDiscoveryEvents } from '@libp2p/interface-peer-discovery'
3
3
  import errCode from 'err-code'
4
4
  import { messages, codes } from '../errors.js'
5
5
  import { EventEmitter } from '@libp2p/interfaces/events'
6
- import { symbol } from '@libp2p/interfaces/peer-discovery'
6
+ import { symbol } from '@libp2p/interface-peer-discovery'
7
7
 
8
8
  export class DummyDHT extends EventEmitter<PeerDiscoveryEvents> implements DualDHT {
9
9
  get [symbol] (): true {
package/src/errors.ts CHANGED
@@ -70,5 +70,8 @@ export enum codes {
70
70
  ERR_NOT_IMPLEMENTED = 'ERR_NOT_IMPLEMENTED',
71
71
  ERR_WRONG_PING_ACK = 'ERR_WRONG_PING_ACK',
72
72
  ERR_INVALID_RECORD = 'ERR_INVALID_RECORD',
73
- ERR_ALREADY_SUCCEEDED = 'ERR_ALREADY_SUCCEEDED'
73
+ ERR_ALREADY_SUCCEEDED = 'ERR_ALREADY_SUCCEEDED',
74
+ ERR_NO_HANDLER_FOR_PROTOCOL = 'ERR_NO_HANDLER_FOR_PROTOCOL',
75
+ ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS = 'ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS',
76
+ ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS = 'ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS'
74
77
  }