Source: DebuggerHost.js

"use strict";

var events = require("events"),
    net = require("net");

var DebuggerCommands = require("./DebuggerCommands"),
    Logger = require("./logger");

module.exports = DebuggerHost;

DebuggerHost.CONNECTION_EVENT = "connection";
DebuggerHost.DISCONNECTION_EVENT = "disconnection";
DebuggerHost.LISTENING_EVENT = "listening";
DebuggerHost.CLOSE_EVENT = "close";

/**
 * @typedef {Object} DebuggerHostConfiguration
 * @property {string|Writable} log - If a string, path to a log file, else a Writable stream.
 * @property {int} port - The port to listen on.
 */

/**
 * A TCP based host that a Perl debugger can connect to.
 * <p>
 * Emits the following events:
 * <ul>
 *  <li>connection - When a client connects to the TCP server.</li>
 *  <li>disconnection - When a client disconnects from the TCP server.</li>
 *  <li>listening - When the TCP server is ready for clients.</li>
 *  <li>close - When the TCP server shuts down.</li>
 * </ul>
 * @param {DebuggerHostConfiguration} config
 * @constructor
 */
function DebuggerHost(config) {
  if (!config.port) {
    throw new Error("Can't start a DebuggerHost without a port");
  }

  this._config = config;
  this._logger = Logger.initLogger(config);

  this._emitter = new events.EventEmitter();
  this._commands = this._createTcpServer({
    log: this._logger.log
  });

  this._proxyServerEvents();
}

/**
 * Listen for events.
 * @param {string} event
 * @param {function} handler
 */
DebuggerHost.prototype.on = function(event, handler) {
  this._emitter.on(event, handler);
};

/**
 * Instruct the host to start listening for clients.
 * <p>
 * Proxies net.Server.listen
 */
DebuggerHost.prototype.listen = function() {
  var port = this._config.port;

  this._server.listen(port);
  this._logger("Listening on port " + port);
};

/**
 * Instruct the host to stop listening for clients
 * <p>
 * Proxies net.Server.close
 */
DebuggerHost.prototype.close = function() {
  this._server.close();
};

/**
 * @returns {DebuggerCommands} An interface for executing commands on the perl debugger.
 */
DebuggerHost.prototype.commands = function() {
  return this._commands;
};

DebuggerHost.prototype._createTcpServer = function(config) {
  var self = this,
      commands = new DebuggerCommands(null, config);

  this._server = net.createServer();

  this._server.on("connection", function(socket) {
    self._logger("Client connected");

    socket.setEncoding("utf8");
    commands.connect(socket);

    socket.on("end", function () {
      self._logger("Client disconnected");

      self._emitter.emit(DebuggerHost.DISCONNECTION_EVENT);
    });

    if (self._logger.log) {
      socket.pipe(self._logger.log);
    }

    self._emitter.emit(DebuggerHost.CONNECTION_EVENT);
  });

  return commands;
};

DebuggerHost.prototype._proxyServerEvents = function() {
  var self = this;

  [ DebuggerHost.LISTENING_EVENT, DebuggerHost.CLOSE_EVENT ].forEach(function(event) {
    self._server.on(event, function() {
      self._emitter.emit(event);
    });
  });
};