libp2p 2.1.3 → 2.1.4-24fa1d5af

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 (40) hide show
  1. package/dist/index.min.js +10 -10
  2. package/dist/src/config.d.ts.map +1 -1
  3. package/dist/src/config.js +1 -3
  4. package/dist/src/config.js.map +1 -1
  5. package/dist/src/connection/index.d.ts.map +1 -1
  6. package/dist/src/connection/index.js +4 -6
  7. package/dist/src/connection/index.js.map +1 -1
  8. package/dist/src/connection-manager/address-sorter.d.ts +25 -0
  9. package/dist/src/connection-manager/address-sorter.d.ts.map +1 -0
  10. package/dist/src/connection-manager/address-sorter.js +112 -0
  11. package/dist/src/connection-manager/address-sorter.js.map +1 -0
  12. package/dist/src/connection-manager/connection-pruner.d.ts +4 -1
  13. package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -1
  14. package/dist/src/connection-manager/connection-pruner.js +13 -7
  15. package/dist/src/connection-manager/connection-pruner.js.map +1 -1
  16. package/dist/src/connection-manager/dial-queue.d.ts +1 -1
  17. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
  18. package/dist/src/connection-manager/dial-queue.js +4 -5
  19. package/dist/src/connection-manager/dial-queue.js.map +1 -1
  20. package/dist/src/connection-manager/index.d.ts +1 -1
  21. package/dist/src/connection-manager/index.d.ts.map +1 -1
  22. package/dist/src/connection-manager/index.js +79 -80
  23. package/dist/src/connection-manager/index.js.map +1 -1
  24. package/dist/src/upgrader.d.ts.map +1 -1
  25. package/dist/src/upgrader.js +23 -21
  26. package/dist/src/upgrader.js.map +1 -1
  27. package/dist/src/version.d.ts +1 -1
  28. package/dist/src/version.d.ts.map +1 -1
  29. package/dist/src/version.js +1 -1
  30. package/dist/src/version.js.map +1 -1
  31. package/package.json +17 -17
  32. package/src/config.ts +1 -3
  33. package/src/connection/index.ts +5 -11
  34. package/src/connection-manager/address-sorter.ts +137 -0
  35. package/src/connection-manager/connection-pruner.ts +16 -8
  36. package/src/connection-manager/dial-queue.ts +5 -6
  37. package/src/connection-manager/index.ts +90 -83
  38. package/src/upgrader.ts +25 -16
  39. package/src/version.ts +1 -1
  40. package/dist/typedoc-urls.json +0 -19
@@ -161,13 +161,6 @@ export class ConnectionImpl implements Connection {
161
161
  }
162
162
 
163
163
  try {
164
- this.log.trace('closing all streams')
165
-
166
- // close all streams gracefully - this can throw if we're not multiplexed
167
- await Promise.all(
168
- this.streams.map(async s => s.close(options))
169
- )
170
-
171
164
  this.log.trace('closing underlying transport')
172
165
 
173
166
  // close raw connection
@@ -184,18 +177,19 @@ export class ConnectionImpl implements Connection {
184
177
  }
185
178
 
186
179
  abort (err: Error): void {
180
+ if (this.status === 'closed') {
181
+ return
182
+ }
183
+
187
184
  this.log.error('aborting connection to %a due to error', this.remoteAddr, err)
188
185
 
189
186
  this.status = 'closing'
190
- this.streams.forEach(s => { s.abort(err) })
191
-
192
- this.log.error('all streams aborted', this.streams.length)
193
187
 
194
188
  // Abort raw connection
195
189
  this._abort(err)
196
190
 
197
- this.timeline.close = Date.now()
198
191
  this.status = 'closed'
192
+ this.timeline.close = Date.now()
199
193
  }
200
194
  }
201
195
 
@@ -0,0 +1,137 @@
1
+ import { isPrivate } from '@libp2p/utils/multiaddr/is-private'
2
+ import { Circuit, WebSockets, WebSocketsSecure, WebRTC, WebRTCDirect, WebTransport, TCP } from '@multiformats/multiaddr-matcher'
3
+ import type { Address } from '@libp2p/interface'
4
+
5
+ /**
6
+ * Sorts addresses by order of reliability, where they have presented the fewest
7
+ * problems:
8
+ *
9
+ * TCP -> WebSockets/Secure -> WebRTC -> WebRTCDirect -> WebTransport
10
+ */
11
+ // eslint-disable-next-line complexity
12
+ export function reliableTransportsFirst (a: Address, b: Address): -1 | 0 | 1 {
13
+ const isATCP = TCP.exactMatch(a.multiaddr)
14
+ const isBTCP = TCP.exactMatch(b.multiaddr)
15
+
16
+ if (isATCP && !isBTCP) {
17
+ return -1
18
+ }
19
+
20
+ if (!isATCP && isBTCP) {
21
+ return 1
22
+ }
23
+
24
+ const isAWebSocketSecure = WebSocketsSecure.exactMatch(a.multiaddr)
25
+ const isBWebSocketSecure = WebSocketsSecure.exactMatch(b.multiaddr)
26
+
27
+ if (isAWebSocketSecure && !isBWebSocketSecure) {
28
+ return -1
29
+ }
30
+
31
+ if (!isAWebSocketSecure && isBWebSocketSecure) {
32
+ return 1
33
+ }
34
+
35
+ const isAWebSocket = WebSockets.exactMatch(a.multiaddr)
36
+ const isBWebSocket = WebSockets.exactMatch(b.multiaddr)
37
+
38
+ if (isAWebSocket && !isBWebSocket) {
39
+ return -1
40
+ }
41
+
42
+ if (!isAWebSocket && isBWebSocket) {
43
+ return 1
44
+ }
45
+
46
+ const isAWebRTC = WebRTC.exactMatch(a.multiaddr)
47
+ const isBWebRTC = WebRTC.exactMatch(b.multiaddr)
48
+
49
+ if (isAWebRTC && !isBWebRTC) {
50
+ return -1
51
+ }
52
+
53
+ if (!isAWebRTC && isBWebRTC) {
54
+ return 1
55
+ }
56
+
57
+ const isAWebRTCDirect = WebRTCDirect.exactMatch(a.multiaddr)
58
+ const isBWebRTCDirect = WebRTCDirect.exactMatch(b.multiaddr)
59
+
60
+ if (isAWebRTCDirect && !isBWebRTCDirect) {
61
+ return -1
62
+ }
63
+
64
+ if (!isAWebRTCDirect && isBWebRTCDirect) {
65
+ return 1
66
+ }
67
+
68
+ const isAWebTransport = WebTransport.exactMatch(a.multiaddr)
69
+ const isBWebTransport = WebTransport.exactMatch(b.multiaddr)
70
+
71
+ if (isAWebTransport && !isBWebTransport) {
72
+ return -1
73
+ }
74
+
75
+ if (!isAWebTransport && isBWebTransport) {
76
+ return 1
77
+ }
78
+
79
+ // ... everything else
80
+ return 0
81
+ }
82
+
83
+ /**
84
+ * Compare function for array.sort() that moves public addresses to the start
85
+ * of the array.
86
+ */
87
+ export function publicAddressesFirst (a: Address, b: Address): -1 | 0 | 1 {
88
+ const isAPrivate = isPrivate(a.multiaddr)
89
+ const isBPrivate = isPrivate(b.multiaddr)
90
+
91
+ if (isAPrivate && !isBPrivate) {
92
+ return 1
93
+ } else if (!isAPrivate && isBPrivate) {
94
+ return -1
95
+ }
96
+
97
+ return 0
98
+ }
99
+
100
+ /**
101
+ * Compare function for array.sort() that moves certified addresses to the start
102
+ * of the array.
103
+ */
104
+ export function certifiedAddressesFirst (a: Address, b: Address): -1 | 0 | 1 {
105
+ if (a.isCertified && !b.isCertified) {
106
+ return -1
107
+ } else if (!a.isCertified && b.isCertified) {
108
+ return 1
109
+ }
110
+
111
+ return 0
112
+ }
113
+
114
+ /**
115
+ * Compare function for array.sort() that moves circuit relay addresses to the
116
+ * end of the array.
117
+ */
118
+ export function circuitRelayAddressesLast (a: Address, b: Address): -1 | 0 | 1 {
119
+ const isACircuit = Circuit.exactMatch(a.multiaddr)
120
+ const isBCircuit = Circuit.exactMatch(b.multiaddr)
121
+
122
+ if (isACircuit && !isBCircuit) {
123
+ return 1
124
+ } else if (!isACircuit && isBCircuit) {
125
+ return -1
126
+ }
127
+
128
+ return 0
129
+ }
130
+
131
+ export function defaultAddressSorter (addresses: Address[]): Address[] {
132
+ return addresses
133
+ .sort(reliableTransportsFirst)
134
+ .sort(certifiedAddressesFirst)
135
+ .sort(circuitRelayAddressesLast)
136
+ .sort(publicAddressesFirst)
137
+ }
@@ -40,21 +40,29 @@ export class ConnectionPruner {
40
40
  this.peerStore = components.peerStore
41
41
  this.events = components.events
42
42
  this.log = components.logger.forComponent('libp2p:connection-manager:connection-pruner')
43
+ this.maybePruneConnections = this.maybePruneConnections.bind(this)
44
+ }
43
45
 
44
- // check the max connection limit whenever a peer connects
45
- components.events.addEventListener('connection:open', () => {
46
- this.maybePruneConnections()
47
- .catch(err => {
48
- this.log.error(err)
49
- })
50
- })
46
+ start (): void {
47
+ this.events.addEventListener('connection:open', this.maybePruneConnections)
48
+ }
49
+
50
+ stop (): void {
51
+ this.events.removeEventListener('connection:open', this.maybePruneConnections)
52
+ }
53
+
54
+ maybePruneConnections (): void {
55
+ this._maybePruneConnections()
56
+ .catch(err => {
57
+ this.log.error('error while pruning connections %e', err)
58
+ })
51
59
  }
52
60
 
53
61
  /**
54
62
  * If we have more connections than our maximum, select some excess connections
55
63
  * to prune based on peer value
56
64
  */
57
- async maybePruneConnections (): Promise<void> {
65
+ private async _maybePruneConnections (): Promise<void> {
58
66
  const connections = this.connectionManager.getConnections()
59
67
  const numConnections = connections.length
60
68
 
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable max-depth */
2
2
  import { TimeoutError, DialError, setMaxListeners, AbortError } from '@libp2p/interface'
3
3
  import { PeerMap } from '@libp2p/peer-collections'
4
- import { defaultAddressSort } from '@libp2p/utils/address-sort'
5
4
  import { PriorityQueue, type PriorityQueueJobOptions } from '@libp2p/utils/priority-queue'
6
5
  import { type Multiaddr, type Resolver, resolvers, multiaddr } from '@multiformats/multiaddr'
7
6
  import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
@@ -11,6 +10,7 @@ import { CustomProgressEvent } from 'progress-events'
11
10
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
12
11
  import { DialDeniedError, NoValidAddressesError } from '../errors.js'
13
12
  import { getPeerAddress } from '../get-peer.js'
13
+ import { defaultAddressSorter } from './address-sorter.js'
14
14
  import {
15
15
  DIAL_TIMEOUT,
16
16
  MAX_PARALLEL_DIALS,
@@ -47,7 +47,6 @@ interface DialerInit {
47
47
  }
48
48
 
49
49
  const defaultOptions = {
50
- addressSorter: defaultAddressSort,
51
50
  maxParallelDials: MAX_PARALLEL_DIALS,
52
51
  maxDialQueueLength: MAX_DIAL_QUEUE_LENGTH,
53
52
  maxPeerAddrsToDial: MAX_PEER_ADDRS_TO_DIAL,
@@ -71,7 +70,7 @@ interface DialQueueComponents {
71
70
  export class DialQueue {
72
71
  public queue: PriorityQueue<Connection, DialQueueJobOptions>
73
72
  private readonly components: DialQueueComponents
74
- private readonly addressSorter: AddressSorter
73
+ private readonly addressSorter?: AddressSorter
75
74
  private readonly maxPeerAddrsToDial: number
76
75
  private readonly maxDialQueueLength: number
77
76
  private readonly dialTimeout: number
@@ -80,7 +79,7 @@ export class DialQueue {
80
79
  private readonly log: Logger
81
80
 
82
81
  constructor (components: DialQueueComponents, init: DialerInit = {}) {
83
- this.addressSorter = init.addressSorter ?? defaultOptions.addressSorter
82
+ this.addressSorter = init.addressSorter
84
83
  this.maxPeerAddrsToDial = init.maxPeerAddrsToDial ?? defaultOptions.maxPeerAddrsToDial
85
84
  this.maxDialQueueLength = init.maxDialQueueLength ?? defaultOptions.maxDialQueueLength
86
85
  this.dialTimeout = init.dialTimeout ?? defaultOptions.dialTimeout
@@ -153,7 +152,7 @@ export class DialQueue {
153
152
  })
154
153
  })
155
154
 
156
- if (existingConnection != null) {
155
+ if (existingConnection?.status === 'open') {
157
156
  this.log('already connected to %a', existingConnection.remoteAddr)
158
157
  options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
159
158
  return existingConnection
@@ -467,7 +466,7 @@ export class DialQueue {
467
466
  gatedAdrs.push(addr)
468
467
  }
469
468
 
470
- const sortedGatedAddrs = gatedAdrs.sort(this.addressSorter)
469
+ const sortedGatedAddrs = this.addressSorter == null ? defaultAddressSorter(gatedAdrs) : gatedAdrs.sort(this.addressSorter)
471
470
 
472
471
  // make sure we actually have some addresses to dial
473
472
  if (sortedGatedAddrs.length === 0) {
@@ -1,6 +1,5 @@
1
- import { InvalidMultiaddrError, InvalidParametersError, InvalidPeerIdError, NotStartedError, start, stop } from '@libp2p/interface'
1
+ import { ConnectionClosedError, InvalidMultiaddrError, InvalidParametersError, InvalidPeerIdError, NotStartedError, start, stop } from '@libp2p/interface'
2
2
  import { PeerMap } from '@libp2p/peer-collections'
3
- import { defaultAddressSort } from '@libp2p/utils/address-sort'
4
3
  import { RateLimiter } from '@libp2p/utils/rate-limiter'
5
4
  import { type Multiaddr, type Resolver, multiaddr } from '@multiformats/multiaddr'
6
5
  import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
@@ -181,6 +180,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
181
180
  private readonly deny: Multiaddr[]
182
181
  private readonly maxIncomingPendingConnections: number
183
182
  private incomingPendingConnections: number
183
+ private outboundPendingConnections: number
184
184
  private readonly maxConnections: number
185
185
 
186
186
  public readonly dialQueue: DialQueue
@@ -214,8 +214,6 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
214
214
 
215
215
  this.onConnect = this.onConnect.bind(this)
216
216
  this.onDisconnect = this.onDisconnect.bind(this)
217
- this.events.addEventListener('connection:open', this.onConnect)
218
- this.events.addEventListener('connection:close', this.onDisconnect)
219
217
 
220
218
  // allow/deny lists
221
219
  this.allow = (init.allow ?? []).map(ma => multiaddr(ma))
@@ -223,6 +221,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
223
221
 
224
222
  this.incomingPendingConnections = 0
225
223
  this.maxIncomingPendingConnections = init.maxIncomingPendingConnections ?? defaultOptions.maxIncomingPendingConnections
224
+ this.outboundPendingConnections = 0
226
225
 
227
226
  // controls individual peers trying to dial us too quickly
228
227
  this.inboundConnectionRateLimiter = new RateLimiter({
@@ -242,7 +241,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
242
241
  })
243
242
 
244
243
  this.dialQueue = new DialQueue(components, {
245
- addressSorter: init.addressSorter ?? defaultAddressSort,
244
+ addressSorter: init.addressSorter,
246
245
  maxParallelDials: init.maxParallelDials ?? MAX_PARALLEL_DIALS,
247
246
  maxDialQueueLength: init.maxDialQueueLength ?? MAX_DIAL_QUEUE_LENGTH,
248
247
  maxPeerAddrsToDial: init.maxPeerAddrsToDial ?? MAX_PEER_ADDRS_TO_DIAL,
@@ -268,10 +267,6 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
268
267
 
269
268
  readonly [Symbol.toStringTag] = '@libp2p/connection-manager'
270
269
 
271
- isStarted (): boolean {
272
- return this.started
273
- }
274
-
275
270
  /**
276
271
  * Starts the Connection Manager. If Metrics are not enabled on libp2p
277
272
  * only event loop and connection limits will be monitored.
@@ -283,16 +278,13 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
283
278
  const metric = {
284
279
  inbound: 0,
285
280
  'inbound pending': this.incomingPendingConnections,
286
- outbound: 0
281
+ outbound: 0,
282
+ 'outbound pending': this.outboundPendingConnections
287
283
  }
288
284
 
289
285
  for (const conns of this.connections.values()) {
290
286
  for (const conn of conns) {
291
- if (conn.direction === 'inbound') {
292
- metric.inbound++
293
- } else {
294
- metric.outbound++
295
- }
287
+ metric[conn.direction]++
296
288
  }
297
289
  }
298
290
 
@@ -356,9 +348,13 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
356
348
  }
357
349
  })
358
350
 
351
+ this.events.addEventListener('connection:open', this.onConnect)
352
+ this.events.addEventListener('connection:close', this.onDisconnect)
353
+
359
354
  await start(
360
355
  this.dialQueue,
361
- this.reconnectQueue
356
+ this.reconnectQueue,
357
+ this.connectionPruner
362
358
  )
363
359
 
364
360
  this.started = true
@@ -369,9 +365,13 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
369
365
  * Stops the Connection Manager
370
366
  */
371
367
  async stop (): Promise<void> {
368
+ this.events.removeEventListener('connection:open', this.onConnect)
369
+ this.events.removeEventListener('connection:close', this.onDisconnect)
370
+
372
371
  await stop(
373
372
  this.reconnectQueue,
374
- this.dialQueue
373
+ this.dialQueue,
374
+ this.connectionPruner
375
375
  )
376
376
 
377
377
  // Close all connections we're tracking
@@ -413,17 +413,19 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
413
413
  return
414
414
  }
415
415
 
416
- const peerId = connection.remotePeer
417
- const storedConns = this.connections.get(peerId)
418
- let isNewPeer = false
419
-
420
- if (storedConns != null) {
421
- storedConns.push(connection)
422
- } else {
423
- isNewPeer = true
424
- this.connections.set(peerId, [connection])
416
+ if (connection.status !== 'open') {
417
+ // this can happen when the remote closes the connection immediately after
418
+ // opening
419
+ return
425
420
  }
426
421
 
422
+ const peerId = connection.remotePeer
423
+ const isNewPeer = !this.connections.has(peerId)
424
+ const storedConns = this.connections.get(peerId) ?? []
425
+ storedConns.push(connection)
426
+
427
+ this.connections.set(peerId, storedConns)
428
+
427
429
  // only need to store RSA public keys, all other types are embedded in the peer id
428
430
  if (peerId.publicKey != null && peerId.type === 'RSA') {
429
431
  await this.peerStore.patch(peerId, {
@@ -441,20 +443,21 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
441
443
  */
442
444
  onDisconnect (evt: CustomEvent<Connection>): void {
443
445
  const { detail: connection } = evt
446
+ const peerId = connection.remotePeer
447
+ const peerConns = this.connections.get(peerId) ?? []
444
448
 
445
- if (!this.started) {
446
- // This can happen when we are in the process of shutting down the node
447
- return
448
- }
449
+ // remove closed connection
450
+ const filteredPeerConns = peerConns.filter(conn => conn.id !== connection.id)
449
451
 
450
- const peerId = connection.remotePeer
451
- let storedConn = this.connections.get(peerId)
452
+ // update peer connections
453
+ this.connections.set(peerId, filteredPeerConns)
452
454
 
453
- if (storedConn != null && storedConn.length > 1) {
454
- storedConn = storedConn.filter((conn) => conn.id !== connection.id)
455
- this.connections.set(peerId, storedConn)
456
- } else if (storedConn != null) {
455
+ if (filteredPeerConns.length === 0) {
456
+ // trigger disconnect event if no connections remain
457
+ this.log('onDisconnect remove all connections for peer %p', peerId)
457
458
  this.connections.delete(peerId)
459
+
460
+ // broadcast disconnect event
458
461
  this.events.safeDispatchEvent('peer:disconnect', { detail: connection.remotePeer })
459
462
  }
460
463
  }
@@ -478,73 +481,77 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
478
481
  }
479
482
 
480
483
  async openConnection (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: OpenConnectionOptions = {}): Promise<Connection> {
481
- if (!this.isStarted()) {
484
+ if (!this.started) {
482
485
  throw new NotStartedError('Not started')
483
486
  }
484
487
 
485
- options.signal?.throwIfAborted()
488
+ this.outboundPendingConnections++
486
489
 
487
- const { peerId } = getPeerAddress(peerIdOrMultiaddr)
490
+ try {
491
+ options.signal?.throwIfAborted()
488
492
 
489
- if (this.peerId.equals(peerId)) {
490
- throw new InvalidPeerIdError('Can not dial self')
491
- }
493
+ const { peerId } = getPeerAddress(peerIdOrMultiaddr)
494
+
495
+ if (this.peerId.equals(peerId)) {
496
+ throw new InvalidPeerIdError('Can not dial self')
497
+ }
492
498
 
493
- if (peerId != null && options.force !== true) {
494
- this.log('dial %p', peerId)
495
- const existingConnection = this.getConnections(peerId)
496
- .find(conn => conn.limits == null)
499
+ if (peerId != null && options.force !== true) {
500
+ this.log('dial %p', peerId)
501
+ const existingConnection = this.getConnections(peerId)
502
+ .find(conn => conn.limits == null)
497
503
 
498
- if (existingConnection != null) {
499
- this.log('had an existing non-limited connection to %p', peerId)
504
+ if (existingConnection != null) {
505
+ this.log('had an existing non-limited connection to %p', peerId)
500
506
 
501
- options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
502
- return existingConnection
507
+ options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
508
+ return existingConnection
509
+ }
503
510
  }
504
- }
505
511
 
506
- const connection = await this.dialQueue.dial(peerIdOrMultiaddr, {
507
- ...options,
508
- priority: options.priority ?? DEFAULT_DIAL_PRIORITY
509
- })
512
+ const connection = await this.dialQueue.dial(peerIdOrMultiaddr, {
513
+ ...options,
514
+ priority: options.priority ?? DEFAULT_DIAL_PRIORITY
515
+ })
510
516
 
511
- if (connection.remotePeer.equals(this.peerId)) {
512
- const err = new InvalidPeerIdError('Can not dial self')
513
- connection.abort(err)
514
- throw err
515
- }
517
+ if (connection.status !== 'open') {
518
+ throw new ConnectionClosedError('Remote closed connection during opening')
519
+ }
516
520
 
517
- let peerConnections = this.connections.get(connection.remotePeer)
521
+ let peerConnections = this.connections.get(connection.remotePeer)
518
522
 
519
- if (peerConnections == null) {
520
- peerConnections = []
521
- this.connections.set(connection.remotePeer, peerConnections)
522
- }
523
+ if (peerConnections == null) {
524
+ peerConnections = []
525
+ this.connections.set(connection.remotePeer, peerConnections)
526
+ }
523
527
 
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
528
+ // we get notified of connections via the Upgrader emitting "connection"
529
+ // events, double check we aren't already tracking this connection before
530
+ // storing it
531
+ let trackedConnection = false
528
532
 
529
- for (const conn of peerConnections) {
530
- if (conn.id === connection.id) {
531
- trackedConnection = true
532
- }
533
+ for (const conn of peerConnections) {
534
+ if (conn.id === connection.id) {
535
+ trackedConnection = true
536
+ }
533
537
 
534
- // make sure we don't already have a connection to this multiaddr
535
- if (options.force !== true && conn.id !== connection.id && conn.remoteAddr.equals(connection.remoteAddr)) {
536
- connection.abort(new InvalidMultiaddrError('Duplicate multiaddr connection'))
538
+ // make sure we don't already have a connection to this multiaddr
539
+ if (options.force !== true && conn.id !== connection.id && conn.remoteAddr.equals(connection.remoteAddr)) {
540
+ connection.abort(new InvalidMultiaddrError('Duplicate multiaddr connection'))
537
541
 
538
- // return the existing connection
539
- return conn
542
+ // return the existing connection
543
+ return conn
544
+ }
540
545
  }
541
- }
542
546
 
543
- if (!trackedConnection) {
544
- peerConnections.push(connection)
545
- }
547
+ if (!trackedConnection) {
548
+ peerConnections.push(connection)
549
+ }
546
550
 
547
- return connection
551
+ return connection
552
+ } finally {
553
+ this.outboundPendingConnections--
554
+ }
548
555
  }
549
556
 
550
557
  async closeConnections (peerId: PeerId, options: AbortOptions = {}): Promise<void> {
package/src/upgrader.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { InvalidMultiaddrError, TooManyInboundProtocolStreamsError, TooManyOutboundProtocolStreamsError, LimitedConnectionError, setMaxListeners } from '@libp2p/interface'
1
+ import { InvalidMultiaddrError, TooManyInboundProtocolStreamsError, TooManyOutboundProtocolStreamsError, LimitedConnectionError, setMaxListeners, InvalidPeerIdError } from '@libp2p/interface'
2
2
  import * as mss from '@libp2p/multistream-select'
3
3
  import { peerIdFromString } from '@libp2p/peer-id'
4
4
  import { anySignal } from 'any-signal'
@@ -304,6 +304,14 @@ export class DefaultUpgrader implements Upgrader {
304
304
  remotePeer = remotePeerId
305
305
  }
306
306
 
307
+ // this can happen if we dial a multiaddr without a peer id, we only find
308
+ // out the identity of the remote after the connection is encrypted
309
+ if (remotePeer.equals(this.components.peerId)) {
310
+ const err = new InvalidPeerIdError('Can not dial self')
311
+ maConn.abort(err)
312
+ throw err
313
+ }
314
+
307
315
  upgradedConn = encryptedConn
308
316
  if (opts?.muxerFactory != null) {
309
317
  muxerFactory = opts.muxerFactory
@@ -326,6 +334,8 @@ export class DefaultUpgrader implements Upgrader {
326
334
  } catch (err: any) {
327
335
  maConn.log.error('failed to upgrade inbound connection %s %a - %e', direction === 'inbound' ? 'from' : 'to', maConn.remoteAddr, err)
328
336
  throw err
337
+ } finally {
338
+ signal.clear()
329
339
  }
330
340
 
331
341
  await this.shouldBlockConnection(direction === 'inbound' ? 'denyInboundUpgradedConnection' : 'denyOutboundUpgradedConnection', remotePeer, maConn)
@@ -538,7 +548,7 @@ export class DefaultUpgrader implements Upgrader {
538
548
  const _timeline = maConn.timeline
539
549
  maConn.timeline = new Proxy(_timeline, {
540
550
  set: (...args) => {
541
- if (connection != null && args[1] === 'close' && args[2] != null && _timeline.close == null) {
551
+ if (args[1] === 'close' && args[2] != null && _timeline.close == null) {
542
552
  // Wait for close to finish before notifying of the closure
543
553
  (async () => {
544
554
  try {
@@ -546,14 +556,14 @@ export class DefaultUpgrader implements Upgrader {
546
556
  await connection.close()
547
557
  }
548
558
  } catch (err: any) {
549
- connection.log.error('error closing connection after timeline close', err)
559
+ connection.log.error('error closing connection after timeline close %e', err)
550
560
  } finally {
551
561
  this.events.safeDispatchEvent('connection:close', {
552
562
  detail: connection
553
563
  })
554
564
  }
555
565
  })().catch(err => {
556
- connection.log.error('error thrown while dispatching connection:close event', err)
566
+ connection.log.error('error thrown while dispatching connection:close event %e', err)
557
567
  })
558
568
  }
559
569
 
@@ -578,25 +588,21 @@ export class DefaultUpgrader implements Upgrader {
578
588
  limits,
579
589
  logger: this.components.logger,
580
590
  newStream: newStream ?? errConnectionNotMultiplexed,
581
- getStreams: () => { if (muxer != null) { return muxer.streams } else { return [] } },
591
+ getStreams: () => {
592
+ return muxer?.streams ?? []
593
+ },
582
594
  close: async (options?: AbortOptions) => {
583
- // Ensure remaining streams are closed gracefully
584
- if (muxer != null) {
585
- connection.log.trace('close muxer')
586
- await muxer.close(options)
587
- }
595
+ // ensure remaining streams are closed gracefully
596
+ await muxer?.close(options)
588
597
 
589
- connection.log.trace('close maconn')
590
598
  // close the underlying transport
591
599
  await maConn.close(options)
592
- connection.log.trace('closed maconn')
593
600
  },
594
601
  abort: (err) => {
595
602
  maConn.abort(err)
596
- // Ensure remaining streams are aborted
597
- if (muxer != null) {
598
- muxer.abort(err)
599
- }
603
+
604
+ // ensure remaining streams are aborted
605
+ muxer?.abort(err)
600
606
  }
601
607
  })
602
608
 
@@ -604,6 +610,9 @@ export class DefaultUpgrader implements Upgrader {
604
610
  detail: connection
605
611
  })
606
612
 
613
+ // @ts-expect-error nah
614
+ connection.__maConnTimeline = _timeline
615
+
607
616
  return connection
608
617
  }
609
618
 
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '2.1.3'
1
+ export const version = '2.1.4-24fa1d5af'
2
2
  export const name = 'libp2p'