Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

No need to poll #71

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ const mdns = new MDNS(peerInfo, options)
mdns.on('peer', (peerInfo) => {
console.log('Found a peer in the local network', peerInfo.id.toB58String())
})
process.on('exit', mdns.stop)

// Broadcast for 20 seconds
mdns.start(() => setTimeout(() => mdns.stop(() => {}), 20 * 1000))
// Listen for peers
mdns.start()
```

- options
- `broadcast` - (true/false) announce our presence through mDNS, default false
- `interval` - query interval, default 10 * 1000 (10 seconds)
- `broadcast` - (true/false) announce our presence through mDNS, default true
- `serviceTag` - name of the service announcedm, default 'ipfs.local`
29 changes: 15 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ class MulticastDNS extends EventEmitter {
options = options || {}

this.broadcast = options.broadcast !== false
this.interval = options.interval || (1e3 * 10)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@richardschneider instead of removing the feature all together, it should support for a single beacon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure why you want a beacon?

interval controlled how often an MDNS query is sent. Every time it is sent, all other IPFS nodes must respond with their DNS records.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diasdavid any comments?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see a need to drop the interval beacon feature. The module should support both, single beacon and interval.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First off, a beacon is not what MDNS is about. Secondly it can be very expensive.

Consider a data centre with 100 servers all running IPFS with an interval of 10 minutes, or 600 seconds. On average, a node will beacon every 6 seconds. Then every 6 seconds all other nodes must wake-up and reply with their DNS records. All nodes will then see all the nodes responses and have to then .emit discovered nodes. It's a real waste of network bandwidth and CPU.

this.serviceTag = options.serviceTag || 'ipfs.local'
this.port = options.port || 5353
this.peerInfo = peerInfo
this._queryInterval = null
}

start (callback) {
Expand All @@ -25,10 +23,11 @@ class MulticastDNS extends EventEmitter {

this.mdns = mdns

this._queryInterval = query.queryLAN(this.mdns, this.serviceTag, this.interval)

mdns.on('response', (event) => {
query.gotResponse(event, this.peerInfo, this.serviceTag, (err, foundPeer) => {
if (!self.mdns) {
return
}
query.gotResponse(event, self.peerInfo, self.serviceTag, (err, foundPeer) => {
if (err) {
return log('Error processing peer response', err)
}
Expand All @@ -38,21 +37,23 @@ class MulticastDNS extends EventEmitter {
})

mdns.on('query', (event) => {
query.gotQuery(event, this.mdns, this.peerInfo, this.serviceTag, this.broadcast)
if (!self.mdns) {
return
}
query.gotQuery(event, self.mdns, self.peerInfo, self.serviceTag, self.broadcast)
})

setImmediate(() => callback())
query.queryLAN(this.mdns, this.serviceTag)
setImmediate(callback)
}

stop (callback) {
if (!this.mdns) {
callback(new Error('MulticastDNS service had not started yet'))
} else {
clearInterval(this._queryInterval)
this._queryInterval = null
this.mdns.destroy(callback)
this.mdns = undefined
const mdns = this.mdns
if (!mdns) {
return callback(new Error('MulticastDNS service had not started yet'))
}
this.mdns = undefined
mdns.destroy(callback)
}
}

Expand Down
20 changes: 7 additions & 13 deletions src/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,13 @@ const tcp = new TCP()
module.exports = {

queryLAN: function (mdns, serviceTag, interval) {
const query = () => {
log('query', serviceTag)
mdns.query({
questions: [{
name: serviceTag,
type: 'PTR'
}]
})
}

// Immediately start a query, then do it every interval.
query()
return setInterval(query, interval)
log('query', serviceTag)
mdns.query({
questions: [{
name: serviceTag,
type: 'PTR'
}]
})
},

gotResponse: function (rsp, peerInfo, serviceTag, callback) {
Expand Down
139 changes: 82 additions & 57 deletions test/multicast-dns.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,29 @@ const expect = chai.expect
chai.use(dirtyChai)
const multiaddr = require('multiaddr')
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
const parallel = require('async/parallel')
const series = require('async/series')

const MulticastDNS = require('./../src')

function createPeer (callback) {
PeerId.create({ bits: 512 }, (err, id) => {
if (err) throw err
PeerInfo.create(id, callback)
})
}

describe('MulticastDNS', () => {
let pA
let pB
let pC
let pD

before(function (done) {
this.timeout(80 * 1000)
parallel([
(cb) => {
PeerInfo.create((err, peer) => {
createPeer((err, peer) => {
expect(err).to.not.exist()

pA = peer
Expand All @@ -31,7 +38,7 @@ describe('MulticastDNS', () => {
})
},
(cb) => {
PeerInfo.create((err, peer) => {
createPeer((err, peer) => {
expect(err).to.not.exist()

pB = peer
Expand All @@ -41,7 +48,7 @@ describe('MulticastDNS', () => {
})
},
(cb) => {
PeerInfo.create((err, peer) => {
createPeer((err, peer) => {
expect(err).to.not.exist()
pC = peer
pC.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/20003'))
Expand All @@ -50,7 +57,7 @@ describe('MulticastDNS', () => {
})
},
(cb) => {
PeerInfo.create((err, peer) => {
createPeer((err, peer) => {
if (err) { cb(err) }
pD = peer
pD.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/30003/ws'))
Expand All @@ -61,8 +68,6 @@ describe('MulticastDNS', () => {
})

it('find another peer', function (done) {
this.timeout(40 * 1000)

const options = {
port: 50001 // port must be the same
}
Expand All @@ -72,25 +77,22 @@ describe('MulticastDNS', () => {
})
const mdnsB = new MulticastDNS(pB, options)

mdnsA.once('peer', (peerInfo) => {
expect(pB.id.toB58String()).to.eql(peerInfo.id.toB58String())
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsB.stop(cb)
], done)
})

mdnsB.once('peer', (peerInfo) => {})
parallel([
(cb) => mdnsA.start(cb),
(cb) => mdnsB.start(cb)
], () => {
mdnsA.once('peer', (peerInfo) => {
expect(pB.id.toB58String()).to.eql(peerInfo.id.toB58String())
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsB.stop(cb)
], done)
})

mdnsB.once('peer', (peerInfo) => {})
})
], () => {})
})

it('only announce TCP multiaddrs', function (done) {
this.timeout(40 * 1000)

const options = {
port: 50003 // port must be the same
}
Expand All @@ -102,74 +104,97 @@ describe('MulticastDNS', () => {
const mdnsC = new MulticastDNS(pC, options)
const mdnsD = new MulticastDNS(pD, options)

mdnsA.once('peer', (peerInfo) => {
expect(pC.id.toB58String()).to.eql(peerInfo.id.toB58String())
expect(peerInfo.multiaddrs.size).to.equal(1)
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsC.stop(cb),
(cb) => mdnsD.stop(cb)
], done)
})
mdnsC.once('peer', (peerInfo) => {})
parallel([
(cb) => mdnsA.start(cb),
(cb) => mdnsC.start(cb),
(cb) => mdnsD.start(cb)

], () => {
mdnsA.once('peer', (peerInfo) => {
expect(pC.id.toB58String()).to.eql(peerInfo.id.toB58String())
expect(peerInfo.multiaddrs.size).to.equal(1)
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsC.stop(cb),
(cb) => mdnsD.stop(cb)
], done)
})

mdnsC.once('peer', (peerInfo) => {})
})
], () => {})
})

it('announces IP6 addresses', function (done) {
this.timeout(40 * 1000)

const options = {
port: 50001 // port must be the same
}
const mdnsA = new MulticastDNS(pA, {
broadcast: false, // do not talk to ourself
port: 50001
})
const mdnsA = new MulticastDNS(pA, options)
const mdnsB = new MulticastDNS(pB, options)

mdnsA.once('peer', (peerInfo) => {
expect(pB.id.toB58String()).to.eql(peerInfo.id.toB58String())
expect(peerInfo.multiaddrs.size).to.equal(2)
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsB.stop(cb)
], done)
})
mdnsB.once('peer', (peerInfo) => {})
series([
(cb) => mdnsB.start(cb),
(cb) => mdnsA.start(cb)
], () => {
mdnsA.once('peer', (peerInfo) => {
expect(pB.id.toB58String()).to.eql(peerInfo.id.toB58String())
expect(peerInfo.multiaddrs.size).to.equal(2)
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsB.stop(cb)
], done)
})

mdnsB.once('peer', (peerInfo) => {})
})
], () => {})
})

it('doesn\'t emit peers after stop', function (done) {
this.timeout(40 * 1000)

const options = {
port: 50004 // port must be the same
}
const mdnsA = new MulticastDNS(pA, options)
const mdnsC = new MulticastDNS(pC, options)
mdnsC.once('peer', (peerInfo) => {
done(new Error('Should not receive new peer.'))
})

series([
(cb) => mdnsA.start(cb),
(cb) => setTimeout(cb, 1000),
(cb) => mdnsA.stop(cb),
(cb) => mdnsC.start(cb)
], () => {
setTimeout(() => mdnsC.stop(done), 5000)
mdnsC.once('peer', (peerInfo) => {
done(new Error('Should not receive new peer.'))
})
setTimeout(() => mdnsC.stop(done), 1000)
})
})

it('find all peers', function (done) {
const options = {
port: 50001 // port must be the same
}
const mdnsA = new MulticastDNS(pA, options)
const mdnsB = new MulticastDNS(pB, options)
const mdnsC = new MulticastDNS(pC, options)
const peersA = {}
const peersB = {}
const peersC = {}

// After all the peers have started, each peer should see two other peers.
function check (receiver, peerInfo) {
receiver[peerInfo.id.toB58String()] = true
if (Object.keys(peersA).length === 2 && Object.keys(peersB).length === 2 && Object.keys(peersC).length === 2) {
parallel([
(cb) => mdnsA.stop(cb),
(cb) => mdnsB.stop(cb),
(cb) => mdnsC.stop(cb)
], done)
}
}
mdnsA.on('peer', (peerInfo) => check(peersA, peerInfo))
mdnsB.on('peer', (peerInfo) => check(peersB, peerInfo))
mdnsC.on('peer', (peerInfo) => check(peersC, peerInfo))
series([
(cb) => mdnsA.start(cb),
(cb) => setTimeout(cb, 500),
(cb) => mdnsB.start(cb),
(cb) => setTimeout(cb, 500),
(cb) => mdnsC.start(cb)
], () => {})
})
})