Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/libp2p/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type { ConnectionManagerInit } from './connection-manager/index.js'
import type { ConnectionMonitorInit } from './connection-monitor.js'
import type { TransportManagerInit } from './transport-manager.js'
import type { Libp2p, ServiceMap, ComponentLogger, NodeInfo, ConnectionProtector, ConnectionEncrypter, ConnectionGater, ContentRouting, Metrics, PeerDiscovery, PeerRouting, StreamMuxerFactory, Transport, PrivateKey } from '@libp2p/interface'
import type { Registrar } from '@libp2p/interface-internal'
import type { PersistentPeerStoreInit } from '@libp2p/peer-store'
import type { DNS } from '@multiformats/dns'
import type { Datastore } from 'interface-datastore'
Expand Down Expand Up @@ -132,6 +133,11 @@ export interface Libp2pInit<T extends ServiceMap = ServiceMap> {
*/
connectionProtector?(components: Components): ConnectionProtector

/**
* A registrar implementation that can be used to modify protocol handling
*/
registrar?(components: Components): Registrar

/**
* Arbitrary libp2p modules
*/
Expand Down
8 changes: 6 additions & 2 deletions packages/libp2p/src/libp2p.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,12 @@ export class Libp2p<T extends ServiceMap = ServiceMap> extends TypedEventEmitter
this.configureComponent('connectionMonitor', new ConnectionMonitor(this.components, init.connectionMonitor))
}

// Create the Registrar
this.configureComponent('registrar', new Registrar(this.components))
// Set up a custom registrar if configured or use default
if (init.registrar != null) {
this.configureComponent('registrar', init.registrar(this.components))
} else {
this.configureComponent('registrar', new Registrar(this.components))
}

// Addresses {listen, announce, noAnnounce}
this.configureComponent('addressManager', new AddressManager(this.components, init.addresses))
Expand Down
77 changes: 77 additions & 0 deletions packages/libp2p/test/registrar/custom-registrar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* eslint-env mocha */

import { expect } from 'aegir/chai'
import sinon from 'sinon'
import { createLibp2p } from '../../src/index.js'
import type { Libp2p } from '@libp2p/interface'
import type { Registrar } from '@libp2p/interface-internal'

describe('custom registrar functionality', () => {
let libp2p: Libp2p
let customRegistrar: {
handle: sinon.SinonStub<any, Promise<void>>
unhandle: sinon.SinonStub<any, Promise<void>>
getProtocols: sinon.SinonStub<any, string[]>
register: sinon.SinonStub<any, Promise<string>>
unregister: sinon.SinonStub<any, void>
getHandler: sinon.SinonStub<any, { handler(): void, options: Record<string, unknown> }>
getTopologies: sinon.SinonStub<any, any[]>
}

beforeEach(async (): Promise<void> => {
customRegistrar = {
handle: sinon.stub().resolves(),
unhandle: sinon.stub().resolves(),
getProtocols: sinon.stub().returns(['/custom/1.0.0']),
register: sinon.stub().resolves('custom-topology-id'),
unregister: sinon.stub(),
getHandler: sinon.stub().returns({ handler: (): void => {}, options: {} }),
getTopologies: sinon.stub().returns([])
}

libp2p = await createLibp2p({
registrar: (_components) => customRegistrar as unknown as Registrar
})
})

afterEach(async (): Promise<void> => {
await libp2p?.stop()
sinon.restore()
})

it('should reflect custom getProtocols implementation', (): void => {
const protocols: string[] = libp2p.getProtocols()
expect(protocols).to.deep.equal(['/custom/1.0.0'])
expect(customRegistrar.getProtocols.callCount).to.equal(1)
})

it('should call custom registrar handle method when registering a protocol handler', async (): Promise<void> => {
const testHandler = (): void => {}
await libp2p.handle('/custom/1.0.0', testHandler)
expect(customRegistrar.handle.callCount).to.equal(1)
expect(customRegistrar.handle.firstCall.args).to.deep.equal(['/custom/1.0.0', testHandler, undefined])
})

it('should call custom registrar unhandle method when unregistering a protocol handler', async (): Promise<void> => {
await libp2p.unhandle('/custom/1.0.0')
expect(customRegistrar.unhandle.callCount).to.equal(1)
expect(customRegistrar.unhandle.firstCall.args).to.deep.equal(['/custom/1.0.0'])
})

it('should use custom registrar register method for topologies', async (): Promise<void> => {
const topology = {
onConnect: (): void => {},
onDisconnect: (): void => {}
}
const topologyId: string = await libp2p.register('/custom/1.0.0', topology)
expect(topologyId).to.equal('custom-topology-id')
expect(customRegistrar.register.callCount).to.equal(1)
expect(customRegistrar.register.firstCall.args).to.deep.equal(['/custom/1.0.0', topology])
})

it('should call custom registrar unregister method for topologies', (): void => {
libp2p.unregister('custom-topology-id')
expect(customRegistrar.unregister.callCount).to.equal(1)
expect(customRegistrar.unregister.firstCall.args).to.deep.equal(['custom-topology-id'])
})
})