Makefile.am             |    8 +
 common/UnitHTTP.cpp     |   41 --------
 common/UnitHTTP.hpp     |   29 +++++
 tools/Connect.cpp       |   11 --
 tools/WebSocketDump.cpp |  240 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 276 insertions(+), 53 deletions(-)

New commits:
commit f68d54e02a43738754ba82a40ab42644cc6565aa
Author: Michael Meeks <michael.me...@collabora.com>
Date:   Tue Apr 17 14:01:49 2018 +0100

    Initial websocket test tool for remote admin connections.
    
    Change-Id: I8be2068bf7d77c70720a044556d11f5fc80b2f0c

diff --git a/Makefile.am b/Makefile.am
index bbff1083e..6aedc561c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,9 @@ SUBDIRS = . test loleaflet
 
 export ENABLE_DEBUG
 
-bin_PROGRAMS = loolwsd loolforkit loolmap loolmount looltool loolstress 
loolconfig
+bin_PROGRAMS = \
+       loolwsd loolforkit loolmap loolmount \
+       looltool loolstress loolconfig loolsocketdump
 
 dist_bin_SCRIPTS = loolwsd-systemplate-setup
 
@@ -131,6 +133,9 @@ loolconfig_SOURCES = tools/Config.cpp \
                     common/Log.cpp \
                     common/Util.cpp
 
+loolsocketdump_SOURCES = tools/WebSocketDump.cpp \
+                        $(shared_sources)
+
 wsd_headers = wsd/Admin.hpp \
               wsd/AdminModel.hpp \
               wsd/Auth.hpp \
diff --git a/tools/Connect.cpp b/tools/Connect.cpp
index 04b55ea0c..26db06674 100644
--- a/tools/Connect.cpp
+++ b/tools/Connect.cpp
@@ -236,13 +236,10 @@ private:
 
 namespace Util
 {
-
-void alertAllUsers(const std::string& cmd, const std::string& kind)
-{
-    std::cout << "error: cmd=" << cmd << " kind=" << kind << std::endl;
-    (void) kind;
-}
-
+    void alertAllUsers(const std::string& cmd, const std::string& kind)
+    {
+        std::cout << "error: cmd=" << cmd << " kind=" << kind << std::endl;
+    }
 }
 
 POCO_APP_MAIN(Connect)
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
new file mode 100644
index 000000000..a72e646c4
--- /dev/null
+++ b/tools/WebSocketDump.cpp
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* A simple tool that accepts web-socket connections and dumps the contents */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <Poco/URI.h>
+#include <Poco/MemoryStream.h>
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/Net/HTTPResponse.h>
+#include <Poco/StringTokenizer.h>
+
+#include <Log.hpp>
+#include <Util.hpp>
+#include <Protocol.hpp>
+#include <ServerSocket.hpp>
+#include <WebSocketHandler.hpp>
+#if ENABLE_SSL
+#  include <SslSocket.hpp>
+#endif
+
+// Dumps incoming websocket messages and doesn't respond.
+class DumpSocketHandler : public WebSocketHandler
+{
+public:
+    DumpSocketHandler()
+    {
+    }
+
+private:
+    /// Process incoming websocket messages
+    void handleMessage(bool fin, WSOpCode code, std::vector<char> &data)
+    {
+        std::cout << "WebSocket message code " << (int)code << " fin " << fin 
<< " data:\n";
+        Util::dumpHex(std::cout, "", "    ", data, false);
+    }
+};
+
+/// Handles incoming connections and dispatches to the appropriate handler.
+class ClientRequestDispatcher : public SocketHandlerInterface
+{
+public:
+    ClientRequestDispatcher()
+    {
+    }
+
+private:
+
+    /// Set the socket associated with this ResponseClient.
+    void onConnect(const std::shared_ptr<StreamSocket>& socket) override
+    {
+        _socket = socket;
+        LOG_TRC("#" << socket->getFD() << " Connected to 
ClientRequestDispatcher.");
+    }
+
+    /// Called after successful socket reads.
+    void handleIncomingMessage(SocketDisposition & /* disposition */) override
+    {
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
+        std::vector<char>& in = socket->_inBuffer;
+        LOG_TRC("#" << socket->getFD() << " handling incoming " << in.size() 
<< " bytes.");
+
+        // Find the end of the header, if any.
+        static const std::string marker("\r\n\r\n");
+        auto itBody = std::search(in.begin(), in.end(),
+                                  marker.begin(), marker.end());
+        if (itBody == in.end())
+        {
+            LOG_DBG("#" << socket->getFD() << " doesn't have enough data 
yet.");
+            return;
+        }
+
+        // Skip the marker.
+        itBody += marker.size();
+
+        Poco::MemoryInputStream message(&in[0], in.size());
+        Poco::Net::HTTPRequest request;
+        try
+        {
+            request.read(message);
+
+            Log::StreamLogger logger = Log::info();
+            if (logger.enabled())
+            {
+                logger << "#" << socket->getFD() << ": Client HTTP Request: "
+                       << request.getMethod() << ' '
+                       << request.getURI() << ' '
+                       << request.getVersion();
+
+                for (const auto& it : request)
+                {
+                    logger << " / " << it.first << ": " << it.second;
+                }
+
+                LOG_END(logger);
+            }
+
+            const std::streamsize contentLength = request.getContentLength();
+            const auto offset = itBody - in.begin();
+            const std::streamsize available = in.size() - offset;
+
+            if (contentLength != 
Poco::Net::HTTPMessage::UNKNOWN_CONTENT_LENGTH && available < contentLength)
+            {
+                LOG_DBG("Not enough content yet: ContentLength: " << 
contentLength << ", available: " << available);
+                return;
+            }
+        }
+        catch (const std::exception& exc)
+        {
+            // Probably don't have enough data just yet.
+            // TODO: timeout if we never get enough.
+            return;
+        }
+
+        try
+        {
+            // Routing
+            Poco::URI requestUri(request.getURI());
+            std::vector<std::string> reqPathSegs;
+            requestUri.getPathSegments(reqPathSegs);
+
+            LOG_INF("Incoming websocket request: " << request.getURI());
+
+            const std::string& requestURI = request.getURI();
+            Poco::StringTokenizer pathTokens(requestURI, "/", 
Poco::StringTokenizer::TOK_IGNORE_EMPTY |
+                                                              
Poco::StringTokenizer::TOK_TRIM);
+
+            if (request.find("Upgrade") != request.end() && 
Poco::icompare(request["Upgrade"], "websocket") == 0)
+                socket->setHandler(std::make_shared<DumpSocketHandler>());
+            else
+            {
+                Poco::Net::HTTPResponse response;
+                
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
+                response.setContentLength(0);
+                LOG_INF("DumpWebSockets bad request");
+                socket->send(response);
+                socket->shutdown();
+            }
+        }
+        catch (const std::exception& exc)
+        {
+            // Bad request.
+            std::ostringstream oss;
+            oss << "HTTP/1.1 400\r\n"
+                << "Date: " << 
Poco::DateTimeFormatter::format(Poco::Timestamp(), 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: LOOLWSD WOPI Agent\r\n"
+                << "Content-Length: 0\r\n"
+                << "\r\n";
+            socket->send(oss.str());
+            socket->shutdown();
+
+            // NOTE: Check _wsState to choose between HTTP response or 
WebSocket (app-level) error.
+            LOG_INF("#" << socket->getFD() << " Exception while processing 
incoming request: [" <<
+                    LOOLProtocol::getAbbreviatedMessage(in) << "]: " << 
exc.what());
+        }
+
+        // if we succeeded - remove the request from our input buffer
+        // we expect one request per socket
+        in.erase(in.begin(), itBody);
+    }
+
+    int getPollEvents(std::chrono::steady_clock::time_point /* now */,
+                      int & /* timeoutMaxMs */) override
+    {
+        return POLLIN;
+    }
+
+    void performWrites() override
+    {
+    }
+
+private:
+    // The socket that owns us (we can't own it).
+    std::weak_ptr<StreamSocket> _socket;
+};
+
+class DumpSocketFactory final : public SocketFactory
+{
+    std::shared_ptr<Socket> create(const int physicalFd) override
+    {
+#if ENABLE_SSL
+        return StreamSocket::create<SslStreamSocket>(physicalFd, 
std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+#else
+        return StreamSocket::create<StreamSocket>(physicalFd, 
std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+#endif
+    }
+};
+
+namespace Util
+{
+    void alertAllUsers(const std::string& cmd, const std::string& kind)
+    {
+        std::cout << "error: cmd=" << cmd << " kind=" << kind << std::endl;
+    }
+}
+
+int main (int argc, char **argv)
+{
+    int port = 9042;
+    (void) argc; (void) argv;
+
+    SocketPoll acceptPoll("accept");
+    SocketPoll DumpSocketPoll("websocket");
+
+    // Setup listening socket with a factory for connected sockets.
+    auto serverSocket = std::make_shared<ServerSocket>(
+        Socket::Type::IPv4, DumpSocketPoll,
+        std::make_shared<DumpSocketFactory>());
+
+    if (!serverSocket->bind(ServerSocket::Type::Public, port))
+    {
+        fprintf(stderr, "Failed to bind websocket to port %d\n", port);
+        return -1;
+    }
+
+    if (!serverSocket->listen())
+    {
+        fprintf(stderr, "Failed to listen on websocket, port %d\n", port);
+        return -1;
+    }
+
+    acceptPoll.startThread();
+    acceptPoll.insertNewSocket(serverSocket);
+
+    while (true)
+    {
+        DumpSocketPoll.poll(1000);
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 81fd84cf4eaefaac7a1412c6bfbfdd271f75e0ec
Author: Michael Meeks <michael.me...@collabora.com>
Date:   Tue Apr 17 16:40:55 2018 +0100

    Move UnitHTTP code to header to avoid linking trouble.
    
    Change-Id: I430110e840fa8b3862c21c1d4e02288ed704e0a3

diff --git a/Makefile.am b/Makefile.am
index ec1faf917..bbff1083e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -61,7 +61,6 @@ shared_sources = common/FileUtil.cpp \
                  common/SigUtil.cpp \
                  common/SpookyV2.cpp \
                  common/Unit.cpp \
-                 common/UnitHTTP.cpp \
                  common/Util.cpp \
                  net/DelaySocket.cpp \
                  net/Socket.cpp
diff --git a/common/UnitHTTP.cpp b/common/UnitHTTP.cpp
deleted file mode 100644
index 0d4b23c07..000000000
--- a/common/UnitHTTP.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include <config.h>
-
-#include <iostream>
-#include "UnitHTTP.hpp"
-
-Poco::Net::HTTPClientSession *UnitHTTP::createSession()
-{
-    // HTTP forced in configure hook.
-    return new Poco::Net::HTTPClientSession ("127.0.0.1",
-                                             ClientPortNumber);
-}
-
-UnitWebSocket::UnitWebSocket(const std::string &docURL)
-{
-    try {
-        UnitHTTPServerResponse response;
-        UnitHTTPServerRequest request(response, docURL);
-
-        _session = UnitHTTP::createSession();
-
-        // FIXME: leaking the session - hey ho ... do we need a UnitSocket ?
-        _socket = new LOOLWebSocket(*_session, request, response);
-    } catch (const Poco::Exception &ex) {
-        std::cerr << "Exception creating websocket " << ex.displayText() << 
std::endl;
-        throw;
-    }
-}
-
-LOOLWebSocket* UnitWebSocket::getLOOLWebSocket() const
-{
-    return _socket;
-}
diff --git a/common/UnitHTTP.hpp b/common/UnitHTTP.hpp
index a6a107d9f..32f2b7416 100644
--- a/common/UnitHTTP.hpp
+++ b/common/UnitHTTP.hpp
@@ -114,7 +114,12 @@ public:
 
 namespace UnitHTTP
 {
-    Poco::Net::HTTPClientSession* createSession();
+    Poco::Net::HTTPClientSession* createSession()
+    {
+        // HTTP forced in configure hook.
+        return new Poco::Net::HTTPClientSession ("127.0.0.1",
+                                                 ClientPortNumber);
+    }
 }
 
 class UnitWebSocket
@@ -124,14 +129,32 @@ class UnitWebSocket
 
 public:
     /// Get a websocket connected for a given URL
-    UnitWebSocket(const std::string& docURL);
+    UnitWebSocket(const std::string& docURL)
+    {
+        try {
+            UnitHTTPServerResponse response;
+            UnitHTTPServerRequest request(response, docURL);
+
+            _session = UnitHTTP::createSession();
+
+            // FIXME: leaking the session - hey ho ... do we need a UnitSocket 
?
+            _socket = new LOOLWebSocket(*_session, request, response);
+        } catch (const Poco::Exception &ex) {
+            std::cerr << "Exception creating websocket " << ex.displayText() 
<< std::endl;
+            throw;
+        }
+    }
+
     ~UnitWebSocket()
     {
         delete _socket;
         delete _session;
     }
 
-    LOOLWebSocket* getLOOLWebSocket() const;
+    LOOLWebSocket* getLOOLWebSocket() const
+    {
+        return _socket;
+    }
 };
 
 #endif
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to