libp2p 0.37.2 → 0.37.3-0bb1b80

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 (161) hide show
  1. package/README.md +1 -0
  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 +2 -2
  7. package/dist/src/circuit/circuit/hop.d.ts.map +1 -1
  8. package/dist/src/circuit/circuit/hop.js +2 -2
  9. package/dist/src/circuit/circuit/hop.js.map +1 -1
  10. package/dist/src/circuit/circuit/stop.d.ts +1 -1
  11. package/dist/src/circuit/circuit/stop.d.ts.map +1 -1
  12. package/dist/src/circuit/circuit/stop.js +1 -1
  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 +2 -2
  17. package/dist/src/circuit/index.d.ts.map +1 -1
  18. package/dist/src/circuit/listener.d.ts +3 -3
  19. package/dist/src/circuit/listener.d.ts.map +1 -1
  20. package/dist/src/circuit/transport.d.ts +5 -5
  21. package/dist/src/circuit/transport.d.ts.map +1 -1
  22. package/dist/src/circuit/transport.js +2 -2
  23. package/dist/src/circuit/transport.js.map +1 -1
  24. package/dist/src/config.d.ts.map +1 -1
  25. package/dist/src/config.js +25 -3
  26. package/dist/src/config.js.map +1 -1
  27. package/dist/src/connection-manager/auto-dialler.d.ts +1 -1
  28. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  29. package/dist/src/connection-manager/dialer/auto-dialer.d.ts +2 -2
  30. package/dist/src/connection-manager/dialer/auto-dialer.d.ts.map +1 -1
  31. package/dist/src/connection-manager/dialer/dial-request.d.ts +1 -1
  32. package/dist/src/connection-manager/dialer/dial-request.d.ts.map +1 -1
  33. package/dist/src/connection-manager/dialer/index.d.ts +9 -9
  34. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  35. package/dist/src/connection-manager/dialer/index.js +11 -11
  36. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  37. package/dist/src/connection-manager/index.d.ts +24 -20
  38. package/dist/src/connection-manager/index.d.ts.map +1 -1
  39. package/dist/src/connection-manager/index.js +199 -82
  40. package/dist/src/connection-manager/index.js.map +1 -1
  41. package/dist/src/constants.d.ts +4 -0
  42. package/dist/src/constants.d.ts.map +1 -1
  43. package/dist/src/constants.js +4 -0
  44. package/dist/src/constants.js.map +1 -1
  45. package/dist/src/content-routing/index.d.ts +4 -4
  46. package/dist/src/content-routing/index.d.ts.map +1 -1
  47. package/dist/src/content-routing/utils.d.ts +2 -2
  48. package/dist/src/content-routing/utils.d.ts.map +1 -1
  49. package/dist/src/dht/dht-content-routing.d.ts +3 -3
  50. package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
  51. package/dist/src/dht/dht-peer-routing.d.ts +5 -5
  52. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  53. package/dist/src/dht/dummy-dht.d.ts +3 -3
  54. package/dist/src/dht/dummy-dht.d.ts.map +1 -1
  55. package/dist/src/dht/dummy-dht.js +1 -1
  56. package/dist/src/dht/dummy-dht.js.map +1 -1
  57. package/dist/src/errors.d.ts +4 -1
  58. package/dist/src/errors.d.ts.map +1 -1
  59. package/dist/src/errors.js +3 -0
  60. package/dist/src/errors.js.map +1 -1
  61. package/dist/src/fetch/index.d.ts +11 -4
  62. package/dist/src/fetch/index.d.ts.map +1 -1
  63. package/dist/src/fetch/index.js +74 -42
  64. package/dist/src/fetch/index.js.map +1 -1
  65. package/dist/src/get-peer.d.ts +2 -2
  66. package/dist/src/get-peer.d.ts.map +1 -1
  67. package/dist/src/get-peer.js +1 -1
  68. package/dist/src/get-peer.js.map +1 -1
  69. package/dist/src/identify/index.d.ts +8 -8
  70. package/dist/src/identify/index.d.ts.map +1 -1
  71. package/dist/src/identify/index.js +23 -32
  72. package/dist/src/identify/index.js.map +1 -1
  73. package/dist/src/index.d.ts +17 -17
  74. package/dist/src/index.d.ts.map +1 -1
  75. package/dist/src/insecure/index.d.ts +2 -2
  76. package/dist/src/insecure/index.d.ts.map +1 -1
  77. package/dist/src/insecure/index.js +1 -1
  78. package/dist/src/insecure/index.js.map +1 -1
  79. package/dist/src/keychain/index.d.ts +2 -2
  80. package/dist/src/keychain/index.d.ts.map +1 -1
  81. package/dist/src/libp2p.d.ts +14 -14
  82. package/dist/src/libp2p.d.ts.map +1 -1
  83. package/dist/src/libp2p.js +16 -16
  84. package/dist/src/libp2p.js.map +1 -1
  85. package/dist/src/metrics/index.d.ts +3 -3
  86. package/dist/src/metrics/index.d.ts.map +1 -1
  87. package/dist/src/metrics/index.js +6 -2
  88. package/dist/src/metrics/index.js.map +1 -1
  89. package/dist/src/metrics/moving-average.d.ts +1 -1
  90. package/dist/src/metrics/moving-average.d.ts.map +1 -1
  91. package/dist/src/metrics/stats.d.ts +1 -1
  92. package/dist/src/metrics/stats.d.ts.map +1 -1
  93. package/dist/src/nat-manager.d.ts +1 -1
  94. package/dist/src/nat-manager.d.ts.map +1 -1
  95. package/dist/src/peer-record-updater.d.ts +1 -1
  96. package/dist/src/peer-record-updater.d.ts.map +1 -1
  97. package/dist/src/peer-routing.d.ts +4 -4
  98. package/dist/src/peer-routing.d.ts.map +1 -1
  99. package/dist/src/ping/index.d.ts +10 -3
  100. package/dist/src/ping/index.d.ts.map +1 -1
  101. package/dist/src/ping/index.js +21 -7
  102. package/dist/src/ping/index.js.map +1 -1
  103. package/dist/src/pnet/index.d.ts +1 -2
  104. package/dist/src/pnet/index.d.ts.map +1 -1
  105. package/dist/src/pnet/index.js.map +1 -1
  106. package/dist/src/pubsub/dummy-pubsub.d.ts +2 -2
  107. package/dist/src/pubsub/dummy-pubsub.d.ts.map +1 -1
  108. package/dist/src/registrar.d.ts +10 -8
  109. package/dist/src/registrar.d.ts.map +1 -1
  110. package/dist/src/registrar.js +63 -44
  111. package/dist/src/registrar.js.map +1 -1
  112. package/dist/src/transport-manager.d.ts +3 -3
  113. package/dist/src/transport-manager.d.ts.map +1 -1
  114. package/dist/src/upgrader.d.ts +12 -6
  115. package/dist/src/upgrader.d.ts.map +1 -1
  116. package/dist/src/upgrader.js +151 -66
  117. package/dist/src/upgrader.js.map +1 -1
  118. package/dist/src/version.d.ts.map +1 -1
  119. package/dist/src/version.js.map +1 -1
  120. package/package.json +56 -32
  121. package/src/address-manager/index.ts +2 -2
  122. package/src/circuit/auto-relay.ts +4 -4
  123. package/src/circuit/circuit/hop.ts +4 -4
  124. package/src/circuit/circuit/stop.ts +2 -2
  125. package/src/circuit/circuit/stream-handler.ts +1 -1
  126. package/src/circuit/index.ts +2 -2
  127. package/src/circuit/listener.ts +3 -3
  128. package/src/circuit/transport.ts +5 -5
  129. package/src/config.ts +25 -3
  130. package/src/connection-manager/auto-dialler.ts +1 -1
  131. package/src/connection-manager/dialer/auto-dialer.ts +2 -2
  132. package/src/connection-manager/dialer/dial-request.ts +1 -1
  133. package/src/connection-manager/dialer/index.ts +15 -15
  134. package/src/connection-manager/index.ts +257 -97
  135. package/src/constants.ts +5 -0
  136. package/src/content-routing/index.ts +2 -2
  137. package/src/content-routing/utils.ts +2 -2
  138. package/src/dht/dht-content-routing.ts +2 -2
  139. package/src/dht/dht-peer-routing.ts +3 -3
  140. package/src/dht/dummy-dht.ts +3 -3
  141. package/src/errors.ts +4 -1
  142. package/src/fetch/index.ts +111 -53
  143. package/src/get-peer.ts +3 -3
  144. package/src/identify/index.ts +34 -40
  145. package/src/index.ts +17 -17
  146. package/src/insecure/index.ts +3 -3
  147. package/src/keychain/index.ts +2 -2
  148. package/src/libp2p.ts +33 -28
  149. package/src/metrics/index.ts +9 -5
  150. package/src/metrics/moving-average.ts +1 -1
  151. package/src/metrics/stats.ts +1 -1
  152. package/src/nat-manager.ts +1 -1
  153. package/src/peer-record-updater.ts +1 -1
  154. package/src/peer-routing.ts +4 -4
  155. package/src/ping/index.ts +35 -12
  156. package/src/pnet/index.ts +1 -2
  157. package/src/pubsub/dummy-pubsub.ts +2 -2
  158. package/src/registrar.ts +79 -55
  159. package/src/transport-manager.ts +3 -3
  160. package/src/upgrader.ts +188 -76
  161. 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,43 @@ 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
+ await Promise.all(
335
+ keepAlivePeers.map(async peer => {
336
+ await this.openConnection(peer, {
337
+ signal: this.connectOnStartupController?.signal
338
+ })
339
+ .catch(err => {
340
+ log.error(err)
341
+ })
342
+ })
343
+ )
344
+ })
345
+ .finally(() => {
346
+ this.connectOnStartupController?.clear()
347
+ })
229
348
  }
230
349
 
231
350
  async beforeStop () {
351
+ // if we are still dialing KEEP_ALIVE peers, abort those dials
352
+ this.connectOnStartupController?.abort()
232
353
  this.components.getUpgrader().removeEventListener('connection', this.onConnect)
233
354
  this.components.getUpgrader().removeEventListener('connectionEnd', this.onDisconnect)
234
355
  }
@@ -271,18 +392,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
271
392
  this.connections.clear()
272
393
  }
273
394
 
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
395
  /**
287
396
  * Checks the libp2p metrics to determine if any values have exceeded
288
397
  * the configured maximums.
@@ -340,11 +449,10 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
340
449
  await this.components.getPeerStore().keyBook.set(peerId, peerId.publicKey)
341
450
  }
342
451
 
343
- if (!this.peerValues.has(peerIdStr)) {
344
- this.peerValues.set(peerIdStr, this.opts.defaultPeerValue)
345
- }
452
+ const numConnections = this.getConnections().length
453
+ const toPrune = numConnections - this.opts.maxConnections
346
454
 
347
- await this._checkMaxLimit('maxConnections', this.getConnections().length)
455
+ await this._checkMaxLimit('maxConnections', numConnections, toPrune)
348
456
  this.dispatchEvent(new CustomEvent<Connection>('peer:connect', { detail: connection }))
349
457
  }
350
458
 
@@ -367,7 +475,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
367
475
  this.connections.set(peerId, storedConn)
368
476
  } else if (storedConn != null) {
369
477
  this.connections.delete(peerId)
370
- this.peerValues.delete(connection.remotePeer.toString())
371
478
  this.dispatchEvent(new CustomEvent<Connection>('peer:disconnect', { detail: connection }))
372
479
 
373
480
  this.components.getMetrics()?.onPeerDisconnected(connection.remotePeer)
@@ -388,7 +495,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
388
495
  return conns
389
496
  }
390
497
 
391
- async openConnection (peerId: PeerId, options?: AbortOptions): Promise<Connection> {
498
+ async openConnection (peerId: PeerId, options: AbortOptions = {}): Promise<Connection> {
392
499
  log('dial to %p', peerId)
393
500
  const existingConnections = this.getConnections(peerId)
394
501
 
@@ -398,30 +505,43 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
398
505
  return existingConnections[0]
399
506
  }
400
507
 
401
- const connection = await this.dialer.dial(peerId, options)
402
- let peerConnections = this.connections.get(peerId.toString())
508
+ let timeoutController: TimeoutController | undefined
403
509
 
404
- if (peerConnections == null) {
405
- peerConnections = []
406
- this.connections.set(peerId.toString(), peerConnections)
510
+ if (options?.signal == null) {
511
+ timeoutController = new TimeoutController(this.dialTimeout)
512
+ options.signal = timeoutController.signal
407
513
  }
408
514
 
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
515
+ try {
516
+ const connection = await this.dialer.dial(peerId, options)
517
+ let peerConnections = this.connections.get(peerId.toString())
413
518
 
414
- for (const conn of peerConnections) {
415
- if (conn.id === connection.id) {
416
- trackedConnection = true
519
+ if (peerConnections == null) {
520
+ peerConnections = []
521
+ this.connections.set(peerId.toString(), peerConnections)
417
522
  }
418
- }
419
523
 
420
- if (!trackedConnection) {
421
- peerConnections.push(connection)
422
- }
524
+ // we get notified of connections via the Upgrader emitting "connection"
525
+ // events, double check we aren't already tracking this connection before
526
+ // storing it
527
+ let trackedConnection = false
423
528
 
424
- return connection
529
+ for (const conn of peerConnections) {
530
+ if (conn.id === connection.id) {
531
+ trackedConnection = true
532
+ }
533
+ }
534
+
535
+ if (!trackedConnection) {
536
+ peerConnections.push(connection)
537
+ }
538
+
539
+ return connection
540
+ } finally {
541
+ if (timeoutController != null) {
542
+ timeoutController.clear()
543
+ }
544
+ }
425
545
  }
426
546
 
427
547
  async closeConnections (peerId: PeerId): Promise<void> {
@@ -459,7 +579,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
459
579
  _onLatencyMeasure (evt: CustomEvent<SummaryObject>) {
460
580
  const { detail: summary } = evt
461
581
 
462
- this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
582
+ this._checkMaxLimit('maxEventLoopDelay', summary.avgMs, 1)
463
583
  .catch(err => {
464
584
  log.error(err)
465
585
  })
@@ -468,46 +588,86 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
468
588
  /**
469
589
  * If the `value` of `name` has exceeded its limit, maybe close a connection
470
590
  */
471
- async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number) {
591
+ async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number, toPrune: number = 1) {
472
592
  const limit = this.opts[name]
473
593
  log.trace('checking limit of %s. current value: %d of %d', name, value, limit)
474
594
  if (value > limit) {
475
- log('%s: limit exceeded: %p, %d', this.components.getPeerId(), name, value)
476
- await this._maybeDisconnectOne()
595
+ log('%s: limit exceeded: %p, %d/%d, pruning %d connection(s)', this.components.getPeerId(), name, value, limit, toPrune)
596
+ await this._maybePruneConnections(toPrune)
477
597
  }
478
598
  }
479
599
 
480
600
  /**
481
- * If we have more connections than our maximum, close a connection
482
- * to the lowest valued peer.
601
+ * If we have more connections than our maximum, select some excess connections
602
+ * to prune based on peer value
483
603
  */
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])))
604
+ async _maybePruneConnections (toPrune: number) {
605
+ const connections = this.getConnections()
606
+
607
+ if (connections.length <= this.opts.minConnections || toPrune < 1) {
608
+ return
609
+ }
487
610
 
488
- log('%p: sorted peer values: %j', this.components.getPeerId(), peerValues)
489
- const disconnectPeer = peerValues[0]
611
+ const peerValues = new PeerMap<number>()
490
612
 
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)
613
+ // work out peer values
614
+ for (const connection of connections) {
615
+ const remotePeer = connection.remotePeer
495
616
 
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
- })
617
+ if (peerValues.has(remotePeer)) {
618
+ continue
619
+ }
502
620
 
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
- }
621
+ const tags = await this.components.getPeerStore().getTags(remotePeer)
622
+
623
+ // sum all tag values
624
+ peerValues.set(remotePeer, tags.reduce((acc, curr) => {
625
+ return acc + curr.value
626
+ }, 0))
627
+ }
628
+
629
+ // sort by value, lowest to highest
630
+ const sortedConnections = connections.sort((a, b) => {
631
+ const peerAValue = peerValues.get(a.remotePeer) ?? 0
632
+ const peerBValue = peerValues.get(b.remotePeer) ?? 0
633
+
634
+ if (peerAValue > peerBValue) {
635
+ return 1
636
+ }
637
+
638
+ if (peerAValue < peerBValue) {
639
+ return -1
640
+ }
641
+
642
+ return 0
643
+ })
644
+
645
+ // close some connections
646
+ const toClose = []
647
+
648
+ for (const connection of sortedConnections) {
649
+ log('too many connections open - closing a connection to %p', connection.remotePeer)
650
+ toClose.push(connection)
651
+
652
+ if (toClose.length === toPrune) {
653
+ break
510
654
  }
511
655
  }
656
+
657
+ // close connections
658
+ await Promise.all(
659
+ toClose.map(async connection => {
660
+ try {
661
+ await connection.close()
662
+ } catch (err) {
663
+ log.error(err)
664
+ }
665
+
666
+ // TODO: should not need to invoke this manually
667
+ this.onDisconnect(new CustomEvent<Connection>('connectionEnd', {
668
+ detail: connection
669
+ }))
670
+ })
671
+ )
512
672
  }
513
673
  }
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
  }