Skip to content

Commit e1aaf4e

Browse files
authored
fix: allow connection gater classes (#3281)
Allow connection gaters to be class instances as well as objects with function properties.
1 parent 9587611 commit e1aaf4e

File tree

3 files changed

+190
-26
lines changed

3 files changed

+190
-26
lines changed

packages/integration-tests/test/connection-gater.spec.ts

Lines changed: 184 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import delay from 'delay'
77
import { createLibp2p } from 'libp2p'
88
import Sinon from 'sinon'
99
import { stubInterface } from 'sinon-ts'
10-
import type { ConnectionGater, Libp2p } from '@libp2p/interface'
10+
import type { ConnectionGater, Libp2p, PeerId } from '@libp2p/interface'
1111

1212
async function createLocalNode (connectionGater: ConnectionGater): Promise<Libp2p> {
1313
return createLibp2p({
@@ -29,7 +29,7 @@ async function createLocalNode (connectionGater: ConnectionGater): Promise<Libp2
2929
})
3030
}
3131

32-
describe('connection-gater', () => {
32+
describe('connection-gater with arrow function properties', () => {
3333
let localNode: Libp2p
3434
let remoteNode: Libp2p
3535

@@ -169,3 +169,185 @@ describe('connection-gater', () => {
169169
expect(connectionGater.denyOutboundUpgradedConnection?.called).to.be.true()
170170
})
171171
})
172+
173+
describe('connection-gater with class methods', () => {
174+
let localNode: Libp2p
175+
let remoteNode: Libp2p
176+
177+
beforeEach(async () => {
178+
remoteNode = await createLibp2p({
179+
addresses: {
180+
listen: [
181+
'/memory/1'
182+
]
183+
},
184+
transports: [
185+
memory()
186+
],
187+
connectionEncrypters: [
188+
plaintext()
189+
],
190+
streamMuxers: [
191+
mplex()
192+
]
193+
})
194+
})
195+
196+
afterEach(async () => {
197+
await localNode?.stop()
198+
await remoteNode?.stop()
199+
})
200+
201+
it('should deny dialling a peer', async () => {
202+
class TestGater implements ConnectionGater {
203+
denyDialPeer (peerId: PeerId): boolean {
204+
return true
205+
}
206+
}
207+
208+
const connectionGater = new TestGater()
209+
const denyDialPeerStub = Sinon.spy(connectionGater, 'denyDialPeer')
210+
211+
localNode = await createLocalNode(connectionGater)
212+
213+
const ma = multiaddr(`/memory/1/p2p/${remoteNode.peerId}`)
214+
215+
await expect(localNode.dial(ma)).to.eventually.be.rejected
216+
.with.property('name', 'DialDeniedError')
217+
218+
expect(denyDialPeerStub.called).to.be.true()
219+
expect(denyDialPeerStub.getCall(0).args[0]).to.deep.equal(
220+
remoteNode.peerId
221+
)
222+
})
223+
224+
it('should deny dialling a multiaddr', async () => {
225+
class TestGater implements ConnectionGater {
226+
denyDialMultiaddr? (multiaddr: any): boolean {
227+
return true
228+
}
229+
}
230+
231+
const connectionGater = new TestGater()
232+
const denyDialMultiaddrStub = Sinon.spy(connectionGater, 'denyDialMultiaddr')
233+
234+
localNode = await createLocalNode(connectionGater)
235+
236+
await expect(localNode.dial(remoteNode.getMultiaddrs())).to.eventually.be.rejected
237+
.with.property('name', 'DialDeniedError')
238+
239+
expect(denyDialMultiaddrStub.called).to.be.true()
240+
})
241+
242+
it('should deny an inbound connection', async () => {
243+
class TestGater implements ConnectionGater {
244+
denyInboundConnection? (maConn: any): boolean {
245+
return true
246+
}
247+
}
248+
249+
const connectionGater = new TestGater()
250+
const denyInboundConnectionStub = Sinon.spy(connectionGater, 'denyInboundConnection')
251+
252+
localNode = await createLocalNode(connectionGater)
253+
254+
await expect(remoteNode.dial(localNode.getMultiaddrs())).to.eventually.be.rejected
255+
.with.property('name', 'EncryptionFailedError')
256+
257+
expect(denyInboundConnectionStub.called).to.be.true()
258+
})
259+
260+
it('should deny an outbound connection', async () => {
261+
class TestGater implements ConnectionGater {
262+
denyOutboundConnection? (peerId: PeerId, maConn: any): boolean {
263+
return true
264+
}
265+
}
266+
267+
const connectionGater = new TestGater()
268+
const denyOutboundConnectionStub = Sinon.spy(connectionGater, 'denyOutboundConnection')
269+
270+
localNode = await createLocalNode(connectionGater)
271+
272+
await expect(localNode.dial(remoteNode.getMultiaddrs(), {
273+
signal: AbortSignal.timeout(10_000)
274+
})).to.eventually.be.rejected
275+
.with.property('name', 'ConnectionInterceptedError')
276+
277+
expect(denyOutboundConnectionStub.called).to.be.true()
278+
})
279+
280+
it('should deny an inbound encrypted connection', async () => {
281+
class TestGater implements ConnectionGater {
282+
denyInboundEncryptedConnection? (peerId: PeerId, maConn: any): boolean {
283+
return true
284+
}
285+
}
286+
287+
const connectionGater = new TestGater()
288+
const denyInboundEncryptedConnectionStub = Sinon.spy(connectionGater, 'denyInboundEncryptedConnection')
289+
290+
localNode = await createLocalNode(connectionGater)
291+
292+
await expect(remoteNode.dial(localNode.getMultiaddrs())).to.eventually.be.rejected
293+
.with.property('name', 'MuxerUnavailableError')
294+
295+
expect(denyInboundEncryptedConnectionStub.called).to.be.true()
296+
})
297+
298+
it('should deny an outbound encrypted connection', async () => {
299+
class TestGater implements ConnectionGater {
300+
denyOutboundEncryptedConnection? (peerId: PeerId, maConn: any): boolean {
301+
return true
302+
}
303+
}
304+
305+
const connectionGater = new TestGater()
306+
const denyOutboundEncryptedConnectionStub = Sinon.spy(connectionGater, 'denyOutboundEncryptedConnection')
307+
308+
localNode = await createLocalNode(connectionGater)
309+
310+
await expect(localNode.dial(remoteNode.getMultiaddrs())).to.eventually.be.rejected
311+
.with.property('name', 'ConnectionInterceptedError')
312+
313+
expect(denyOutboundEncryptedConnectionStub.called).to.be.true()
314+
})
315+
316+
it('should deny an inbound upgraded connection', async () => {
317+
class TestGater implements ConnectionGater {
318+
denyInboundUpgradedConnection? (peerId: PeerId, maConn: any): boolean {
319+
return true
320+
}
321+
}
322+
323+
const connectionGater = new TestGater()
324+
const denyInboundUpgradedConnectionStub = Sinon.spy(connectionGater, 'denyInboundUpgradedConnection')
325+
326+
localNode = await createLocalNode(connectionGater)
327+
328+
remoteNode.dial(localNode.getMultiaddrs()).catch(() => {})
329+
330+
await delay(100)
331+
332+
expect(localNode.getConnections()).to.be.empty()
333+
expect(denyInboundUpgradedConnectionStub.called).to.be.true()
334+
})
335+
336+
it('should deny an outbound upgraded connection', async () => {
337+
class TestGater implements ConnectionGater {
338+
denyOutboundUpgradedConnection? (peerId: PeerId, maConn: any): boolean {
339+
return true
340+
}
341+
}
342+
343+
const connectionGater = new TestGater()
344+
const denyOutboundUpgradedConnectionStub = Sinon.spy(connectionGater, 'denyOutboundUpgradedConnection')
345+
346+
localNode = await createLocalNode(connectionGater)
347+
348+
await expect(localNode.dial(remoteNode.getMultiaddrs())).to.eventually.be.rejected
349+
.with.property('name', 'ConnectionInterceptedError')
350+
351+
expect(denyOutboundUpgradedConnectionStub.called).to.be.true()
352+
})
353+
})

packages/libp2p/src/config/connection-gater.browser.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,17 @@ import type { Multiaddr } from '@multiformats/multiaddr'
1313
* confusion.
1414
*/
1515
export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
16-
return {
17-
denyDialPeer: async () => false,
18-
denyDialMultiaddr: async (multiaddr: Multiaddr) => {
16+
if (gater.denyDialMultiaddr == null) {
17+
gater.denyDialMultiaddr = (multiaddr: Multiaddr) => {
1918
// do not connect to insecure websockets by default
2019
if (WebSockets.matches(multiaddr)) {
2120
return true
2221
}
2322

2423
// do not connect to private addresses by default
2524
return isPrivate(multiaddr)
26-
},
27-
denyInboundConnection: async () => false,
28-
denyOutboundConnection: async () => false,
29-
denyInboundEncryptedConnection: async () => false,
30-
denyOutboundEncryptedConnection: async () => false,
31-
denyInboundUpgradedConnection: async () => false,
32-
denyOutboundUpgradedConnection: async () => false,
33-
filterMultiaddrForPeer: async () => true,
34-
...gater
25+
}
3526
}
27+
28+
return gater
3629
}

packages/libp2p/src/config/connection-gater.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,5 @@ import type { ConnectionGater } from '@libp2p/interface'
44
* Returns a default connection gater implementation that allows everything
55
*/
66
export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
7-
return {
8-
denyDialPeer: async () => false,
9-
denyDialMultiaddr: async () => false,
10-
denyInboundConnection: async () => false,
11-
denyOutboundConnection: async () => false,
12-
denyInboundEncryptedConnection: async () => false,
13-
denyOutboundEncryptedConnection: async () => false,
14-
denyInboundUpgradedConnection: async () => false,
15-
denyOutboundUpgradedConnection: async () => false,
16-
filterMultiaddrForPeer: async () => true,
17-
...gater
18-
}
7+
return gater
198
}

0 commit comments

Comments
 (0)