diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 5c855651c..188da5aea 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -806,8 +806,20 @@ function handleClientReady(client, message) clientVars.userName = authorName; } - //Send the clientVars to the Client - client.json.send(clientVars); + //This is a reconnect, so we don't have to send the client the ClientVars again + if(message.reconnect == true) + { + //Save the revision in sessioninfos, we take the revision from the info the client send to us + sessioninfos[client.id].rev = message.client_rev; + } + //This is a normal first connect + else + { + //Send the clientVars to the Client + client.json.send(clientVars); + //Save the revision in sessioninfos + sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); + } //Save the revision and the author id in sessioninfos sessioninfos[client.id].rev = pad.getHeadRevisionNumber(); diff --git a/static/css/pad.css b/static/css/pad.css index 3d170c3dd..ec89edbac 100644 --- a/static/css/pad.css +++ b/static/css/pad.css @@ -469,7 +469,7 @@ table#otheruserstable { display: none; } .modaldialog.cboxreconnecting .modaldialog-inner, .modaldialog.cboxconnecting .modaldialog-inner { - background: url(static/img/connectingbar.gif) no-repeat center 60px; + background: url(../../static/img/connectingbar.gif) no-repeat center 60px; height: 100px; } .modaldialog.cboxreconnecting { diff --git a/static/img/connectingbar.gif b/static/img/connectingbar.gif new file mode 100644 index 000000000..34f54e90c Binary files /dev/null and b/static/img/connectingbar.gif differ diff --git a/static/js/collab_client.js b/static/js/collab_client.js index c5635aa5f..c64b99949 100644 --- a/static/js/collab_client.js +++ b/static/js/collab_client.js @@ -77,10 +77,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { if (socket) { -/*socket.onclosed = function() {}; - socket.onhiccup = function() {}; - socket.disconnect(true);*/ - socket.disconnect(); + setChannelState("DISCONNECTED", "unload"); } }); if ($.browser.mozilla) @@ -100,18 +97,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) editor.setBaseAttributedText(serverVars.initialAttributedText, serverVars.apool); editor.setUserChangeNotificationCallback(wrapRecordingErrors("handleUserChanges", handleUserChanges)); - function abandonConnection(reason) - { - if (socket) - { -/*socket.onclosed = function() {}; - socket.onhiccup = function() {};*/ - socket.disconnect(); - } - socket = null; - setChannelState("DISCONNECTED", reason); - } - function dmesg(str) { if (typeof window.ajlog == "string") window.ajlog += str + '\n'; @@ -124,7 +109,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) { - abandonConnection("initsocketfail"); // give up + setChannelState("DISCONNECTED", "initsocketfail"); } else { @@ -141,8 +126,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) if (state == "COMMITTING" && (t - lastCommitTime) > 20000) { // a commit is taking too long - appLevelDisconnectReason = "slowcommit"; - socket.disconnect(); + setChannelState("DISCONNECTED", "slowcommit"); } else if (state == "COMMITTING" && (t - lastCommitTime) > 5000) { @@ -230,11 +214,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) handleMessageFromServer(obj); });*/ - socket.on('disconnect', function(obj) - { - handleSocketClosed(true); - }); - /*var success = false; callCatchingErrors("setUpSocket", function() { appLevelDisconnectReason = null; @@ -366,7 +345,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) if (newRev != (rev + 1)) { dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1)); - socket.disconnect(); + setChannelState("DISCONNECTED", "badmessage_newchanges"); return; } rev = newRev; @@ -378,7 +357,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) if (newRev != (rev + 1)) { dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1)); - socket.disconnect(); + setChannelState("DISCONNECTED", "badmessage_acceptcommit"); return; } rev = newRev; @@ -520,48 +499,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) //pad.dmesg($.map(getConnectedUsers(), function(u) { return u.userId.slice(-2); }).join(',')); } - function handleSocketClosed(params) - { - socket = null; - - $.each(keys(userSet), function() - { - var uid = String(this); - if (uid != userId) - { - var userInfo = userSet[uid]; - delete userSet[uid]; - callbacks.onUserLeave(userInfo); - dmesgUsers(); - } - }); - - var reason = appLevelDisconnectReason || params.reason; - var shouldReconnect = params.reconnect; - if (shouldReconnect) - { - - // determine if this is a tight reconnect loop due to weird connectivity problems - reconnectTimes.push(+new Date()); - var TOO_MANY_RECONNECTS = 8; - var TOO_SHORT_A_TIME_MS = 10000; - if (reconnectTimes.length >= TOO_MANY_RECONNECTS && ((+new Date()) - reconnectTimes[reconnectTimes.length - TOO_MANY_RECONNECTS]) < TOO_SHORT_A_TIME_MS) - { - setChannelState("DISCONNECTED", "looping"); - } - else - { - setChannelState("RECONNECTING", reason); - setUpSocket(); - } - - } - else - { - setChannelState("DISCONNECTED", reason); - } - } - function setChannelState(newChannelState, moreInfo) { if (newChannelState != channelState) @@ -650,128 +587,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) return rev; } - function getDiagnosticInfo() - { - var maxCaughtErrors = 3; - var maxAceErrors = 3; - var maxDebugMessages = 50; - var longStringCutoff = 500; - - function trunc(str) - { - return String(str).substring(0, longStringCutoff); - } - - var info = { - errors: { - length: 0 - } - }; - - function addError(e, catcher, time) - { - var error = { - catcher: catcher - }; - if (time) error.time = time; - - // a little over-cautious? - try - { - if (e.description) error.description = e.description; - } - catch (x) - {} - try - { - if (e.fileName) error.fileName = e.fileName; - } - catch (x) - {} - try - { - if (e.lineNumber) error.lineNumber = e.lineNumber; - } - catch (x) - {} - try - { - if (e.message) error.message = e.message; - } - catch (x) - {} - try - { - if (e.name) error.name = e.name; - } - catch (x) - {} - try - { - if (e.number) error.number = e.number; - } - catch (x) - {} - try - { - if (e.stack) error.stack = trunc(e.stack); - } - catch (x) - {} - - info.errors[info.errors.length] = error; - info.errors.length++; - } - for (var i = 0; - ((i < caughtErrors.length) && (i < maxCaughtErrors)); i++) - { - addError(caughtErrors[i], caughtErrorCatchers[i], caughtErrorTimes[i]); - } - if (editor) - { - var aceErrors = editor.getUnhandledErrors(); - for (var i = 0; - ((i < aceErrors.length) && (i < maxAceErrors)); i++) - { - var errorRecord = aceErrors[i]; - addError(errorRecord.error, "ACE", errorRecord.time); - } - } - - info.time = +new Date(); - info.collabState = state; - info.channelState = channelState; - info.lastCommitTime = lastCommitTime; - info.numSocketReconnects = reconnectTimes.length; - info.userId = userId; - info.currentRev = rev; - info.participants = (function() - { - var pp = []; - for (var u in userSet) - { - pp.push(u); - } - return pp.join(','); - })(); - - if (debugMessages.length > maxDebugMessages) - { - debugMessages = debugMessages.slice(debugMessages.length - maxDebugMessages, debugMessages.length); - } - - info.debugMessages = { - length: 0 - }; - for (var i = 0; i < debugMessages.length; i++) - { - info.debugMessages[i] = trunc(debugMessages[i]); - info.debugMessages.length++; - } - - return info; - } - function getMissedChanges() { var obj = {}; @@ -863,10 +678,10 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) sendClientMessage: sendClientMessage, sendMessage: sendMessage, getCurrentRevisionNumber: getCurrentRevisionNumber, - getDiagnosticInfo: getDiagnosticInfo, getMissedChanges: getMissedChanges, callWhenNotCommitting: callWhenNotCommitting, - addHistoricalAuthors: tellAceAboutHistoricalAuthors + addHistoricalAuthors: tellAceAboutHistoricalAuthors, + setChannelState: setChannelState }); } diff --git a/static/js/pad2.js b/static/js/pad2.js index d6429eea9..d194b6607 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -167,15 +167,17 @@ function handshake() var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io"; //connect socket = io.connect(url, { - resource: resource + resource: resource, + 'max reconnection attempts': 3 }); - socket.once('connect', function() + function sendClientReady(isReconnect) { var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1); padId = unescape(padId); // unescape neccesary due to Safari and Opera interpretation of spaces - document.title = document.title + " | " + padId; + if(!isReconnect) + document.title = document.title + " | " + padId; var token = readCookie("token"); if (token == null) @@ -196,7 +198,43 @@ function handshake() "token": token, "protocolVersion": 2 }; + + //this is a reconnect, lets tell the server our revisionnumber + if(isReconnect == true) + { + msg.client_rev=pad.collabClient.getCurrentRevisionNumber(); + msg.reconnect=true; + } + socket.json.send(msg); + }; + + var disconnectTimeout; + + socket.once('connect', function () { + sendClientReady(false); + }); + + socket.on('reconnect', function () { + //reconnect is before the timeout, lets stop the timeout + if(disconnectTimeout) + { + clearTimeout(disconnectTimeout); + } + + pad.collabClient.setChannelState("CONNECTED"); + sendClientReady(true); + }); + + socket.on('disconnect', function () { + function disconnectEvent() + { + pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout"); + } + + pad.collabClient.setChannelState("RECONNECTING"); + + disconnectTimeout = setTimeout(disconnectEvent, 10000); }); var receivedClientVars = false; @@ -352,7 +390,6 @@ var pad = { //initialize the chat chat.init(); - pad.diagnosticInfo.uniqueId = padutils.uniqueId(); pad.initTime = +(new Date()); pad.padOptions = clientVars.initialOptions; @@ -649,7 +686,22 @@ var pad = { else if (newState == "DISCONNECTED") { pad.diagnosticInfo.disconnectedMessage = message; - pad.diagnosticInfo.padInitTime = pad.initTime; + pad.diagnosticInfo.padId = pad.getPadId(); + pad.diagnosticInfo.socket = {}; + + //we filter non objects from the socket object and put them in the diagnosticInfo + //this ensures we have no cyclic data - this allows us to stringify the data + for(var i in socket.socket) + { + var value = socket.socket[i]; + var type = typeof value; + + if(type == "string" || type == "number") + { + pad.diagnosticInfo.socket[i] = value; + } + } + pad.asyncSendDiagnosticInfo(); if (typeof window.ajlog == "string") { @@ -721,7 +773,6 @@ var pad = { }, asyncSendDiagnosticInfo: function() { - pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo(); window.setTimeout(function() { $.ajax( @@ -729,7 +780,6 @@ var pad = { type: 'post', url: '/ep/pad/connection-diagnostic-info', data: { - padId: pad.getPadId(), diagnosticInfo: JSON.stringify(pad.diagnosticInfo) }, success: function() @@ -809,7 +859,7 @@ var pad = { }, preloadImages: function() { - var images = []; // Removed as we now use CSS and JS for colorpicker + var images = ["../static/img/connectingbar.gif"]; function loadNextImage() {