diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index eab6ca82d..40c4cf20d 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -195,6 +195,36 @@ L.Socket = L.Class.extend({ else if (textMsg === 'shuttingdown') { msg = _('Server is shutting down for maintenance (auto-saving)'); } + else if (textMsg === 'recycling') { + msg = _('Server is recycling and will be available shortly'); + + this._map._active = false; + + // Prevent reconnecting the world at the same time. + var min = 5000; + var max = 10000; + var timeoutMs = Math.floor(Math.random() * (max - min) + min); + + socket = this; + map = this._map; + vex.timer = setInterval(function() { + if (socket.connected()) { + // We're connected: cancel timer and dialog. + clearTimeout(vex.timer); + if (vex.dialogID > 0) { + var id = vex.dialogID; + vex.dialogID = -1; + vex.close(id); + } + return; + } + + try { + socket.initialize(map); + } catch (error) { + } + }, timeoutMs); + } // Close any open dialogs first. if (vex.dialogID > 0) { diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 25a4bab04..6b143a453 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -175,6 +175,10 @@ static std::atomic OutstandingForks(1); // Forkit always spawns 1. static std::map> DocBrokers; static std::mutex DocBrokersMutex; +/// Used when shutting down to notify them all that the server is recycling. +static std::vector> ClientWebSockets; +static std::mutex ClientWebSocketsMutex; + #if ENABLE_DEBUG static int careerSpanSeconds = 0; #endif @@ -983,6 +987,13 @@ private: } } + if (SigUtil::isShuttingDown()) + { + std::lock_guard lock(ClientWebSocketsMutex); + LOG_TRC("Capturing Client WS for [" << id << "]"); + ClientWebSockets.push_back(ws); + } + LOOLWSD::dumpEventTrace(docBroker->getJailId(), id, "EndSession: " + uri); LOG_INF("Finishing GET request handler for session [" << id << "]."); } @@ -1011,11 +1022,14 @@ private: } else { - // something wrong, with internal exceptions - LOG_TRC("Abnormal close handshake."); - session->closeFrame(); - // FIXME: handle exception thrown from here ? ... - ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY); + if (!SigUtil::isShuttingDown()) + { + // something wrong, with internal exceptions + LOG_TRC("Abnormal close handshake."); + session->closeFrame(); + // FIXME: handle exception thrown from here ? ... + ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY); + } } LOG_INF("Finished GET request handler for session [" << id << "]."); @@ -2149,6 +2163,27 @@ int LOOLWSD::main(const std::vector& /*args*/) FileUtil::removeFile(path, true); } + if (SigUtil::isShuttingDown()) + { + // At this point there should be no other thread, but... + std::lock_guard lock(ClientWebSocketsMutex); + + LOG_INF("Notifying clients that we are recycling."); + static const std::string msg("close: recycling"); + for (auto& ws : ClientWebSockets) + { + try + { + ws->sendFrame(msg.data(), msg.size()); + ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY); + } + catch (const std::exception& ex) + { + LOG_ERR("Error while notifying client of recycle: " << ex.what()); + } + } + } + // Finally, we no longer need SSL. if (LOOLWSD::isSSLEnabled()) { diff --git a/wsd/protocol.txt b/wsd/protocol.txt index 2ebdc332e..b6d55ef39 100644 --- a/wsd/protocol.txt +++ b/wsd/protocol.txt @@ -248,11 +248,13 @@ close: ability to kill all other sessions if EnableOwnerTermination flag in WOPI CheckFileInfo is 'true' (assumed to be 'false' by default). - * shuttingdown - Sent when the server is going down in a graceful fashion. The server doesn't disconnect from clients yet, but starts saving document and tearing down internals. + * recycling - The last message sent from the server when it is gracefully + shutting down to let clients know they can try connecting + after a short interval. getchildid: id=