summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorgokaysatir <gokaysatir@collabora.com>2020-07-02 19:54:56 +0300
committerTamás Zolnai <tamas.zolnai@collabora.com>2020-09-09 15:50:10 +0200
commit1951fdbd426781fd652af2e517e2e61b22b2831d (patch)
tree551a006ebbff9cc0d6b837507d7980e23c8724c4 /test
parentloleaflet: Impress slide scroll improved. (diff)
downloadonline-1951fdbd426781fd652af2e517e2e61b22b2831d.tar.gz
online-1951fdbd426781fd652af2e517e2e61b22b2831d.zip
lool: php proxy simulation.
Change-Id: I5ea5515e317242f2ad2abd3209ce0241d64b631b Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97820 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zolnai@collabora.com>
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am4
-rw-r--r--test/UnitPHPProxy.cpp271
-rw-r--r--test/helpers.hpp54
3 files changed, 329 insertions, 0 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index 9fa6506b1b..899dd07647 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 0000000000..57c44ac30d
--- /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 79948f836e..67aac63eda 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()
{