lib/discovery/mdns.js

const _ = require('lodash')
const { join } = require('path')
const mdns = require('mdns-js')
const { EventEmitter2 } = require('eventemitter2')

/**
 * Creates an instance of mDNS browser.
 * @constructor
 * @param {Object} [options] options object
 * @param {String} [options.name=https] Service Name {@link http://www.dns-sd.org/serviceTypes.html|See mDNS Service Types}
 * @param {String} [options.protocol=tcp] Service Protocol to search on, udp or tcp
 * @param {Array}  [options.subtypes] Array of subtypes
 * @param {Number} [options.timeout=5000] Timeout to stop search
 * @param {logger} [options.logger] Optional logger
 * @fires DiscoveryMdns#found
 * @fires DiscoveryMdns#stop
 * @see {@link https://github.com/mdns-js/node-mdns-js/|mdns-js}
 */
class DiscoveryMdns extends EventEmitter2 {
  constructor (opts = {}) {
    super({
      wildcard: true
    })
    opts = _.defaults(opts, {
      name: 'https',
      protocol: 'tcp',
      subtypes: [],
      timeout: 5000,
      excludeInterfaces: [],
      logger: false
    })
    /**
     * @prop {logger} logger the logger of this instance
     */
    if (_.isBoolean(opts.logger)) {
      opts.logger = new (require(join(__dirname, '..', 'logger')))({
        level: opts.logger === true ? 'debug' : 'fatal',
        pretty: opts.logger
      })
    }
    this.logger = opts.logger
    /**
     * @prop {Array} excludeInterfaces network interfaces that are excluded
     */
    this.excludeInterfaces = opts.excludeInterfaces
    this.excludeInterfaces.forEach(function (i) {
      if (i && i.length) mdns.excludeInterface(i)
    })
    /**
     * @prop {Number} timeout search timeout in milliseconds
     */
    this.timeout = opts.timeout
    /**
     * @prop {String} name Service name
     */
    this.name = opts.name
    /**
     * @prop {String} protocol Service protocol
     */
    this.protocol = opts.protocol
    /**
     * @prop {Array} subtypes Service subtypes to search on
     */
    this.subtypes = opts.subtypes
    /**
     * @prop {Object} browser mDNS browser
     * @see {@link https://github.com/mdns-js/node-mdns-js/|mdns-js}
     */
    this.browser = null
    /**
     * @prop {Function} timeoutFn The timeout search function
     * @private
     */
    this.timeoutFn = null
    return this
  }
  makeBrowser () {
    let _this = this
    return mdns.createBrowser(
      mdns.makeServiceType({
        name: _this.name,
        protocol: _this.protocol,
        subtypes: _this.subtypes
      })
    )
  }

  start () {
    let _this = this
    if (_.isNull(this.browser)) {
      this.browser = this.makeBrowser()
    }
    this.logger.debug({ name: 'discovery.mdns', msg: 'start', browser: this.browser })

    // check for duplicate listeners
    if (this.browser.listeners('ready').length === 0) {
      this.browser.on('ready', function () {
        if (_this.timeout > 0) {
          _this.timeoutFn = setTimeout(function () {
            _this.stop()
          }, _this.timeout)
        }
        _this.browser.discover()
      })
    }
    if (this.browser.listeners('update').length === 0) {
      this.browser.on('update', function (srv) {
        _this.logger.debug({ name: 'discovery.mdns', msg: 'service.found', properties: srv })
        /**
         * service found
         * @event Mdns#found
         * @type {Object}
         */
        _this.emit('found', srv)
      })
    }
    return this
  }

  stop () {
    this.logger.debug({ name: 'discovery.mdns', msg: 'stop' })
    if (this.browser) {
      /**
       * service search finished
       * @event Mdns#done
       * @type {Object}
       */
      this.browser.stop()
      clearTimeout(this.timeoutFn)
      this.browser = null
      this.emit('stop', this.foundServices)
    }
    return this
  }
}

module.exports = DiscoveryMdns