123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- 'use strict'
- var dnsEqual = require('dns-equal')
- var flatten = require('array-flatten')
- var Service = require('./service')
- var REANNOUNCE_MAX_MS = 60 * 60 * 1000
- var REANNOUNCE_FACTOR = 3
- module.exports = Registry
- function Registry (server) {
- this._server = server
- this._services = []
- }
- Registry.prototype.publish = function (opts) {
- var service = new Service(opts)
- service.start = start.bind(service, this)
- service.stop = stop.bind(service, this)
- service.start({ probe: opts.probe !== false })
- return service
- }
- Registry.prototype.unpublishAll = function (cb) {
- teardown(this._server, this._services, cb)
- this._services = []
- }
- Registry.prototype.destroy = function () {
- this._services.forEach(function (service) {
- service._destroyed = true
- })
- }
- function start (registry, opts) {
- if (this._activated) return
- this._activated = true
- registry._services.push(this)
- if (opts.probe) {
- var service = this
- probe(registry._server.mdns, this, function (exists) {
- if (exists) {
- service.stop()
- service.emit('error', new Error('Service name is already in use on the network'))
- return
- }
- announce(registry._server, service)
- })
- } else {
- announce(registry._server, this)
- }
- }
- function stop (registry, cb) {
- if (!this._activated) return
- teardown(registry._server, this, cb)
- var index = registry._services.indexOf(this)
- if (index !== -1) registry._services.splice(index, 1)
- }
- function probe (mdns, service, cb) {
- var sent = false
- var retries = 0
- var timer
- mdns.on('response', onresponse)
- setTimeout(send, Math.random() * 250)
- function send () {
-
- if (!service._activated || service._destroyed) return
- mdns.query(service.fqdn, 'ANY', function () {
-
-
- sent = true
- timer = setTimeout(++retries < 3 ? send : done, 250)
- timer.unref()
- })
- }
- function onresponse (packet) {
-
-
-
-
-
- if (!sent) return
- if (packet.answers.some(matchRR) || packet.additionals.some(matchRR)) done(true)
- }
- function matchRR (rr) {
- return dnsEqual(rr.name, service.fqdn)
- }
- function done (exists) {
- mdns.removeListener('response', onresponse)
- clearTimeout(timer)
- cb(!!exists)
- }
- }
- function announce (server, service) {
- var delay = 1000
- var packet = service._records()
- server.register(packet)
- ;(function broadcast () {
-
- if (!service._activated || service._destroyed) return
- server.mdns.respond(packet, function () {
-
-
- if (!service.published) {
- service._activated = true
- service.published = true
- service.emit('up')
- }
- delay = delay * REANNOUNCE_FACTOR
- if (delay < REANNOUNCE_MAX_MS && !service._destroyed) {
- setTimeout(broadcast, delay).unref()
- }
- })
- })()
- }
- function teardown (server, services, cb) {
- if (!Array.isArray(services)) services = [services]
- services = services.filter(function (service) {
- return service._activated
- })
- var records = flatten.depth(services.map(function (service) {
- service._activated = false
- var records = service._records()
- records.forEach(function (record) {
- record.ttl = 0
- })
- return records
- }), 1)
- if (records.length === 0) return cb && cb()
- server.unregister(records)
-
- server.mdns.respond(records, function () {
- services.forEach(function (service) {
- service.published = false
- })
- if (cb) cb.apply(null, arguments)
- })
- }
|