[Libreoffice-commits] online.git: loolwsd/Admin.cpp loolwsd/Admin.hpp

Pranav Kant pranavk at collabora.com
Tue May 3 07:30:34 UTC 2016


 loolwsd/Admin.cpp |  323 ++++++++++++++++++++++--------------------------------
 loolwsd/Admin.hpp |    6 -
 2 files changed, 142 insertions(+), 187 deletions(-)

New commits:
commit 0559b581446fbd14c40961b10e565e8fe4fffc5b
Author: Pranav Kant <pranavk at collabora.com>
Date:   Tue May 3 12:40:21 2016 +0530

    loolwsd: Use SocketProcessor from IoUtil in Admin
    
    Change-Id: Ic89f04ee61f58ae13da9205e84d36ec06ed1d7ee

diff --git a/loolwsd/Admin.cpp b/loolwsd/Admin.cpp
index 38cae5b..8799db7 100644
--- a/loolwsd/Admin.cpp
+++ b/loolwsd/Admin.cpp
@@ -12,7 +12,6 @@
 #include <sys/poll.h>
 
 #include <Poco/Net/HTTPCookie.h>
-#include <Poco/Net/HTTPBasicCredentials.h>
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPRequestHandler.h>
 #include <Poco/Net/HTTPServerParams.h>
@@ -41,7 +40,6 @@
 using namespace LOOLProtocol;
 
 using Poco::StringTokenizer;
-using Poco::Net::HTTPBasicCredentials;
 using Poco::Net::HTTPCookie;
 using Poco::Net::HTTPRequest;
 using Poco::Net::HTTPRequestHandler;
@@ -57,219 +55,172 @@ using Poco::Net::WebSocket;
 using Poco::Net::WebSocketException;
 using Poco::Util::Application;
 
-/// Handle admin requests.
-void AdminRequestHandler::handleWSRequests(HTTPServerRequest& request, HTTPServerResponse& response, int nSessionId)
+bool AdminRequestHandler::adminCommandHandler(const std::vector<char>& payload)
 {
-    try
-    {
-        auto ws = std::make_shared<WebSocket>(request, response);
+    const std::string firstLine = getFirstLine(payload.data(), payload.size());
+    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+    Log::trace("Recv: " + firstLine);
 
+    if (tokens.count() < 1)
+        return false;
+
+    std::unique_lock<std::mutex> modelLock(_admin->getLock());
+    AdminModel& model = _admin->getModel();
+
+    if (tokens[0] == "documents" ||
+        tokens[0] == "active_users_count" ||
+        tokens[0] == "active_docs_count" ||
+        tokens[0] == "mem_stats" ||
+        tokens[0] == "cpu_stats" )
+    {
+        const std::string responseFrame = tokens[0] + " " + model.query(tokens[0]);
+        sendTextFrame(responseFrame);
+    }
+    else if (tokens[0] == "subscribe" && tokens.count() > 1)
+    {
+        for (unsigned i = 0; i < tokens.count() - 1; i++)
         {
-            std::unique_lock<std::mutex> modelLock(_admin->getLock());
-            // Subscribe the websocket of any AdminModel updates
-            AdminModel& model = _admin->getModel();
-            model.subscribe(nSessionId, ws);
+            model.subscribe(_sessionId, tokens[i + 1]);
         }
-
-        const Poco::Timespan waitTime(POLL_TIMEOUT_MS * 1000);
-        int flags = 0;
-        int n = 0;
-        ws->setReceiveTimeout(0);
-        do
+    }
+    else if (tokens[0] == "unsubscribe" && tokens.count() > 1)
+    {
+        for (unsigned i = 0; i < tokens.count() - 1; i++)
         {
-            char buffer[200000]; //FIXME: Dynamic?
-
-            if (ws->poll(waitTime, Socket::SELECT_READ))
+            model.unsubscribe(_sessionId, tokens[i + 1]);
+        }
+    }
+    else if (tokens[0] == "total_mem")
+    {
+        unsigned totalMem = _admin->getTotalMemoryUsage(model);
+        std::string responseFrame = "total_mem " + std::to_string(totalMem);
+        sendTextFrame(responseFrame);
+    }
+    else if (tokens[0] == "kill" && tokens.count() == 2)
+    {
+        try
+        {
+            const auto pid = std::stoi(tokens[1]);
+            if (kill(pid, SIGINT) != 0 && kill(pid, 0) !=0)
+            {
+                Log::syserror("Cannot terminate PID: " + tokens[0]);
+            }
+        }
+        catch(std::invalid_argument& exc)
+        {
+            Log::warn() << "Invalid PID to kill: " << tokens[0] << Log::end;
+            return false;
+        }
+    }
+    else if (tokens[0] == "settings")
+    {
+        // for now, we have only these settings
+        std::ostringstream oss;
+        oss << tokens[0] << " "
+            << "mem_stats_size=" << model.query("mem_stats_size") << " "
+            << "mem_stats_interval=" << std::to_string(_admin->getMemStatsInterval()) << " "
+            << "cpu_stats_size="  << model.query("cpu_stats_size") << " "
+            << "cpu_stats_interval=" << std::to_string(_admin->getCpuStatsInterval());
+
+        std::string responseFrame = oss.str();
+        sendTextFrame(responseFrame);
+    }
+    else if (tokens[0] == "set" && tokens.count() > 1)
+    {
+        for (unsigned i = 1; i < tokens.count(); i++)
+        {
+            StringTokenizer setting(tokens[i], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+            unsigned settingVal = 0;
+            try
+            {
+                settingVal = std::stoi(setting[1]);
+            }
+            catch (const std::exception& exc)
             {
-                n = IoUtil::receiveFrame(*ws, buffer, sizeof(buffer), flags);
+                Log::warn() << "Invalid setting value: "
+                            << setting[1] << " for "
+                            << setting[0] << Log::end;
+                return false;
+            }
 
-                if (n <= 0)
+            if (setting[0] == "mem_stats_size")
+            {
+                if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
                 {
-                    // Connection closed.
-                    Log::warn() << "Received " << n
-                                << " bytes. Connection closed. Flags: "
-                                << std::hex << flags << Log::end;
-                    break;
+                    model.setMemStatsSize(settingVal);
                 }
-                else
+            }
+            else if (setting[0] == "mem_stats_interval")
+            {
+                if (settingVal != _admin->getMemStatsInterval())
                 {
-                    assert(n > 0);
-                    const std::string firstLine = getFirstLine(buffer, n);
-                    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-                    Log::trace("Recv: " + firstLine);
-
-                    if (tokens.count() < 1)
-                        continue;
-
-                    // Lock the model mutex before interacting with it
-                    std::unique_lock<std::mutex> modelLock(_admin->getLock());
-                    AdminModel& model = _admin->getModel();
-
-                    if (tokens[0] == "documents" ||
-                        tokens[0] == "active_users_count" ||
-                        tokens[0] == "active_docs_count" ||
-                        tokens[0] == "mem_stats" ||
-                        tokens[0] == "cpu_stats" )
-                    {
-                        const std::string responseFrame = tokens[0] + " " + model.query(tokens[0]);
-                        sendTextFrame(ws, responseFrame);
-                    }
-                    else if (tokens[0] == "subscribe" && tokens.count() > 1)
-                    {
-                        for (unsigned i = 0; i < tokens.count() - 1; i++)
-                        {
-                            model.subscribe(nSessionId, tokens[i + 1]);
-                        }
-                    }
-                    else if (tokens[0] == "unsubscribe" && tokens.count() > 1)
-                    {
-                        for (unsigned i = 0; i < tokens.count() - 1; i++)
-                        {
-                            model.unsubscribe(nSessionId, tokens[i + 1]);
-                        }
-                    }
-                    else if (tokens[0] == "total_mem")
-                    {
-                        unsigned totalMem = _admin->getTotalMemoryUsage(model);
-                        std::string responseFrame = "total_mem " + std::to_string(totalMem);
-                        sendTextFrame(ws, responseFrame);
-                    }
-                    else if (tokens[0] == "kill" && tokens.count() == 2)
-                    {
-                        try
-                        {
-                            const auto pid = std::stoi(tokens[1]);
-                            if (kill(pid, SIGINT) != 0 && kill(pid, 0) !=0)
-                            {
-                                Log::syserror("Cannot terminate PID: " + tokens[0]);
-                            }
-                        }
-                        catch(std::invalid_argument& exc)
-                        {
-                            Log::warn() << "Invalid PID to kill: " << tokens[0] << Log::end;
-                        }
-                    }
-                    else if (tokens[0] == "settings")
-                    {
-                        // for now, we have only these settings
-                        std::ostringstream oss;
-                        oss << tokens[0] << " "
-                            << "mem_stats_size=" << model.query("mem_stats_size") << " "
-                            << "mem_stats_interval=" << std::to_string(_admin->getMemStatsInterval()) << " "
-                            << "cpu_stats_size="  << model.query("cpu_stats_size") << " "
-                            << "cpu_stats_interval=" << std::to_string(_admin->getCpuStatsInterval());
-
-                        std::string responseFrame = oss.str();
-                        sendTextFrame(ws, responseFrame);
-                    }
-                    else if (tokens[0] == "set" && tokens.count() > 1)
-                    {
-                        for (unsigned i = 1; i < tokens.count(); i++)
-                        {
-                            StringTokenizer setting(tokens[i], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-                            unsigned settingVal = 0;
-                            try
-                            {
-                                settingVal = std::stoi(setting[1]);
-                            }
-                            catch (const std::exception& exc)
-                            {
-                                Log::warn() << "Invalid setting value: "
-                                            << setting[1] << " for "
-                                            << setting[0] << Log::end;
-                                continue;
-                            }
-
-                            if (setting[0] == "mem_stats_size")
-                            {
-                                if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
-                                {
-                                    model.setMemStatsSize(settingVal);
-                                }
-                            }
-                            else if (setting[0] == "mem_stats_interval")
-                            {
-                                if (settingVal != _admin->getMemStatsInterval())
-                                {
-                                    _admin->rescheduleMemTimer(settingVal);
-                                    model.clearMemStats();
-                                    model.notify("settings mem_stats_interval=" + std::to_string(settingVal));
-                                }
-                            }
-                            else if (setting[0] == "cpu_stats_size")
-                            {
-                                if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
-                                {
-                                    model.setCpuStatsSize(settingVal);
-                                }
-                            }
-                            else if (setting[0] == "cpu_stats_interval")
-                            {
-                                if (settingVal != _admin->getCpuStatsInterval())
-                                {
-                                    _admin->rescheduleCpuTimer(settingVal);
-                                    model.clearCpuStats();
-                                    model.notify("settings cpu_stats_interval=" + std::to_string(settingVal));
-                                }
-                            }
-                        }
-                    }
+                    _admin->rescheduleMemTimer(settingVal);
+                    model.clearMemStats();
+                    model.notify("settings mem_stats_interval=" + std::to_string(settingVal));
+                }
+            }
+            else if (setting[0] == "cpu_stats_size")
+            {
+                if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
+                {
+                    model.setCpuStatsSize(settingVal);
+                }
+            }
+            else if (setting[0] == "cpu_stats_interval")
+            {
+                if (settingVal != _admin->getCpuStatsInterval())
+                {
+                    _admin->rescheduleCpuTimer(settingVal);
+                    model.clearCpuStats();
+                    model.notify("settings cpu_stats_interval=" + std::to_string(settingVal));
                 }
             }
-        }
-        while (!TerminationFlag &&
-               (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
-        Log::debug() << "Finishing AdminProcessor. TerminationFlag: " << TerminationFlag
-                     << ", payload size: " << n
-                     << ", flags: " << std::hex << flags << Log::end;
-    }
-    catch (const WebSocketException& exc)
-    {
-        Log::error("AdminRequestHandler::handleRequest: WebSocketException: " + exc.message());
-        switch (exc.code())
-        {
-        case WebSocket::WS_ERR_HANDSHAKE_UNSUPPORTED_VERSION:
-            response.set("Sec-WebSocket-Version", WebSocket::WEBSOCKET_VERSION);
-            // fallthrough
-        case WebSocket::WS_ERR_NO_HANDSHAKE:
-        case WebSocket::WS_ERR_HANDSHAKE_NO_VERSION:
-        case WebSocket::WS_ERR_HANDSHAKE_NO_KEY:
-            response.setStatusAndReason(HTTPResponse::HTTP_BAD_REQUEST);
-            response.setContentLength(0);
-            response.send();
-            break;
         }
     }
-    catch (const Poco::Net::NotAuthenticatedException& exc)
-    {
-        Log::info("NotAuthenticatedException");
-        response.set("WWW-Authenticate", "Basic realm=\"ws-online\"");
-        response.setStatusAndReason(HTTPResponse::HTTP_UNAUTHORIZED);
-        response.setContentLength(0);
-        response.send();
-    }
-    catch (const std::exception& exc)
+
+    return true;
+}
+
+/// Handle admin requests.
+void AdminRequestHandler::handleWSRequests(HTTPServerRequest& request, HTTPServerResponse& response, int sessionId)
+{
+    _adminWs = std::make_shared<WebSocket>(request, response);
+
     {
-        Log::error(std::string("AdminRequestHandler::handleRequest: Exception: ") + exc.what());
+        std::unique_lock<std::mutex> modelLock(_admin->getLock());
+        // Subscribe the websocket of any AdminModel updates
+        AdminModel& model = _admin->getModel();
+        _sessionId = sessionId;
+        model.subscribe(_sessionId, _adminWs);
     }
+
+    IoUtil::SocketProcessor(_adminWs,
+                            [this](const std::vector<char>& payload)
+                            {
+                                return adminCommandHandler(payload);
+                            },
+                            []() { },
+                            []() { return TerminationFlag; });
+
+    Log::debug() << "Finishing Admin Session " << Util::encodeId(sessionId);
 }
 
 AdminRequestHandler::AdminRequestHandler(Admin* adminManager)
     : _admin(adminManager)
 {    }
 
-void AdminRequestHandler::sendTextFrame(std::shared_ptr<Poco::Net::WebSocket>& socket, const std::string& message)
+void AdminRequestHandler::sendTextFrame(const std::string& message)
 {
     UnitWSD::get().onAdminQueryMessage(message);
-    socket->sendFrame(message.data(), message.size());
+    _adminWs->sendFrame(message.data(), message.size());
 }
 
 void AdminRequestHandler::handleRequest(HTTPServerRequest& request, HTTPServerResponse& response)
 {
     // Different session id pool for admin sessions (?)
-    const auto nSessionId = Util::decodeId(LOOLWSD::GenSessionId());
+    const auto sessionId = Util::decodeId(LOOLWSD::GenSessionId());
 
-    Util::setThreadName("admin_ws_" + std::to_string(nSessionId));
+    Util::setThreadName("admin_ws_" + std::to_string(sessionId));
 
     Log::debug("Thread started.");
 
@@ -283,7 +234,7 @@ void AdminRequestHandler::handleRequest(HTTPServerRequest& request, HTTPServerRe
             if (!FileServerRequestHandler::isAdminLoggedIn(request, response))
                 throw Poco::Net::NotAuthenticatedException("Invalid admin login");
 
-            handleWSRequests(request, response, nSessionId);
+            handleWSRequests(request, response, sessionId);
         }
     }
     catch(const Poco::Net::NotAuthenticatedException& exc)
diff --git a/loolwsd/Admin.hpp b/loolwsd/Admin.hpp
index 6ef3ad5..5c40146 100644
--- a/loolwsd/Admin.hpp
+++ b/loolwsd/Admin.hpp
@@ -33,10 +33,14 @@ public:
 private:
     void handleWSRequests(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response, int nSessionId);
 
-    void sendTextFrame(std::shared_ptr<Poco::Net::WebSocket>& socket, const std::string& message);
+    void sendTextFrame(const std::string& message);
+
+    bool adminCommandHandler(const std::vector<char>& payload);
 
 private:
     Admin* _admin;
+    std::shared_ptr<Poco::Net::WebSocket> _adminWs;
+    int _sessionId;
 };
 
 /// An admin command processor.


More information about the Libreoffice-commits mailing list