-
Notifications
You must be signed in to change notification settings - Fork 3
feat: rendezvous protocol initial implementation #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
b8bf044
d7290df
b080670
ebb22d1
9765a95
b6edaf3
0e304f9
b248924
47641f7
b668c8a
7e3c541
1a1590d
b357829
4abd363
7763df2
63d607b
5f45c6f
640b64f
8f6e148
894ad2e
ee10d69
3798fbb
cdf2f6b
9fe0691
52fa2bd
06d53ac
d501d97
a2d5f83
83cd4b7
9b294f5
9bf5bcb
7a569c7
c297156
e1cd224
264ce2a
01ec7bc
5f025d3
2840251
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,4 @@ | |
|
|
||
| exports.PROTOCOL_MULTICODEC = '/rendezvous/1.0.0' | ||
| exports.MAX_NS_LENGTH = 255 // TODO: spec this | ||
| exports.MAX_LIMIT = 1000 // TODO: spec this | ||
| exports.MAX_LIMIT = 1000 | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,24 +21,69 @@ const rpc = require('./rpc') | |
| class RendezvousServer { | ||
| /** | ||
| * @constructor | ||
| * @param {object} params | ||
| * @param {Registrar} params.registrar | ||
| * @param {Registrar} registrar | ||
| * @param {object} options | ||
| * @param {number} options.gcInterval | ||
| */ | ||
| constructor ({ registrar }) { | ||
| constructor (registrar, { gcInterval = 3e5 } = {}) { | ||
| this._registrar = registrar | ||
| this._gcInterval = gcInterval | ||
|
|
||
| /** | ||
| * Registrations per namespace. | ||
| * @type {Map<string, Map<string, Registration>>} | ||
| */ | ||
| this.registrations = new Map() | ||
| } | ||
|
|
||
| /** | ||
| * Start rendezvous server for handling rendezvous streams and gc. | ||
| * @returns {void} | ||
| */ | ||
| start () { | ||
| if (this._interval) { | ||
| return | ||
| } | ||
|
|
||
| log('starting') | ||
|
|
||
| // Garbage collection | ||
| this._interval = setInterval(this._gc, this._gcInterval) | ||
|
|
||
| // Incoming streams handling | ||
| this._registrar.handle(PROTOCOL_MULTICODEC, rpc(this)) | ||
|
|
||
| log('started') | ||
| } | ||
|
|
||
| /** | ||
| * Stops rendezvous server gc and clears registrations | ||
| */ | ||
| stop () { | ||
| clearInterval(this._interval) | ||
| this._interval = undefined | ||
| this.registrations.clear() | ||
|
|
||
| log('stopped') | ||
|
||
| } | ||
|
|
||
| // TODO: Should we have a start method to gv the expired registrations? | ||
| // I am removing them on discover, but it should be useful to have a gc too | ||
| /** | ||
| * Garbage collector to removed outdated registrations. | ||
| * @returns {void} | ||
| */ | ||
| _gc () { | ||
| const now = Date.now() | ||
|
|
||
| // Iterate namespaces | ||
| this.registrations.forEach((nsRegistrations) => { | ||
| // Iterate registrations for namespaces | ||
| nsRegistrations.forEach((reg, idStr) => { | ||
| if (now >= reg.expiration) { | ||
| nsRegistrations.delete(idStr) | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Add a peer registration to a namespace. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| 'use strict' | ||
| /* eslint-env mocha */ | ||
|
|
||
| const chai = require('chai') | ||
| chai.use(require('dirty-chai')) | ||
| chai.use(require('chai-as-promised')) | ||
| const { expect } = chai | ||
|
|
||
| const delay = require('delay') | ||
| const sinon = require('sinon') | ||
| const multiaddr = require('multiaddr') | ||
|
|
||
| const RendezvousServer = require('../src/server') | ||
|
|
||
| const { createPeerId } = require('./utils') | ||
|
|
||
| const registrar = { | ||
| handle: () => { } | ||
| } | ||
| const testNamespace = 'test-namespace' | ||
| const multiaddrs = [multiaddr('/ip4/127.0.0.1/tcp/0')].map((m) => m.buffer) | ||
|
|
||
| describe('rendezvous server', () => { | ||
| let rServer | ||
| let peerIds | ||
|
|
||
| before(async () => { | ||
| peerIds = await createPeerId({ number: 3 }) | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| rServer && rServer.stop() | ||
| }) | ||
|
|
||
| it('calls registrar handle on start once', () => { | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Spy for handle | ||
| const spyHandle = sinon.spy(registrar, 'handle') | ||
|
|
||
| rServer.start() | ||
| expect(spyHandle).to.have.property('callCount', 1) | ||
|
|
||
| rServer.start() | ||
| expect(spyHandle).to.have.property('callCount', 1) | ||
| }) | ||
|
|
||
| it('can add registrations to multiple namespaces', () => { | ||
| const otherNamespace = 'other-namespace' | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 1000) | ||
| // Add registration for peer 1 in a different namespace | ||
| rServer.addRegistration(otherNamespace, peerIds[0], multiaddrs, 1000) | ||
|
|
||
| // Add registration for peer 2 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[1], multiaddrs, 1000) | ||
|
|
||
| const testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(2) | ||
|
|
||
| const otherNsRegistrations = rServer.getRegistrations(otherNamespace) | ||
| expect(otherNsRegistrations).to.have.lengthOf(1) | ||
| }) | ||
|
|
||
| it('should be able to limit registrations to get', () => { | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 1000) | ||
| // Add registration for peer 2 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[1], multiaddrs, 1000) | ||
|
|
||
| let testNsRegistrations = rServer.getRegistrations(testNamespace, 1) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
|
|
||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(2) | ||
| }) | ||
|
|
||
| it('can remove registrations from a peer in a given namespace', () => { | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 1000) | ||
| // Add registration for peer 2 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[1], multiaddrs, 1000) | ||
|
|
||
| let testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(2) | ||
|
|
||
| // Remove registration for peer0 | ||
| rServer.removeRegistration(testNamespace, peerIds[0]) | ||
|
|
||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
| }) | ||
|
|
||
| it('can remove all registrations from a peer', () => { | ||
| const otherNamespace = 'other-namespace' | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 1000) | ||
| // Add registration for peer 1 in a different namespace | ||
| rServer.addRegistration(otherNamespace, peerIds[0], multiaddrs, 1000) | ||
|
|
||
| let testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
|
|
||
| let otherNsRegistrations = rServer.getRegistrations(otherNamespace) | ||
| expect(otherNsRegistrations).to.have.lengthOf(1) | ||
|
|
||
| // Remove all registrations for peer0 | ||
| rServer.removePeerRegistrations(peerIds[0]) | ||
|
|
||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(0) | ||
|
|
||
| otherNsRegistrations = rServer.getRegistrations(otherNamespace) | ||
| expect(otherNsRegistrations).to.have.lengthOf(0) | ||
| }) | ||
|
|
||
| it('can attempt to remove a registration for a non existent namespace', () => { | ||
| const otherNamespace = 'other-namespace' | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| rServer.removeRegistration(otherNamespace, peerIds[0]) | ||
| }) | ||
|
|
||
| it('can attempt to remove a registration for a non existent peer', () => { | ||
| rServer = new RendezvousServer(registrar) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 1000) | ||
|
|
||
| let testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
|
|
||
| // Remove registration for peer0 | ||
| rServer.removeRegistration(testNamespace, peerIds[1]) | ||
|
|
||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
| }) | ||
|
|
||
| it('gc expired records', async () => { | ||
| rServer = new RendezvousServer(registrar, { gcInterval: 300 }) | ||
|
|
||
| // Add registration for peer 1 in test namespace | ||
| rServer.addRegistration(testNamespace, peerIds[0], multiaddrs, 500) | ||
| rServer.addRegistration(testNamespace, peerIds[1], multiaddrs, 1000) | ||
|
|
||
| let testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(2) | ||
|
|
||
| // wait for firt record to be removed | ||
| await delay(650) | ||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(1) | ||
|
|
||
| await delay(400) | ||
| testNsRegistrations = rServer.getRegistrations(testNamespace) | ||
| expect(testNsRegistrations).to.have.lengthOf(0) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This spec doesn't mention this protocol at all, we should clarify this there and that this protocol runs over libp2p streams.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
libp2p/specs#302