[Libreoffice-commits] online.git: common/Util.hpp test/helpers.hpp test/Makefile.am test/UnitPHPProxy.cpp

gokaysatir (via logerrit) logerrit at kemper.freedesktop.org
Wed Sep 9 13:50:29 UTC 2020


 common/Util.hpp       |    6 +
 test/Makefile.am      |    4 
 test/UnitPHPProxy.cpp |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test/helpers.hpp      |   54 +++++++++
 4 files changed, 335 insertions(+)

New commits:
commit 1951fdbd426781fd652af2e517e2e61b22b2831d
Author:     gokaysatir <gokaysatir at collabora.com>
AuthorDate: Thu Jul 2 19:54:56 2020 +0300
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Wed Sep 9 15:50:10 2020 +0200

    lool: php proxy simulation.
    
    Change-Id: I5ea5515e317242f2ad2abd3209ce0241d64b631b
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97820
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/common/Util.hpp b/common/Util.hpp
index 9dbfebe8b..03719e97f 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -1217,6 +1217,12 @@ int main(int argc, char**argv)
         return pair.second ? pair : std::make_pair(def, false);
     }
 
+    /// Get system_clock now in miliseconds.
+    inline int64_t getNowInMS()
+    {
+        return std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
+    }
+
 } // end namespace Util
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/Makefile.am b/test/Makefile.am
index 9fa6506b1..899dd0764 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -37,6 +37,7 @@ noinst_LTLIBRARIES = \
 	unit-load.la \
 	unit-cursor.la \
 	unit-calc.la \
+	unit-php-proxy.la \
 	unit-insert-delete.la \
 	unit-close.la \
 	unit-bad-doc-load.la \
@@ -178,6 +179,8 @@ unit_cursor_la_SOURCES = UnitCursor.cpp
 unit_cursor_la_LIBADD = $(CPPUNIT_LIBS)
 unit_calc_la_SOURCES = UnitCalc.cpp
 unit_calc_la_LIBADD = $(CPPUNIT_LIBS)
+unit_php_proxy_la_SOURCES = UnitPHPProxy.cpp
+unit_php_proxy_la_LIBADD = $(CPPUNIT_LIBS)
 unit_insert_delete_la_SOURCES = UnitInsertDelete.cpp
 unit_insert_delete_la_LIBADD = $(CPPUNIT_LIBS)
 unit_close_la_SOURCES = UnitClose.cpp
@@ -225,6 +228,7 @@ TESTS = \
 	unit-load.la \
 	unit-cursor.la \
 	unit-calc.la \
+	unit-php-proxy.la \
 	unit-insert-delete.la \
 	unit-close.la \
 	unit-bad-doc-load.la \
diff --git a/test/UnitPHPProxy.cpp b/test/UnitPHPProxy.cpp
new file mode 100644
index 000000000..57c44ac30
--- /dev/null
+++ b/test/UnitPHPProxy.cpp
@@ -0,0 +1,271 @@
+/* -*- 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/.
+ */
+
+/*
+    road path:
+        * cypress test => php server => loolwsd
+        * loolwsd => php server => cypress test
+*/
+
+#include <memory>
+#include <ostream>
+#include <set>
+#include <string>
+
+#include <Poco/Exception.h>
+#include <Poco/RegularExpression.h>
+#include <Poco/URI.h>
+#include <test/lokassert.hpp>
+
+#include <Unit.hpp>
+#include <helpers.hpp>
+#include "net/ServerSocket.hpp"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <LOOLWSD.hpp>
+
+#define _PORT_ 9979
+
+const int bufferSize = 16 * 1024;
+std::atomic<int64_t> lastRequestMS;
+
+class SocketThread
+{
+public:
+    const std::string _proxyPrefix = "ProxyPrefix: http://localhost:" + std::to_string(_PORT_) + "/proxytest.php?req=\n";
+
+    void replaceRequest(std::vector<char>& message)
+    {
+        // First line includes the request. We will remove proxy prefix && get real request.
+        std::vector<char>::iterator firstLineEnd = std::find(message.begin(), message.end(), '\n');
+        std::string firstLine(message.begin(), firstLineEnd);
+        std::vector<char>::iterator firstSpace = std::find(message.begin(), firstLineEnd, ' '); // Position of first space char.
+        std::string request = Util::splitStringToVector(firstLine, ' ')[1]; // First line's format is: METHOD (space) REQUEST (space) HTPP_VERSION
+
+        if (request.find("proxytest.php?req=") != std::string::npos)
+        {
+            // We have a proper proxy request.
+            std::vector<char>::iterator equalSign = std::find(firstSpace + 1, firstLineEnd, '='); // Position of first '=' sign.
+            if (equalSign != firstLineEnd)
+            {
+                // Remove chars from first space until '=' sign (including '=' sign).
+                // So we remove "http://localhost:_PORT_/proxytest.php?req=" and get the real request.
+                for (std::vector<char>::iterator it = equalSign; it > firstSpace; it--)
+                {
+                    message.erase(it);
+                }
+            }
+        }
+        else
+        {
+            // We don't have a proper request. Since we are testing, we will accept this one.
+            // We will remove only "http://localhost:_PORT_"
+            std::vector<char>::iterator portNumberLastChar = std::find(firstSpace + 1, firstLineEnd, '9'); // Position of first char of the port number.
+            if (portNumberLastChar != firstLineEnd)
+            {
+                portNumberLastChar = std::next(portNumberLastChar, 3); // We move it position to the last char of the port number.
+
+                for (std::vector<char>::iterator it = portNumberLastChar; it > firstSpace; it--) // Erase including the last char.
+                {
+                    message.erase(it);
+                }
+            }
+            else
+            {
+                LOG_ERR("We could not find the port number's char.");
+            }
+        }
+    }
+    void addProxyHeader(std::vector<char>& message)
+    {
+        std::vector<char>::iterator it = std::find(message.begin(), message.end(), '\n');
+
+        // Found the first line break. We will paste the prefix on the second line.
+        if (it == message.end())
+        {
+            message.insert(it, _proxyPrefix.data(), &_proxyPrefix.data()[_proxyPrefix.size()]);
+        }
+        else
+        {
+            message.insert(it + 1, _proxyPrefix.data(), &_proxyPrefix.data()[_proxyPrefix.size()]);
+        }
+    }
+    bool sendMessage(int socketFD, std::vector<char>& message)
+    {
+        int res;
+        std::size_t wroteLen = 0;
+        do
+        {
+            res = send(socketFD, &message[wroteLen], (wroteLen + bufferSize < message.size() ? bufferSize: message.size() - wroteLen), MSG_NOSIGNAL);
+            wroteLen += bufferSize;
+        }
+        while (wroteLen < message.size() && res > 0);
+        return res > 0;
+    }
+    bool readMessage(int socketFD, std::vector<char>& inBuffer)
+    {
+        char buf[16 * 1024];
+        ssize_t len;
+        do
+        {
+            do
+            {
+                len = recv(socketFD, buf, sizeof(buf), 0);
+            }
+            while (len < 0 && errno == EINTR);
+
+            if (len > 0)
+            {
+                inBuffer.insert(inBuffer.end(), &buf[0], &buf[len]);
+            }
+        }
+        while (len == (sizeof(buf)));
+        return len > 0;
+    }
+    void handleRegularSocket(std::shared_ptr<StreamSocket> socket)
+    {
+        socket->setThreadOwner(std::this_thread::get_id());
+
+        replaceRequest(socket->getInBuffer());
+        addProxyHeader(socket->getInBuffer());
+
+        int loolSocket = helpers::connectToLocalServer(LOOLWSD::getClientPortNumber(), 1000, true); // Create a socket for loolwsd.
+        if (loolSocket > 0)
+        {
+            sendMessage(loolSocket, socket->getInBuffer());
+            std::vector<char> buffer;
+            while(readMessage(loolSocket, buffer)){};
+            socket->send(buffer.data(), buffer.size()); // Send the response to client.
+            close(loolSocket);
+        }
+        socket->closeConnection(); // Close client socket.
+    }
+    static void startThread(std::shared_ptr<StreamSocket> socket)
+    {
+        SocketThread worker;
+        // Set socket's option to blocking mode.
+        helpers::setSocketBlockingMode(socket->getFD(), true);
+
+        std::thread regularSocketThread(&SocketThread::handleRegularSocket, worker, socket);
+        regularSocketThread.detach();
+    }
+};
+
+class PHPClientRequestHandler: public SimpleSocketHandler
+{
+private:
+    std::weak_ptr<StreamSocket> _socket;
+
+public:
+    PHPClientRequestHandler()
+    {
+    }
+
+private:
+    void onConnect(const std::shared_ptr<StreamSocket>& socket) override
+    {
+        _socket = socket;
+    }
+    int getPollEvents(std::chrono::steady_clock::time_point /* now */, int64_t & /* timeoutMaxMs */) override
+    {
+        return POLLIN;
+    }
+    void performWrites() override
+    {
+    }
+
+    void handleIncomingMessage(SocketDisposition& disposition) override
+    {
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
+        disposition.setMove([=] (const std::shared_ptr<Socket> &moveSocket)
+        {
+            moveSocket->setThreadOwner(std::thread::id(0));
+            SocketThread::startThread(socket);
+        });
+    }
+};
+
+class PHPServerSocketFactory final : public SocketFactory
+{
+public:
+    PHPServerSocketFactory()
+    {
+    }
+
+    std::shared_ptr<Socket> create(const int physicalFd) override
+    {
+        // This socket is test's client.
+        std::shared_ptr<Socket> socket = StreamSocket::create<StreamSocket>(physicalFd, false, std::make_shared<PHPClientRequestHandler>());
+        lastRequestMS = Util::getNowInMS();
+        return socket;
+    }
+};
+
+class UnitPHPProxy : public UnitWSD
+{
+private:
+    std::shared_ptr<SocketPoll> _poll;
+
+public:
+    UnitPHPProxy()
+    {
+    }
+
+    void configure(Poco::Util::LayeredConfiguration& config) override
+    {
+        UnitWSD::configure(config);
+        config.setBool("ssl.enable", false);
+        config.setBool("net.proxy_prefix", true);
+    }
+
+    void invokeTest()
+    {
+        try
+        {
+            _poll = std::make_shared<SocketPoll>("php client poll");
+            _poll->startThread();
+            ServerSocket::Type clientPortProto = ServerSocket::Type::Public;
+            Socket::Type sType = Socket::Type::IPv4;
+            std::shared_ptr<SocketFactory> factory = std::make_shared<PHPServerSocketFactory>();
+            std::shared_ptr<ServerSocket> _serverSocket = std::make_shared<ServerSocket>(sType, *_poll, factory);
+            _serverSocket->bind(clientPortProto, _PORT_);
+            _serverSocket->listen(10);
+            _poll->insertNewSocket(_serverSocket);
+
+            lastRequestMS = Util::getNowInMS();
+            int64_t diff = 0;
+            while (diff < 15000)
+            {
+                auto nowMS = Util::getNowInMS();
+                diff = nowMS - lastRequestMS;
+            }
+
+            _poll->joinThread();
+
+            exitTest(UnitBase::TestResult::Ok);
+        }
+        catch(const std::exception& e)
+        {
+            std::cerr << e.what() << '\n';
+            exitTest(UnitBase::TestResult::Failed);
+        }
+    }
+};
+
+UnitBase* unit_create_wsd(void) { return new UnitPHPProxy(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/test/helpers.hpp b/test/helpers.hpp
index 79948f836..67aac63ed 100644
--- a/test/helpers.hpp
+++ b/test/helpers.hpp
@@ -246,6 +246,60 @@ inline std::shared_ptr<Poco::Net::StreamSocket> createRawSocket()
         (Poco::Net::SocketAddress("127.0.0.1", ClientPortNumber));
 }
 
+// Sets read / write timeout for the given file descriptor.
+inline void setSocketTimeOut(int socketFD, int timeMS)
+{
+    struct timeval tv;
+    tv.tv_sec = (float)timeMS / (float)1000;
+    tv.tv_usec = timeMS;
+    setsockopt(socketFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
+}
+
+// Sets socket's blocking mode. true for blocking, false for non blocking.
+inline void setSocketBlockingMode(int socketFD, bool blocking)
+{
+    ioctl(socketFD, FIONBIO, blocking == true ? 0: 1);
+}
+
+// Creates a socket and connects it to a local server. Returns the file descriptor.
+inline int connectToLocalServer(int portNumber, int socketTimeOutMS, bool blocking)
+{
+    int socketFD = 0;
+    struct sockaddr_in serv_addr;
+
+    if ((socketFD = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    {
+        LOG_ERR("helpers::connectToLocalServer: Server client could not be created.");
+        return -1;
+    }
+    else
+    {
+        serv_addr.sin_family = AF_INET;
+        serv_addr.sin_port = htons(portNumber);
+        if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
+        {
+            LOG_ERR("helpers::connectToLocalServer: Invalid address.");
+            close(socketFD);
+            return -1;
+        }
+        else
+        {
+            if (connect(socketFD, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
+            {
+                LOG_ERR("helpers::connectToLocalServer: Connection failed.");
+                close(socketFD);
+                return -1;
+            }
+            else
+            {
+                setSocketTimeOut(socketFD, socketTimeOutMS);
+                setSocketBlockingMode(socketFD, blocking);
+                return socketFD;
+            }
+        }
+    }
+}
+
 inline
 std::string const & getTestServerURI()
 {


More information about the Libreoffice-commits mailing list