loolwsd/ChildProcessSession.cpp | 28 +++++++++++++++++++--- loolwsd/ChildProcessSession.hpp | 3 ++ loolwsd/LOOLKit.cpp | 8 +++++- loolwsd/LOOLSession.cpp | 23 ++++++++++++++++-- loolwsd/LOOLSession.hpp | 13 ++++++++-- loolwsd/LOOLWSD.cpp | 5 ++- loolwsd/MasterProcessSession.cpp | 49 +++++++++++++++++++++++++++++++-------- loolwsd/MasterProcessSession.hpp | 3 ++ loolwsd/protocol.txt | 5 +++ loolwsd/test/httpwstest.cpp | 2 + 10 files changed, 119 insertions(+), 20 deletions(-)
New commits: commit dd374d8aec29cad0a1548ea718aa5e8846e8e949 Author: Ashod Nakashian <[email protected]> Date: Thu Jan 21 09:26:34 2016 -0500 loolwsd: disconnect command to gracefully shutdown a socket Change-Id: I8beb4c14fc95bdb2a98c7e5da44408511bce5e28 Reviewed-on: https://gerrit.libreoffice.org/21683 Reviewed-by: Ashod Nakashian <[email protected]> Tested-by: Ashod Nakashian <[email protected]> diff --git a/loolwsd/ChildProcessSession.cpp b/loolwsd/ChildProcessSession.cpp index c8ccc79..76c6d46 100644 --- a/loolwsd/ChildProcessSession.cpp +++ b/loolwsd/ChildProcessSession.cpp @@ -64,12 +64,28 @@ ChildProcessSession::~ChildProcessSession() { Log::info("~ChildProcessSession dtor [" + getName() + "]."); + disconnect(); +} + +void ChildProcessSession::disconnect(const std::string& reason) +{ + if (!isDisconnected()) + { std::unique_lock<std::recursive_mutex> lock(Mutex); - if (_multiView) - _loKitDocument->pClass->setView(_loKitDocument, _viewId); + if (_multiView) + _loKitDocument->pClass->setView(_loKitDocument, _viewId); - _onUnload(getId()); + //TODO: Handle saving to temp etc. + _onUnload(getId()); + + LOOLSession::disconnect(reason); + } +} + +bool ChildProcessSession::handleDisconnect(Poco::StringTokenizer& tokens) +{ + return LOOLSession::handleDisconnect(tokens); } bool ChildProcessSession::_handleInput(const char *buffer, int length) @@ -136,6 +152,7 @@ bool ChildProcessSession::_handleInput(const char *buffer, int length) // i.e. need to be handled in a child process. assert(tokens[0] == "clientzoom" || + tokens[0] == "disconnect" || tokens[0] == "downloadas" || tokens[0] == "getchildid" || tokens[0] == "gettextselection" || @@ -165,6 +182,11 @@ bool ChildProcessSession::_handleInput(const char *buffer, int length) { return clientZoom(buffer, length, tokens); } + else if (tokens[0] == "disconnect") + { + // This was the last we would hear from the client on this socket. + return handleDisconnect(tokens); + } else if (tokens[0] == "downloadas") { return downloadAs(buffer, length, tokens); diff --git a/loolwsd/ChildProcessSession.hpp b/loolwsd/ChildProcessSession.hpp index 5c7754c..6d9f326 100644 --- a/loolwsd/ChildProcessSession.hpp +++ b/loolwsd/ChildProcessSession.hpp @@ -46,6 +46,9 @@ public: virtual bool getPartPageRectangles(const char *buffer, int length) override; + virtual void disconnect(const std::string& reason = "") override; + virtual bool handleDisconnect(Poco::StringTokenizer& tokens) override; + int getViewId() const { return _viewId; } const std::string& getDocType() const { return _docType; } diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index c9160c3..e474efa 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -392,6 +392,12 @@ public: StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + if (firstLine == "disconnect") + { + Log::info("Client disconnected [" + (tokens.count() == 2 ? tokens[1] : std::string("no reason")) + "]."); + break; + } + // Check if it is a "nextmessage:" and in that case read the large // follow-up message separately, and handle that only. int size; @@ -418,7 +424,7 @@ public: queue.put("eof"); queueHandlerThread.join(); - // We should probably send the Client some sensible message and reason. + _session->sendTextFrame("disconnect"); _session->sendTextFrame("eof"); } catch (const Exception& exc) diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp index 0941bf5..3522bd8 100644 --- a/loolwsd/LOOLSession.cpp +++ b/loolwsd/LOOLSession.cpp @@ -86,9 +86,13 @@ LOOLSession::LOOLSession(const std::string& id, const Kind kind, _kindString(kind == Kind::ToClient ? "ToClient" : kind == Kind::ToMaster ? "ToMaster" : "ToPrisoner"), _ws(ws), - _docURL(""), - _bShutdown(false) + _bShutdown(false), + _disconnected(false) { + // Only a post request can have a null ws. + if (_kind != Kind::ToClient) + assert(_ws); + setId(id); } @@ -173,6 +177,21 @@ void LOOLSession::parseDocOptions(const StringTokenizer& tokens, int& part, std: } } +void LOOLSession::disconnect(const std::string& reason) +{ + if (!_disconnected) + { + sendTextFrame("disconnect " + reason); + _disconnected = true; + } +} + +bool LOOLSession::handleDisconnect(Poco::StringTokenizer& /*tokens*/) +{ + _disconnected = true; + return false; +} + bool LOOLSession::handleInput(const char *buffer, int length) { assert(buffer != nullptr); diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp index c03de93..70aff29 100644 --- a/loolwsd/LOOLSession.hpp +++ b/loolwsd/LOOLSession.hpp @@ -44,6 +44,7 @@ public: const std::string& getId() const { return _id; } const std::string& getName() const { return _name; } + bool isDisconnected() const { return _disconnected; } void sendTextFrame(const std::string& text); @@ -55,6 +56,12 @@ public: bool handleInput(const char *buffer, int length); + /// Invoked when we want to disconnect a session. + virtual void disconnect(const std::string& reason = ""); + + /// Called to handle disconnection command from socket. + virtual bool handleDisconnect(Poco::StringTokenizer& tokens); + protected: LOOLSession(const std::string& id, const Kind kind, std::shared_ptr<Poco::Net::WebSocket> ws); @@ -108,10 +115,12 @@ private: virtual bool _handleInput(const char *buffer, int length) = 0; private: - // A session ID specific to an end-to-end connection (from user to lokit). + /// A session ID specific to an end-to-end connection (from user to lokit). std::string _id; - // A readable name that identifies our peer and ID. + /// A readable name that identifies our peer and ID. std::string _name; + /// True if we have been disconnected. + bool _disconnected; std::mutex _mutex; }; diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index 2c3384c..531818d 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -1021,8 +1021,6 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) if (LOOLWSD::DoTest) inputThread.join(); - close(BrokerWritePipe); - // stop the service, no more request srv.stop(); srv2.stop(); @@ -1032,6 +1030,7 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) threadPool2.joinAll(); // Terminate child processes + Util::writeFIFO(LOOLWSD::BrokerWritePipe, "eof"); for (auto i : MasterProcessSession::ChildProcesses) { Log::info("Requesting child process " + std::to_string(i.first) + " to terminate"); @@ -1041,6 +1040,8 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) // wait broker process finish waitpid(-1, &status, WUNTRACED); + close(BrokerWritePipe); + Log::info("Cleaning up childroot directory [" + ChildRoot + "]."); std::vector<std::string> jails; File(ChildRoot).list(jails); diff --git a/loolwsd/MasterProcessSession.cpp b/loolwsd/MasterProcessSession.cpp index aebb477..370202b 100644 --- a/loolwsd/MasterProcessSession.cpp +++ b/loolwsd/MasterProcessSession.cpp @@ -56,22 +56,45 @@ MasterProcessSession::~MasterProcessSession() { // We could be unwinding because our peer's connection // died. Handle I/O errors in that case. + disconnect(); + } + catch (const std::exception& exc) + { + Log::error(std::string("Exception: ") + exc.what()); + } +} + +void MasterProcessSession::disconnect(const std::string& reason) +{ + if (!isDisconnected()) + { + LOOLSession::disconnect(reason); + auto peer = _peer.lock(); - if (_kind == Kind::ToClient && peer) - { - peer->sendTextFrame("eof"); - } - else - if (_kind == Kind::ToPrisoner && peer) + if (peer) { - peer->_bShutdown = true; - Util::shutdownWebSocket(*(peer->_ws)); + peer->disconnect(reason); } } - catch (const std::exception& exc) +} + +bool MasterProcessSession::handleDisconnect(Poco::StringTokenizer& tokens) +{ + Log::info("Graceful disconnect on " + getName() + " [" + + (tokens.count() > 1 ? tokens[1] : std::string("no reason")) + + "]."); + + LOOLSession::handleDisconnect(tokens); + disconnect(tokens.count() > 1 ? tokens[1] : std::string()); + + auto peer = _peer.lock(); + if (peer) { - Log::error(std::string("Exception: ") + exc.what()); + const auto reason = (tokens.count() > 1 ? tokens[1] : std::string()); + peer->disconnect(reason); } + + return false; } bool MasterProcessSession::_handleInput(const char *buffer, int length) @@ -271,6 +294,7 @@ bool MasterProcessSession::_handleInput(const char *buffer, int length) else if (tokens[0] != "canceltiles" && tokens[0] != "clientzoom" && tokens[0] != "commandvalues" && + tokens[0] != "disconnect" && tokens[0] != "downloadas" && tokens[0] != "getchildid" && tokens[0] != "gettextselection" && @@ -351,6 +375,11 @@ bool MasterProcessSession::_handleInput(const char *buffer, int length) { _tileCache->documentSaved(); } + else if (tokens[0] == "disconnect") + { + // This was the last we would hear from the client on this socket. + return handleDisconnect(tokens); + } } return true; } diff --git a/loolwsd/MasterProcessSession.hpp b/loolwsd/MasterProcessSession.hpp index 54f51cc..73ede56 100644 --- a/loolwsd/MasterProcessSession.hpp +++ b/loolwsd/MasterProcessSession.hpp @@ -34,6 +34,9 @@ public: virtual bool getPartPageRectangles(const char *buffer, int length) override; + virtual void disconnect(const std::string& reason = "") override; + virtual bool handleDisconnect(Poco::StringTokenizer& tokens) override; + /** * Return the URL of the saved-as document when it's ready. If called * before it's ready, the call blocks till then. diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt index fd10e40..b30f180 100644 --- a/loolwsd/protocol.txt +++ b/loolwsd/protocol.txt @@ -27,6 +27,11 @@ canceltiles dropped and will not be handled. There is no guarantee of exactly which tile: messages might still be sent back to the client. +disconnect [reason] + + Sent before disconnecting gracefully. + reason: optional human-readable reason to disconnect. + downloadas downloadas name=<fileName> id=<id> format=<document format> options=<SkipImages, etc> Exports the current document to the desired format and returns a download URL diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp index 7b8d939..79dedf9 100644 --- a/loolwsd/test/httpwstest.cpp +++ b/loolwsd/test/httpwstest.cpp @@ -84,6 +84,7 @@ void HTTPWSTest::testPaste() } } while (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE); + sendTextFrame(_socket, "disconnect"); _socket.shutdown(); CPPUNIT_ASSERT_EQUAL(std::string("aaa bbb ccc"), selection); } @@ -152,6 +153,7 @@ void HTTPWSTest::testRenderingOptions() } } while (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE); + sendTextFrame(_socket, "disconnect"); _socket.shutdown(); // Expected format is something like 'type=text parts=2 current=0 width=12808 height=1142'. Poco::StringTokenizer tokens(status, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM); _______________________________________________ Libreoffice-commits mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
