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
This commit is contained in:
calzoneman 2013-09-08 17:43:30 -05:00
parent b3ea7069a8
commit f7e968a13c
6 changed files with 61 additions and 5 deletions

View File

@ -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 Sat Sep 7 23:38 2013 CDT
* lib/user.js: Add "loggingIn" field to act as a lock on async logins. * lib/user.js: Add "loggingIn" field to act as a lock on async logins.
Delay further login attempts until the current login attempt finishes. Delay further login attempts until the current login attempt finishes.

View File

@ -119,8 +119,12 @@ var Channel = function(name, Server) {
self.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)] self.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)]
} }
Server.db.loadChannelData(self, function () { Server.db.loadChannelData(self, function (err) {
if (err && err === "channel_dead")
return;
else if (!err || err === "channel_unregistered")
self.dbloaded = true; self.dbloaded = true;
if(self.registered) { if(self.registered) {
self.loadDump(); self.loadDump();
} }
@ -150,6 +154,9 @@ Channel.prototype.loadDump = function() {
return; return;
fs.stat(path.join(__dirname, "../chandump", self.name), fs.stat(path.join(__dirname, "../chandump", self.name),
function (err, stats) { function (err, stats) {
if (self.dead)
return;
if(!err) { if(!err) {
var mb = stats.size / 1048576; var mb = stats.size / 1048576;
mb = parseInt(mb * 100) / 100; mb = parseInt(mb * 100) / 100;
@ -165,6 +172,9 @@ Channel.prototype.loadDump = function() {
} }
fs.readFile(path.join(__dirname, "../chandump", self.name), fs.readFile(path.join(__dirname, "../chandump", self.name),
function(err, data) { function(err, data) {
if (self.dead)
return;
if(err) { if(err) {
if(err.code == "ENOENT") { if(err.code == "ENOENT") {
Logger.errlog.log("WARN: missing dump for " + self.name); Logger.errlog.log("WARN: missing dump for " + self.name);

View File

@ -405,7 +405,12 @@ Database.prototype.loadChannelData = function (chan, callback) {
} }
if(res.length == 0) { if(res.length == 0) {
callback("Channel is unregistered", null); callback("channel_unregistered", null);
return;
}
if (chan.dead) {
callback("channel_dead", null);
return; return;
} }
@ -416,6 +421,11 @@ Database.prototype.loadChannelData = function (chan, callback) {
// Load bans // Load bans
query = "SELECT * FROM `chan_" + chan.name + "_bans`"; query = "SELECT * FROM `chan_" + chan.name + "_bans`";
self.query(query, function (err, res) { self.query(query, function (err, res) {
if (chan.dead) {
callback("channel_dead", null);
return;
}
if(err) { if(err) {
callback(err, null); callback(err, null);
return; return;

View File

@ -59,14 +59,26 @@ function Playlist(chan) {
this.server = chan.server; this.server = chan.server;
var pl = this; var pl = this;
this.on("mediaUpdate", function(m) { this.on("mediaUpdate", function(m) {
if (chan.dead) {
pl.die();
return;
}
chan.sendAll("mediaUpdate", m.timeupdate()); chan.sendAll("mediaUpdate", m.timeupdate());
}); });
this.on("changeMedia", function(m) { this.on("changeMedia", function(m) {
if (chan.dead) {
pl.die();
return;
}
chan.onVideoChange(); chan.onVideoChange();
chan.sendAll("setCurrent", pl.current.uid); chan.sendAll("setCurrent", pl.current.uid);
chan.sendAll("changeMedia", m.fullupdate()); chan.sendAll("changeMedia", m.fullupdate());
}); });
this.on("remove", function(item) { this.on("remove", function(item) {
if (chan.dead) {
pl.die();
return;
}
chan.broadcastPlaylistMeta(); chan.broadcastPlaylistMeta();
chan.sendAll("delete", { chan.sendAll("delete", {
uid: item.uid uid: item.uid

View File

@ -62,8 +62,11 @@ var Server = {
break; break;
} }
} }
chan.name = ""; var keys = Object.keys(chan);
chan.canonical_name = ""; for (var i in keys) {
delete chan[keys[i]];
}
chan.dead = true;
}, },
stats: null, stats: null,
app: null, app: null,

9
tests/fastQuit.js Normal file
View File

@ -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();
});