diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index e1ac994e8..36da4842b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -37,6 +37,7 @@ var _ = require('underscore'); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js"); var channels = require("channels"); var stats = require('../stats'); +var remoteAddress = require("../utils/RemoteAddress").remoteAddress; /** * A associative array that saves informations about a session @@ -115,14 +116,16 @@ exports.handleDisconnect = function(client) //if this connection was already etablished with a handshake, send a disconnect message to the others if(session && session.author) { - client.get('remoteAddress', function(er, ip) { - //Anonymize the IP address if IP logging is disabled - if(settings.disableIPlogging) { - ip = 'ANONYMOUS'; - } - accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad') - }) + // Get the IP address from our persistant object + var ip = remoteAddress[client.id]; + + // Anonymize the IP address if IP logging is disabled + if(settings.disableIPlogging) { + ip = 'ANONYMOUS'; + } + + accessLogger.info('[LEAVE] Pad "'+session.padId+'": Author "'+session.author+'" on client '+client.id+' with IP "'+ip+'" left the pad') //get the author color out of the db authorManager.getAuthorColorId(session.author, function(err, color) @@ -753,7 +756,13 @@ function handleUserChanges(data, cb) exports.updatePadClients = function(pad, callback) { //skip this step if noone is on this pad - var roomClients = socketio.sockets.clients(pad.id); + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + if(roomClients.length==0) return callback(); @@ -766,10 +775,8 @@ exports.updatePadClients = function(pad, callback) var revCache = {}; //go trough all sessions on this pad - async.forEach(roomClients, function(client, callback) - { + async.forEach(roomClients, function(client, callback){ var sid = client.id; - //https://github.com/caolan/async#whilst //send them all new changesets async.whilst( @@ -1015,7 +1022,13 @@ function handleClientReady(client, message) return callback(); //Check if this author is already on the pad, if yes, kick the other sessions! - var roomClients = socketio.sockets.clients(padIds.padId); + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + for(var i = 0; i < roomClients.length; i++) { var sinfo = sessioninfos[roomClients[i].id]; if(sinfo && sinfo.author == author) { @@ -1032,19 +1045,19 @@ function handleClientReady(client, message) sessioninfos[client.id].readonly = padIds.readonly; //Log creation/(re-)entering of a pad - client.get('remoteAddress', function(er, ip) { - //Anonymize the IP address if IP logging is disabled - if(settings.disableIPlogging) { - ip = 'ANONYMOUS'; - } + var ip = remoteAddress[client.id]; - if(pad.head > 0) { - accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad'); - } - else if(pad.head == 0) { - accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad'); - } - }) + //Anonymize the IP address if IP logging is disabled + if(settings.disableIPlogging) { + ip = 'ANONYMOUS'; + } + + if(pad.head > 0) { + accessLogger.info('[ENTER] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" entered the pad'); + } + else if(pad.head == 0) { + accessLogger.info('[CREATE] Pad "'+padIds.padId+'": Client '+client.id+' with IP "'+ip+'" created the pad'); + } //If this is a reconnect, we don't have to send the client the ClientVars again if(message.reconnect == true) @@ -1165,7 +1178,14 @@ function handleClientReady(client, message) client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers); //Run trough all sessions of this pad - async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) + var roomClients = [], room = socketio.sockets.adapter.rooms[pad.id]; + if (room) { + for (var id in room) { + roomClients.push(socketio.sockets.adapter.nsp.connected[id]); + } + } + + async.forEach(roomClients, function(roomClient, callback) { var author; diff --git a/src/node/handler/SocketIORouter.js b/src/node/handler/SocketIORouter.js index b3e046d2c..0a7361f42 100644 --- a/src/node/handler/SocketIORouter.js +++ b/src/node/handler/SocketIORouter.js @@ -24,6 +24,7 @@ var log4js = require('log4js'); var messageLogger = log4js.getLogger("message"); var securityManager = require("../db/SecurityManager"); var readOnlyManager = require("../db/ReadOnlyManager"); +var remoteAddress = require("../utils/RemoteAddress").remoteAddress; var settings = require('../utils/Settings'); /** @@ -56,11 +57,15 @@ exports.setSocketIO = function(_socket) { socket.sockets.on('connection', function(client) { + + // Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js + // Fixed by having a persistant object, ideally this would actually be in the database layer + // TODO move to database layer if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){ - client.set('remoteAddress', client.handshake.headers['x-forwarded-for']); + remoteAddress[client.id] = client.handshake.headers['x-forwarded-for']; } else{ - client.set('remoteAddress', client.handshake.address.address); + remoteAddress[client.id] = client.handshake.address; } var clientAuthorized = false; diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 524bab3d9..e88a3f4c0 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -1,10 +1,17 @@ var log4js = require('log4js'); -var socketio = require('socket.io'); var settings = require('../../utils/Settings'); var socketIORouter = require("../../handler/SocketIORouter"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var webaccess = require("ep_etherpad-lite/node/hooks/express/webaccess"); +// there shouldn't be a browser that isn't compatible to all +// transports in this list at once +// e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling + +var socketio = require('socket.io')({ + transports: settings.socketTransportProtocols +}); + var padMessageHandler = require("../../handler/PadMessageHandler"); var connect = require('connect'); @@ -16,7 +23,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { /* Require an express session cookie to be present, and load the * session. See http://www.danielbaulig.de/socket-ioexpress for more * info */ - io.set('authorization', function (data, accept) { + + io.use(function(socket, accept) { + var data = socket.request; if (!data.headers.cookie) return accept('No session cookie transmitted.', false); // Use connect's cookie parser, because it knows how to parse signed cookies @@ -36,35 +45,17 @@ exports.expressCreateServer = function (hook_name, args, cb) { }); }); - // there shouldn't be a browser that isn't compatible to all - // transports in this list at once - // e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling - io.set('transports', settings.socketTransportProtocols ); - - var socketIOLogger = log4js.getLogger("socket.io"); - io.set('logger', { - debug: function (str) - { - socketIOLogger.debug.apply(socketIOLogger, arguments); - }, - info: function (str) - { - socketIOLogger.info.apply(socketIOLogger, arguments); - }, - warn: function (str) - { - socketIOLogger.warn.apply(socketIOLogger, arguments); - }, - error: function (str) - { - socketIOLogger.error.apply(socketIOLogger, arguments); - }, - }); + // var socketIOLogger = log4js.getLogger("socket.io"); + // Debug logging now has to be set at an environment level, this is stupid. + // https://github.com/Automattic/socket.io/wiki/Migrating-to-1.0 + // This debug logging environment is set in Settings.js //minify socket.io javascript - if(settings.minify) - io.enable('browser client minification'); - + // Due to a shitty decision by the SocketIO team minification is + // no longer available, details available at: + // http://stackoverflow.com/questions/23981741/minify-socket-io-socket-io-js-with-1-0 + // if(settings.minify) io.enable('browser client minification'); + //Initalize the Socket.IO Router socketIORouter.setSocketIO(io); socketIORouter.addComponent("pad", padMessageHandler); diff --git a/src/node/utils/RemoteAddress.js b/src/node/utils/RemoteAddress.js new file mode 100644 index 000000000..86a4a5b26 --- /dev/null +++ b/src/node/utils/RemoteAddress.js @@ -0,0 +1 @@ +exports.remoteAddress = {}; diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index c455617b0..1f22ccfd7 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -228,6 +228,7 @@ exports.reloadSettings = function reloadSettings() { log4js.configure(exports.logconfig);//Configure the logging appenders log4js.setGlobalLogLevel(exports.loglevel);//set loglevel + process.env['DEBUG'] = 'socket.io:' + exports.loglevel; // Used by SocketIO for Debug log4js.replaceConsole(); if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here diff --git a/src/package.json b/src/package.json index 7cabf6e61..9c4851a9f 100644 --- a/src/package.json +++ b/src/package.json @@ -15,9 +15,9 @@ "request" : "2.9.100", "require-kernel" : "1.0.5", "resolve" : ">=1.0.0", - "socket.io" : "0.9.x", + "socket.io" : ">=1.2.0", "ueberDB" : ">=0.2.9", - "express" : "3.1.0", + "express" : ">3.1.0 <3.9.0", "async" : "0.1.x", "connect" : "2.7.x", "clean-css" : "0.3.2",