Use an object instead of an array to store connection callbacks. This way we can ensure a callback is never called after its stop handle is called.

This commit is contained in:
Nick Martin
2013-12-04 21:52:45 -08:00
parent 8f05510b59
commit 6a2c952cd5
2 changed files with 45 additions and 7 deletions

View File

@@ -995,9 +995,12 @@ _.extend(Subscription.prototype, {
Server = function () {
var self = this;
// List of callbacks to call when a new connection comes in to the
// server and completes DDP version negotiation.
self.connectionCallbacks = [];
// Map of callbacks to call when a new connection comes in to the
// server and completes DDP version negotiation. Use an object instead
// of an array so we can safely remove one from the list while
// iterating over it.
self.connectionCallbacks = {};
self.nextConnectionCallbackId = 0;
self.publish_handlers = {};
self.universal_publish_handlers = [];
@@ -1073,11 +1076,12 @@ _.extend(Server.prototype, {
fn = Meteor.bindEnvironment(fn, "onConnection callback");
self.connectionCallbacks.push(fn);
var id = self.nextConnectionCallbackId++;
self.connectionCallbacks[id] = fn;
return {
stop: function () {
self.connectionCallbacks = _.without(self.connectionCallbacks, fn);
delete self.connectionCallbacks[id];
}
};
},
@@ -1092,9 +1096,11 @@ _.extend(Server.prototype, {
// Creating a new session
socket._meteorSession = new Session(self, version, socket);
self.sessions[socket._meteorSession.id] = socket._meteorSession;
_.each(self.connectionCallbacks, function (callback) {
if (socket._meteorSession)
_.each(_.keys(self.connectionCallbacks), function (id) {
if (_.has(self.connectionCallbacks, id) && socket._meteorSession) {
var callback = self.connectionCallbacks[id];
callback(socket._meteorSession.connectionHandle);
}
});
} else if (!msg.version) {
// connect message without a version. This means an old (pre-pre1)

View File

@@ -52,6 +52,38 @@ Tinytest.addAsync(
);
testAsyncMulti(
"livedata server - onConnection doesn't get callback after stop.",
[function (test, expect) {
var afterStop = false;
var expectStop1 = expect();
var stopHandle1 = Meteor.onConnection(function (conn) {
stopHandle2.stop();
stopHandle1.stop();
afterStop = true;
// yield to the event loop for a moment to see that no other calls
// to listener2 are called.
Meteor.setTimeout(expectStop1, 10);
});
var stopHandle2 = Meteor.onConnection(function (conn) {
test.isFalse(afterStop);
});
// trigger a connection
var expectConnection = expect();
makeTestConnection(
test,
function (clientConn, serverConn) {
// Close the connection from the client.
clientConn.disconnect();
expectConnection();
},
expectConnection
);
}]
);
Meteor.methods({
livedata_server_test_inner: function () {
return this.connection.id;