ws-server.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 'use strict';
  2. var parser = require('ua-parser-js');
  3. // Start Binary.js server
  4. var BinaryServer = require('binaryjs').BinaryServer;
  5. exports.create = function(server) {
  6. // link it to express
  7. var bs = BinaryServer({
  8. server: server,
  9. path: '/binary'
  10. });
  11. function guid() {
  12. function s4() {
  13. return Math.floor((1 + Math.random()) * 0x10000)
  14. .toString(16)
  15. .substring(1);
  16. }
  17. return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
  18. s4() + '-' + s4() + s4() + s4();
  19. }
  20. function getDeviceName(req) {
  21. var ua = parser(req.headers['user-agent']);
  22. return {
  23. model: ua.device.model,
  24. os: ua.os.name,
  25. browser: ua.browser.name,
  26. type: ua.device.type
  27. };
  28. }
  29. function hash(text) {
  30. // A string hashing function based on Daniel J. Bernstein's popular 'times 33' hash algorithm.
  31. var h = 5381,
  32. index = text.length;
  33. while (index) {
  34. h = (h * 33) ^ text.charCodeAt(--index);
  35. }
  36. return h >>> 0;
  37. }
  38. function getIP(socket) {
  39. return socket.upgradeReq.headers['x-forwarded-for'] || socket.upgradeReq.connection.remoteAddress;
  40. }
  41. // Wait for new user connections
  42. bs.on('connection', function(client) {
  43. client.uuidRaw = guid();
  44. //ip is hashed to prevent injections by spoofing the 'x-forwarded-for' header
  45. // client.hashedIp = 1; //use this to test locally
  46. client.hashedIp = hash(getIP(client._socket));
  47. client.deviceName = getDeviceName(client._socket.upgradeReq);
  48. // Incoming stream from browsers
  49. client.on('stream', function(stream, meta) {
  50. if (meta && meta.serverMsg === 'rtc-support') {
  51. client.uuid = (meta.rtc ? 'rtc_' : '') + client.uuidRaw;
  52. client.send({
  53. isSystemEvent: true,
  54. type: 'handshake',
  55. name: client.deviceName,
  56. uuid: client.uuid
  57. });
  58. return;
  59. }
  60. if (meta && meta.serverMsg === 'device-name') {
  61. //max name length = 40
  62. if (meta.name && meta.name.length > 40) {
  63. return;
  64. }
  65. client.name = meta.name;
  66. return;
  67. }
  68. meta.from = client.uuid;
  69. // broadcast to the other client
  70. for (var id in bs.clients) {
  71. if (bs.clients.hasOwnProperty(id)) {
  72. var otherClient = bs.clients[id];
  73. if (otherClient !== client && meta.toPeer === otherClient.uuid) {
  74. var send = otherClient.createStream(meta);
  75. stream.pipe(send, meta);
  76. }
  77. }
  78. }
  79. });
  80. });
  81. function forEachClient(fn) {
  82. for (var id in bs.clients) {
  83. if (bs.clients.hasOwnProperty(id)) {
  84. var client = bs.clients[id];
  85. fn(client);
  86. }
  87. }
  88. }
  89. function notifyBuddies() {
  90. var locations = {};
  91. //group all clients by location (by public ip address)
  92. forEachClient(function(client) {
  93. var ip = client.hashedIp;
  94. locations[ip] = locations[ip] || [];
  95. locations[ip].push({
  96. socket: client,
  97. contact: {
  98. peerId: client.uuid,
  99. name: client.name || client.deviceName,
  100. device: client.name ? client.deviceName : undefined
  101. }
  102. });
  103. });
  104. //notify every location
  105. Object.keys(locations).forEach(function(locationKey) {
  106. //notify every client of all other clients in this location
  107. var location = locations[locationKey];
  108. location.forEach(function(client) {
  109. //all other clients
  110. var buddies = location.reduce(function(result, otherClient) {
  111. if (otherClient !== client) {
  112. result.push(otherClient.contact);
  113. }
  114. return result;
  115. }, []);
  116. var currState = hash(JSON.stringify(buddies));
  117. console.log(currState);
  118. var socket = client.socket;
  119. //protocol
  120. var msg = {
  121. buddies: buddies,
  122. isSystemEvent: true,
  123. type: 'buddies'
  124. };
  125. //send only if state changed
  126. if (currState !== socket.lastState) {
  127. socket.send(msg);
  128. socket.lastState = currState;
  129. return;
  130. }
  131. });
  132. });
  133. }
  134. setInterval(notifyBuddies, 3000);
  135. };