Source: index.js

'use strict';

const ClientRegister = require('./lib/client-register');
const ClientMessager = require('./lib/client-messager');
const HTTP           = require('http');
const Initializers   = require('./initializers');
const Logger         = require('./lib/logger');
const Teamster       = require('teamster');
const WSServer       = require('ws').Server;
const app            = require('koa')();

/**
 * A collection of private functions, run automatically when the app starts, for
 * creating our Koa app and HTTP and WS servers.
 *
 * @module Main
 */

Initializers.start()
  .then(createTeamster)
  .catch(err => { throw err; });

/**
 * Start 1 or more web processes running our app.
 *
 * @private
 */
function createTeamster() {
  Teamster.run(startServers, {
    fork      : process.env.NODE_ENV === 'production',
    numWorkers: parseInt(process.env.NUM_WORKERS, 10) || 1,
  });
}

/**
 * Start HTTP and WS servers for our app.
 *
 * @private
 */
function startServers() {
  const port = parseInt(process.env.PORT, 10) || 5000;

  HTTP.createServer(app.callback()).listen(port, () => {
    Logger.log({ event: `HTTP server listening on port ${port}` });
    startWSServer(this);
  });
}

/**
 * Start a WS server for handling WebSocket connections.
 *
 * @private
 * @param {HTTP.Server} httpServer The httpServer to attach the WS server to
 */
function startWSServer(httpServer) {
  const wsServer = new WSServer({ server: httpServer });
  wsServer.on('connection', onConnection);
}

/**
 * Handle a newly connected WebSocket connection
 *
 * @private
 * @param {WS.WebSocket} socket The new WebSocket connection
 */
function onConnection(socket) {
  const requestID = socket.upgradeReq.headers['x-request-id'];

  ClientRegister.registerClient(socket).then(client => {
    Logger.clientLog(client, { event: 'New WebSocket client connected' });
    client.socket.on('close', () => onClose(client));
    client.socket.on('message', message => onMessage(client, message));
  }).catch(err => {
    ClientMessager.error({ socket: socket }, 'Error when joining new client');
    Logger.log({ request_id: requestID, event: 'Could not create new client' });
    Logger.error(err);
    socket.close();
  });
}

/**
 * Handle a message sent by a client.
 *
 * @private
 * @param {Client} client The client that sent the message
 * @param {string} message The message sent by the client
 */
function onMessage(client, message) {
  try {
    message = JSON.parse(message);
  } catch(err) {
    Logger.error(err);
    ClientMessager.error(client, 'Unparsable message sent');
    return;
  }

  if (message.action !== 'ping') {
    const err = `Unrecognized action sent: ${message.action}`;
    Logger.clientLog(client, { event: err });
    ClientMessager.error(client, err);
    return;
  }

  ClientRegister.renewClient(client).then(() => {
    Logger.clientLog(client, { event: 'Presence renewed' });
  });
}

/**
 * Handle a client's socket closing.
 *
 * @private
 * @param {Client} client The client whose socket has closed.
 */
function onClose(client) {
  Logger.clientLog(client, { event: 'Client closed socket connection' });
  ClientRegister.deregisterClient(client);
}