libp2p 0.37.2 → 0.37.3-2836acc

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 (151) 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 +11 -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 +5 -5
  34. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  35. package/dist/src/connection-manager/dialer/index.js +1 -1
  36. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  37. package/dist/src/connection-manager/index.d.ts +16 -19
  38. package/dist/src/connection-manager/index.d.ts.map +1 -1
  39. package/dist/src/connection-manager/index.js +95 -59
  40. package/dist/src/connection-manager/index.js.map +1 -1
  41. package/dist/src/content-routing/index.d.ts +4 -4
  42. package/dist/src/content-routing/index.d.ts.map +1 -1
  43. package/dist/src/content-routing/utils.d.ts +2 -2
  44. package/dist/src/content-routing/utils.d.ts.map +1 -1
  45. package/dist/src/dht/dht-content-routing.d.ts +3 -3
  46. package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
  47. package/dist/src/dht/dht-peer-routing.d.ts +5 -5
  48. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  49. package/dist/src/dht/dummy-dht.d.ts +3 -3
  50. package/dist/src/dht/dummy-dht.d.ts.map +1 -1
  51. package/dist/src/dht/dummy-dht.js +1 -1
  52. package/dist/src/dht/dummy-dht.js.map +1 -1
  53. package/dist/src/errors.d.ts +4 -1
  54. package/dist/src/errors.d.ts.map +1 -1
  55. package/dist/src/errors.js +3 -0
  56. package/dist/src/errors.js.map +1 -1
  57. package/dist/src/fetch/index.d.ts +7 -4
  58. package/dist/src/fetch/index.d.ts.map +1 -1
  59. package/dist/src/fetch/index.js +61 -39
  60. package/dist/src/fetch/index.js.map +1 -1
  61. package/dist/src/get-peer.d.ts +2 -2
  62. package/dist/src/get-peer.d.ts.map +1 -1
  63. package/dist/src/get-peer.js +1 -1
  64. package/dist/src/get-peer.js.map +1 -1
  65. package/dist/src/identify/index.d.ts +7 -7
  66. package/dist/src/identify/index.d.ts.map +1 -1
  67. package/dist/src/identify/index.js +15 -23
  68. package/dist/src/identify/index.js.map +1 -1
  69. package/dist/src/index.d.ts +17 -17
  70. package/dist/src/index.d.ts.map +1 -1
  71. package/dist/src/insecure/index.d.ts +2 -2
  72. package/dist/src/insecure/index.d.ts.map +1 -1
  73. package/dist/src/insecure/index.js +1 -1
  74. package/dist/src/insecure/index.js.map +1 -1
  75. package/dist/src/keychain/index.d.ts +2 -2
  76. package/dist/src/keychain/index.d.ts.map +1 -1
  77. package/dist/src/libp2p.d.ts +14 -14
  78. package/dist/src/libp2p.d.ts.map +1 -1
  79. package/dist/src/libp2p.js +14 -15
  80. package/dist/src/libp2p.js.map +1 -1
  81. package/dist/src/metrics/index.d.ts +2 -2
  82. package/dist/src/metrics/index.d.ts.map +1 -1
  83. package/dist/src/metrics/moving-average.d.ts +1 -1
  84. package/dist/src/metrics/moving-average.d.ts.map +1 -1
  85. package/dist/src/metrics/stats.d.ts +1 -1
  86. package/dist/src/metrics/stats.d.ts.map +1 -1
  87. package/dist/src/nat-manager.d.ts +1 -1
  88. package/dist/src/nat-manager.d.ts.map +1 -1
  89. package/dist/src/peer-record-updater.d.ts +1 -1
  90. package/dist/src/peer-record-updater.d.ts.map +1 -1
  91. package/dist/src/peer-routing.d.ts +4 -4
  92. package/dist/src/peer-routing.d.ts.map +1 -1
  93. package/dist/src/ping/index.d.ts +6 -3
  94. package/dist/src/ping/index.d.ts.map +1 -1
  95. package/dist/src/ping/index.js +6 -2
  96. package/dist/src/ping/index.js.map +1 -1
  97. package/dist/src/pnet/index.d.ts +1 -2
  98. package/dist/src/pnet/index.d.ts.map +1 -1
  99. package/dist/src/pnet/index.js.map +1 -1
  100. package/dist/src/pubsub/dummy-pubsub.d.ts +2 -2
  101. package/dist/src/pubsub/dummy-pubsub.d.ts.map +1 -1
  102. package/dist/src/registrar.d.ts +10 -8
  103. package/dist/src/registrar.d.ts.map +1 -1
  104. package/dist/src/registrar.js +63 -44
  105. package/dist/src/registrar.js.map +1 -1
  106. package/dist/src/transport-manager.d.ts +3 -3
  107. package/dist/src/transport-manager.d.ts.map +1 -1
  108. package/dist/src/upgrader.d.ts +6 -6
  109. package/dist/src/upgrader.d.ts.map +1 -1
  110. package/dist/src/upgrader.js +69 -8
  111. package/dist/src/upgrader.js.map +1 -1
  112. package/package.json +53 -30
  113. package/src/address-manager/index.ts +2 -2
  114. package/src/circuit/auto-relay.ts +4 -4
  115. package/src/circuit/circuit/hop.ts +4 -4
  116. package/src/circuit/circuit/stop.ts +2 -2
  117. package/src/circuit/circuit/stream-handler.ts +1 -1
  118. package/src/circuit/index.ts +2 -2
  119. package/src/circuit/listener.ts +3 -3
  120. package/src/circuit/transport.ts +5 -5
  121. package/src/config.ts +11 -3
  122. package/src/connection-manager/auto-dialler.ts +1 -1
  123. package/src/connection-manager/dialer/auto-dialer.ts +2 -2
  124. package/src/connection-manager/dialer/dial-request.ts +1 -1
  125. package/src/connection-manager/dialer/index.ts +5 -5
  126. package/src/connection-manager/index.ts +126 -71
  127. package/src/content-routing/index.ts +2 -2
  128. package/src/content-routing/utils.ts +2 -2
  129. package/src/dht/dht-content-routing.ts +2 -2
  130. package/src/dht/dht-peer-routing.ts +3 -3
  131. package/src/dht/dummy-dht.ts +3 -3
  132. package/src/errors.ts +4 -1
  133. package/src/fetch/index.ts +91 -50
  134. package/src/get-peer.ts +3 -3
  135. package/src/identify/index.ts +24 -29
  136. package/src/index.ts +17 -17
  137. package/src/insecure/index.ts +3 -3
  138. package/src/keychain/index.ts +2 -2
  139. package/src/libp2p.ts +31 -27
  140. package/src/metrics/index.ts +2 -2
  141. package/src/metrics/moving-average.ts +1 -1
  142. package/src/metrics/stats.ts +1 -1
  143. package/src/nat-manager.ts +1 -1
  144. package/src/peer-record-updater.ts +1 -1
  145. package/src/peer-routing.ts +4 -4
  146. package/src/ping/index.ts +12 -5
  147. package/src/pnet/index.ts +1 -2
  148. package/src/pubsub/dummy-pubsub.ts +2 -2
  149. package/src/registrar.ts +79 -55
  150. package/src/transport-manager.ts +3 -3
  151. package/src/upgrader.ts +94 -16
@@ -5,7 +5,7 @@ import { setMaxListeners } from 'events'
5
5
  import { codes } from '../../errors.js'
6
6
  import { logger } from '@libp2p/logger'
7
7
  import type { Multiaddr } from '@multiformats/multiaddr'
8
- import type { Connection } from '@libp2p/interfaces/connection'
8
+ import type { Connection } from '@libp2p/interface-connection'
9
9
  import type { AbortOptions } from '@libp2p/interfaces'
10
10
  import type { Dialer } from './index.js'
11
11
 
@@ -18,16 +18,16 @@ import {
18
18
  MAX_PER_PEER_DIALS,
19
19
  MAX_ADDRS_TO_DIAL
20
20
  } from '../../constants.js'
21
- import type { Connection } from '@libp2p/interfaces/connection'
21
+ import type { Connection } from '@libp2p/interface-connection'
22
22
  import type { AbortOptions } from '@libp2p/interfaces'
23
23
  import type { Startable } from '@libp2p/interfaces/startable'
24
- import type { PeerId } from '@libp2p/interfaces/peer-id'
24
+ import type { PeerId } from '@libp2p/interface-peer-id'
25
25
  import { getPeer } from '../../get-peer.js'
26
26
  import sort from 'it-sort'
27
- import { Components, Initializable } from '@libp2p/interfaces/components'
27
+ import { Components, Initializable } from '@libp2p/components'
28
28
  import map from 'it-map'
29
- import type { AddressSorter } from '@libp2p/interfaces/peer-store'
30
- import type { ComponentMetricsTracker } from '@libp2p/interfaces/metrics'
29
+ import type { AddressSorter } from '@libp2p/interface-peer-store'
30
+ import type { ComponentMetricsTracker } from '@libp2p/interface-metrics'
31
31
 
32
32
  const log = logger('libp2p:dialer')
33
33
 
@@ -9,15 +9,18 @@ import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
9
9
  import type { Startable } from '@libp2p/interfaces/startable'
10
10
  import { trackedMap } from '@libp2p/tracked-map'
11
11
  import { codes } from '../errors.js'
12
- import { isPeerId, PeerId } from '@libp2p/interfaces/peer-id'
12
+ import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
13
13
  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'
14
+ import type { Connection } from '@libp2p/interface-connection'
15
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
16
+ import { Components, Initializable } from '@libp2p/components'
17
+ import * as STATUS from '@libp2p/interface-connection/status'
18
18
  import { Dialer } from './dialer/index.js'
19
- import type { AddressSorter } from '@libp2p/interfaces/peer-store'
19
+ import type { AddressSorter } from '@libp2p/interface-peer-store'
20
20
  import type { Resolver } from '@multiformats/multiaddr'
21
+ import { PeerMap } from '@libp2p/peer-collections'
22
+ import { TimeoutController } from 'timeout-abort-controller'
23
+ import { KEEP_ALIVE } from '@libp2p/interface-peer-store/tags'
21
24
 
22
25
  const log = logger('libp2p:connection-manager')
23
26
 
@@ -30,13 +33,12 @@ const defaultOptions: Partial<ConnectionManagerInit> = {
30
33
  maxEventLoopDelay: Infinity,
31
34
  pollInterval: 2000,
32
35
  autoDialInterval: 10000,
33
- movingAverageInterval: 60000,
34
- defaultPeerValue: 1
36
+ movingAverageInterval: 60000
35
37
  }
36
38
 
37
39
  const METRICS_COMPONENT = 'connection-manager'
38
40
  const METRICS_PEER_CONNECTIONS = 'peer-connections'
39
- const METRICS_PEER_VALUES = 'peer-values'
41
+ const STARTUP_RECONNECT_TIMEOUT = 60000
40
42
 
41
43
  export interface ConnectionManagerInit {
42
44
  /**
@@ -79,11 +81,6 @@ export interface ConnectionManagerInit {
79
81
  */
80
82
  movingAverageInterval?: number
81
83
 
82
- /**
83
- * The value of the peer
84
- */
85
- defaultPeerValue?: number
86
-
87
84
  /**
88
85
  * If true, try to connect to all discovered peers up to the connection manager limit
89
86
  */
@@ -124,6 +121,12 @@ export interface ConnectionManagerInit {
124
121
  * Multiaddr resolvers to use when dialing
125
122
  */
126
123
  resolvers?: Record<string, Resolver>
124
+
125
+ /**
126
+ * On startup we try to dial any peer that has previously been
127
+ * tagged with KEEP_ALIVE up to this timeout in ms. (default: 60000)
128
+ */
129
+ startupReconnectTimeout?: number
127
130
  }
128
131
 
129
132
  export interface ConnectionManagerEvents {
@@ -138,11 +141,12 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
138
141
  public readonly dialer: Dialer
139
142
  private components = new Components()
140
143
  private readonly opts: Required<ConnectionManagerInit>
141
- private readonly peerValues: Map<string, number>
142
144
  private readonly connections: Map<string, Connection[]>
143
145
  private started: boolean
144
146
  private timer?: ReturnType<retimer>
145
147
  private readonly latencyMonitor: LatencyMonitor
148
+ private readonly startupReconnectTimeout: number
149
+ private connectOnStartupController?: TimeoutController
146
150
 
147
151
  constructor (init: ConnectionManagerInit) {
148
152
  super()
@@ -155,17 +159,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
155
159
 
156
160
  log('options: %o', this.opts)
157
161
 
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
162
  /**
170
163
  * Map of connections per peer
171
164
  */
@@ -192,6 +185,8 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
192
185
 
193
186
  this.onConnect = this.onConnect.bind(this)
194
187
  this.onDisconnect = this.onDisconnect.bind(this)
188
+
189
+ this.startupReconnectTimeout = init.startupReconnectTimeout ?? STARTUP_RECONNECT_TIMEOUT
195
190
  }
196
191
 
197
192
  init (components: Components): void {
@@ -226,9 +221,43 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
226
221
  async afterStart () {
227
222
  this.components.getUpgrader().addEventListener('connection', this.onConnect)
228
223
  this.components.getUpgrader().addEventListener('connectionEnd', this.onDisconnect)
224
+
225
+ // re-connect to any peers with the KEEP_ALIVE tag
226
+ void Promise.resolve()
227
+ .then(async () => {
228
+ const keepAlivePeers: PeerId[] = []
229
+
230
+ for (const peer of await this.components.getPeerStore().all()) {
231
+ const tags = await this.components.getPeerStore().getTags(peer.id)
232
+ const hasKeepAlive = tags.filter(tag => tag.name === KEEP_ALIVE).length > 0
233
+
234
+ if (hasKeepAlive) {
235
+ keepAlivePeers.push(peer.id)
236
+ }
237
+ }
238
+
239
+ this.connectOnStartupController?.clear()
240
+ this.connectOnStartupController = new TimeoutController(this.startupReconnectTimeout)
241
+
242
+ await Promise.all(
243
+ keepAlivePeers.map(async peer => {
244
+ await this.openConnection(peer, {
245
+ signal: this.connectOnStartupController?.signal
246
+ })
247
+ .catch(err => {
248
+ log.error(err)
249
+ })
250
+ })
251
+ )
252
+ })
253
+ .finally(() => {
254
+ this.connectOnStartupController?.clear()
255
+ })
229
256
  }
230
257
 
231
258
  async beforeStop () {
259
+ // if we are still dialing KEEP_ALIVE peers, abort those dials
260
+ this.connectOnStartupController?.abort()
232
261
  this.components.getUpgrader().removeEventListener('connection', this.onConnect)
233
262
  this.components.getUpgrader().removeEventListener('connectionEnd', this.onDisconnect)
234
263
  }
@@ -271,18 +300,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
271
300
  this.connections.clear()
272
301
  }
273
302
 
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
303
  /**
287
304
  * Checks the libp2p metrics to determine if any values have exceeded
288
305
  * the configured maximums.
@@ -340,11 +357,10 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
340
357
  await this.components.getPeerStore().keyBook.set(peerId, peerId.publicKey)
341
358
  }
342
359
 
343
- if (!this.peerValues.has(peerIdStr)) {
344
- this.peerValues.set(peerIdStr, this.opts.defaultPeerValue)
345
- }
360
+ const numConnections = this.getConnections().length
361
+ const toPrune = numConnections - this.opts.maxConnections
346
362
 
347
- await this._checkMaxLimit('maxConnections', this.getConnections().length)
363
+ await this._checkMaxLimit('maxConnections', numConnections, toPrune)
348
364
  this.dispatchEvent(new CustomEvent<Connection>('peer:connect', { detail: connection }))
349
365
  }
350
366
 
@@ -367,7 +383,6 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
367
383
  this.connections.set(peerId, storedConn)
368
384
  } else if (storedConn != null) {
369
385
  this.connections.delete(peerId)
370
- this.peerValues.delete(connection.remotePeer.toString())
371
386
  this.dispatchEvent(new CustomEvent<Connection>('peer:disconnect', { detail: connection }))
372
387
 
373
388
  this.components.getMetrics()?.onPeerDisconnected(connection.remotePeer)
@@ -459,7 +474,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
459
474
  _onLatencyMeasure (evt: CustomEvent<SummaryObject>) {
460
475
  const { detail: summary } = evt
461
476
 
462
- this._checkMaxLimit('maxEventLoopDelay', summary.avgMs)
477
+ this._checkMaxLimit('maxEventLoopDelay', summary.avgMs, 1)
463
478
  .catch(err => {
464
479
  log.error(err)
465
480
  })
@@ -468,46 +483,86 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
468
483
  /**
469
484
  * If the `value` of `name` has exceeded its limit, maybe close a connection
470
485
  */
471
- async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number) {
486
+ async _checkMaxLimit (name: keyof ConnectionManagerInit, value: number, toPrune: number = 1) {
472
487
  const limit = this.opts[name]
473
488
  log.trace('checking limit of %s. current value: %d of %d', name, value, limit)
474
489
  if (value > limit) {
475
- log('%s: limit exceeded: %p, %d', this.components.getPeerId(), name, value)
476
- await this._maybeDisconnectOne()
490
+ log('%s: limit exceeded: %p, %d/%d, pruning %d connection(s)', this.components.getPeerId(), name, value, limit, toPrune)
491
+ await this._maybePruneConnections(toPrune)
477
492
  }
478
493
  }
479
494
 
480
495
  /**
481
- * If we have more connections than our maximum, close a connection
482
- * to the lowest valued peer.
496
+ * If we have more connections than our maximum, select some excess connections
497
+ * to prune based on peer value
483
498
  */
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])))
499
+ async _maybePruneConnections (toPrune: number) {
500
+ const connections = this.getConnections()
487
501
 
488
- log('%p: sorted peer values: %j', this.components.getPeerId(), peerValues)
489
- const disconnectPeer = peerValues[0]
502
+ if (connections.length <= this.opts.minConnections || toPrune < 1) {
503
+ return
504
+ }
490
505
 
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)
506
+ const peerValues = new PeerMap<number>()
495
507
 
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
- })
508
+ // work out peer values
509
+ for (const connection of connections) {
510
+ const remotePeer = connection.remotePeer
502
511
 
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
- }
512
+ if (peerValues.has(remotePeer)) {
513
+ continue
510
514
  }
515
+
516
+ const tags = await this.components.getPeerStore().getTags(remotePeer)
517
+
518
+ // sum all tag values
519
+ peerValues.set(remotePeer, tags.reduce((acc, curr) => {
520
+ return acc + curr.value
521
+ }, 0))
511
522
  }
523
+
524
+ // sort by value, lowest to highest
525
+ const sortedConnections = connections.sort((a, b) => {
526
+ const peerAValue = peerValues.get(a.remotePeer) ?? 0
527
+ const peerBValue = peerValues.get(b.remotePeer) ?? 0
528
+
529
+ if (peerAValue > peerBValue) {
530
+ return 1
531
+ }
532
+
533
+ if (peerAValue < peerBValue) {
534
+ return -1
535
+ }
536
+
537
+ return 0
538
+ })
539
+
540
+ // close some connections
541
+ const toClose = []
542
+
543
+ for (const connection of sortedConnections) {
544
+ log('too many connections open - closing a connection to %p', connection.remotePeer)
545
+ toClose.push(connection)
546
+
547
+ if (toClose.length === toPrune) {
548
+ break
549
+ }
550
+ }
551
+
552
+ // close connections
553
+ await Promise.all(
554
+ toClose.map(async connection => {
555
+ try {
556
+ await connection.close()
557
+ } catch (err) {
558
+ log.error(err)
559
+ }
560
+
561
+ // TODO: should not need to invoke this manually
562
+ this.onDisconnect(new CustomEvent<Connection>('connectionEnd', {
563
+ detail: connection
564
+ }))
565
+ })
566
+ )
512
567
  }
513
568
  }
@@ -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
  }
@@ -3,21 +3,24 @@ import errCode from 'err-code'
3
3
  import { codes } from '../errors.js'
4
4
  import * as lp from 'it-length-prefixed'
5
5
  import { FetchRequest, FetchResponse } from './pb/proto.js'
6
- import { handshake } from 'it-handshake'
7
6
  import { PROTOCOL_NAME, PROTOCOL_VERSION } from './constants.js'
8
- import type { PeerId } from '@libp2p/interfaces/peer-id'
7
+ import type { PeerId } from '@libp2p/interface-peer-id'
9
8
  import type { Startable } from '@libp2p/interfaces/startable'
10
- import type { Stream } from '@libp2p/interfaces/connection'
11
- import type { IncomingStreamData } from '@libp2p/interfaces/registrar'
12
- import type { Components } from '@libp2p/interfaces/components'
9
+ import type { Stream } from '@libp2p/interface-connection'
10
+ import type { IncomingStreamData } from '@libp2p/interface-registrar'
11
+ import type { Components } from '@libp2p/components'
13
12
  import type { AbortOptions } from '@libp2p/interfaces'
14
13
  import type { Duplex } from 'it-stream-types'
15
14
  import { abortableDuplex } from 'abortable-iterator'
15
+ import { pipe } from 'it-pipe'
16
+ import first from 'it-first'
16
17
 
17
18
  const log = logger('libp2p:fetch')
18
19
 
19
20
  export interface FetchServiceInit {
20
21
  protocolPrefix: string
22
+ maxInboundStreams: number
23
+ maxOutboundStreams: number
21
24
  }
22
25
 
23
26
  export interface HandleMessageOptions {
@@ -40,6 +43,7 @@ export class FetchService implements Startable {
40
43
  private readonly components: Components
41
44
  private readonly lookupFunctions: Map<string, LookupFunction>
42
45
  private started: boolean
46
+ private readonly init: FetchServiceInit
43
47
 
44
48
  constructor (components: Components, init: FetchServiceInit) {
45
49
  this.started = false
@@ -47,13 +51,21 @@ export class FetchService implements Startable {
47
51
  this.protocol = `/${init.protocolPrefix ?? 'libp2p'}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`
48
52
  this.lookupFunctions = new Map() // Maps key prefix to value lookup function
49
53
  this.handleMessage = this.handleMessage.bind(this)
54
+ this.init = init
50
55
  }
51
56
 
52
57
  async start () {
53
58
  await this.components.getRegistrar().handle(this.protocol, (data) => {
54
- void this.handleMessage(data).catch(err => {
55
- log.error(err)
56
- })
59
+ void this.handleMessage(data)
60
+ .catch(err => {
61
+ log.error(err)
62
+ })
63
+ .finally(() => {
64
+ data.stream.close()
65
+ })
66
+ }, {
67
+ maxInboundStreams: this.init.maxInboundStreams,
68
+ maxOutboundStreams: this.init.maxOutboundStreams
57
69
  })
58
70
  this.started = true
59
71
  }
@@ -74,7 +86,7 @@ export class FetchService implements Startable {
74
86
  log('dialing %s to %p', this.protocol, peer)
75
87
 
76
88
  const connection = await this.components.getConnectionManager().openConnection(peer, options)
77
- const { stream } = await connection.newStream([this.protocol], options)
89
+ const stream = await connection.newStream([this.protocol], options)
78
90
  let source: Duplex<Uint8Array> = stream
79
91
 
80
92
  // make stream abortable if AbortSignal passed
@@ -82,28 +94,42 @@ export class FetchService implements Startable {
82
94
  source = abortableDuplex(stream, options.signal)
83
95
  }
84
96
 
85
- const shake = handshake(source)
86
-
87
- // send message
88
- shake.write(lp.encode.single(FetchRequest.encode({ identifier: key })).slice())
89
-
90
- // read response
91
- // @ts-expect-error fromReader returns a Source which has no .next method
92
- const response = FetchResponse.decode((await lp.decode.fromReader(shake.reader).next()).value.slice())
93
- switch (response.status) {
94
- case (FetchResponse.StatusCode.OK): {
95
- return response.data
96
- }
97
- case (FetchResponse.StatusCode.NOT_FOUND): {
98
- return null
99
- }
100
- case (FetchResponse.StatusCode.ERROR): {
101
- const errmsg = (new TextDecoder()).decode(response.data)
102
- throw errCode(new Error('Error in fetch protocol response: ' + errmsg), codes.ERR_INVALID_PARAMETERS)
103
- }
104
- default: {
105
- throw errCode(new Error('Unknown response status'), codes.ERR_INVALID_MESSAGE)
106
- }
97
+ try {
98
+ const result = await pipe(
99
+ [FetchRequest.encode({ identifier: key })],
100
+ lp.encode(),
101
+ source,
102
+ lp.decode(),
103
+ async function (source) {
104
+ const buf = await first(source)
105
+
106
+ if (buf == null) {
107
+ throw errCode(new Error('No data received'), codes.ERR_INVALID_MESSAGE)
108
+ }
109
+
110
+ const response = FetchResponse.decode(buf)
111
+
112
+ switch (response.status) {
113
+ case (FetchResponse.StatusCode.OK): {
114
+ return response.data
115
+ }
116
+ case (FetchResponse.StatusCode.NOT_FOUND): {
117
+ return null
118
+ }
119
+ case (FetchResponse.StatusCode.ERROR): {
120
+ const errmsg = (new TextDecoder()).decode(response.data)
121
+ throw errCode(new Error('Error in fetch protocol response: ' + errmsg), codes.ERR_INVALID_PARAMETERS)
122
+ }
123
+ default: {
124
+ throw errCode(new Error('Unknown response status'), codes.ERR_INVALID_MESSAGE)
125
+ }
126
+ }
127
+ }
128
+ )
129
+
130
+ return result ?? null
131
+ } finally {
132
+ stream.close()
107
133
  }
108
134
  }
109
135
 
@@ -114,25 +140,40 @@ export class FetchService implements Startable {
114
140
  */
115
141
  async handleMessage (data: IncomingStreamData) {
116
142
  const { stream } = data
117
- const shake = handshake(stream)
118
- // @ts-expect-error fromReader returns a Source which has no .next method
119
- const request = FetchRequest.decode((await lp.decode.fromReader(shake.reader).next()).value.slice())
120
-
121
- let response: FetchResponse
122
- const lookup = this._getLookupFunction(request.identifier)
123
- if (lookup != null) {
124
- const data = await lookup(request.identifier)
125
- if (data != null) {
126
- response = { status: FetchResponse.StatusCode.OK, data }
127
- } else {
128
- response = { status: FetchResponse.StatusCode.NOT_FOUND, data: new Uint8Array(0) }
129
- }
130
- } else {
131
- const errmsg = (new TextEncoder()).encode('No lookup function registered for key: ' + request.identifier)
132
- response = { status: FetchResponse.StatusCode.ERROR, data: errmsg }
133
- }
134
-
135
- shake.write(lp.encode.single(FetchResponse.encode(response)).slice())
143
+ const self = this
144
+
145
+ await pipe(
146
+ stream,
147
+ lp.decode(),
148
+ async function * (source) {
149
+ const buf = await first(source)
150
+
151
+ if (buf == null) {
152
+ throw errCode(new Error('No data received'), codes.ERR_INVALID_MESSAGE)
153
+ }
154
+
155
+ // for await (const buf of source) {
156
+ const request = FetchRequest.decode(buf)
157
+
158
+ let response: FetchResponse
159
+ const lookup = self._getLookupFunction(request.identifier)
160
+ if (lookup != null) {
161
+ const data = await lookup(request.identifier)
162
+ if (data != null) {
163
+ response = { status: FetchResponse.StatusCode.OK, data }
164
+ } else {
165
+ response = { status: FetchResponse.StatusCode.NOT_FOUND, data: new Uint8Array(0) }
166
+ }
167
+ } else {
168
+ const errmsg = (new TextEncoder()).encode('No lookup function registered for key: ' + request.identifier)
169
+ response = { status: FetchResponse.StatusCode.ERROR, data: errmsg }
170
+ }
171
+
172
+ yield FetchResponse.encode(response)
173
+ },
174
+ lp.encode(),
175
+ stream
176
+ )
136
177
  }
137
178
 
138
179
  /**
package/src/get-peer.ts CHANGED
@@ -2,9 +2,9 @@ import { peerIdFromString } from '@libp2p/peer-id'
2
2
  import { Multiaddr } from '@multiformats/multiaddr'
3
3
  import errCode from 'err-code'
4
4
  import { codes } from './errors.js'
5
- import { isPeerId } from '@libp2p/interfaces/peer-id'
6
- import type { PeerId } from '@libp2p/interfaces/peer-id'
7
- import type { PeerInfo } from '@libp2p/interfaces/peer-info'
5
+ import { isPeerId } from '@libp2p/interface-peer-id'
6
+ import type { PeerId } from '@libp2p/interface-peer-id'
7
+ import type { PeerInfo } from '@libp2p/interface-peer-info'
8
8
 
9
9
  function peerIdFromMultiaddr (ma: Multiaddr) {
10
10
  const idStr = ma.getPeerId()