freedomjs/freedom-for-node

View on GitHub
providers/core.udpsocket.js

Summary

Maintainability
A
0 mins
Test Coverage
/*globals require, console, Uint8Array */
/*jslint node:true,sloppy:true */

/**
 * A freedom.js udp socket provider on Node Streams
 * @constructor
 * @private
 * @param {Port} channel the module creating this provider.
 * @param {Function} dispatchEvent Method for emitting events.
 */
var UdpSocket_node = function (cap, dispatchEvent) {
  this.dispatchEvent = dispatchEvent;
  this.dgram = require('dgram');

  this.state = UdpSocket_node.state.NEW;
};

UdpSocket_node.state = {
  NEW: 0,
  BINDING: 1,
  OPEN: 2,
  CLOSED: 3
};

UdpSocket_node.prototype.getInfo = function (callback) {
  if (this.state === UdpSocket_node.state.NEW ||
      this.state === UdpSocket_node.state.CLOSED) {
    return callback({});
  } else {
    callback({
      localAddress: this.connection.address().address,
      localPort: this.connection.address().port
    });
  }
};

UdpSocket_node.prototype.sendTo = function (data, host, port, callback) {
  if (this.state !== UdpSocket_node.state.OPEN) {
    callback(undefined, {
      errcode: "SOCKET_CLOSED",
      message: "Cannot Write on non-connected Socket"
    });
    return;
  }
  var buffer = new Buffer(new Uint8Array(data));
  this.connection.send(buffer, 0, buffer.length, port, host, function (err, bytes) {
    if (err) {
      callback(undefined, {
        errcode: "",
        message: "Send Failed " + err
      });
    } else {
      callback(bytes);
    }
  });
};

/**
 * Connect to a designated location and begin reading.
 * @method bind
 * @param {String} hostname The local host to bind to.
 * @param {number} port The port to bind on.
 * @param {Function} cb Function to call with completion or error.
 */
UdpSocket_node.prototype.bind = function (hostname, port, cb) {
  if (this.state !== UdpSocket_node.state.NEW) {
    console.warn('Attempting to connect on in use socket');
    return cb(false);
  }
  
  this.connection = this.dgram.createSocket('udp4');
  this.state = UdpSocket_node.state.BINDING;
  this.callback = cb;
  
  this.connection.on('message', this.onMessage.bind(this));
  this.connection.on('error', this.onError.bind(this));
  this.connection.bind(port, hostname, this.onConnect.bind(this));
};

UdpSocket_node.prototype.onConnect = function () {
  if (this.state === UdpSocket_node.state.BINDING) {
    this.state = UdpSocket_node.state.OPEN;
  } else {
    console.warn('Connection on invalid state socket!');
    return;
  }

  if (this.callback) {
    this.callback(0);
    delete this.callback;
  }
};

UdpSocket_node.prototype.onError = function (error) {
  if (this.state === UdpSocket_node.state.BINDING) {
    this.callback(undefined, {
      errcode: "BIND_FAILED",
      message: "Bind Failed: " + error.message
    });
    delete this.callback;
    delete this.connection;
    this.state = UdpSocket_node.state.CLOSED;
    return;
  }

  if (this.state === UdpSocket_node.state.CONNECTED) {
    console.warn('Socket Error: ' + error);
    this.dispatchEvent('onData', {
      resultCode: -1
    });
    delete this.connection;
    this.state = UdpSocket_node.state.CLOSED;
  }
};


/*
 * Read a message from the socket.
 * @method onMessage
 * @private
 */
UdpSocket_node.prototype.onMessage = function (data, remote) {
  var arrayBuffer = new Uint8Array(data).buffer;
  this.dispatchEvent('onData', {
    resultCode: 0,
    address: remote.address,
    port: remote.port,
    data: arrayBuffer
  });
};

/**
 * Close a socket
 * @method destroy
 * @param {Function} continuation Function to call once socket is closed.
 */
UdpSocket_node.prototype.destroy = function (continuation) {
  if (this.connection) {
    this.connection.close();
    delete this.connection;
  }
  this.state = UdpSocket_node.state.CLOSED;
  continuation();
};

/** REGISTER PROVIDER **/
exports.provider = UdpSocket_node;
exports.name = 'core.udpsocket';