libp2p 0.37.3-e6f646e → 0.38.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 (119) hide show
  1. package/README.md +0 -2
  2. package/dist/src/circuit/circuit/hop.d.ts +5 -3
  3. package/dist/src/circuit/circuit/hop.d.ts.map +1 -1
  4. package/dist/src/circuit/circuit/hop.js +8 -4
  5. package/dist/src/circuit/circuit/hop.js.map +1 -1
  6. package/dist/src/circuit/circuit/stop.d.ts +5 -3
  7. package/dist/src/circuit/circuit/stop.d.ts.map +1 -1
  8. package/dist/src/circuit/circuit/stop.js +4 -2
  9. package/dist/src/circuit/circuit/stop.js.map +1 -1
  10. package/dist/src/circuit/circuit/stream-handler.d.ts +2 -1
  11. package/dist/src/circuit/circuit/stream-handler.d.ts.map +1 -1
  12. package/dist/src/circuit/circuit/stream-handler.js +2 -2
  13. package/dist/src/circuit/circuit/stream-handler.js.map +1 -1
  14. package/dist/src/circuit/index.d.ts +2 -10
  15. package/dist/src/circuit/index.d.ts.map +1 -1
  16. package/dist/src/circuit/index.js.map +1 -1
  17. package/dist/src/circuit/pb/index.d.ts +5 -4
  18. package/dist/src/circuit/pb/index.d.ts.map +1 -1
  19. package/dist/src/circuit/pb/index.js +107 -11
  20. package/dist/src/circuit/pb/index.js.map +1 -1
  21. package/dist/src/circuit/transport.d.ts +3 -0
  22. package/dist/src/circuit/transport.d.ts.map +1 -1
  23. package/dist/src/circuit/transport.js +79 -57
  24. package/dist/src/circuit/transport.js.map +1 -1
  25. package/dist/src/config.d.ts.map +1 -1
  26. package/dist/src/config.js +19 -4
  27. package/dist/src/config.js.map +1 -1
  28. package/dist/src/connection-manager/dialer/auto-dialer.d.ts.map +1 -1
  29. package/dist/src/connection-manager/dialer/auto-dialer.js +6 -0
  30. package/dist/src/connection-manager/dialer/auto-dialer.js.map +1 -1
  31. package/dist/src/connection-manager/dialer/index.d.ts +4 -4
  32. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  33. package/dist/src/connection-manager/dialer/index.js +10 -10
  34. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  35. package/dist/src/connection-manager/index.d.ts +8 -1
  36. package/dist/src/connection-manager/index.d.ts.map +1 -1
  37. package/dist/src/connection-manager/index.js +42 -16
  38. package/dist/src/connection-manager/index.js.map +1 -1
  39. package/dist/src/constants.d.ts +4 -0
  40. package/dist/src/constants.d.ts.map +1 -1
  41. package/dist/src/constants.js +4 -0
  42. package/dist/src/constants.js.map +1 -1
  43. package/dist/src/fetch/index.d.ts +4 -0
  44. package/dist/src/fetch/index.d.ts.map +1 -1
  45. package/dist/src/fetch/index.js +25 -6
  46. package/dist/src/fetch/index.js.map +1 -1
  47. package/dist/src/fetch/pb/proto.d.ts +4 -3
  48. package/dist/src/fetch/pb/proto.d.ts.map +1 -1
  49. package/dist/src/fetch/pb/proto.js +88 -8
  50. package/dist/src/fetch/pb/proto.js.map +1 -1
  51. package/dist/src/identify/index.d.ts +1 -1
  52. package/dist/src/identify/index.d.ts.map +1 -1
  53. package/dist/src/identify/index.js +35 -12
  54. package/dist/src/identify/index.js.map +1 -1
  55. package/dist/src/identify/pb/message.d.ts +2 -1
  56. package/dist/src/identify/pb/message.d.ts.map +1 -1
  57. package/dist/src/identify/pb/message.js +94 -10
  58. package/dist/src/identify/pb/message.js.map +1 -1
  59. package/dist/src/index.d.ts +1 -0
  60. package/dist/src/index.d.ts.map +1 -1
  61. package/dist/src/index.js.map +1 -1
  62. package/dist/src/insecure/index.d.ts.map +1 -1
  63. package/dist/src/insecure/index.js +7 -3
  64. package/dist/src/insecure/index.js.map +1 -1
  65. package/dist/src/insecure/pb/proto.d.ts +4 -3
  66. package/dist/src/insecure/pb/proto.d.ts.map +1 -1
  67. package/dist/src/insecure/pb/proto.js +89 -9
  68. package/dist/src/insecure/pb/proto.js.map +1 -1
  69. package/dist/src/libp2p.d.ts.map +1 -1
  70. package/dist/src/libp2p.js +3 -2
  71. package/dist/src/libp2p.js.map +1 -1
  72. package/dist/src/metrics/index.d.ts +1 -2
  73. package/dist/src/metrics/index.d.ts.map +1 -1
  74. package/dist/src/metrics/index.js +3 -4
  75. package/dist/src/metrics/index.js.map +1 -1
  76. package/dist/src/ping/index.d.ts +4 -0
  77. package/dist/src/ping/index.d.ts.map +1 -1
  78. package/dist/src/ping/index.js +27 -8
  79. package/dist/src/ping/index.js.map +1 -1
  80. package/dist/src/pnet/index.d.ts.map +1 -1
  81. package/dist/src/pnet/index.js +2 -1
  82. package/dist/src/pnet/index.js.map +1 -1
  83. package/dist/src/registrar.d.ts +2 -2
  84. package/dist/src/registrar.d.ts.map +1 -1
  85. package/dist/src/registrar.js +2 -2
  86. package/dist/src/registrar.js.map +1 -1
  87. package/dist/src/upgrader.d.ts +6 -0
  88. package/dist/src/upgrader.d.ts.map +1 -1
  89. package/dist/src/upgrader.js +121 -75
  90. package/dist/src/upgrader.js.map +1 -1
  91. package/dist/src/version.d.ts.map +1 -1
  92. package/dist/src/version.js.map +1 -1
  93. package/package.json +48 -47
  94. package/src/circuit/README.md +2 -2
  95. package/src/circuit/circuit/hop.ts +18 -9
  96. package/src/circuit/circuit/stop.ts +9 -4
  97. package/src/circuit/circuit/stream-handler.ts +5 -4
  98. package/src/circuit/index.ts +2 -11
  99. package/src/circuit/pb/index.ts +133 -15
  100. package/src/circuit/transport.ts +87 -58
  101. package/src/config.ts +19 -4
  102. package/src/connection-manager/dialer/auto-dialer.ts +6 -0
  103. package/src/connection-manager/dialer/index.ts +10 -10
  104. package/src/connection-manager/index.ts +54 -19
  105. package/src/constants.ts +5 -0
  106. package/src/fetch/index.ts +34 -8
  107. package/src/fetch/pb/proto.ts +109 -11
  108. package/src/identify/index.ts +41 -19
  109. package/src/identify/pb/message.ts +111 -11
  110. package/src/index.ts +1 -0
  111. package/src/insecure/index.ts +8 -3
  112. package/src/insecure/pb/proto.ts +111 -12
  113. package/src/libp2p.ts +3 -2
  114. package/src/metrics/index.ts +4 -7
  115. package/src/ping/index.ts +37 -11
  116. package/src/pnet/index.ts +2 -0
  117. package/src/registrar.ts +2 -2
  118. package/src/upgrader.ts +138 -78
  119. package/src/version.ts +1 -2
@@ -17,12 +17,23 @@ import type { AbortOptions } from '@libp2p/interfaces'
17
17
  import type { IncomingStreamData } from '@libp2p/interface-registrar'
18
18
  import type { Listener, Transport, CreateListenerOptions, ConnectionHandler } from '@libp2p/interface-transport'
19
19
  import type { Connection } from '@libp2p/interface-connection'
20
+ import type { RelayConfig } from '../index.js'
21
+ import { abortableDuplex } from 'abortable-iterator'
22
+ import { TimeoutController } from 'timeout-abort-controller'
23
+ import { setMaxListeners } from 'events'
24
+ import type { Uint8ArrayList } from 'uint8arraylist'
25
+ import type { Duplex } from 'it-stream-types'
20
26
 
21
27
  const log = logger('libp2p:circuit')
22
28
 
23
29
  export class Circuit implements Transport, Initializable {
24
30
  private handler?: ConnectionHandler
25
31
  private components: Components = new Components()
32
+ private readonly _init: RelayConfig
33
+
34
+ constructor (init: RelayConfig) {
35
+ this._init = init
36
+ }
26
37
 
27
38
  init (components: Components): void {
28
39
  this.components = components
@@ -54,49 +65,25 @@ export class Circuit implements Transport, Initializable {
54
65
 
55
66
  async _onProtocol (data: IncomingStreamData) {
56
67
  const { connection, stream } = data
57
- const streamHandler = new StreamHandler({ stream })
58
- const request = await streamHandler.read()
59
-
60
- if (request == null) {
61
- log('request was invalid, could not read from stream')
62
- streamHandler.write({
63
- type: CircuitPB.Type.STATUS,
64
- code: CircuitPB.Status.MALFORMED_MESSAGE
65
- })
66
- streamHandler.close()
67
- return
68
- }
68
+ const controller = new TimeoutController(this._init.hop.timeout)
69
69
 
70
- let virtualConnection
70
+ try {
71
+ // fails on node < 15.4
72
+ setMaxListeners?.(Infinity, controller.signal)
73
+ } catch {}
71
74
 
72
- switch (request.type) {
73
- case CircuitPB.Type.CAN_HOP: {
74
- log('received CAN_HOP request from %p', connection.remotePeer)
75
- await handleCanHop({ circuit: this, connection, streamHandler })
76
- break
77
- }
78
- case CircuitPB.Type.HOP: {
79
- log('received HOP request from %p', connection.remotePeer)
80
- virtualConnection = await handleHop({
81
- connection,
82
- request,
83
- streamHandler,
84
- circuit: this,
85
- connectionManager: this.components.getConnectionManager()
86
- })
87
- break
88
- }
89
- case CircuitPB.Type.STOP: {
90
- log('received STOP request from %p', connection.remotePeer)
91
- virtualConnection = await handleStop({
92
- connection,
93
- request,
94
- streamHandler
95
- })
96
- break
97
- }
98
- default: {
99
- log('Request of type %s not supported', request.type)
75
+ try {
76
+ const source = abortableDuplex(stream, controller.signal)
77
+ const streamHandler = new StreamHandler({
78
+ stream: {
79
+ ...stream,
80
+ ...source
81
+ }
82
+ })
83
+ const request = await streamHandler.read()
84
+
85
+ if (request == null) {
86
+ log('request was invalid, could not read from stream')
100
87
  streamHandler.write({
101
88
  type: CircuitPB.Type.STATUS,
102
89
  code: CircuitPB.Status.MALFORMED_MESSAGE
@@ -104,27 +91,68 @@ export class Circuit implements Transport, Initializable {
104
91
  streamHandler.close()
105
92
  return
106
93
  }
107
- }
108
94
 
109
- if (virtualConnection != null) {
110
- // @ts-expect-error dst peer will not be undefined
111
- const remoteAddr = new Multiaddr(request.dstPeer.addrs[0])
112
- // @ts-expect-error dst peer will not be undefined
113
- const localAddr = new Multiaddr(request.srcPeer.addrs[0])
114
- const maConn = streamToMaConnection({
115
- stream: virtualConnection,
116
- remoteAddr,
117
- localAddr
118
- })
119
- const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
120
- log('new %s connection %s', type, maConn.remoteAddr)
95
+ let virtualConnection: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array> | undefined
96
+
97
+ switch (request.type) {
98
+ case CircuitPB.Type.CAN_HOP: {
99
+ log('received CAN_HOP request from %p', connection.remotePeer)
100
+ await handleCanHop({ circuit: this, connection, streamHandler })
101
+ break
102
+ }
103
+ case CircuitPB.Type.HOP: {
104
+ log('received HOP request from %p', connection.remotePeer)
105
+ await handleHop({
106
+ connection,
107
+ request,
108
+ streamHandler,
109
+ circuit: this,
110
+ connectionManager: this.components.getConnectionManager()
111
+ })
112
+ break
113
+ }
114
+ case CircuitPB.Type.STOP: {
115
+ log('received STOP request from %p', connection.remotePeer)
116
+ virtualConnection = await handleStop({
117
+ connection,
118
+ request,
119
+ streamHandler
120
+ })
121
+ break
122
+ }
123
+ default: {
124
+ log('Request of type %s not supported', request.type)
125
+ streamHandler.write({
126
+ type: CircuitPB.Type.STATUS,
127
+ code: CircuitPB.Status.MALFORMED_MESSAGE
128
+ })
129
+ streamHandler.close()
130
+ return
131
+ }
132
+ }
121
133
 
122
- const conn = await this.components.getUpgrader().upgradeInbound(maConn)
123
- log('%s connection %s upgraded', type, maConn.remoteAddr)
134
+ if (virtualConnection != null) {
135
+ const remoteAddr = connection.remoteAddr
136
+ .encapsulate('/p2p-circuit')
137
+ .encapsulate(new Multiaddr(request.dstPeer?.addrs[0]))
138
+ const localAddr = new Multiaddr(request.srcPeer?.addrs[0])
139
+ const maConn = streamToMaConnection({
140
+ stream: virtualConnection,
141
+ remoteAddr,
142
+ localAddr
143
+ })
144
+ const type = request.type === CircuitPB.Type.HOP ? 'relay' : 'inbound'
145
+ log('new %s connection %s', type, maConn.remoteAddr)
146
+
147
+ const conn = await this.components.getUpgrader().upgradeInbound(maConn)
148
+ log('%s connection %s upgraded', type, maConn.remoteAddr)
124
149
 
125
- if (this.handler != null) {
126
- this.handler(conn)
150
+ if (this.handler != null) {
151
+ this.handler(conn)
152
+ }
127
153
  }
154
+ } finally {
155
+ controller.clear()
128
156
  }
129
157
  }
130
158
 
@@ -160,6 +188,7 @@ export class Circuit implements Transport, Initializable {
160
188
 
161
189
  try {
162
190
  const virtualConnection = await hop({
191
+ ...options,
163
192
  connection: relayConnection,
164
193
  request: {
165
194
  type: CircuitPB.Type.HOP,
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
  }
@@ -2,6 +2,7 @@ import type { PeerInfo } from '@libp2p/interface-peer-info'
2
2
  import { logger } from '@libp2p/logger'
3
3
  import type { Components } from '@libp2p/components'
4
4
  import { TimeoutController } from 'timeout-abort-controller'
5
+ import { setMaxListeners } from 'events'
5
6
 
6
7
  const log = logger('libp2p:dialer:auto-dialer')
7
8
 
@@ -44,6 +45,11 @@ export class AutoDialer {
44
45
 
45
46
  const controller = new TimeoutController(this.dialTimeout)
46
47
 
48
+ try {
49
+ // fails on node < 15.4
50
+ setMaxListeners?.(Infinity, controller.signal)
51
+ } catch {}
52
+
47
53
  void this.components.getConnectionManager().openConnection(peer.id, {
48
54
  signal: controller.signal
49
55
  })
@@ -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)
@@ -107,10 +107,17 @@ export interface ConnectionManagerInit {
107
107
  maxAddrsToDial?: number
108
108
 
109
109
  /**
110
- * 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.
111
112
  */
112
113
  dialTimeout?: number
113
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
+
114
121
  /**
115
122
  * Number of max concurrent dials per peer
116
123
  */
@@ -146,6 +153,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
146
153
  private readonly latencyMonitor: LatencyMonitor
147
154
  private readonly startupReconnectTimeout: number
148
155
  private connectOnStartupController?: TimeoutController
156
+ private readonly dialTimeout: number
149
157
 
150
158
  constructor (init: ConnectionManagerInit) {
151
159
  super()
@@ -182,6 +190,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
182
190
  this.onDisconnect = this.onDisconnect.bind(this)
183
191
 
184
192
  this.startupReconnectTimeout = init.startupReconnectTimeout ?? STARTUP_RECONNECT_TIMEOUT
193
+ this.dialTimeout = init.dialTimeout ?? 30000
185
194
  }
186
195
 
187
196
  init (components: Components): void {
@@ -322,6 +331,11 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
322
331
  this.connectOnStartupController?.clear()
323
332
  this.connectOnStartupController = new TimeoutController(this.startupReconnectTimeout)
324
333
 
334
+ try {
335
+ // fails on node < 15.4
336
+ setMaxListeners?.(Infinity, this.connectOnStartupController.signal)
337
+ } catch {}
338
+
325
339
  await Promise.all(
326
340
  keepAlivePeers.map(async peer => {
327
341
  await this.openConnection(peer, {
@@ -333,6 +347,9 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
333
347
  })
334
348
  )
335
349
  })
350
+ .catch(err => {
351
+ log.error(err)
352
+ })
336
353
  .finally(() => {
337
354
  this.connectOnStartupController?.clear()
338
355
  })
@@ -486,7 +503,7 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
486
503
  return conns
487
504
  }
488
505
 
489
- async openConnection (peerId: PeerId, options?: AbortOptions): Promise<Connection> {
506
+ async openConnection (peerId: PeerId, options: AbortOptions = {}): Promise<Connection> {
490
507
  log('dial to %p', peerId)
491
508
  const existingConnections = this.getConnections(peerId)
492
509
 
@@ -496,30 +513,48 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
496
513
  return existingConnections[0]
497
514
  }
498
515
 
499
- const connection = await this.dialer.dial(peerId, options)
500
- let peerConnections = this.connections.get(peerId.toString())
516
+ let timeoutController: TimeoutController | undefined
501
517
 
502
- if (peerConnections == null) {
503
- peerConnections = []
504
- this.connections.set(peerId.toString(), peerConnections)
518
+ if (options?.signal == null) {
519
+ timeoutController = new TimeoutController(this.dialTimeout)
520
+ options.signal = timeoutController.signal
521
+
522
+ try {
523
+ // fails on node < 15.4
524
+ setMaxListeners?.(Infinity, timeoutController.signal)
525
+ } catch {}
505
526
  }
506
527
 
507
- // we get notified of connections via the Upgrader emitting "connection"
508
- // events, double check we aren't already tracking this connection before
509
- // storing it
510
- let trackedConnection = false
528
+ try {
529
+ const connection = await this.dialer.dial(peerId, options)
530
+ let peerConnections = this.connections.get(peerId.toString())
511
531
 
512
- for (const conn of peerConnections) {
513
- if (conn.id === connection.id) {
514
- trackedConnection = true
532
+ if (peerConnections == null) {
533
+ peerConnections = []
534
+ this.connections.set(peerId.toString(), peerConnections)
515
535
  }
516
- }
517
536
 
518
- if (!trackedConnection) {
519
- peerConnections.push(connection)
520
- }
537
+ // we get notified of connections via the Upgrader emitting "connection"
538
+ // events, double check we aren't already tracking this connection before
539
+ // storing it
540
+ let trackedConnection = false
541
+
542
+ for (const conn of peerConnections) {
543
+ if (conn.id === connection.id) {
544
+ trackedConnection = true
545
+ }
546
+ }
547
+
548
+ if (!trackedConnection) {
549
+ peerConnections.push(connection)
550
+ }
521
551
 
522
- return connection
552
+ return connection
553
+ } finally {
554
+ if (timeoutController != null) {
555
+ timeoutController.clear()
556
+ }
557
+ }
523
558
  }
524
559
 
525
560
  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,11 @@ 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
+ import { setMaxListeners } from 'events'
17
18
 
18
19
  const log = logger('libp2p:fetch')
19
20
 
@@ -21,6 +22,11 @@ export interface FetchServiceInit {
21
22
  protocolPrefix: string
22
23
  maxInboundStreams: number
23
24
  maxOutboundStreams: number
25
+
26
+ /**
27
+ * How long we should wait for a remote peer to send any data
28
+ */
29
+ timeout: number
24
30
  }
25
31
 
26
32
  export interface HandleMessageOptions {
@@ -86,15 +92,29 @@ export class FetchService implements Startable {
86
92
  log('dialing %s to %p', this.protocol, peer)
87
93
 
88
94
  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)
95
+ let timeoutController
96
+ let signal = options.signal
97
+ let stream: Stream | undefined
98
+
99
+ // create a timeout if no abort signal passed
100
+ if (signal == null) {
101
+ timeoutController = new TimeoutController(this.init.timeout)
102
+ signal = timeoutController.signal
103
+
104
+ try {
105
+ // fails on node < 15.4
106
+ setMaxListeners?.(Infinity, timeoutController.signal)
107
+ } catch {}
95
108
  }
96
109
 
97
110
  try {
111
+ stream = await connection.newStream([this.protocol], {
112
+ signal
113
+ })
114
+
115
+ // make stream abortable
116
+ const source = abortableDuplex(stream, signal)
117
+
98
118
  const result = await pipe(
99
119
  [FetchRequest.encode({ identifier: key })],
100
120
  lp.encode(),
@@ -129,7 +149,13 @@ export class FetchService implements Startable {
129
149
 
130
150
  return result ?? null
131
151
  } finally {
132
- stream.close()
152
+ if (timeoutController != null) {
153
+ timeoutController.clear()
154
+ }
155
+
156
+ if (stream != null) {
157
+ stream.close()
158
+ }
133
159
  }
134
160
  }
135
161