Skip to content

Commit 69fae82

Browse files
committed
fix: allow connection gater classes (#3281)
Allow connection gaters to be class instances as well as objects with function properties.
1 parent 956ec37 commit 69fae82

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

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

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ const CODEC_IP6 = 0x29
1616
* confusion.
1717
*/
1818
export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
19-
return {
20-
denyDialPeer: async () => false,
21-
denyDialMultiaddr: async (multiaddr: Multiaddr) => {
19+
if (gater.denyDialMultiaddr == null) {
20+
gater.denyDialMultiaddr = (multiaddr: Multiaddr) => {
2221
// do not connect to insecure websockets by default
2322
if (WebSockets.matches(multiaddr)) {
2423
return false
@@ -32,14 +31,8 @@ export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
3231
}
3332

3433
return false
35-
},
36-
denyInboundConnection: async () => false,
37-
denyOutboundConnection: async () => false,
38-
denyInboundEncryptedConnection: async () => false,
39-
denyOutboundEncryptedConnection: async () => false,
40-
denyInboundUpgradedConnection: async () => false,
41-
denyOutboundUpgradedConnection: async () => false,
42-
filterMultiaddrForPeer: async () => true,
43-
...gater
34+
}
4435
}
36+
37+
return gater
4538
}

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)