libp2p 0.43.4 → 0.44.0

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 (105) hide show
  1. package/dist/index.min.js +30 -30
  2. package/dist/src/autonat/index.d.ts +1 -2
  3. package/dist/src/autonat/index.d.ts.map +1 -1
  4. package/dist/src/autonat/index.js +4 -6
  5. package/dist/src/autonat/index.js.map +1 -1
  6. package/dist/src/circuit-relay/transport/reservation-store.d.ts +1 -1
  7. package/dist/src/circuit-relay/transport/reservation-store.d.ts.map +1 -1
  8. package/dist/src/circuit-relay/utils.d.ts.map +1 -1
  9. package/dist/src/circuit-relay/utils.js +3 -1
  10. package/dist/src/circuit-relay/utils.js.map +1 -1
  11. package/dist/src/components.d.ts +1 -6
  12. package/dist/src/components.d.ts.map +1 -1
  13. package/dist/src/components.js +0 -10
  14. package/dist/src/components.js.map +1 -1
  15. package/dist/src/config.d.ts.map +1 -1
  16. package/dist/src/config.js +0 -9
  17. package/dist/src/config.js.map +1 -1
  18. package/dist/src/connection-manager/auto-dial.d.ts +36 -0
  19. package/dist/src/connection-manager/auto-dial.d.ts.map +1 -0
  20. package/dist/src/connection-manager/auto-dial.js +122 -0
  21. package/dist/src/connection-manager/auto-dial.js.map +1 -0
  22. package/dist/src/connection-manager/connection-pruner.d.ts +28 -0
  23. package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -0
  24. package/dist/src/connection-manager/connection-pruner.js +96 -0
  25. package/dist/src/connection-manager/connection-pruner.js.map +1 -0
  26. package/dist/src/connection-manager/constants.d.ts +49 -0
  27. package/dist/src/connection-manager/constants.d.ts.map +1 -0
  28. package/dist/src/connection-manager/constants.js +49 -0
  29. package/dist/src/connection-manager/constants.js.map +1 -0
  30. package/dist/src/connection-manager/dial-queue.d.ts +77 -0
  31. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -0
  32. package/dist/src/connection-manager/dial-queue.js +366 -0
  33. package/dist/src/connection-manager/dial-queue.js.map +1 -0
  34. package/dist/src/connection-manager/index.d.ts +65 -69
  35. package/dist/src/connection-manager/index.d.ts.map +1 -1
  36. package/dist/src/connection-manager/index.js +116 -234
  37. package/dist/src/connection-manager/index.js.map +1 -1
  38. package/dist/src/connection-manager/utils.d.ts +9 -0
  39. package/dist/src/connection-manager/utils.d.ts.map +1 -0
  40. package/dist/src/connection-manager/utils.js +66 -0
  41. package/dist/src/connection-manager/utils.js.map +1 -0
  42. package/dist/src/fetch/index.js +3 -3
  43. package/dist/src/fetch/index.js.map +1 -1
  44. package/dist/src/identify/index.js +5 -5
  45. package/dist/src/identify/index.js.map +1 -1
  46. package/dist/src/insecure/index.d.ts.map +1 -1
  47. package/dist/src/insecure/index.js +3 -1
  48. package/dist/src/insecure/index.js.map +1 -1
  49. package/dist/src/libp2p.d.ts +10 -2
  50. package/dist/src/libp2p.d.ts.map +1 -1
  51. package/dist/src/libp2p.js +7 -13
  52. package/dist/src/libp2p.js.map +1 -1
  53. package/dist/src/upgrader.d.ts +1 -1
  54. package/dist/src/upgrader.d.ts.map +1 -1
  55. package/dist/src/upgrader.js +2 -1
  56. package/dist/src/upgrader.js.map +1 -1
  57. package/dist/typedoc-urls.json +1 -1
  58. package/package.json +18 -18
  59. package/src/autonat/index.ts +6 -9
  60. package/src/circuit-relay/transport/reservation-store.ts +1 -1
  61. package/src/circuit-relay/utils.ts +3 -1
  62. package/src/components.ts +1 -17
  63. package/src/config.ts +0 -9
  64. package/src/connection-manager/auto-dial.ts +171 -0
  65. package/src/connection-manager/connection-pruner.ts +137 -0
  66. package/src/connection-manager/constants.ts +60 -0
  67. package/src/connection-manager/dial-queue.ts +504 -0
  68. package/src/connection-manager/index.ts +190 -323
  69. package/src/connection-manager/utils.ts +76 -0
  70. package/src/fetch/index.ts +4 -4
  71. package/src/identify/index.ts +5 -5
  72. package/src/insecure/index.ts +5 -1
  73. package/src/libp2p.ts +17 -15
  74. package/src/upgrader.ts +3 -2
  75. package/src/version.ts +1 -1
  76. package/dist/src/connection-manager/auto-dialler.d.ts +0 -42
  77. package/dist/src/connection-manager/auto-dialler.d.ts.map +0 -1
  78. package/dist/src/connection-manager/auto-dialler.js +0 -105
  79. package/dist/src/connection-manager/auto-dialler.js.map +0 -1
  80. package/dist/src/connection-manager/dialer/dial-request.d.ts +0 -27
  81. package/dist/src/connection-manager/dialer/dial-request.d.ts.map +0 -1
  82. package/dist/src/connection-manager/dialer/dial-request.js +0 -114
  83. package/dist/src/connection-manager/dialer/dial-request.js.map +0 -1
  84. package/dist/src/connection-manager/dialer/index.d.ts +0 -121
  85. package/dist/src/connection-manager/dialer/index.d.ts.map +0 -1
  86. package/dist/src/connection-manager/dialer/index.js +0 -324
  87. package/dist/src/connection-manager/dialer/index.js.map +0 -1
  88. package/dist/src/connection-manager/latency-monitor.d.ts +0 -120
  89. package/dist/src/connection-manager/latency-monitor.d.ts.map +0 -1
  90. package/dist/src/connection-manager/latency-monitor.js +0 -220
  91. package/dist/src/connection-manager/latency-monitor.js.map +0 -1
  92. package/dist/src/connection-manager/visibility-change-emitter.d.ts +0 -66
  93. package/dist/src/connection-manager/visibility-change-emitter.d.ts.map +0 -1
  94. package/dist/src/connection-manager/visibility-change-emitter.js +0 -118
  95. package/dist/src/connection-manager/visibility-change-emitter.js.map +0 -1
  96. package/dist/src/constants.d.ts +0 -27
  97. package/dist/src/constants.d.ts.map +0 -1
  98. package/dist/src/constants.js +0 -31
  99. package/dist/src/constants.js.map +0 -1
  100. package/src/connection-manager/auto-dialler.ts +0 -158
  101. package/src/connection-manager/dialer/dial-request.ts +0 -144
  102. package/src/connection-manager/dialer/index.ts +0 -461
  103. package/src/connection-manager/latency-monitor.ts +0 -319
  104. package/src/connection-manager/visibility-change-emitter.ts +0 -135
  105. package/src/constants.ts +0 -36
@@ -1,69 +1,92 @@
1
1
  import { logger } from '@libp2p/logger'
2
2
  import { CodeError } from '@libp2p/interfaces/errors'
3
- import mergeOptions from 'merge-options'
4
- import { LatencyMonitor, SummaryObject } from './latency-monitor.js'
5
3
  import type { AbortOptions } from '@libp2p/interfaces'
6
4
  import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
7
5
  import type { Startable } from '@libp2p/interfaces/startable'
8
6
  import { codes } from '../errors.js'
9
- import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
7
+ import type { PeerId } from '@libp2p/interface-peer-id'
10
8
  import { setMaxListeners } from 'events'
11
9
  import type { Connection, MultiaddrConnection } from '@libp2p/interface-connection'
12
- import type { ConnectionManager, ConnectionManagerEvents, Dialer } from '@libp2p/interface-connection-manager'
13
- import * as STATUS from '@libp2p/interface-connection/status'
10
+ import type { ConnectionManager, ConnectionManagerEvents } from '@libp2p/interface-connection-manager'
14
11
  import type { AddressSorter, PeerStore } from '@libp2p/interface-peer-store'
15
- import { multiaddr, Multiaddr, Resolver } from '@multiformats/multiaddr'
16
- import { PeerMap } from '@libp2p/peer-collections'
17
- import { TimeoutController } from 'timeout-abort-controller'
12
+ import { Multiaddr, Resolver, multiaddr } from '@multiformats/multiaddr'
18
13
  import { KEEP_ALIVE } from '@libp2p/interface-peer-store/tags'
19
14
  import { RateLimiterMemory } from 'rate-limiter-flexible'
20
15
  import type { Metrics } from '@libp2p/interface-metrics'
21
- import type { Upgrader } from '@libp2p/interface-transport'
16
+ import type { TransportManager, Upgrader } from '@libp2p/interface-transport'
22
17
  import { getPeerAddress } from '../get-peer.js'
18
+ import { AutoDial } from './auto-dial.js'
19
+ import { DialQueue } from './dial-queue.js'
20
+ import { ConnectionPruner } from './connection-pruner.js'
21
+ import type { ConnectionGater } from '@libp2p/interface-connection-gater'
22
+ import { PeerMap } from '@libp2p/peer-collections'
23
+ import { publicAddressesFirst } from '@libp2p/utils/address-sort'
24
+ import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_PRIORITY, DIAL_TIMEOUT, INBOUND_CONNECTION_THRESHOLD, MAX_CONNECTIONS, MAX_INCOMING_PENDING_CONNECTIONS, MAX_PARALLEL_DIALS, MAX_PEER_ADDRS_TO_DIAL, MIN_CONNECTIONS } from './constants.js'
25
+ import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
26
+ import type { PendingDial } from '../libp2p.js'
23
27
 
24
28
  const log = logger('libp2p:connection-manager')
25
29
 
26
- export interface ConnectionManagerConfig {
30
+ const DEFAULT_DIAL_PRIORITY = 50
31
+
32
+ export interface ConnectionManagerInit {
27
33
  /**
28
- * The maximum number of connections libp2p is willing to have before it starts disconnecting. Defaults to `Infinity`
34
+ * The maximum number of connections libp2p is willing to have before it starts
35
+ * pruning connections to reduce resource usage. (default: 300)
29
36
  */
30
- maxConnections: number
37
+ maxConnections?: number
31
38
 
32
39
  /**
33
- * The minimum number of connections below which libp2p not activate preemptive disconnections. Defaults to `0`.
40
+ * The minimum number of connections below which libp2p will start to dial peers
41
+ * from the peer book. Setting this to 0 effectively disables this behaviour.
42
+ * (default: 50)
34
43
  */
35
- minConnections: number
44
+ minConnections?: number
36
45
 
37
46
  /**
38
- * Sets the maximum event loop delay (measured in milliseconds) this node is willing to endure before it starts disconnecting peers. Defaults to `Infinity`.
47
+ * How long to wait between attempting to keep our number of concurrent connections
48
+ * above minConnections (default: 5000)
39
49
  */
40
- maxEventLoopDelay?: number
50
+ autoDialInterval?: number
41
51
 
42
52
  /**
43
- * Sets the poll interval (in milliseconds) for assessing the current state and determining if this peer needs to force a disconnect. Defaults to `2000` (2 seconds).
53
+ * When dialling peers from the peer book to keep the number of open connections
54
+ * above `minConnections`, add dials for this many peers to the dial queue
55
+ * at once. (default: 25)
44
56
  */
45
- pollInterval?: number
57
+ autoDialConcurrency?: number
46
58
 
47
59
  /**
48
- * How long to wait between attempting to keep our number of concurrent connections
49
- * above minConnections
60
+ * To allow user dials to take priority over auto dials, use this value as the
61
+ * dial priority. (default: 0)
50
62
  */
51
- autoDialInterval: number
63
+ autoDialPriority?: number
52
64
 
53
65
  /**
54
- * Sort the known addresses of a peer before trying to dial
66
+ * Sort the known addresses of a peer before trying to dial, By default public
67
+ * addresses will be dialled before private (e.g. loopback or LAN) addresses.
55
68
  */
56
69
  addressSorter?: AddressSorter
57
70
 
58
71
  /**
59
- * Number of max concurrent dials
72
+ * The maximum number of dials across all peers to execute in parallel.
73
+ * (default: 100)
60
74
  */
61
75
  maxParallelDials?: number
62
76
 
63
77
  /**
64
- * Number of max addresses to dial for a given peer
78
+ * To prevent individual peers with large amounts of multiaddrs swamping the
79
+ * dial queue, this value controls how many addresses to dial in parallel per
80
+ * peer. So for example if two peers have 10 addresses and this value is set
81
+ * at 5, we will dial 5 addresses from each at a time. (default: 10)
65
82
  */
66
- maxAddrsToDial?: number
83
+ maxParallelDialsPerPeer?: number
84
+
85
+ /**
86
+ * Maximum number of addresses allowed for a given peer - if a peer has more
87
+ * addresses than this then the dial will fail. (default: 25)
88
+ */
89
+ maxPeerAddrsToDial?: number
67
90
 
68
91
  /**
69
92
  * How long a dial attempt is allowed to take, including DNS resolution
@@ -73,26 +96,15 @@ export interface ConnectionManagerConfig {
73
96
 
74
97
  /**
75
98
  * When a new inbound connection is opened, the upgrade process (e.g. protect,
76
- * encrypt, multiplex etc) must complete within this number of ms.
77
- */
78
- inboundUpgradeTimeout: number
79
-
80
- /**
81
- * Number of max concurrent dials per peer
99
+ * encrypt, multiplex etc) must complete within this number of ms. (default: 30s)
82
100
  */
83
- maxDialsPerPeer?: number
101
+ inboundUpgradeTimeout?: number
84
102
 
85
103
  /**
86
- * Multiaddr resolvers to use when dialing
104
+ * Multiaddr resolvers to use when dialling
87
105
  */
88
106
  resolvers?: Record<string, Resolver>
89
107
 
90
- /**
91
- * On startup we try to dial any peer that has previously been
92
- * tagged with KEEP_ALIVE up to this timeout in ms. (default: 60000)
93
- */
94
- startupReconnectTimeout?: number
95
-
96
108
  /**
97
109
  * A list of multiaddrs that will always be allowed (except if they are in the
98
110
  * deny list) to open connections to this node even if we've reached maxConnections
@@ -107,103 +119,152 @@ export interface ConnectionManagerConfig {
107
119
 
108
120
  /**
109
121
  * If more than this many connections are opened per second by a single
110
- * host, reject subsequent connections
122
+ * host, reject subsequent connections. (default: 5)
111
123
  */
112
124
  inboundConnectionThreshold?: number
113
125
 
114
126
  /**
115
127
  * The maximum number of parallel incoming connections allowed that have yet to
116
- * complete the connection upgrade - e.g. choosing connection encryption, muxer, etc
128
+ * complete the connection upgrade - e.g. choosing connection encryption, muxer, etc.
129
+ * (default: 10)
117
130
  */
118
131
  maxIncomingPendingConnections?: number
119
132
  }
120
133
 
121
- const defaultOptions: Partial<ConnectionManagerConfig> = {
122
- maxConnections: Infinity,
123
- minConnections: 0,
124
- maxEventLoopDelay: Infinity,
125
- pollInterval: 2000,
126
- autoDialInterval: 10000,
127
- inboundConnectionThreshold: 5,
128
- maxIncomingPendingConnections: 10
134
+ const defaultOptions = {
135
+ minConnections: MIN_CONNECTIONS,
136
+ maxConnections: MAX_CONNECTIONS,
137
+ inboundConnectionThreshold: INBOUND_CONNECTION_THRESHOLD,
138
+ maxIncomingPendingConnections: MAX_INCOMING_PENDING_CONNECTIONS,
139
+ autoDialConcurrency: AUTO_DIAL_CONCURRENCY,
140
+ autoDialPriority: AUTO_DIAL_PRIORITY
129
141
  }
130
142
 
131
- const STARTUP_RECONNECT_TIMEOUT = 60000
132
-
133
143
  export interface DefaultConnectionManagerComponents {
134
144
  peerId: PeerId
135
145
  metrics?: Metrics
136
146
  upgrader: Upgrader
137
147
  peerStore: PeerStore
138
- dialer: Dialer
148
+ transportManager: TransportManager
149
+ connectionGater: ConnectionGater
139
150
  }
140
151
 
141
- export type ConnectionManagerInit = ConnectionManagerConfig
152
+ export interface OpenConnectionOptions extends AbortOptions {
153
+ priority?: number
154
+ }
142
155
 
143
156
  /**
144
157
  * Responsible for managing known connections.
145
158
  */
146
159
  export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEvents> implements ConnectionManager, Startable {
147
- private readonly components: DefaultConnectionManagerComponents
148
- private readonly opts: ConnectionManagerInit
149
- private readonly connections: Map<string, Connection[]>
150
160
  private started: boolean
151
- private readonly latencyMonitor?: LatencyMonitor
152
- private readonly startupReconnectTimeout: number
153
- private connectOnStartupController?: TimeoutController
154
- private readonly dialTimeout: number
161
+ private readonly connections: PeerMap<Connection[]>
155
162
  private readonly allow: Multiaddr[]
156
163
  private readonly deny: Multiaddr[]
157
- private readonly inboundConnectionRateLimiter: RateLimiterMemory
164
+ private readonly maxIncomingPendingConnections: number
158
165
  private incomingPendingConnections: number
166
+ private readonly maxConnections: number
159
167
 
160
- constructor (components: DefaultConnectionManagerComponents, init: ConnectionManagerConfig) {
168
+ public readonly dialQueue: DialQueue
169
+ public readonly autoDial: AutoDial
170
+ public readonly connectionPruner: ConnectionPruner
171
+ private readonly inboundConnectionRateLimiter: RateLimiterMemory
172
+
173
+ private readonly peerStore: PeerStore
174
+ private readonly metrics?: Metrics
175
+
176
+ constructor (components: DefaultConnectionManagerComponents, init: ConnectionManagerInit = {}) {
161
177
  super()
162
178
 
163
- this.opts = mergeOptions.call({ ignoreUndefined: true }, defaultOptions, init)
179
+ this.maxConnections = init.maxConnections ?? defaultOptions.maxConnections
180
+ const minConnections = init.minConnections ?? defaultOptions.minConnections
164
181
 
165
- if (this.opts.maxConnections < this.opts.minConnections) {
182
+ if (this.maxConnections < minConnections) {
166
183
  throw new CodeError('Connection Manager maxConnections must be greater than minConnections', codes.ERR_INVALID_PARAMETERS)
167
184
  }
168
185
 
169
- log('options: %o', this.opts)
170
-
171
- this.components = components
172
-
173
186
  /**
174
187
  * Map of connections per peer
175
188
  */
176
- this.connections = new Map()
189
+ this.connections = new PeerMap()
177
190
 
178
191
  this.started = false
179
192
 
180
- if (init.maxEventLoopDelay != null && init.maxEventLoopDelay > 0 && init.maxEventLoopDelay !== Infinity) {
181
- this.latencyMonitor = new LatencyMonitor({
182
- latencyCheckIntervalMs: init.pollInterval,
183
- dataEmitIntervalMs: init.pollInterval
184
- })
185
- }
186
-
187
193
  try {
188
194
  // This emitter gets listened to a lot
189
195
  setMaxListeners?.(Infinity, this)
190
196
  } catch {}
191
197
 
198
+ this.peerStore = components.peerStore
199
+ this.metrics = components.metrics
200
+
192
201
  this.onConnect = this.onConnect.bind(this)
193
202
  this.onDisconnect = this.onDisconnect.bind(this)
194
203
 
195
- this.startupReconnectTimeout = init.startupReconnectTimeout ?? STARTUP_RECONNECT_TIMEOUT
196
- this.dialTimeout = init.dialTimeout ?? 30000
197
-
204
+ // allow/deny lists
198
205
  this.allow = (init.allow ?? []).map(ma => multiaddr(ma))
199
206
  this.deny = (init.deny ?? []).map(ma => multiaddr(ma))
200
207
 
208
+ this.incomingPendingConnections = 0
209
+ this.maxIncomingPendingConnections = init.maxIncomingPendingConnections ?? defaultOptions.maxIncomingPendingConnections
210
+
211
+ // controls individual peers trying to dial us too quickly
201
212
  this.inboundConnectionRateLimiter = new RateLimiterMemory({
202
- points: this.opts.inboundConnectionThreshold,
213
+ points: init.inboundConnectionThreshold ?? defaultOptions.inboundConnectionThreshold,
203
214
  duration: 1
204
215
  })
205
216
 
206
- this.incomingPendingConnections = 0
217
+ // controls what happens when we don't have enough connections
218
+ this.autoDial = new AutoDial({
219
+ connectionManager: this,
220
+ peerStore: components.peerStore
221
+ }, {
222
+ minConnections,
223
+ autoDialConcurrency: init.autoDialConcurrency ?? defaultOptions.autoDialConcurrency,
224
+ autoDialPriority: init.autoDialPriority ?? defaultOptions.autoDialPriority
225
+ })
226
+ // check the min connection limit whenever a peer disconnects
227
+ this.addEventListener('peer:disconnect', () => {
228
+ this.autoDial.autoDial()
229
+ .catch(err => {
230
+ log.error(err)
231
+ })
232
+ })
233
+
234
+ // controls what happens when we have too many connections
235
+ this.connectionPruner = new ConnectionPruner({
236
+ connectionManager: this,
237
+ peerStore: components.peerStore
238
+ }, {
239
+ maxConnections: this.maxConnections,
240
+ allow: this.allow
241
+ })
242
+ // check the max connection limit whenever a peer connects
243
+ this.addEventListener('peer:connect', () => {
244
+ this.connectionPruner.maybePruneConnections()
245
+ .catch(err => {
246
+ log.error(err)
247
+ })
248
+ })
249
+
250
+ this.dialQueue = new DialQueue({
251
+ peerId: components.peerId,
252
+ metrics: components.metrics,
253
+ peerStore: components.peerStore,
254
+ transportManager: components.transportManager,
255
+ connectionGater: components.connectionGater
256
+ }, {
257
+ addressSorter: init.addressSorter ?? publicAddressesFirst,
258
+ maxParallelDials: init.maxParallelDials ?? MAX_PARALLEL_DIALS,
259
+ maxPeerAddrsToDial: init.maxPeerAddrsToDial ?? MAX_PEER_ADDRS_TO_DIAL,
260
+ dialTimeout: init.dialTimeout ?? DIAL_TIMEOUT,
261
+ resolvers: init.resolvers ?? {
262
+ dnsaddr: dnsaddrResolver
263
+ }
264
+ })
265
+
266
+ components.upgrader.addEventListener('connection', this.onConnect)
267
+ components.upgrader.addEventListener('connectionEnd', this.onDisconnect)
207
268
  }
208
269
 
209
270
  isStarted (): boolean {
@@ -216,7 +277,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
216
277
  */
217
278
  async start (): Promise<void> {
218
279
  // track inbound/outbound connections
219
- this.components.metrics?.registerMetricGroup('libp2p_connection_manager_connections', {
280
+ this.metrics?.registerMetricGroup('libp2p_connection_manager_connections', {
220
281
  calculate: () => {
221
282
  const metric = {
222
283
  inbound: 0,
@@ -238,7 +299,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
238
299
  })
239
300
 
240
301
  // track total number of streams per protocol
241
- this.components.metrics?.registerMetricGroup('libp2p_protocol_streams_total', {
302
+ this.metrics?.registerMetricGroup('libp2p_protocol_streams_total', {
242
303
  label: 'protocol',
243
304
  calculate: () => {
244
305
  const metric: Record<string, number> = {}
@@ -258,7 +319,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
258
319
  })
259
320
 
260
321
  // track 90th percentile of streams per protocol
261
- this.components.metrics?.registerMetricGroup('libp2p_connection_manager_protocol_streams_per_connection_90th_percentile', {
322
+ this.metrics?.registerMetricGroup('libp2p_connection_manager_protocol_streams_per_connection_90th_percentile', {
262
323
  label: 'protocol',
263
324
  calculate: () => {
264
325
  const allStreams: Record<string, number[]> = {}
@@ -293,26 +354,20 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
293
354
  }
294
355
  })
295
356
 
296
- // latency monitor
297
- this.latencyMonitor?.start()
298
- this._onLatencyMeasure = this._onLatencyMeasure.bind(this)
299
- this.latencyMonitor?.addEventListener('data', this._onLatencyMeasure)
357
+ this.autoDial.start()
300
358
 
301
359
  this.started = true
302
360
  log('started')
303
361
  }
304
362
 
305
363
  async afterStart (): Promise<void> {
306
- this.components.upgrader.addEventListener('connection', this.onConnect)
307
- this.components.upgrader.addEventListener('connectionEnd', this.onDisconnect)
308
-
309
364
  // re-connect to any peers with the KEEP_ALIVE tag
310
365
  void Promise.resolve()
311
366
  .then(async () => {
312
367
  const keepAlivePeers: PeerId[] = []
313
368
 
314
- for (const peer of await this.components.peerStore.all()) {
315
- const tags = await this.components.peerStore.getTags(peer.id)
369
+ for (const peer of await this.peerStore.all()) {
370
+ const tags = await this.peerStore.getTags(peer.id)
316
371
  const hasKeepAlive = tags.filter(tag => tag.name === KEEP_ALIVE).length > 0
317
372
 
318
373
  if (hasKeepAlive) {
@@ -320,19 +375,9 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
320
375
  }
321
376
  }
322
377
 
323
- this.connectOnStartupController?.clear()
324
- this.connectOnStartupController = new TimeoutController(this.startupReconnectTimeout)
325
-
326
- try {
327
- // fails on node < 15.4
328
- setMaxListeners?.(Infinity, this.connectOnStartupController.signal)
329
- } catch {}
330
-
331
378
  await Promise.all(
332
379
  keepAlivePeers.map(async peer => {
333
- await this.openConnection(peer, {
334
- signal: this.connectOnStartupController?.signal
335
- })
380
+ await this.openConnection(peer)
336
381
  .catch(err => {
337
382
  log.error(err)
338
383
  })
@@ -342,34 +387,17 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
342
387
  .catch(err => {
343
388
  log.error(err)
344
389
  })
345
- .finally(() => {
346
- this.connectOnStartupController?.clear()
347
- })
348
- }
349
390
 
350
- async beforeStop (): Promise<void> {
351
- // if we are still dialing KEEP_ALIVE peers, abort those dials
352
- this.connectOnStartupController?.abort()
353
- this.components.upgrader.removeEventListener('connection', this.onConnect)
354
- this.components.upgrader.removeEventListener('connectionEnd', this.onDisconnect)
391
+ this.autoDial.afterStart()
355
392
  }
356
393
 
357
394
  /**
358
395
  * Stops the Connection Manager
359
396
  */
360
397
  async stop (): Promise<void> {
361
- this.latencyMonitor?.removeEventListener('data', this._onLatencyMeasure)
362
- this.latencyMonitor?.stop()
363
-
364
- this.started = false
365
- await this._close()
366
- log('stopped')
367
- }
398
+ this.dialQueue.stop()
399
+ this.autoDial.stop()
368
400
 
369
- /**
370
- * Cleans up the connections
371
- */
372
- async _close (): Promise<void> {
373
401
  // Close all connections we're tracking
374
402
  const tasks: Array<Promise<void>> = []
375
403
  for (const connectionList of this.connections.values()) {
@@ -387,6 +415,8 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
387
415
  log('closing %d connections', tasks.length)
388
416
  await Promise.all(tasks)
389
417
  this.connections.clear()
418
+
419
+ log('stopped')
390
420
  }
391
421
 
392
422
  onConnect (evt: CustomEvent<Connection>): void {
@@ -408,23 +438,18 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
408
438
  }
409
439
 
410
440
  const peerId = connection.remotePeer
411
- const peerIdStr = peerId.toString()
412
- const storedConns = this.connections.get(peerIdStr)
441
+ const storedConns = this.connections.get(peerId)
413
442
 
414
443
  if (storedConns != null) {
415
444
  storedConns.push(connection)
416
445
  } else {
417
- this.connections.set(peerIdStr, [connection])
446
+ this.connections.set(peerId, [connection])
418
447
  }
419
448
 
420
449
  if (peerId.publicKey != null) {
421
- await this.components.peerStore.keyBook.set(peerId, peerId.publicKey)
450
+ await this.peerStore.keyBook.set(peerId, peerId.publicKey)
422
451
  }
423
452
 
424
- const numConnections = this.getConnections().length
425
- const toPrune = numConnections - this.opts.maxConnections
426
-
427
- await this._checkMaxLimit('maxConnections', numConnections, toPrune)
428
453
  this.dispatchEvent(new CustomEvent<Connection>('peer:connect', { detail: connection }))
429
454
  }
430
455
 
@@ -439,7 +464,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
439
464
  return
440
465
  }
441
466
 
442
- const peerId = connection.remotePeer.toString()
467
+ const peerId = connection.remotePeer
443
468
  let storedConn = this.connections.get(peerId)
444
469
 
445
470
  if (storedConn != null && storedConn.length > 1) {
@@ -453,7 +478,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
453
478
 
454
479
  getConnections (peerId?: PeerId): Connection[] {
455
480
  if (peerId != null) {
456
- return this.connections.get(peerId.toString()) ?? []
481
+ return this.connections.get(peerId) ?? []
457
482
  }
458
483
 
459
484
  let conns: Connection[] = []
@@ -465,19 +490,15 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
465
490
  return conns
466
491
  }
467
492
 
468
- getConnectionsMap (): Map<string, Connection[]> {
493
+ getConnectionsMap (): PeerMap<Connection[]> {
469
494
  return this.connections
470
495
  }
471
496
 
472
- async openConnection (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: AbortOptions = {}): Promise<Connection> {
473
- const { peerId, multiaddrs } = getPeerAddress(peerIdOrMultiaddr)
474
-
475
- if (peerId == null && multiaddrs.length === 0) {
476
- throw new CodeError('Can only open connections to PeerIds or Multiaddrs', codes.ERR_INVALID_PARAMETERS)
477
- }
497
+ async openConnection (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: OpenConnectionOptions = {}): Promise<Connection> {
498
+ const { peerId } = getPeerAddress(peerIdOrMultiaddr)
478
499
 
479
500
  if (peerId != null) {
480
- log('dial to', peerId)
501
+ log('dial %p', peerId)
481
502
  const existingConnections = this.getConnections(peerId)
482
503
 
483
504
  if (existingConnections.length > 0) {
@@ -487,53 +508,37 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
487
508
  }
488
509
  }
489
510
 
490
- let timeoutController: TimeoutController | undefined
491
-
492
- if (options?.signal == null) {
493
- timeoutController = new TimeoutController(this.dialTimeout)
494
- options.signal = timeoutController.signal
511
+ const connection = await this.dialQueue.dial(peerIdOrMultiaddr, {
512
+ ...options,
513
+ priority: options.priority ?? DEFAULT_DIAL_PRIORITY
514
+ })
515
+ let peerConnections = this.connections.get(connection.remotePeer)
495
516
 
496
- try {
497
- // fails on node < 15.4
498
- setMaxListeners?.(Infinity, timeoutController.signal)
499
- } catch {}
517
+ if (peerConnections == null) {
518
+ peerConnections = []
519
+ this.connections.set(connection.remotePeer, peerConnections)
500
520
  }
501
521
 
502
- try {
503
- // @ts-expect-error until https://github.com/libp2p/js-libp2p-interfaces/pull/351 is merged
504
- const connection = await this.components.dialer.dial(peerIdOrMultiaddr, options)
505
- let peerConnections = this.connections.get(connection.remotePeer.toString())
506
-
507
- if (peerConnections == null) {
508
- peerConnections = []
509
- this.connections.set(connection.remotePeer.toString(), peerConnections)
510
- }
511
-
512
- // we get notified of connections via the Upgrader emitting "connection"
513
- // events, double check we aren't already tracking this connection before
514
- // storing it
515
- let trackedConnection = false
522
+ // we get notified of connections via the Upgrader emitting "connection"
523
+ // events, double check we aren't already tracking this connection before
524
+ // storing it
525
+ let trackedConnection = false
516
526
 
517
- for (const conn of peerConnections) {
518
- if (conn.id === connection.id) {
519
- trackedConnection = true
520
- }
521
- }
522
-
523
- if (!trackedConnection) {
524
- peerConnections.push(connection)
527
+ for (const conn of peerConnections) {
528
+ if (conn.id === connection.id) {
529
+ trackedConnection = true
525
530
  }
531
+ }
526
532
 
527
- return connection
528
- } finally {
529
- if (timeoutController != null) {
530
- timeoutController.clear()
531
- }
533
+ if (!trackedConnection) {
534
+ peerConnections.push(connection)
532
535
  }
536
+
537
+ return connection
533
538
  }
534
539
 
535
540
  async closeConnections (peerId: PeerId): Promise<void> {
536
- const connections = this.connections.get(peerId.toString()) ?? []
541
+ const connections = this.connections.get(peerId) ?? []
537
542
 
538
543
  await Promise.all(
539
544
  connections.map(async connection => {
@@ -542,148 +547,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
542
547
  )
543
548
  }
544
549
 
545
- /**
546
- * Get all open connections with a peer
547
- */
548
- getAll (peerId: PeerId): Connection[] {
549
- if (!isPeerId(peerId)) {
550
- throw new CodeError('peerId must be an instance of peer-id', codes.ERR_INVALID_PARAMETERS)
551
- }
552
-
553
- const id = peerId.toString()
554
- const connections = this.connections.get(id)
555
-
556
- // Return all open connections
557
- if (connections != null) {
558
- return connections.filter(connection => connection.stat.status === STATUS.OPEN)
559
- }
560
-
561
- return []
562
- }
563
-
564
- /**
565
- * If the event loop is slow, maybe close a connection
566
- */
567
- _onLatencyMeasure (evt: CustomEvent<SummaryObject>): void {
568
- const { detail: summary } = evt
569
-
570
- this._checkMaxLimit('maxEventLoopDelay', summary.avgMs, 1)
571
- .catch(err => {
572
- log.error(err)
573
- })
574
- }
575
-
576
- /**
577
- * If the `value` of `name` has exceeded its limit, maybe close a connection
578
- */
579
- async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number, toPrune: number = 1): Promise<void> {
580
- const limit = this.opts[name]
581
-
582
- if (limit == null) {
583
- log.trace('limit %s was not set so it cannot be applied', name)
584
- return
585
- }
586
- if (limit === Infinity) {
587
- log.trace('limit %s is Infinity so it cannot be applied', name)
588
- return
589
- }
590
-
591
- log.trace('checking limit of %s. current value: %d of %d', name, value, limit)
592
- if (value > limit) {
593
- log('%s: limit exceeded: %p, %d/%d, pruning %d connection(s)', this.components.peerId, name, value, limit, toPrune)
594
- await this._pruneConnections(toPrune)
595
- }
596
- }
597
-
598
- /**
599
- * If we have more connections than our maximum, select some excess connections
600
- * to prune based on peer value
601
- */
602
- async _pruneConnections (toPrune: number): Promise<void> {
603
- const connections = this.getConnections()
604
- const peerValues = new PeerMap<number>()
605
-
606
- // work out peer values
607
- for (const connection of connections) {
608
- const remotePeer = connection.remotePeer
609
-
610
- if (peerValues.has(remotePeer)) {
611
- continue
612
- }
613
-
614
- const tags = await this.components.peerStore.getTags(remotePeer)
615
-
616
- // sum all tag values
617
- peerValues.set(remotePeer, tags.reduce((acc, curr) => {
618
- return acc + curr.value
619
- }, 0))
620
- }
621
-
622
- // sort by value, lowest to highest
623
- const sortedConnections = connections.sort((a, b) => {
624
- const peerAValue = peerValues.get(a.remotePeer) ?? 0
625
- const peerBValue = peerValues.get(b.remotePeer) ?? 0
626
-
627
- if (peerAValue > peerBValue) {
628
- return 1
629
- }
630
-
631
- if (peerAValue < peerBValue) {
632
- return -1
633
- }
634
-
635
- // if the peers have an equal tag value then we want to close short-lived connections first
636
- const connectionALifespan = a.stat.timeline.open
637
- const connectionBLifespan = b.stat.timeline.open
638
-
639
- if (connectionALifespan < connectionBLifespan) {
640
- return 1
641
- }
642
-
643
- if (connectionALifespan > connectionBLifespan) {
644
- return -1
645
- }
646
-
647
- return 0
648
- })
649
-
650
- // close some connections
651
- const toClose = []
652
-
653
- for (const connection of sortedConnections) {
654
- log('too many connections open - closing a connection to %p', connection.remotePeer)
655
- // check allow list
656
- const connectionInAllowList = this.allow.some((ma) => {
657
- return connection.remoteAddr.toString().startsWith(ma.toString())
658
- })
659
-
660
- // Connections in the allow list should be excluded from pruning
661
- if (!connectionInAllowList) {
662
- toClose.push(connection)
663
- }
664
-
665
- if (toClose.length === toPrune) {
666
- break
667
- }
668
- }
669
-
670
- // close connections
671
- await Promise.all(
672
- toClose.map(async connection => {
673
- try {
674
- await connection.close()
675
- } catch (err) {
676
- log.error(err)
677
- }
678
-
679
- // TODO: should not need to invoke this manually
680
- this.onDisconnect(new CustomEvent<Connection>('connectionEnd', {
681
- detail: connection
682
- }))
683
- })
684
- )
685
- }
686
-
687
550
  async acceptIncomingConnection (maConn: MultiaddrConnection): Promise<boolean> {
688
551
  // check deny list
689
552
  const denyConnection = this.deny.some(ma => {
@@ -707,7 +570,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
707
570
  }
708
571
 
709
572
  // check pending connections
710
- if (this.incomingPendingConnections === this.opts.maxIncomingPendingConnections) {
573
+ if (this.incomingPendingConnections === this.maxIncomingPendingConnections) {
711
574
  log('connection from %s refused - incomingPendingConnections exceeded by peer %s', maConn.remoteAddr)
712
575
  return false
713
576
  }
@@ -723,7 +586,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
723
586
  }
724
587
  }
725
588
 
726
- if (this.getConnections().length < this.opts.maxConnections) {
589
+ if (this.getConnections().length < this.maxConnections) {
727
590
  this.incomingPendingConnections++
728
591
 
729
592
  return true
@@ -736,4 +599,8 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
736
599
  afterUpgradeInbound (): void {
737
600
  this.incomingPendingConnections--
738
601
  }
602
+
603
+ getDialQueue (): PendingDial[] {
604
+ return this.dialQueue.pendingDials
605
+ }
739
606
  }