From b3c85e8534a0cf72d226febe2c0234c420fec1ef Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sat, 6 Feb 2016 19:40:50 -0800 Subject: [PATCH] Limit requestPlaylist to once per 60 seconds If clients call it quickly in succession with large playlists, it can cause node to get stuck stringifying socket.io frames and cause an out of memory crash. --- package.json | 2 +- src/channel/playlist.js | 23 ++++++++++++++++++++--- src/user.js | 1 + www/js/ui.js | 27 ++++++++++++++++++++++++--- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 437bd331..658856d9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.14.2", + "version": "3.14.3", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/channel/playlist.js b/src/channel/playlist.js index 0beb9777..f4a40bf0 100644 --- a/src/channel/playlist.js +++ b/src/channel/playlist.js @@ -11,6 +11,13 @@ var CustomEmbedFilter = require("../customembed").filter; var XSS = require("../xss"); const MAX_ITEMS = Config.get("playlist.max-items"); +// Limit requestPlaylist to once per 60 seconds +const REQ_PLAYLIST_THROTTLE = { + burst: 1, + sustained: 0, + cooldown: 60 +}; + const TYPE_QUEUE = { id: "string,boolean", @@ -216,9 +223,7 @@ PlaylistModule.prototype.onUserPostJoin = function (user) { user.socket.on("playerReady", function () { self.sendChangeMedia([user]); }); - user.socket.on("requestPlaylist", function () { - self.sendPlaylist([user]); - }); + user.socket.on("requestPlaylist", this.handleRequestPlaylist.bind(this, user)); user.on("login", function () { self.sendPlaylist([user]); }); @@ -1323,4 +1328,16 @@ PlaylistModule.prototype.handleQueuePlaylist = function (user, data) { }); }; +PlaylistModule.prototype.handleRequestPlaylist = function (user) { + if (user.reqPlaylistLimiter.throttle(REQ_PLAYLIST_THROTTLE)) { + user.socket.emit("errorMsg", { + msg: "Get Playlist URLs is limited to 1 usage every 60 seconds. " + + "Please try again later.", + code: "REQ_PLAYLIST_LIMIT_REACHED" + }); + } else { + this.sendPlaylist([user]); + } +}; + module.exports = PlaylistModule; diff --git a/src/user.js b/src/user.js index 0c21b68f..1e6612c0 100644 --- a/src/user.js +++ b/src/user.js @@ -21,6 +21,7 @@ function User(socket) { self.channel = null; self.queueLimiter = util.newRateLimiter(); self.chatLimiter = util.newRateLimiter(); + self.reqPlaylistLimiter = util.newRateLimiter(); self.awaytimer = false; var announcement = Server.getServer().announcement; diff --git a/www/js/ui.js b/www/js/ui.js index 5219aa85..bb80663a 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -490,9 +490,14 @@ $("#voteskip").click(function() { $("#getplaylist").click(function() { var callback = function(data) { hidePlayer(); - socket.listeners("playlist").splice( - socket.listeners("playlist").indexOf(callback) - ); + var idx = socket.listeners("errorMsg").indexOf(errCallback); + if (idx >= 0) { + socket.listeners("errorMsg").splice(idx); + } + idx = socket.listeners("playlist").indexOf(callback); + if (idx >= 0) { + socket.listeners("playlist").splice(idx); + } var list = []; for(var i = 0; i < data.length; i++) { var entry = formatURL(data[i].media); @@ -524,6 +529,22 @@ $("#getplaylist").click(function() { outer.modal(); }; socket.on("playlist", callback); + var errCallback = function(data) { + if (data.code !== "REQ_PLAYLIST_LIMIT_REACHED") { + return; + } + + var idx = socket.listeners("errorMsg").indexOf(errCallback); + if (idx >= 0) { + socket.listeners("errorMsg").splice(idx); + } + + idx = socket.listeners("playlist").indexOf(callback); + if (idx >= 0) { + socket.listeners("playlist").splice(idx); + } + }; + socket.on("errorMsg", errCallback); socket.emit("requestPlaylist"); });