[Libreoffice-commits] online.git: common/Unit.hpp test/Makefile.am test/UnitOAuth.cpp wsd/LOOLWSD.cpp

Jan Holesovsky kendy at collabora.com
Wed Aug 9 20:28:57 UTC 2017


 common/Unit.hpp    |    6 +
 test/Makefile.am   |    8 +-
 test/UnitOAuth.cpp |  192 ++++++++++++++++++++++++++++++-----------------------
 wsd/LOOLWSD.cpp    |   10 +-
 4 files changed, 126 insertions(+), 90 deletions(-)

New commits:
commit cef217ea24c6a048b4c6a4b7ad883621de332a0e
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Aug 9 22:02:29 2017 +0200

    wsd: Fix the OAuth unit test.
    
    Trying to combine the Poco's http server together with our polling loop leads
    only to problem; so instead let's introduce a hook where we can do the WOPI
    serving directly in the unit test.
    
    Change-Id: Id3fec6ff93c3ad652aa4e0fc6309c5b7639728cb

diff --git a/common/Unit.hpp b/common/Unit.hpp
index 1b0822bb..f43b4d32 100644
--- a/common/Unit.hpp
+++ b/common/Unit.hpp
@@ -108,6 +108,12 @@ public:
         return false;
     }
 
+    /// Custom response to a http request.
+    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& /*request*/, std::shared_ptr<StreamSocket>& /*socket*/)
+    {
+        return false;
+    }
+
     /// If the test times out this gets invoked, the default just exits.
     virtual void timeout();
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 237c2e1a..9ff9bb44 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,9 +13,9 @@ AM_CXXFLAGS = $(CPPUNIT_CFLAGS) -DTDOC=\"$(top_srcdir)/test/data\" \
 	-I${top_srcdir}/common -I${top_srcdir}/net -I${top_srcdir}/wsd -I${top_srcdir}/kit
 
 noinst_LTLIBRARIES = \
-        unit-timeout.la unit-prefork.la \
-        unit-storage.la unit-client.la \
-        unit-admin.la unit-tilecache.la \
+	unit-timeout.la unit-prefork.la \
+	unit-storage.la unit-client.la \
+	unit-admin.la unit-tilecache.la \
 	unit-fuzz.la unit-oob.la unit-oauth.la
 
 MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy
@@ -77,7 +77,7 @@ check-local:
 	./run_unit.sh --log-file test.log --trs-file test.trs
 # FIXME 2: unit-oob.la fails with symbol undefined:
 # UnitWSD::testHandleRequest(UnitWSD::TestRequest, UnitHTTPServerRequest&, UnitHTTPServerResponse&) ,
-TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la # unit-client.la # - enable to run unit-tests in wsd ...
+TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la unit-oauth.la # unit-client.la unit-storage.la unit-admin.la # - enable to run unit-tests in wsd ...
 else
 TESTS = ${top_builddir}/test/test
 endif
diff --git a/test/UnitOAuth.cpp b/test/UnitOAuth.cpp
index 2a24ed9b..0136d0c8 100644
--- a/test/UnitOAuth.cpp
+++ b/test/UnitOAuth.cpp
@@ -12,56 +12,62 @@
 //#include "Exceptions.hpp"
 #include "Log.hpp"
 #include "Unit.hpp"
+#include "UnitHTTP.hpp"
 #include "helpers.hpp"
 #include <Poco/JSON/Object.h>
 #include <Poco/LocalDateTime.h>
 #include <Poco/DateTimeFormat.h>
 #include <Poco/DateTimeFormatter.h>
 #include <Poco/Net/HTTPRequest.h>
-#include <Poco/Net/HTTPResponse.h>
-#include <Poco/Net/HTTPServer.h>
-#include <Poco/Net/HTTPRequestHandlerFactory.h>
-#include <Poco/Net/HTTPRequestHandler.h>
-#include <Poco/Net/HTTPServerRequest.h>
-#include <Poco/Net/HTTPServerResponse.h>
-#include <Poco/Net/HTTPServerParams.h>
-#include <Poco/Net/ServerSocket.h>
 #include <Poco/Net/OAuth20Credentials.h>
+#include <Poco/Util/LayeredConfiguration.h>
 
 using Poco::DateTimeFormatter;
 using Poco::DateTimeFormat;
-using Poco::JSON::Object;
-using Poco::Net::HTTPServer;
-using Poco::Net::HTTPRequest;
-using Poco::Net::HTTPResponse;
-using Poco::Net::HTTPRequestHandlerFactory;
-using Poco::Net::HTTPRequestHandler;
-using Poco::Net::HTTPServerRequest;
-using Poco::Net::HTTPServerResponse;
-using Poco::Net::HTTPServerParams;
 using Poco::Net::OAuth20Credentials;
-using Poco::Net::ServerSocket;
 
-class WopiHostRequestHandler: public HTTPRequestHandler
+class UnitOAuth : public UnitWSD
 {
+    enum class Phase {
+        Load,   // loading the document
+        Polling // let the loading progress, and when it succeeds, finish
+    } _phase;
+
 public:
-    void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response)
+    UnitOAuth() :
+        _phase(Phase::Load)
     {
-        Poco::URI uriReq(request.getURI());
+    }
 
-        // The resource server MUST validate the access token
-        // and ensure that it has not expired and that its scope
-        // covers the requested resource.
-        OAuth20Credentials creds(request);
-        assert (creds.getBearerToken() == "s3hn3ct0k3v");
+    /// Here we act as a WOPI server, so that we have a server that responds to
+    /// the wopi requests without additional expensive setup.
+    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, std::shared_ptr<StreamSocket>& socket) override
+    {
+        static const std::string hello("Hello, world");
+
+        Poco::URI uriReq(request.getURI());
+        LOG_INF("Fake wopi host request: " << uriReq.toString());
 
         // CheckFileInfo
         if (uriReq.getPath() == "/wopi/files/0")
         {
+            LOG_INF("Fake wopi host request, handling CheckFileInfo.");
+
+            // check that the request contains the Authorization: header
+            try {
+                OAuth20Credentials creds(request);
+                CPPUNIT_ASSERT_EQUAL(creds.getBearerToken(), std::string("s3hn3ct0k3v"));
+            }
+            catch (const std::exception&)
+            {
+                // fail as fast as possible
+                exit(1);
+            }
+
             Poco::LocalDateTime now;
-            Object::Ptr fileInfo = new Object();
-            fileInfo->set("BaseFileName", "empty.odt");
-            fileInfo->set("Size", "1024");
+            Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object();
+            fileInfo->set("BaseFileName", "hello.txt");
+            fileInfo->set("Size", hello.size());
             fileInfo->set("Version", "1.0");
             fileInfo->set("OwnerId", "test");
             fileInfo->set("UserId", "test");
@@ -70,74 +76,94 @@ public:
             fileInfo->set("PostMessageOrigin", "localhost");
             fileInfo->set("LastModifiedTime", DateTimeFormatter::format(now, DateTimeFormat::ISO8601_FORMAT));
 
+            std::ostringstream jsonStream;
+            fileInfo->stringify(jsonStream);
+            std::string responseString = jsonStream.str();
+
+            const std::string mimeType = "application/json; charset=utf-8";
+
             std::ostringstream oss;
-            fileInfo->stringify(oss);
-            response.setContentType("application/json; charset=utf-8");
-            std::ostream& ostr = response.send();
-            ostr << oss.str();
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "Content-Length: " << responseString.size() << "\r\n"
+                << "Content-Type: " << mimeType << "\r\n"
+                << "\r\n"
+                << responseString;
+
+            socket->send(oss.str());
+            socket->shutdown();
+
+            return true;
         }
         // GetFile
         else if (uriReq.getPath() == "/wopi/files/0/contents")
         {
-            response.sendFile(Poco::Path(TDOC, "empty.odt").toString(), "application/vnd.oasis.opendocument.text");
-            response.setStatusAndReason(HTTPResponse::HTTP_OK);
-        }
-    }
+            LOG_INF("Fake wopi host request, handling GetFile.");
 
-};
+            // check that the request contains the Authorization: header
+            try {
+                OAuth20Credentials creds(request);
+                CPPUNIT_ASSERT_EQUAL(creds.getBearerToken(), std::string("s3hn3ct0k3v"));
+            }
+            catch (const std::exception&)
+            {
+                // fail as fast as possible
+                exit(1);
+            }
 
-class WopiHostRequestHandlerFactory: public HTTPRequestHandlerFactory
-{
-public:
-    HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& /*request*/)
-    {
-        return new WopiHostRequestHandler();
-    }
-};
+            const std::string mimeType = "text/plain; charset=utf-8";
 
+            std::ostringstream oss;
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "Content-Length: " << hello.size() << "\r\n"
+                << "Content-Type: " << mimeType << "\r\n"
+                << "\r\n"
+                << hello;
 
-class UnitOAuth : public UnitWSD
-{
-public:
-    UnitOAuth()
-    {
-    }
+            socket->send(oss.str());
+            socket->shutdown();
 
-    virtual void configure(Poco::Util::LayeredConfiguration& /*config*/) override
-    {
+            exitTest(TestResult::Ok);
+
+            return true;
+        }
+
+        return false;
     }
 
     void invokeTest() override
     {
-        HTTPResponse response;
-        ServerSocket wopiSocket(0);
-        HTTPServerParams* wopiParams = new HTTPServerParams();
-        wopiParams->setKeepAlive(false);
-        HTTPServer fakeWopiHost(new WopiHostRequestHandlerFactory, wopiSocket, wopiParams);
-        fakeWopiHost.start();
-
-        std::string WopiSrc;
-        const std::string testName = "UnitOAuth ";
-
-        // RFC 6749
-        // 7. Accessing Protected Resources
-        // The client accesses protected resources by presenting the access
-        // token (access_token) to the resource server.
-        Poco::URI wopiURL("http://localhost/wopi/files/0?access_token=s3hn3ct0k3v");
-        wopiURL.setPort(wopiSocket.address().port());
-        Poco::URI::encode(wopiURL.toString(), ":/?", WopiSrc);
-        Poco::URI loolUri(helpers::getTestServerURI());
-        HTTPRequest request(HTTPRequest::HTTP_GET, "lool/" + WopiSrc + "/ws");
-
-        auto socket = helpers::connectLOKit(loolUri, request, response);
-        helpers::sendTextFrame(socket, "load url=" + WopiSrc, testName);
-
-        const auto status = helpers::assertResponseString(socket, "status:", testName);
-
-        Poco::Thread::sleep(1000);
-        fakeWopiHost.stop();
-
-        exitTest(TestResult::Ok);
+        constexpr char testName[] = "UnitOAuth";
+
+        switch (_phase)
+        {
+            case Phase::Load:
+            {
+                Poco::URI wopiURL(helpers::getTestServerURI() + "/wopi/files/0?access_token=s3hn3ct0k3v");
+                //wopiURL.setPort(_wopiSocket->address().port());
+                std::string wopiSrc;
+                Poco::URI::encode(wopiURL.toString(), ":/?", wopiSrc);
+                Poco::URI loolUri(helpers::getTestServerURI());
+
+                LOG_INF("Connecting to the fake WOPI server: /lool/" << wopiSrc << "/ws");
+
+                std::unique_ptr<UnitWebSocket> ws(new UnitWebSocket("/lool/" + wopiSrc + "/ws"));
+                assert(ws.get());
+
+                helpers::sendTextFrame(*ws->getLOOLWebSocket(), "load url=" + wopiSrc, testName);
+
+                _phase = Phase::Polling;
+                break;
+            }
+            case Phase::Polling:
+            {
+                // let handleHttpRequest() perform the checks...
+                break;
+            }
+        }
     }
 };
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index d38918fd..17eb4ff6 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1678,14 +1678,18 @@ private:
             std::vector<std::string> reqPathSegs;
             requestUri.getPathSegments(reqPathSegs);
 
-            // File server
-            if (reqPathSegs.size() >= 1 && reqPathSegs[0] == "loleaflet")
+            if (UnitWSD::get().handleHttpRequest(request, socket))
             {
+                // Unit testing, nothing to do here
+            }
+            else if (reqPathSegs.size() >= 1 && reqPathSegs[0] == "loleaflet")
+            {
+                // File server
                 handleFileServerRequest(request, message);
             }
-            // Admin connections
             else if (reqPathSegs.size() >= 2 && reqPathSegs[0] == "lool" && reqPathSegs[1] == "adminws")
             {
+                // Admin connections
                 LOG_INF("Admin request: " << request.getURI());
                 if (AdminSocketHandler::handleInitialRequest(_socket, request))
                 {


More information about the Libreoffice-commits mailing list