From 9fe0cd64d0ef33dad286bcd8a5726b33bcebbd97 Mon Sep 17 00:00:00 2001
From: Moritz Langenstein <ml5717@ic.ac.uk>
Date: Sat, 12 Oct 2019 13:46:41 +0100
Subject: [PATCH] (ml5717) Pre-built signalbuddy to dist/

---
 .gitignore      |   1 -
 dist/server.js  | 125 +++++++++++++++++++++++++++++++
 dist/sockets.js | 190 ++++++++++++++++++++++++++++++++++++++++++++++++
 dist/util.js    |  12 +++
 4 files changed, 327 insertions(+), 1 deletion(-)
 create mode 100755 dist/server.js
 create mode 100755 dist/sockets.js
 create mode 100644 dist/util.js

diff --git a/.gitignore b/.gitignore
index bf0ecd7..c388811 100755
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@ node_modules
 ecosystem.config.js
 Dockerfile
 .drone.yml
-dist
 .jshintignore
 .jshintrc
 yarn-error.log
diff --git a/dist/server.js b/dist/server.js
new file mode 100755
index 0000000..3800b3b
--- /dev/null
+++ b/dist/server.js
@@ -0,0 +1,125 @@
+'use strict';
+
+var _getconfig = require('getconfig');
+
+var _getconfig2 = _interopRequireDefault(_getconfig);
+
+var _fs = require('fs');
+
+var _fs2 = _interopRequireDefault(_fs);
+
+var _os = require('os');
+
+var _os2 = _interopRequireDefault(_os);
+
+var _stickySession = require('sticky-session');
+
+var _stickySession2 = _interopRequireDefault(_stickySession);
+
+var _farmhash = require('farmhash');
+
+var _farmhash2 = _interopRequireDefault(_farmhash);
+
+var _net = require('net');
+
+var _net2 = _interopRequireDefault(_net);
+
+var _cluster = require('cluster');
+
+var _cluster2 = _interopRequireDefault(_cluster);
+
+var _http = require('http');
+
+var _http2 = _interopRequireDefault(_http);
+
+var _https = require('https');
+
+var _https2 = _interopRequireDefault(_https);
+
+var _sockets = require('./sockets');
+
+var _sockets2 = _interopRequireDefault(_sockets);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var port = parseInt(process.env.PORT || _getconfig2.default.server.port, 10);
+var redisEndpoint = process.env.REDIS_ENDPOINT || _getconfig2.default.redis.endpoint;
+var redisPort = process.env.REDIS_PORT || _getconfig2.default.redis.port;
+var numProcesses = _os2.default.cpus().length;
+
+if (_cluster2.default.isMaster) {
+  var workers = [];
+  var spawn = function spawn(i) {
+    workers[i] = _cluster2.default.fork();
+    // Persistence
+    workers[i].on('exit', function (code, signal) {
+      console.log('Worker ' + i + ' exited with signal ' + signal);
+      console.log('Respawning worker', i);
+      spawn(i);
+    });
+  };
+
+  for (var i = 0; i < numProcesses; i += 1) {
+    console.log('Starting worker ' + (i + 1));
+    spawn(i);
+  }
+
+  var workerIndex = function workerIndex(ip, len) {
+    return (// Farmhash is the fastest and works with IPv6, too
+      _farmhash2.default.fingerprint32(ip) % len
+    );
+  };
+
+  // Create the outside facing server listening on our port.
+  var masterServer = _net2.default.createServer({ pauseOnConnect: true }, function (connection) {
+    // We received a connection and need to pass it to the appropriate
+    // worker. Get the worker for this connection's source IP and pass
+    // it the connection.
+    var worker = workers[workerIndex(connection.remoteAddress, numProcesses)];
+    worker.send('sticky-session:connection', connection);
+  }).listen(port);
+
+  console.log('Listening at ' + (_getconfig2.default.server.secure ? 'https' : 'http') + '://' + (process.env.NODE_ENV === 'production' ? '0.0.0.0' : 'localhost') + ':' + port + '/');
+} else {
+  var serverHandler = function serverHandler(req, res) {
+    if (req.url === '/healthcheck') {
+      console.log(Date.now(), 'healthcheck');
+      res.writeHead(200);
+      res.end();
+      return;
+    }
+    res.writeHead(404);
+    res.end('worker: ' + _cluster2.default.worker.id);
+  };
+
+  var server = null;
+
+  // Create an http(s) server instance to that socket.io can listen to
+  if (_getconfig2.default.server.secure) {
+    server = _https2.default.Server({
+      key: _fs2.default.readFileSync(process.env.PRIV_KEY || _getconfig2.default.server.key),
+      cert: _fs2.default.readFileSync(process.env.CERT || _getconfig2.default.server.cert),
+      passphrase: _getconfig2.default.server.password
+    }, serverHandler);
+  } else {
+    server = _http2.default.Server(serverHandler);
+  }
+  if (!_stickySession2.default.listen(server, port)) {
+    // Master
+  } else {
+      // Worker
+    }
+  server.listen(0);
+  (0, _sockets2.default)(server, Object.assign({ redisEndpoint: redisEndpoint, redisPort: redisPort }, _getconfig2.default));
+  if (_getconfig2.default.uid) process.setuid(_getconfig2.default.uid);
+  process.on('message', function (message, connection) {
+    if (message !== 'sticky-session:connection') {
+      return;
+    }
+    // Emulate a connection event on the server by emitting the
+    // event with the connection the master sent us.
+    server.emit('connection', connection);
+
+    connection.resume();
+  });
+}
\ No newline at end of file
diff --git a/dist/sockets.js b/dist/sockets.js
new file mode 100755
index 0000000..fdae315
--- /dev/null
+++ b/dist/sockets.js
@@ -0,0 +1,190 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+var _arguments = arguments;
+
+var _socket = require('socket.io');
+
+var _socket2 = _interopRequireDefault(_socket);
+
+var _v = require('uuid/v4');
+
+var _v2 = _interopRequireDefault(_v);
+
+var _crypto = require('crypto');
+
+var _crypto2 = _interopRequireDefault(_crypto);
+
+var _socket3 = require('socket.io-redis');
+
+var _socket4 = _interopRequireDefault(_socket3);
+
+var _util = require('./util');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = function (server, config) {
+  var io = _socket2.default.listen(server);
+  io.adapter((0, _socket4.default)({ host: config.redis.host, port: config.redis.port }));
+  io.on('connection', function (client) {
+    client.resources = {
+      screen: false,
+      video: true,
+      audio: false
+    };
+
+    // pass a message to another id
+    client.on('message', function (details) {
+      if (!details) return;
+      var otherClient = io.to(details.to);
+      if (!otherClient) return;
+      details.from = client.id;
+      otherClient.emit('message', details);
+    });
+
+    client.on('join', join);
+    client.on('getClients', getClients);
+    client.on('getClientCount', getClientCount);
+    client.on('getMyId', getClientId);
+
+    function removeFeed(type) {
+      if (client.room) {
+        io.in(client.room).emit('remove', {
+          id: client.id,
+          type: type
+        });
+        if (!type) {
+          client.leave(client.room);
+          client.room = undefined;
+        }
+      }
+    }
+
+    function join(name, cb) {
+      // sanity check
+      if (typeof name !== 'string') return;
+      // check if maximum number of clients reached
+      if (config.rooms && config.rooms.maxClients > 0) {
+        getClientCount(name).then(function (count) {
+          if (count > config.rooms.maxClients) {
+            removeFeed();
+          }
+        });
+      }
+      // leave any existing rooms
+      removeFeed();
+      getClients(name, function (err, clients) {
+        return (0, _util.safeCb)(cb)(err, Object.assign({}, { you: client.id }, clients));
+      });
+      client.join(name);
+      client.room = name;
+    }
+
+    function getClients(roomName, callback) {
+      describeRoom(roomName).then(function (description) {
+        var obj = { clients: {} };
+        description.forEach(function (k) {
+          obj.clients[k] = client.resources;
+        });
+        (0, _util.safeCb)(callback)(null, obj);
+      }).catch(function (err) {
+        return (0, _util.safeCb)(callback)(err, null);
+      });
+    }
+
+    function getClientCount(roomName, callback) {
+      clientsInRoom(roomName).then(function (num) {
+        if (roomName) (0, _util.safeCb)(callback)(num);
+      });
+    }
+
+    function getClientId(callback) {
+      (0, _util.safeCb)(callback)(client.id);
+    }
+
+    // we don't want to pass "leave" directly because the
+    // event type string of "socket end" gets passed too.
+    client.on('disconnect', function () {
+      removeFeed();
+    });
+
+    client.on('leave', function () {
+      removeFeed();
+    });
+
+    client.on('create', function (name, cb) {
+      if (_arguments.length === 2) {
+        cb = typeof cb === 'function' ? cb : function () {};
+        name = name || (0, _v2.default)();
+      } else {
+        cb = name;
+        name = (0, _v2.default)();
+      }
+      // check if exists
+      io.in(name).clients(function (err, clients) {
+        if (clients && clients.length) {
+          (0, _util.safeCb)(cb)('taken');
+        } else {
+          join(name);
+          (0, _util.safeCb)(cb)(null, name);
+        }
+      });
+    });
+
+    /*
+    client.on('trace', (data) => {
+      // console.log('trace', JSON.stringify([data.type, data.session, data.prefix, data.peer, data.time, data.value]));
+    });
+    */
+
+    // tell client about stun and turn servers and generate nonces
+    client.emit('stunservers', config.stunservers || []);
+
+    // create shared secret nonces for TURN authentication
+    // the process is described in draft-uberti-behave-turn-rest
+    var credentials = [];
+    // allow selectively vending turn credentials based on origin.
+    var origin = client.handshake.headers.origin;
+
+    if (!config.turnorigins || config.turnorigins.includes(origin)) {
+      config.turnservers.forEach(function (server) {
+        var hmac = _crypto2.default.createHmac('sha1', server.secret);
+        // default to 86400 seconds timeout unless specified
+        var username = '' + (Math.floor(new Date().getTime() / 1000) + parseInt(server.expiry || 86400, 10));
+        hmac.update(username);
+        credentials.push({
+          username: username,
+          credential: hmac.digest('base64'),
+          urls: server.urls || server.url
+        });
+      });
+    }
+    client.emit('turnservers', credentials);
+  });
+
+  function describeRoom(roomName) {
+    return new Promise(function (resolve, reject) {
+      io.in(roomName).clients(function (err, clients) {
+        if (err) {
+          reject(err);
+          return;
+        }
+        resolve(clients);
+      });
+    });
+  }
+
+  function clientsInRoom(roomName) {
+    return new Promise(function (resolve, reject) {
+      io.in(roomName).clients(function (err, clients) {
+        if (err) {
+          reject(err);
+          return;
+        }
+        resolve(clients.length);
+      });
+    });
+  }
+};
\ No newline at end of file
diff --git a/dist/util.js b/dist/util.js
new file mode 100644
index 0000000..56930e9
--- /dev/null
+++ b/dist/util.js
@@ -0,0 +1,12 @@
+'use strict';
+
+function safeCb(cb) {
+  if (typeof cb === 'function') {
+    return cb;
+  }
+  return function () {};
+}
+
+module.exports = {
+  safeCb: safeCb
+};
\ No newline at end of file
-- 
GitLab