mrf.js

const esl = require('modesl') ;
const assert = require('assert') ;
const MediaServer = require('./mediaserver') ;
const Emitter = require('events').EventEmitter ;
const os = require('os');
const parseBodyText = require('./utils').parseBodyText;

/**
 * Creates a media resource framework instance.
 * @constructor
 * @param {Srf} srf Srf instance
 * @param {Mrf~createOptions} [opts] configuration options
 */
class Mrf extends Emitter {

  constructor(srf, opts) {
    super() ;

    opts = opts || {} ;

    this._srf = srf ;
    this.debugDir = opts.debugDir ;
    this.debugSendonly = opts.sendonly ;
    this.mediaservers = [] ;
    this.localAddresses = [];

    const interfaces = os.networkInterfaces();
    for (const k in interfaces) {
      for (const k2 in interfaces[k]) {
        const address = interfaces[k][k2];
        if (address.family === 'IPv4' && !address.internal) {
          this.localAddresses.push(address.address);
        }
      }
    }
  }

  get srf() {
    return this._srf ;
  }

  /**
   * connect to a specified media server
   * @param  {Mrf~ConnectOptions}   opts options describing media server to connect to
   * @param  {Mrf~ConnectCallback} [callback] callback
   * @return {Promise} if no callback is specified
   */
  connect(opts, callback) {
    assert.equal(typeof opts, 'object', 'argument \'opts\' must be provided with connection options') ;
    assert.equal(typeof opts.address, 'string', `argument \'opts.address\' containing 
      media server address is required`) ;

    const address = opts.address ;
    const port = opts.port || 8021 ;
    const secret = opts.secret || 'ClueCon' ;
    const listenPort = opts.listenPort || 0 ; // 0 means any available port
    const listenAddress = opts.listenAddress || this.localAddresses[0] || '0.0.0.0' ;

    function _onError(callback, err) {
      callback(err);
    }

    const __x = (callback) => {
      const listener = _onError.bind(this, callback) ;
      const conn = new esl.Connection(address, port, secret, () => {

        //...until we have initially connected and created a MediaServer object (which takes over error reporting)
        conn.removeListener('error', listener) ;

        const ms = new MediaServer(conn, this, listenAddress, listenPort) ;
        this.mediaservers.push(ms) ;

        ms.once('ready', () => {
          callback(null, ms) ;
        }) ;
      });

      conn.on('error', listener);
      conn.on('esl::event::raw::text/rude-rejection', _onError.bind(this, callback, new Error('acl-error')));
    };

    if (callback) return __x(callback) ;

    return new Promise((resolve, reject) => {
      __x((err, mediaserver) => {
        if (err) return reject(err);
        resolve(mediaserver);
      });
    });
  }

}

/**
/**
 * This callback provides the response to a connection attempt to a freeswitch server
 * @callback Mrf~ConnectCallback
 * @param {Error} err connection error, if any
 * @param {MediaServer} ms - MediaServer instance
 */


/**
 * Arguments provided when connecting to a freeswitch media server
 * @typedef {Object} Mrf~ConnectOptions
 * @property {String} address - hostname or IP address to connect to
 * @property {Number} [port=8021] - TCP port to connect to (freeswitch event socket)
 * @property {String} [secret=ClueCon] - freeswitch authentication secret
 * @property {String} [listenAddress=auto-discovered public IP address or 127.0.0.1] -
 * local TCP address to listen for external connections from the freeswitch media server
 * @property {Number} [listenPort=8085] -  local TCP port to listen for external
 * connections from the freeswitch media server
 */

/**
 * Options governing the creation of an mrf instance
 * @typedef {Object} Mrf~createOptions
 * @property {string} [debugDir] directory into which message trace files;
 the presence of this param will enable debug tracing
 */


Mrf.utils = {parseBodyText};

module.exports = exports = Mrf ;