From 14ff5153e38860f09947c2a339f6634cad62cbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20=C3=85slund?= Date: Sun, 21 May 2017 14:17:11 +0700 Subject: [PATCH 1/2] Replaced waitForConnectionCount() with emitting events from RealTimeServer. --- src/server/_real_time_server_test.js | 77 ++++++++++++++++------------ src/server/real_time_server.js | 15 ++++-- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/server/_real_time_server_test.js b/src/server/_real_time_server_test.js index 0df87fab8..5893c4e27 100644 --- a/src/server/_real_time_server_test.js +++ b/src/server/_real_time_server_test.js @@ -35,22 +35,41 @@ httpServer.start(PORT, done); }); - afterEach(function(done) { - waitForConnectionCount(0, "afterEach() requires all sockets to be closed", function() { - httpServer.stop(done); - }); + afterEach(function(done) { + assert.equal(realTimeServer.numberOfActiveConnections(), 0, "afterEach() requires all sockets to be closed"); + httpServer.stop(done); }); - it("counts the number of connections", function(done) { - assert.equal(realTimeServer.numberOfActiveConnections(), 0, "before opening connection"); + it("emits event when all sockets have disconnected", function (done) { + var isAfterDisconnect = false; + realTimeServer.on('disconnect_all', function () { + assert.equal(isAfterDisconnect, true, "after closing connection"); + done(); + }); - var socket = createSocket(); - waitForConnectionCount(1, "after opening connection", function() { - assert.equal(realTimeServer.numberOfActiveConnections(), 1, "after opening connection"); - closeSocket(socket, function() { - waitForConnectionCount(0, "after closing connection", done); - }); - }); + var socket = createSocket(); + socket.on('connect', function () { + socket.disconnect(); + isAfterDisconnect = true; + }); + + }); + + it("counts the number of connections", function(done) { + assert.equal(realTimeServer.numberOfActiveConnections(), 0, "before opening connection"); + + var gotConnectionEvent = false; + realTimeServer.on('connection', function () { + assert.equal(realTimeServer.numberOfActiveConnections(), 1, "after opening connection"); + gotConnectionEvent = true; + }).on('disconnect', function () { + assert.equal(gotConnectionEvent, true, "expected connection event before disconnect event"); + assert.equal(realTimeServer.numberOfActiveConnections(), 0, "after closing connection"); + done(); + }); + + var socket = createSocket(); + socket.once('connect', socket.disconnect); }); it("broadcasts pointer events from one client to all others", function(done) { @@ -89,8 +108,8 @@ realTimeServer.handleClientEvent(clientEvent, EMITTER_ID); - function end() { - async.each([ receiver1, receiver2 ], closeSocket, done); + function end() { + disconnectAll([receiver1, receiver2], done); } }); @@ -119,8 +138,8 @@ event3.toServerEvent() ]); } - finally { - closeSocket(client, done); + finally { + disconnectAll([client], done); } } }); @@ -148,24 +167,16 @@ emitter.emit(clientEvent.name(), clientEvent.toSerializableObject()); - function end() { - async.each([emitter, receiver1, receiver2], closeSocket, done); + function end() { + disconnectAll([emitter, receiver1, receiver2], done); } - } + } - function waitForConnectionCount(expectedConnections, message, callback) { - var TIMEOUT = 1000; // milliseconds - var RETRY_PERIOD = 10; // milliseconds - - var retryOptions = { times: TIMEOUT / RETRY_PERIOD, interval: RETRY_PERIOD }; - async.retry(retryOptions, function(next) { - if (realTimeServer.numberOfActiveConnections() === expectedConnections) return next(); - else return next("fail"); - }, function(err) { - if (err) return assert.equal(realTimeServer.numberOfActiveConnections(), expectedConnections, message); - else setTimeout(callback, 0); - }); - } + function disconnectAll(sockets, callback) { + realTimeServer.once('disconnect_all', callback); + for (var socket of sockets) + socket.disconnect(); + } function createSocket() { return io("http://localhost:" + PORT); diff --git a/src/server/real_time_server.js b/src/server/real_time_server.js index fb33068c8..39641448f 100644 --- a/src/server/real_time_server.js +++ b/src/server/real_time_server.js @@ -8,6 +8,7 @@ var ClientDrawEvent = require("../shared/client_draw_event.js"); var ClientClearScreenEvent = require("../shared/client_clear_screen_event.js"); var EventRepository = require("./event_repository.js"); + var EventEmitter = require("../client/network/vendor/emitter-1.2.1.js"); // Consider Jay Bazuzi's suggestions from E494 comments (direct connection from client to server when testing) // http://disq.us/p/1gobws6 http://www.letscodejavascript.com/v3/comments/live/494 @@ -16,10 +17,12 @@ this._socketIoConnections = {}; }; + RealTimeServer.prototype = Object.create(EventEmitter.prototype); + RealTimeServer.prototype.start = function(httpServer) { this._ioServer = io(httpServer); - trackSocketIoConnections(this._socketIoConnections, this._ioServer); + trackSocketIoConnections(this._socketIoConnections, this._ioServer, this); handleSocketIoEvents(this, this._ioServer); }; @@ -32,13 +35,19 @@ return Object.keys(this._socketIoConnections).length; }; - function trackSocketIoConnections(connections, ioServer) { + function trackSocketIoConnections(connections, ioServer, self) { // Inspired by isaacs https://github.com/isaacs/server-destroy/commit/71f1a988e1b05c395e879b18b850713d1774fa92 ioServer.on("connection", function(socket) { var key = socket.id; connections[key] = socket; - socket.on("disconnect", function() { + self.emit('connection', key); + + socket.on("disconnect", function () { delete connections[key]; + self.emit('disconnect', key); + + if (self.numberOfActiveConnections() === 0) + self.emit('disconnect_all'); }); }); } From 20e12d26021d2fa95d0739d8ad33ed09e255fffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20=C3=85slund?= Date: Sun, 21 May 2017 15:20:25 +0700 Subject: [PATCH 2/2] Server disconnects all RealTimeServer sockets when stopping. --- src/server/_real_time_server_test.js | 41 ++++++++++------------------ src/server/_server_test.js | 6 +--- src/server/real_time_server.js | 17 ++++++++++-- src/server/server.js | 13 +++++---- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/server/_real_time_server_test.js b/src/server/_real_time_server_test.js index 5893c4e27..17088d56b 100644 --- a/src/server/_real_time_server_test.js +++ b/src/server/_real_time_server_test.js @@ -36,8 +36,9 @@ }); afterEach(function(done) { - assert.equal(realTimeServer.numberOfActiveConnections(), 0, "afterEach() requires all sockets to be closed"); - httpServer.stop(done); + realTimeServer.disconnectAll(function () { + httpServer.stop(done); + }); }); it("emits event when all sockets have disconnected", function (done) { @@ -109,7 +110,7 @@ realTimeServer.handleClientEvent(clientEvent, EMITTER_ID); function end() { - disconnectAll([receiver1, receiver2], done); + setTimeout(done, 0); } }); @@ -130,18 +131,16 @@ client.on(ServerDrawEvent.EVENT_NAME, function(event) { replayedEvents.push(ServerDrawEvent.fromSerializableObject(event)); if (replayedEvents.length === 3) { - try { - // if we don't get the events, the test will time out - assert.deepEqual(replayedEvents, [ - event1.toServerEvent(), - event2.toServerEvent(), - event3.toServerEvent() - ]); - } - finally { - disconnectAll([client], done); - } - } + + // if we don't get the events, the test will time out + assert.deepEqual(replayedEvents, [ + event1.toServerEvent(), + event2.toServerEvent(), + event3.toServerEvent() + ]); + + setTimeout(done, 10); + } }); }); @@ -163,19 +162,9 @@ next(); } }); - }, end); + }, done); emitter.emit(clientEvent.name(), clientEvent.toSerializableObject()); - - function end() { - disconnectAll([emitter, receiver1, receiver2], done); - } - } - - function disconnectAll(sockets, callback) { - realTimeServer.once('disconnect_all', callback); - for (var socket of sockets) - socket.disconnect(); } function createSocket() { diff --git a/src/server/_server_test.js b/src/server/_server_test.js index f9bb7c40f..16e2ab1d0 100644 --- a/src/server/_server_test.js +++ b/src/server/_server_test.js @@ -65,14 +65,10 @@ receiver.on(ServerPointerEvent.EVENT_NAME, function(data) { assert.deepEqual(data, clientEvent.toServerEvent(emitter.id).toSerializableObject()); - end(); + setTimeout(done, 10); }); emitter.emit(clientEvent.name(), clientEvent.toSerializableObject()); - - function end() { - async.each([ emitter, receiver ], closeSocket, done); - } }); function createSocket() { diff --git a/src/server/real_time_server.js b/src/server/real_time_server.js index 39641448f..41e363807 100644 --- a/src/server/real_time_server.js +++ b/src/server/real_time_server.js @@ -2,7 +2,8 @@ (function() { "use strict"; - var io = require('socket.io'); + var io = require('socket.io'); + var async = require('async'); var ClientPointerEvent = require("../shared/client_pointer_event.js"); var ClientRemovePointerEvent = require("../shared/client_remove_pointer_event.js"); var ClientDrawEvent = require("../shared/client_draw_event.js"); @@ -33,7 +34,19 @@ RealTimeServer.prototype.numberOfActiveConnections = function() { return Object.keys(this._socketIoConnections).length; - }; + }; + + RealTimeServer.prototype.disconnectAll = function (callback) { + if (this.numberOfActiveConnections() === 0) { + if (callback) callback(); + } + else { + if (callback) + this.once('disconnect_all', callback); + + async.each(this._socketIoConnections, function (socket) { socket.disconnect(); }); + } + }; function trackSocketIoConnections(connections, ioServer, self) { // Inspired by isaacs https://github.com/isaacs/server-destroy/commit/71f1a988e1b05c395e879b18b850713d1774fa92 diff --git a/src/server/server.js b/src/server/server.js index d5dddca7e..cce35d8e3 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -13,14 +13,17 @@ this._httpServer = new HttpServer(contentDir, notFoundPageToServe); this._httpServer.start(portNumber, callback); - var realTimeServer = new RealTimeServer(); - realTimeServer.start(this._httpServer.getNodeServer()); + this._realTimeServer = new RealTimeServer(); + this._realTimeServer.start(this._httpServer.getNodeServer()); }; - Server.prototype.stop = function(callback) { - if (this._httpServer === undefined) return callback(new Error("stop() called before server started")); + Server.prototype.stop = function (callback) { + var self = this; + if (self._httpServer === undefined) return callback(new Error("stop() called before server started")); - this._httpServer.stop(callback); + self._realTimeServer.disconnectAll(function () { + self._httpServer.stop(callback); + }); }; }()); \ No newline at end of file