From f7e968a13cd840060d2875297ab645f4dabf6276 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 8 Sep 2013 17:43:30 -0500 Subject: [PATCH] Alter channel unload behavior, add additional checks - Should prevent "write after end" errors caused by unloading a channel before it finishes loading - Might prevent strange cases of playlists gone wild --- changelog | 12 ++++++++++++ lib/channel.js | 14 ++++++++++++-- lib/database.js | 12 +++++++++++- lib/playlist.js | 12 ++++++++++++ lib/server.js | 7 +++++-- tests/fastQuit.js | 9 +++++++++ 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 tests/fastQuit.js diff --git a/changelog b/changelog index 02ca3865..9d2a2100 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,15 @@ +Sun Sep 8 17:41 2013 CDT + * lib/server.js: Change behavior of unloadChannel - deletes all object + keys in the channel object and then sets channel.dead = true + * lib/channel.js: Add extra checks in callbacks to ensure certain things + don't happen if the channel is dead + * lib/playlist.js: Add extra checks to kill the playlist if the channel + it is associated with is dead + * lib/database.js: Add extra checks to prevent loading channel data into + a dead channel object + * tests/fastQuit.js: A simple script to open a connection, join a + channel, and disconnect immediately to test for race conditions + Sat Sep 7 23:38 2013 CDT * lib/user.js: Add "loggingIn" field to act as a lock on async logins. Delay further login attempts until the current login attempt finishes. diff --git a/lib/channel.js b/lib/channel.js index a220ace1..287ff821 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -119,8 +119,12 @@ var Channel = function(name, Server) { self.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)] } - Server.db.loadChannelData(self, function () { - self.dbloaded = true; + Server.db.loadChannelData(self, function (err) { + if (err && err === "channel_dead") + return; + else if (!err || err === "channel_unregistered") + self.dbloaded = true; + if(self.registered) { self.loadDump(); } @@ -150,6 +154,9 @@ Channel.prototype.loadDump = function() { return; fs.stat(path.join(__dirname, "../chandump", self.name), function (err, stats) { + if (self.dead) + return; + if(!err) { var mb = stats.size / 1048576; mb = parseInt(mb * 100) / 100; @@ -165,6 +172,9 @@ Channel.prototype.loadDump = function() { } fs.readFile(path.join(__dirname, "../chandump", self.name), function(err, data) { + if (self.dead) + return; + if(err) { if(err.code == "ENOENT") { Logger.errlog.log("WARN: missing dump for " + self.name); diff --git a/lib/database.js b/lib/database.js index 3764b45b..d122ae84 100644 --- a/lib/database.js +++ b/lib/database.js @@ -405,7 +405,12 @@ Database.prototype.loadChannelData = function (chan, callback) { } if(res.length == 0) { - callback("Channel is unregistered", null); + callback("channel_unregistered", null); + return; + } + + if (chan.dead) { + callback("channel_dead", null); return; } @@ -416,6 +421,11 @@ Database.prototype.loadChannelData = function (chan, callback) { // Load bans query = "SELECT * FROM `chan_" + chan.name + "_bans`"; self.query(query, function (err, res) { + if (chan.dead) { + callback("channel_dead", null); + return; + } + if(err) { callback(err, null); return; diff --git a/lib/playlist.js b/lib/playlist.js index d6ce6af3..9ec0ac8c 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -59,14 +59,26 @@ function Playlist(chan) { this.server = chan.server; var pl = this; this.on("mediaUpdate", function(m) { + if (chan.dead) { + pl.die(); + return; + } chan.sendAll("mediaUpdate", m.timeupdate()); }); this.on("changeMedia", function(m) { + if (chan.dead) { + pl.die(); + return; + } chan.onVideoChange(); chan.sendAll("setCurrent", pl.current.uid); chan.sendAll("changeMedia", m.fullupdate()); }); this.on("remove", function(item) { + if (chan.dead) { + pl.die(); + return; + } chan.broadcastPlaylistMeta(); chan.sendAll("delete", { uid: item.uid diff --git a/lib/server.js b/lib/server.js index b2f068d9..4fb34907 100644 --- a/lib/server.js +++ b/lib/server.js @@ -62,8 +62,11 @@ var Server = { break; } } - chan.name = ""; - chan.canonical_name = ""; + var keys = Object.keys(chan); + for (var i in keys) { + delete chan[keys[i]]; + } + chan.dead = true; }, stats: null, app: null, diff --git a/tests/fastQuit.js b/tests/fastQuit.js new file mode 100644 index 00000000..b8888720 --- /dev/null +++ b/tests/fastQuit.js @@ -0,0 +1,9 @@ +var io = require('socket.io-client'); + +var socket = io.connect('http://localhost:1337'); + +// connect, join a room, then disconnect as quickly as possible +socket.on('connect', function () { + socket.emit('joinChannel', { name: 'test' }); + socket.disconnect(); +});