socketstream/ss-engine.io

View on GitHub
lib/index.js

Summary

Maintainability
B
5 hrs
Test Coverage
// Engine.IO server-side wrapper
'use strict';


// Dependencies
//
var cookieParser  = require('cookie-parser'),
    fs            = require('fs'),
    qs            = require('querystring'),
    engine        = require('engine.io'),
    path          = require('path'),
    log;

var openSocketsById = {};

var processSession = function(socket, secret) {
  try {
    var cookie_obj = qs.parse(socket.request.headers.cookie, ';');
    // for reasons mysterious the connect.sid key sometimes comes with 1 leading whitespace
    var cursor = cookie_obj['connect.sid'] ? cookie_obj['connect.sid'] : cookie_obj[' connect.sid'];
    socket.sessionId = cookieParser.signedCookie(cursor, secret);
    return true;
  }
  catch(e) {
    log.warn('Warning: connect.sid session cookie not detected. User may have cookies disabled or session cookie has expired');
    return false;
  }
};

module.exports = function(ss, messageEmitter, httpServer, config, sessionOptions){
  var engineioClient, code, ws;
  log = ss.log;
  config = config || {};
  config.server = config.server || {};
  config.client = config.client || {};


  // Send Engine.IO client-side code
  engineioClient = fs.readFileSync(path.join(__dirname,'../node_modules/engine.io-client/engine.io.js'), 'utf8');
  ss.client.send('lib', 'engine.io-client', engineioClient, {minified: false}); // tested this with minified: true ; worked fine.

  // Send socketstream-transport module
  code = fs.readFileSync(path.join(__dirname,'../client','/wrapper.js'), 'utf8');
  ss.client.send('mod', 'socketstream-transport', code);

  // Tell the SocketStream client to use this transport, passing any client-side config along to the wrapper
  ss.client.send('code', 'transport', 'require(\'socketstream\').assignTransport(' + JSON.stringify(config.client) + ');');

  // don't set up server for CLI and test
  if (!httpServer) { return; }

  // Create a new Engine.IO server and bind to /ws
  ws = engine.attach(httpServer, config.server);
  // ws.installHandlers(httpServer, {prefix: '/ws'});

  // Handle incoming connections
  ws.on('connection', function(socket) {

    if (processSession(socket, sessionOptions.secret)) {
    // Store this here before it gets cleaned up after the websocket upgrade
    socket.remoteAddress = socket.request.connection.remoteAddress;

    // Get real IP if behind proxy
    var xForwardedFor = socket.request.headers['x-forwarded-for'];
    if (xForwardedFor) {
      socket.remoteAddress = xForwardedFor.split(',')[0];
    }

    // Allow this connection to be addressed by the socket ID
    openSocketsById[socket.id] = socket;
    ss.session.find(socket.sessionId, socket.id, function () {
      socket.send('X|OK');
    });

      // changed from data
      socket.on('message', function(msg) {
        try {
          var i,responderId,content,meta;
          if ( (i = msg.indexOf('|')) > 0) {
            responderId = msg.substr(0, i);
            content = msg.substr(i+1);
          } else { throw('Message does not contain a responderId');}

          meta = {socketId: socket.id, sessionId: socket.sessionId, clientIp: socket.remoteAddress, transport: 'engineio'};

          // Invoke the relevant Request Responder, passing a callback function which
          // will automatically direct any response back to the correct client-side code
          messageEmitter.emit(responderId, content, meta, function(data){
            return socket.send(responderId + '|' + data);
          });
        }
        catch (e) {
          log.error('Invalid websocket message received:', msg);
        }
      });

      // If the browser disconnects, remove this connection from openSocketsById
      socket.on('close', function() {
        if(openSocketsById[socket.id]) {
          delete openSocketsById[socket.id];
        }
      });
    }
  });

  // Return API for sending events
  // Note the '0' message prefix (which signifies the responderId) is reserved for sending events
  return {

    event: function() {

      return {

        // Send the same message to every open socket
        all: function(msg) {
          for (var id in openSocketsById) {
            if (openSocketsById.hasOwnProperty(id)) {
              openSocketsById[id].send('0|' + msg + '|null');
            }
          }
        },

        // Send an event to a specific socket
        // Note: 'meta' is typically the name of the channel
        socketId: function(id, msg, meta) {
          var socket = openSocketsById[id];
          if (socket) {
            return socket.send('0|' + msg + '|' + meta);
          } else {
            return false;
          }

        }

      };
    }
  };
};