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 1fc6adeb747165e1c4d8c4670c4836d27a5ac64a Author: Jan Holesovsky <ke...@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 Reviewed-on: https://gerrit.libreoffice.org/40943 Reviewed-by: Henry Castro <hcas...@collabora.com> Tested-by: Henry Castro <hcas...@collabora.com> diff --git a/common/Unit.hpp b/common/Unit.hpp index 5f8d20ea..335a3c81 100644 --- a/common/Unit.hpp +++ b/common/Unit.hpp @@ -102,6 +102,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 cef15a91..04f4a293 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-admin.la unit-tilecache.la \ + unit-timeout.la unit-prefork.la \ + unit-storage.la \ + unit-admin.la unit-tilecache.la \ unit-fuzz.la unit-oob.la unit-oauth.la MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy @@ -75,7 +75,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-storage.la # unit-admin.la +TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la unit-oauth.la # unit-storage.la # unit-admin.la 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 af0d9c15..643b14bc 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -1665,14 +1665,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)) { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits