[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