libp2p 0.37.3-b1b2b21 → 0.37.3-ba56c64

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 (75) hide show
  1. package/dist/src/circuit/circuit/hop.d.ts +3 -2
  2. package/dist/src/circuit/circuit/hop.d.ts.map +1 -1
  3. package/dist/src/circuit/circuit/hop.js +8 -4
  4. package/dist/src/circuit/circuit/hop.js.map +1 -1
  5. package/dist/src/circuit/circuit/stop.d.ts +2 -1
  6. package/dist/src/circuit/circuit/stop.d.ts.map +1 -1
  7. package/dist/src/circuit/circuit/stop.js +4 -2
  8. package/dist/src/circuit/circuit/stop.js.map +1 -1
  9. package/dist/src/circuit/index.d.ts +2 -10
  10. package/dist/src/circuit/index.d.ts.map +1 -1
  11. package/dist/src/circuit/index.js.map +1 -1
  12. package/dist/src/circuit/transport.d.ts +3 -0
  13. package/dist/src/circuit/transport.d.ts.map +1 -1
  14. package/dist/src/circuit/transport.js +73 -57
  15. package/dist/src/circuit/transport.js.map +1 -1
  16. package/dist/src/config.d.ts.map +1 -1
  17. package/dist/src/config.js +19 -4
  18. package/dist/src/config.js.map +1 -1
  19. package/dist/src/connection-manager/dialer/index.d.ts +4 -4
  20. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  21. package/dist/src/connection-manager/dialer/index.js +10 -10
  22. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  23. package/dist/src/connection-manager/index.d.ts +15 -1
  24. package/dist/src/connection-manager/index.d.ts.map +1 -1
  25. package/dist/src/connection-manager/index.js +135 -23
  26. package/dist/src/connection-manager/index.js.map +1 -1
  27. package/dist/src/constants.d.ts +4 -0
  28. package/dist/src/constants.d.ts.map +1 -1
  29. package/dist/src/constants.js +4 -0
  30. package/dist/src/constants.js.map +1 -1
  31. package/dist/src/fetch/index.d.ts +4 -0
  32. package/dist/src/fetch/index.d.ts.map +1 -1
  33. package/dist/src/fetch/index.js +19 -6
  34. package/dist/src/fetch/index.js.map +1 -1
  35. package/dist/src/identify/index.d.ts +1 -1
  36. package/dist/src/identify/index.d.ts.map +1 -1
  37. package/dist/src/identify/index.js +13 -11
  38. package/dist/src/identify/index.js.map +1 -1
  39. package/dist/src/index.d.ts +1 -0
  40. package/dist/src/index.d.ts.map +1 -1
  41. package/dist/src/index.js.map +1 -1
  42. package/dist/src/libp2p.d.ts.map +1 -1
  43. package/dist/src/libp2p.js +3 -13
  44. package/dist/src/libp2p.js.map +1 -1
  45. package/dist/src/metrics/index.d.ts +2 -2
  46. package/dist/src/metrics/index.d.ts.map +1 -1
  47. package/dist/src/metrics/index.js +6 -2
  48. package/dist/src/metrics/index.js.map +1 -1
  49. package/dist/src/ping/index.d.ts +4 -0
  50. package/dist/src/ping/index.d.ts.map +1 -1
  51. package/dist/src/ping/index.js +20 -7
  52. package/dist/src/ping/index.js.map +1 -1
  53. package/dist/src/upgrader.d.ts +6 -0
  54. package/dist/src/upgrader.d.ts.map +1 -1
  55. package/dist/src/upgrader.js +83 -59
  56. package/dist/src/upgrader.js.map +1 -1
  57. package/dist/src/version.d.ts.map +1 -1
  58. package/dist/src/version.js.map +1 -1
  59. package/package.json +13 -12
  60. package/src/circuit/circuit/hop.ts +13 -6
  61. package/src/circuit/circuit/stop.ts +7 -3
  62. package/src/circuit/index.ts +2 -11
  63. package/src/circuit/transport.ts +79 -58
  64. package/src/config.ts +19 -4
  65. package/src/connection-manager/dialer/index.ts +10 -10
  66. package/src/connection-manager/index.ts +178 -26
  67. package/src/constants.ts +5 -0
  68. package/src/fetch/index.ts +28 -8
  69. package/src/identify/index.ts +16 -14
  70. package/src/index.ts +1 -0
  71. package/src/libp2p.ts +3 -14
  72. package/src/metrics/index.ts +8 -4
  73. package/src/ping/index.ts +30 -10
  74. package/src/upgrader.ts +95 -61
  75. package/src/version.ts +1 -2
package/src/config.ts CHANGED
@@ -10,6 +10,7 @@ import type { Libp2pInit } from './index.js'
10
10
  import { codes, messages } from './errors.js'
11
11
  import errCode from 'err-code'
12
12
  import type { RecursivePartial } from '@libp2p/interfaces'
13
+ import { isNode, isBrowser, isWebWorker, isElectronMain, isElectronRenderer, isReactNative } from 'wherearewe'
13
14
 
14
15
  const DefaultConfig: Partial<Libp2pInit> = {
15
16
  addresses: {
@@ -26,6 +27,7 @@ const DefaultConfig: Partial<Libp2pInit> = {
26
27
  maxParallelDials: Constants.MAX_PARALLEL_DIALS,
27
28
  maxDialsPerPeer: Constants.MAX_PER_PEER_DIALS,
28
29
  dialTimeout: Constants.DIAL_TIMEOUT,
30
+ inboundUpgradeTimeout: Constants.INBOUND_UPGRADE_TIMEOUT,
29
31
  resolvers: {
30
32
  dnsaddr: dnsaddrResolver
31
33
  },
@@ -67,7 +69,8 @@ const DefaultConfig: Partial<Libp2pInit> = {
67
69
  },
68
70
  hop: {
69
71
  enabled: false,
70
- active: false
72
+ active: false,
73
+ timeout: 30000
71
74
  },
72
75
  autoRelay: {
73
76
  enabled: false,
@@ -79,7 +82,8 @@ const DefaultConfig: Partial<Libp2pInit> = {
79
82
  host: {
80
83
  agentVersion: AGENT_VERSION
81
84
  },
82
- timeout: 30000,
85
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48
86
+ timeout: 60000,
83
87
  maxInboundStreams: 1,
84
88
  maxOutboundStreams: 1,
85
89
  maxPushIncomingStreams: 1,
@@ -88,12 +92,14 @@ const DefaultConfig: Partial<Libp2pInit> = {
88
92
  ping: {
89
93
  protocolPrefix: 'ipfs',
90
94
  maxInboundStreams: 1,
91
- maxOutboundStreams: 1
95
+ maxOutboundStreams: 1,
96
+ timeout: 10000
92
97
  },
93
98
  fetch: {
94
99
  protocolPrefix: 'libp2p',
95
100
  maxInboundStreams: 1,
96
- maxOutboundStreams: 1
101
+ maxOutboundStreams: 1,
102
+ timeout: 10000
97
103
  }
98
104
  }
99
105
 
@@ -112,5 +118,14 @@ export function validateConfig (opts: RecursivePartial<Libp2pInit>): Libp2pInit
112
118
  throw errCode(new Error(messages.ERR_PROTECTOR_REQUIRED), codes.ERR_PROTECTOR_REQUIRED)
113
119
  }
114
120
 
121
+ // Append user agent version to default AGENT_VERSION depending on the environment
122
+ if (resultingOptions.identify.host.agentVersion === AGENT_VERSION) {
123
+ if (isNode || isElectronMain) {
124
+ resultingOptions.identify.host.agentVersion += ` UserAgent=${globalThis.process.version}`
125
+ } else if (isBrowser || isWebWorker || isElectronRenderer || isReactNative) {
126
+ resultingOptions.identify.host.agentVersion += ` UserAgent=${globalThis.navigator.userAgent}`
127
+ }
128
+ }
129
+
115
130
  return resultingOptions
116
131
  }
@@ -177,7 +177,7 @@ export class Dialer implements Startable, Initializable {
177
177
 
178
178
  log('creating dial target for %p', id)
179
179
 
180
- const dialTarget = await this._createCancellableDialTarget(id)
180
+ const dialTarget = await this._createCancellableDialTarget(id, options)
181
181
 
182
182
  if (dialTarget.addrs.length === 0) {
183
183
  throw errCode(new Error('The dial request has no valid addresses'), codes.ERR_NO_VALID_ADDRESSES)
@@ -207,7 +207,7 @@ export class Dialer implements Startable, Initializable {
207
207
  * The dial to the first address that is successfully able to upgrade a connection
208
208
  * will be used.
209
209
  */
210
- async _createCancellableDialTarget (peer: PeerId): Promise<DialTarget> {
210
+ async _createCancellableDialTarget (peer: PeerId, options: AbortOptions): Promise<DialTarget> {
211
211
  // Make dial target promise cancellable
212
212
  const id = `${(parseInt(String(Math.random() * 1e9), 10)).toString()}${Date.now()}`
213
213
  const cancellablePromise = new Promise<DialTarget>((resolve, reject) => {
@@ -216,7 +216,7 @@ export class Dialer implements Startable, Initializable {
216
216
 
217
217
  try {
218
218
  const dialTarget = await Promise.race([
219
- this._createDialTarget(peer),
219
+ this._createDialTarget(peer, options),
220
220
  cancellablePromise
221
221
  ])
222
222
 
@@ -232,7 +232,7 @@ export class Dialer implements Startable, Initializable {
232
232
  * If a multiaddr is received it should be the first address attempted.
233
233
  * Multiaddrs not supported by the available transports will be filtered out.
234
234
  */
235
- async _createDialTarget (peer: PeerId): Promise<DialTarget> {
235
+ async _createDialTarget (peer: PeerId, options: AbortOptions): Promise<DialTarget> {
236
236
  const knownAddrs = await pipe(
237
237
  await this.components.getPeerStore().addressBook.get(peer),
238
238
  (source) => filter(source, async (address) => {
@@ -253,7 +253,7 @@ export class Dialer implements Startable, Initializable {
253
253
 
254
254
  const addrs: Multiaddr[] = []
255
255
  for (const a of knownAddrs) {
256
- const resolvedAddrs = await this._resolve(a)
256
+ const resolvedAddrs = await this._resolve(a, options)
257
257
  resolvedAddrs.forEach(ra => addrs.push(ra))
258
258
  }
259
259
 
@@ -341,7 +341,7 @@ export class Dialer implements Startable, Initializable {
341
341
  /**
342
342
  * Resolve multiaddr recursively
343
343
  */
344
- async _resolve (ma: Multiaddr): Promise<Multiaddr[]> {
344
+ async _resolve (ma: Multiaddr, options: AbortOptions): Promise<Multiaddr[]> {
345
345
  // TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place
346
346
  // Now only supporting resolve for dnsaddr
347
347
  const resolvableProto = ma.protoNames().includes('dnsaddr')
@@ -351,9 +351,9 @@ export class Dialer implements Startable, Initializable {
351
351
  return [ma]
352
352
  }
353
353
 
354
- const resolvedMultiaddrs = await this._resolveRecord(ma)
354
+ const resolvedMultiaddrs = await this._resolveRecord(ma, options)
355
355
  const recursiveMultiaddrs = await Promise.all(resolvedMultiaddrs.map(async (nm) => {
356
- return await this._resolve(nm)
356
+ return await this._resolve(nm, options)
357
357
  }))
358
358
 
359
359
  const addrs = recursiveMultiaddrs.flat()
@@ -368,10 +368,10 @@ export class Dialer implements Startable, Initializable {
368
368
  /**
369
369
  * Resolve a given multiaddr. If this fails, an empty array will be returned
370
370
  */
371
- async _resolveRecord (ma: Multiaddr): Promise<Multiaddr[]> {
371
+ async _resolveRecord (ma: Multiaddr, options: AbortOptions): Promise<Multiaddr[]> {
372
372
  try {
373
373
  ma = new Multiaddr(ma.toString()) // Use current multiaddr module
374
- const multiaddrs = await ma.resolve()
374
+ const multiaddrs = await ma.resolve(options)
375
375
  return multiaddrs
376
376
  } catch (err) {
377
377
  log.error(`multiaddr ${ma.toString()} could not be resolved`, err)
@@ -7,7 +7,6 @@ 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
11
  import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
13
12
  import { setMaxListeners } from 'events'
@@ -19,6 +18,8 @@ import { Dialer } from './dialer/index.js'
19
18
  import type { AddressSorter } from '@libp2p/interface-peer-store'
20
19
  import type { Resolver } from '@multiformats/multiaddr'
21
20
  import { PeerMap } from '@libp2p/peer-collections'
21
+ import { TimeoutController } from 'timeout-abort-controller'
22
+ import { KEEP_ALIVE } from '@libp2p/interface-peer-store/tags'
22
23
 
23
24
  const log = logger('libp2p:connection-manager')
24
25
 
@@ -34,8 +35,9 @@ const defaultOptions: Partial<ConnectionManagerInit> = {
34
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'
40
+ const STARTUP_RECONNECT_TIMEOUT = 60000
39
41
 
40
42
  export interface ConnectionManagerInit {
41
43
  /**
@@ -105,10 +107,17 @@ export interface ConnectionManagerInit {
105
107
  maxAddrsToDial?: number
106
108
 
107
109
  /**
108
- * 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.
109
112
  */
110
113
  dialTimeout?: number
111
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
+
112
121
  /**
113
122
  * Number of max concurrent dials per peer
114
123
  */
@@ -118,6 +127,12 @@ export interface ConnectionManagerInit {
118
127
  * Multiaddr resolvers to use when dialing
119
128
  */
120
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
121
136
  }
122
137
 
123
138
  export interface ConnectionManagerEvents {
@@ -136,6 +151,9 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
136
151
  private started: boolean
137
152
  private timer?: ReturnType<retimer>
138
153
  private readonly latencyMonitor: LatencyMonitor
154
+ private readonly startupReconnectTimeout: number
155
+ private connectOnStartupController?: TimeoutController
156
+ private readonly dialTimeout: number
139
157
 
140
158
  constructor (init: ConnectionManagerInit) {
141
159
  super()
@@ -151,11 +169,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
151
169
  /**
152
170
  * Map of connections per peer
153
171
  */
154
- this.connections = trackedMap({
155
- component: METRICS_COMPONENT,
156
- metric: METRICS_PEER_CONNECTIONS,
157
- metrics: this.components.getMetrics()
158
- })
172
+ this.connections = new Map()
159
173
 
160
174
  this.started = false
161
175
  this._checkMetrics = this._checkMetrics.bind(this)
@@ -174,12 +188,103 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
174
188
 
175
189
  this.onConnect = this.onConnect.bind(this)
176
190
  this.onDisconnect = this.onDisconnect.bind(this)
191
+
192
+ this.startupReconnectTimeout = init.startupReconnectTimeout ?? STARTUP_RECONNECT_TIMEOUT
193
+ this.dialTimeout = init.dialTimeout ?? 30000
177
194
  }
178
195
 
179
196
  init (components: Components): void {
180
197
  this.components = components
181
198
 
182
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
+ })
183
288
  }
184
289
 
185
290
  isStarted () {
@@ -208,9 +313,43 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
208
313
  async afterStart () {
209
314
  this.components.getUpgrader().addEventListener('connection', this.onConnect)
210
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
+ })
211
348
  }
212
349
 
213
350
  async beforeStop () {
351
+ // if we are still dialing KEEP_ALIVE peers, abort those dials
352
+ this.connectOnStartupController?.abort()
214
353
  this.components.getUpgrader().removeEventListener('connection', this.onConnect)
215
354
  this.components.getUpgrader().removeEventListener('connectionEnd', this.onDisconnect)
216
355
  }
@@ -356,7 +495,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
356
495
  return conns
357
496
  }
358
497
 
359
- async openConnection (peerId: PeerId, options?: AbortOptions): Promise<Connection> {
498
+ async openConnection (peerId: PeerId, options: AbortOptions = {}): Promise<Connection> {
360
499
  log('dial to %p', peerId)
361
500
  const existingConnections = this.getConnections(peerId)
362
501
 
@@ -366,30 +505,43 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
366
505
  return existingConnections[0]
367
506
  }
368
507
 
369
- const connection = await this.dialer.dial(peerId, options)
370
- let peerConnections = this.connections.get(peerId.toString())
508
+ let timeoutController: TimeoutController | undefined
371
509
 
372
- if (peerConnections == null) {
373
- peerConnections = []
374
- this.connections.set(peerId.toString(), peerConnections)
510
+ if (options?.signal == null) {
511
+ timeoutController = new TimeoutController(this.dialTimeout)
512
+ options.signal = timeoutController.signal
375
513
  }
376
514
 
377
- // we get notified of connections via the Upgrader emitting "connection"
378
- // events, double check we aren't already tracking this connection before
379
- // storing it
380
- let trackedConnection = false
515
+ try {
516
+ const connection = await this.dialer.dial(peerId, options)
517
+ let peerConnections = this.connections.get(peerId.toString())
381
518
 
382
- for (const conn of peerConnections) {
383
- if (conn.id === connection.id) {
384
- trackedConnection = true
519
+ if (peerConnections == null) {
520
+ peerConnections = []
521
+ this.connections.set(peerId.toString(), peerConnections)
385
522
  }
386
- }
387
523
 
388
- if (!trackedConnection) {
389
- peerConnections.push(connection)
390
- }
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
391
528
 
392
- 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
+ }
393
545
  }
394
546
 
395
547
  async closeConnections (peerId: PeerId): Promise<void> {
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
  */
@@ -10,10 +10,10 @@ import type { Stream } from '@libp2p/interface-connection'
10
10
  import type { IncomingStreamData } from '@libp2p/interface-registrar'
11
11
  import type { Components } from '@libp2p/components'
12
12
  import type { AbortOptions } from '@libp2p/interfaces'
13
- import type { Duplex } from 'it-stream-types'
14
13
  import { abortableDuplex } from 'abortable-iterator'
15
14
  import { pipe } from 'it-pipe'
16
15
  import first from 'it-first'
16
+ import { TimeoutController } from 'timeout-abort-controller'
17
17
 
18
18
  const log = logger('libp2p:fetch')
19
19
 
@@ -21,6 +21,11 @@ export interface FetchServiceInit {
21
21
  protocolPrefix: string
22
22
  maxInboundStreams: number
23
23
  maxOutboundStreams: number
24
+
25
+ /**
26
+ * How long we should wait for a remote peer to send any data
27
+ */
28
+ timeout: number
24
29
  }
25
30
 
26
31
  export interface HandleMessageOptions {
@@ -86,15 +91,24 @@ export class FetchService implements Startable {
86
91
  log('dialing %s to %p', this.protocol, peer)
87
92
 
88
93
  const connection = await this.components.getConnectionManager().openConnection(peer, options)
89
- const stream = await connection.newStream([this.protocol], options)
90
- let source: Duplex<Uint8Array> = stream
91
-
92
- // make stream abortable if AbortSignal passed
93
- if (options.signal != null) {
94
- source = abortableDuplex(stream, options.signal)
94
+ let timeoutController
95
+ let signal = options.signal
96
+ let stream: Stream | undefined
97
+
98
+ // create a timeout if no abort signal passed
99
+ if (signal == null) {
100
+ timeoutController = new TimeoutController(this.init.timeout)
101
+ signal = timeoutController.signal
95
102
  }
96
103
 
97
104
  try {
105
+ stream = await connection.newStream([this.protocol], {
106
+ signal
107
+ })
108
+
109
+ // make stream abortable
110
+ const source = abortableDuplex(stream, signal)
111
+
98
112
  const result = await pipe(
99
113
  [FetchRequest.encode({ identifier: key })],
100
114
  lp.encode(),
@@ -129,7 +143,13 @@ export class FetchService implements Startable {
129
143
 
130
144
  return result ?? null
131
145
  } finally {
132
- stream.close()
146
+ if (timeoutController != null) {
147
+ timeoutController.clear()
148
+ }
149
+
150
+ if (stream != null) {
151
+ stream.close()
152
+ }
133
153
  }
134
154
  }
135
155
 
@@ -30,9 +30,6 @@ import type { Duplex } from 'it-stream-types'
30
30
 
31
31
  const log = logger('libp2p:identify')
32
32
 
33
- // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48
34
- const IDENTIFY_TIMEOUT = 60000
35
-
36
33
  // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52
37
34
  const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8
38
35
 
@@ -54,7 +51,7 @@ export interface IdentifyServiceInit {
54
51
  /**
55
52
  * How long we should wait for a remote peer to send their identify response
56
53
  */
57
- timeout?: number
54
+ timeout: number
58
55
 
59
56
  /**
60
57
  * Identify responses larger than this in bytes will be rejected (default: 8192)
@@ -167,7 +164,7 @@ export class IdentifyService implements Startable {
167
164
  const protocols = await this.components.getPeerStore().protoBook.get(this.components.getPeerId())
168
165
 
169
166
  const pushes = connections.map(async connection => {
170
- const timeoutController = new TimeoutController(this.init.timeout ?? IDENTIFY_TIMEOUT)
167
+ const timeoutController = new TimeoutController(this.init.timeout)
171
168
  let stream: Stream | undefined
172
169
 
173
170
  try {
@@ -229,21 +226,24 @@ export class IdentifyService implements Startable {
229
226
  }
230
227
 
231
228
  async _identify (connection: Connection, options: AbortOptions = {}): Promise<Identify> {
232
- const stream = await connection.newStream([this.identifyProtocolStr], options)
233
- let source: Duplex<Uint8Array> = stream
234
229
  let timeoutController
235
230
  let signal = options.signal
231
+ let stream: Stream | undefined
236
232
 
237
233
  // create a timeout if no abort signal passed
238
234
  if (signal == null) {
239
- timeoutController = new TimeoutController(this.init.timeout ?? IDENTIFY_TIMEOUT)
235
+ timeoutController = new TimeoutController(this.init.timeout)
240
236
  signal = timeoutController.signal
241
237
  }
242
238
 
243
- // make stream abortable if AbortSignal passed
244
- source = abortableDuplex(stream, signal)
245
-
246
239
  try {
240
+ stream = await connection.newStream([this.identifyProtocolStr], {
241
+ signal
242
+ })
243
+
244
+ // make stream abortable
245
+ const source = abortableDuplex(stream, signal)
246
+
247
247
  const data = await pipe(
248
248
  [],
249
249
  source,
@@ -267,7 +267,9 @@ export class IdentifyService implements Startable {
267
267
  timeoutController.clear()
268
268
  }
269
269
 
270
- stream.close()
270
+ if (stream != null) {
271
+ stream.close()
272
+ }
271
273
  }
272
274
  }
273
275
 
@@ -370,7 +372,7 @@ export class IdentifyService implements Startable {
370
372
  */
371
373
  async _handleIdentify (data: IncomingStreamData) {
372
374
  const { connection, stream } = data
373
- const timeoutController = new TimeoutController(this.init.timeout ?? IDENTIFY_TIMEOUT)
375
+ const timeoutController = new TimeoutController(this.init.timeout)
374
376
 
375
377
  try {
376
378
  const publicKey = this.components.getPeerId().publicKey ?? new Uint8Array(0)
@@ -421,7 +423,7 @@ export class IdentifyService implements Startable {
421
423
  */
422
424
  async _handlePush (data: IncomingStreamData) {
423
425
  const { connection, stream } = data
424
- const timeoutController = new TimeoutController(this.init.timeout ?? IDENTIFY_TIMEOUT)
426
+ const timeoutController = new TimeoutController(this.init.timeout)
425
427
 
426
428
  let message: Identify | undefined
427
429
  try {
package/src/index.ts CHANGED
@@ -50,6 +50,7 @@ export interface MetricsConfig {
50
50
  export interface HopConfig {
51
51
  enabled?: boolean
52
52
  active?: boolean
53
+ timeout: number
53
54
  }
54
55
 
55
56
  export interface RelayConfig {
package/src/libp2p.ts CHANGED
@@ -124,7 +124,8 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
124
124
  // Set up the Upgrader
125
125
  this.components.setUpgrader(new DefaultUpgrader(this.components, {
126
126
  connectionEncryption: (init.connectionEncryption ?? []).map(component => this.configureComponent(component)),
127
- muxers: (init.streamMuxers ?? []).map(component => this.configureComponent(component))
127
+ muxers: (init.streamMuxers ?? []).map(component => this.configureComponent(component)),
128
+ inboundUpgradeTimeout: init.connectionManager.inboundUpgradeTimeout
128
129
  }))
129
130
 
130
131
  // Create the Connection Manager
@@ -217,7 +218,7 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
217
218
  })))
218
219
 
219
220
  if (init.relay.enabled) {
220
- this.components.getTransportManager().add(this.configureComponent(new Circuit()))
221
+ this.components.getTransportManager().add(this.configureComponent(new Circuit(init.relay)))
221
222
 
222
223
  this.configureComponent(new Relay(this.components, {
223
224
  addressSorter: init.connectionManager.addressSorter,
@@ -309,18 +310,6 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
309
310
  )
310
311
 
311
312
  log('libp2p has started')
312
-
313
- // Once we start, emit any peers we may have already discovered
314
- // TODO: this should be removed, as we already discovered these peers in the past
315
- await this.components.getPeerStore().forEach(peer => {
316
- this.dispatchEvent(new CustomEvent<PeerInfo>('peer:discovery', {
317
- detail: {
318
- id: peer.id,
319
- multiaddrs: peer.addresses.map(addr => addr.multiaddr),
320
- protocols: peer.protocols
321
- }
322
- }))
323
- })
324
313
  } catch (err: any) {
325
314
  log.error('An error occurred starting libp2p', err)
326
315
  await this.stop()