Skip to content

Commit 9e75969

Browse files
committed
fix: updated connection limits to filter for inbound/outbound (#1511)
1 parent 3f20c93 commit 9e75969

File tree

2 files changed

+65
-62
lines changed

2 files changed

+65
-62
lines changed

src/connection-manager/index.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,9 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
173173

174174
this.opts = mergeOptions.call({ ignoreUndefined: true }, defaultOptions, init)
175175

176-
if (this.opts.maxIncomingConnections < this.opts.minConnections) {
176+
if ((this.opts.maxIncomingConnections + this.opts.maxOutgoingConnections) < this.opts.minConnections) {
177177
throw errCode(
178-
new Error('Connection Manager maxIncomingConnections must be greater than minConnections'),
179-
codes.ERR_INVALID_PARAMETERS
180-
)
181-
}
182-
183-
if (this.opts.maxOutgoingConnections < this.opts.minConnections) {
184-
throw errCode(
185-
new Error('Connection Manager maxOutgoingConnections must be greater than minConnections'),
178+
new Error('Connection Manager maxIncomingConnections + maxOutgoingConnections must be greater than minConnections'),
186179
codes.ERR_INVALID_PARAMETERS
187180
)
188181
}
@@ -442,10 +435,24 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
442435
await this.components.peerStore.keyBook.set(peerId, peerId.publicKey)
443436
}
444437

445-
const numConnections = this.getConnections().length
446-
const toPrune = numConnections - this.opts.maxOutgoingConnections
438+
let [outboundConnections, inboundConnections] = [0, 0]
439+
440+
const connections = this.getConnections()
441+
442+
connections.forEach(connection => {
443+
if (connection.stat.direction === 'inbound') {
444+
inboundConnections++
445+
} else {
446+
outboundConnections++
447+
}
448+
})
449+
450+
const numIncomingToPrune = inboundConnections - this.opts.maxIncomingConnections
451+
const numOutgoingToPrune = outboundConnections - this.opts.maxOutgoingConnections
452+
453+
await this._checkMaxLimit('maxOutgoingConnections', outboundConnections, numOutgoingToPrune)
454+
await this._checkMaxLimit('maxIncomingConnections', inboundConnections, numIncomingToPrune)
447455

448-
await this._checkMaxLimit('maxOutgoingConnections', numConnections, toPrune)
449456
this.dispatchEvent(new CustomEvent<Connection>('peer:connect', { detail: connection }))
450457
}
451458

@@ -505,9 +512,12 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
505512
}
506513
}
507514

508-
if ((this.getConnections().length + 1) > this.opts.maxOutgoingConnections) {
515+
const connections = this.getConnections()
516+
const totalOutboundConnections = connections.filter(connection => connection.stat.direction === 'outbound').length
517+
518+
if ((totalOutboundConnections + 1) > this.opts.maxOutgoingConnections) {
509519
throw errCode(
510-
new Error('Connection Manager maxOutgoing connections exceeded'),
520+
new Error('Connection Manager max connections exceeded'),
511521
codes.ERR_CONNECTION_DENIED
512522
)
513523
}
@@ -548,6 +558,8 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
548558
peerConnections.push(connection)
549559
}
550560

561+
connection.stat.direction = 'outbound'
562+
551563
return connection
552564
} finally {
553565
if (timeoutController != null) {
@@ -707,23 +719,27 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
707719
return false
708720
}
709721

722+
// check pending connections
723+
if (this.incomingPendingConnections >= Number(this.opts.maxIncomingPendingConnections)) {
724+
log('connection from %s refused - incomingPendingConnections exceeded by peer %s', maConn.remoteAddr)
725+
return false
726+
}
727+
728+
const connections = this.getConnections()
729+
730+
const totalIncomingConnections = connections.filter(connection => connection.stat.direction === 'inbound').length
731+
710732
// check allow list
711733
const allowConnection = this.allow.some(ma => {
712734
return maConn.remoteAddr.toString().startsWith(ma.toString())
713735
})
714736

715-
if (allowConnection) {
716-
this.incomingPendingConnections++
717-
718-
return true
719-
}
720-
721-
// check pending connections
722-
if (this.incomingPendingConnections === this.opts.maxIncomingPendingConnections) {
723-
log('connection from %s refused - incomingPendingConnections exceeded by peer %s', maConn.remoteAddr)
737+
if ((totalIncomingConnections + 1 > this.opts.maxIncomingConnections) && !allowConnection) {
738+
log('connection from %s refused - maxIncomingConnections exceeded', maConn.remoteAddr)
724739
return false
725740
}
726741

742+
// Check the rate limiter
727743
if (maConn.remoteAddr.isThinWaistAddress()) {
728744
const host = maConn.remoteAddr.nodeAddress().address
729745

@@ -735,14 +751,9 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
735751
}
736752
}
737753

738-
if (this.getConnections().length < this.opts.maxIncomingConnections) {
739-
this.incomingPendingConnections++
740-
741-
return true
742-
}
754+
this.incomingPendingConnections++
743755

744-
log('connection from %s refused - maxConnections exceeded', maConn.remoteAddr)
745-
return false
756+
return true
746757
}
747758

748759
afterUpgradeInbound () {

test/connection-manager/index.spec.ts

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -265,30 +265,19 @@ describe('Connection Manager', () => {
265265
expect(spy).to.have.property('callCount', 1)
266266
})
267267

268-
it('should fail if the connection manager has mismatched incoming connection limit options', async () => {
268+
it('should fail if the connection manager has mismatched incoming + outgoing connection limit options', async () => {
269269
await expect(createNode({
270270
config: createBaseOptions({
271271
connectionManager: {
272272
maxIncomingConnections: 5,
273-
minConnections: 6
273+
maxOutgoingConnections: 1,
274+
minConnections: 7
274275
}
275276
}),
276277
started: false
277278
})).to.eventually.rejected('maxIncomingConnections must be greater')
278279
})
279280

280-
it('should fail if the connection manager has mismatched outgoing connection limit options', async () => {
281-
await expect(createNode({
282-
config: createBaseOptions({
283-
connectionManager: {
284-
maxOutgoingConnections: 5,
285-
minConnections: 6
286-
}
287-
}),
288-
started: false
289-
})).to.eventually.rejected('maxOutgoingConnections must be greater')
290-
})
291-
292281
it('should reconnect to important peers on startup', async () => {
293282
const peerId = await createEd25519PeerId()
294283

@@ -343,31 +332,34 @@ describe('Connection Manager', () => {
343332
})
344333

345334
it('should deny connections when maxIncomingConnections is exceeded', async () => {
346-
const dialer = stubInterface<Dialer>()
347-
dialer.dial.resolves(stubInterface<Connection>())
348-
349-
const connectionManager = new DefaultConnectionManager({
350-
peerId: libp2p.peerId,
351-
upgrader: stubInterface<Upgrader>(),
352-
peerStore: stubInterface<PeerStore>(),
353-
dialer
354-
}, {
355-
...defaultOptions,
356-
maxIncomingConnections: 1
335+
libp2p = await createNode({
336+
config: createBaseOptions({
337+
connectionManager: {
338+
maxIncomingConnections: 1
339+
}
340+
}),
341+
started: false
357342
})
358343

359-
// max out the connection limit
360-
await connectionManager.openConnection(await createEd25519PeerId())
344+
await libp2p.start()
345+
346+
const connectionManager = libp2p.connectionManager as DefaultConnectionManager
347+
348+
// max out the connection limit by having an inbound connection already
349+
const connection = mockConnection(mockMultiaddrConnection(mockDuplex(), await createEd25519PeerId()))
350+
connection.stat.direction = 'inbound'
351+
await connectionManager._onConnect(new CustomEvent('connection', { detail: connection }))
352+
361353
expect(connectionManager.getConnections()).to.have.lengthOf(1)
362354

363-
// an inbound connection is opened
364-
const remotePeer = await createEd25519PeerId()
365-
const maConn = mockMultiaddrConnection({
355+
// another inbound connection is opened
356+
const remotePeer2 = await createEd25519PeerId()
357+
const maConn2 = mockMultiaddrConnection({
366358
source: [],
367359
sink: async () => {}
368-
}, remotePeer)
360+
}, remotePeer2)
369361

370-
await expect(connectionManager.acceptIncomingConnection(maConn))
362+
await expect(connectionManager.acceptIncomingConnection(maConn2))
371363
.to.eventually.be.false()
372364
})
373365

0 commit comments

Comments
 (0)