[Libreoffice-commits] online.git: loolwsd/ChildProcessSession.cpp loolwsd/ChildProcessSession.hpp loolwsd/LOOLKit.cpp loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLWSD.cpp loolwsd/MasterProcessSession.cpp loolwsd/MasterProcessSession.hpp loolwsd/protocol.txt loolwsd/test

Ashod Nakashian ashod.nakashian at collabora.co.uk
Thu Jan 21 08:06:14 PST 2016


 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 <ashod.nakashian at collabora.co.uk>
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 <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

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);


More information about the Libreoffice-commits mailing list