diff --git a/common/Util.hpp b/common/Util.hpp index cec20cb34515e..147744d1e04d9 100644 --- a/common/Util.hpp +++ b/common/Util.hpp @@ -41,6 +41,7 @@ #include #include +#include "config.h" #define STRINGIFY(X) #X diff --git a/docker/from-source-gh-action/Dockerfile b/docker/from-source-gh-action/Dockerfile index 9eb102fc4d1bb..0b3b5aaf44139 100644 --- a/docker/from-source-gh-action/Dockerfile +++ b/docker/from-source-gh-action/Dockerfile @@ -2,7 +2,9 @@ FROM ubuntu:24.04 AS builder ENV CORE_ASSETS https://github.com/CollaboraOnline/online/releases/download/for-code-assets/core-co-24.04-assets.tar.gz ENV BUILDDIR /build -ENV ONLINE_EXTRA_BUILD_OPTIONS --enable-experimental +ENV ONLINE_EXTRA_BUILD_OPTIONS --enable-experimental --disable-werror +ENV COLLABORA_ONLINE_REPO https://github.com/Rash419/online.git +ENV COLLABORA_ONLINE_BRANCH live-cluster-upgrading WORKDIR /build diff --git a/docker/from-source-gh-action/build.sh b/docker/from-source-gh-action/build.sh index 8bea53f7d768e..6d838a03fa70a 100755 --- a/docker/from-source-gh-action/build.sh +++ b/docker/from-source-gh-action/build.sh @@ -74,7 +74,7 @@ fi # Clone online repo if test ! -d online ; then - git clone --depth=1 "$COLLABORA_ONLINE_REPO" online || exit 1 + git clone "$COLLABORA_ONLINE_REPO" online || exit 1 fi ( cd online && git fetch --all && git checkout -f $COLLABORA_ONLINE_BRANCH && git clean -f -d && git pull -r ) || exit 1 diff --git a/net/HttpRequest.hpp b/net/HttpRequest.hpp index 1d617d657595d..b1c7c8bfa6fdd 100644 --- a/net/HttpRequest.hpp +++ b/net/HttpRequest.hpp @@ -11,8 +11,11 @@ #pragma once +#include +#include #include #include +#include #include #include #include @@ -682,6 +685,22 @@ class Request final body.size()); } + void setBody(Poco::MemoryInputStream& message) + { + std::streampos currentPos = message.tellg(); + message.seekg(0, std::ios::end); + size_t bodySize = message.tellg(); + message.seekg(currentPos); + + setBodySource( + [&message](char* buf, int64_t len) -> int64_t + { + message.read(buf, len); + return message.gcount(); + }, + bodySize); + } + Stage stage() const { return _stage; } bool writeData(Buffer& out, std::size_t capacity) diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp index c6a2586ec044c..bec6f8688d79e 100644 --- a/wsd/Admin.cpp +++ b/wsd/Admin.cpp @@ -9,10 +9,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include #include #include +#include #include +#include #include #include @@ -41,7 +45,6 @@ using namespace COOLProtocol; -using Poco::Net::HTTPResponse; using Poco::Util::Application; const int Admin::MinStatsIntervalMs = 50; @@ -418,6 +421,10 @@ void AdminSocketHandler::handleMessage(const std::vector &payload) { _admin->setCloseMonitorFlag(); } + else if (tokens.equals(0, "rollingupdate") && tokens.size() > 1) + { + _admin->setRollingUpdateInfo(tokens[1]); + } } AdminSocketHandler::AdminSocketHandler(Admin* adminManager, @@ -1307,6 +1314,48 @@ void Admin::deleteMonitorSocket(const std::string& uriWithoutParam) } } +void Admin::setRollingUpdateInfo(const std::string& jsonString) +{ + Poco::JSON::Object::Ptr object; + if (JsonUtil::parseJSON(jsonString, object)) + { + bool status = JsonUtil::getJSONValue(object, "inprogress"); + setRollingUpdateStatus(status); + Poco::JSON::Array::Ptr infoArray = object->getArray("serverinfo"); + if (!infoArray.isNull()) + { + for(size_t i=0; i < infoArray->size(); i++) + { + if (!infoArray->isObject(i)) + { + return; + } + const auto serverInfoObject = infoArray->getObject(i); + const std::string gitHash = JsonUtil::getJSONValue(serverInfoObject , "gitHash"); + const std::string serverId = JsonUtil::getJSONValue(serverInfoObject, "serverId"); + const std::string routeToken = JsonUtil::getJSONValue(serverInfoObject, "routeToken"); + _rollingUpdateInfo.insert_or_assign(gitHash, RollingUpdateServerInfo(gitHash, serverId, routeToken)); + } + } + } +} + +std::string Admin::getBuddyServer(const std::string& gitHash) +{ + LOG_DBG("Getting routeToken for gitHash[" << gitHash << ']'); + for (auto iterator : _rollingUpdateInfo) + { + LOG_DBG("gitHash[" << iterator.first << "] routeToken[" << iterator.second.getRouteToken() + << "] serverId[" << iterator.second.getServerId() << ']'); + } + auto iterator = _rollingUpdateInfo.find(gitHash); + if (iterator != _rollingUpdateInfo.end()) + { + return iterator->second.getRouteToken(); + } + return std::string(); +} + void Admin::stop() { joinThread(); diff --git a/wsd/Admin.hpp b/wsd/Admin.hpp index 3b5e93725727c..679e3c4229216 100644 --- a/wsd/Admin.hpp +++ b/wsd/Admin.hpp @@ -15,6 +15,8 @@ #include "net/WebSocketHandler.hpp" #include "COOLWSD.hpp" +#include +#include class Admin; @@ -187,6 +189,14 @@ class Admin : public SocketPoll void setCloseMonitorFlag() { _closeMonitor = true; } + void setRollingUpdateInfo(const std::string& jsonString); + + void setRollingUpdateStatus(bool status) { _rollingUpdateStatus = status; } + + bool getRollingUpdateStatus() { return _rollingUpdateStatus; } + + std::string getBuddyServer(const std::string& gitHash); + private: /// Notify Forkit of changed settings. void notifyForkit(); @@ -254,6 +264,30 @@ class Admin : public SocketPoll std::map> _monitorSockets; std::atomic _closeMonitor = false; + + class RollingUpdateServerInfo + { + public: + std::string getGitHash() { return _gitHash; } + std::string getServerId() { return _serverId; } + std::string getRouteToken() { return _routeToken; } + + RollingUpdateServerInfo(const std::string& gitHash, const std::string& serverId, + const std::string& routeToken) + : _gitHash(gitHash) + , _serverId(serverId) + , _routeToken(routeToken) + { + } + + private: + std::string _gitHash; + std::string _serverId; + std::string _routeToken; + }; + + std::map _rollingUpdateInfo; + std::atomic _rollingUpdateStatus; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/ClientRequestDispatcher.cpp b/wsd/ClientRequestDispatcher.cpp index a7ee28fc35abc..13d78280f0a51 100644 --- a/wsd/ClientRequestDispatcher.cpp +++ b/wsd/ClientRequestDispatcher.cpp @@ -9,7 +9,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "Log.hpp" #include +#include #include @@ -680,7 +682,7 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi // re-write ServiceRoot and cache. RequestDetails requestDetails(request, COOLWSD::ServiceRoot); - // LOG_TRC("Request details " << requestDetails.toString()); + LOG_DBG("Request details " << requestDetails.toString()); // Config & security ... if (requestDetails.isProxy()) @@ -718,19 +720,36 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi else if (requestDetails.equals(RequestDetails::Field::Type, "browser") || requestDetails.equals(RequestDetails::Field::Type, "wopi")) { + std::string protocol = "http"; + if (socket->sniffSSL()) + protocol = "https"; + + Poco::URI requestUri(protocol + "://" + request.getHost() + request.getURI()); + const std::string& path = requestUri.getPath(); + bool versionMismatch = false; + + if ((path.find("browser/" COOLWSD_VERSION_HASH "/") == std::string::npos && + path.find("browser/" + Util::getCoolVersionHash() + "/") == std::string::npos) && + path.find("admin/") == std::string::npos) + { + LOG_DBG("Client - server version mismatch, proxy request to different server " + "Expected: " COOLWSD_VERSION_HASH "; Actual URI path with version hash: " + << path); + versionMismatch = true; + } + // File server assert(socket && "Must have a valid socket"); constexpr auto ProxyRemote = "/remote/"; constexpr auto ProxyRemoteLen = sizeof(ProxyRemote) - 1; constexpr auto ProxyRemoteStatic = "/remote/static/"; - const auto uri = requestDetails.getURI(); - const auto pos = uri.find(ProxyRemoteStatic); + const auto pos = path.find(ProxyRemoteStatic); if (pos != std::string::npos) { - if (uri.ends_with("lokit-extra-img.svg")) + if (path.ends_with("lokit-extra-img.svg")) { - ProxyRequestHandler::handleRequest(uri.substr(pos + ProxyRemoteLen), socket, - ProxyRequestHandler::getProxyRatingServer()); + ProxyRequestHandler::handleRequest(path.substr(pos + ProxyRemoteLen), socket, + ProxyRequestHandler::getProxyRatingServer(), message, http::Request::VERB_GET); served = true; } #if ENABLE_FEATURE_LOCK @@ -743,12 +762,67 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi const std::string& serverUri = unlockImageUri.getScheme() + "://" + unlockImageUri.getAuthority(); ProxyRequestHandler::handleRequest( - uri.substr(pos + sizeof("/remote/static") - 1), socket, serverUri); + path.substr(pos + sizeof("/remote/static") - 1), socket, serverUri, message, http::Request::VERB_GET); served = true; } } #endif } + else if (COOLWSD::IndirectionServerEnabled && versionMismatch && + Admin::instance().getRollingUpdateStatus()) + { + std::string searchString = "/browser/"; + size_t startHashPos = path.find(searchString); + if (startHashPos != std::string::npos) + { + startHashPos += searchString.length(); + size_t endHashPos = path.find('/', startHashPos); + + std::string gitHash; + if (endHashPos != std::string::npos) + { + gitHash = path.substr(startHashPos, endHashPos - startHashPos); + } + else + { + gitHash = path.substr(startHashPos); + } + + std::string hash(gitHash); + hash.resize(std::min(8, (int)hash.length())); + std::string routeToken = Admin::instance().getBuddyServer(hash); + if (!routeToken.empty()) + { + Poco::URI::QueryParameters params = requestUri.getQueryParameters(); + const auto routeTokenIt = + std::find_if(params.begin(), params.end(), + [](const std::pair& element) + { return element.first == "RouteToken"; }); + if (routeTokenIt == params.end()) + { + LOG_DBG("Adding routeToken[" << routeToken + << "] as a parameter to requestUri[" + << requestUri.toString() << ']'); + + requestUri.addQueryParameter("RouteToken", routeToken); + } + else + { + LOG_DBG("Updating routeToken[" << routeToken + << "] parameter in requestUri[" + << requestUri.toString() << ']'); + + routeTokenIt->second = routeToken; + requestUri.setQueryParameters(params); + } + } + + ProxyRequestHandler::handleRequest(requestUri.getPathAndQuery(), socket, + requestUri.getScheme() + "://" + + requestUri.getAuthority(), message, request.getMethod()); + served = true; + } + } else { FileServerRequestHandler::ResourceAccessDetails accessDetails; diff --git a/wsd/ProxyRequestHandler.cpp b/wsd/ProxyRequestHandler.cpp index c210cb1526009..8035b55700d4f 100644 --- a/wsd/ProxyRequestHandler.cpp +++ b/wsd/ProxyRequestHandler.cpp @@ -9,6 +9,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include #include #include @@ -17,13 +18,16 @@ #include "ProxyRequestHandler.hpp" #include #include +#include std::unordered_map> ProxyRequestHandler::CacheFileHash; std::chrono::system_clock::time_point ProxyRequestHandler::MaxAge; void ProxyRequestHandler::handleRequest(const std::string& relPath, const std::shared_ptr& socket, - const std::string& serverUri) + const std::string& serverUri, + Poco::MemoryInputStream& message, + const std::string& verb = http::Request::VERB_GET) { Poco::URI uriProxy(serverUri); @@ -36,19 +40,25 @@ void ProxyRequestHandler::handleRequest(const std::string& relPath, MaxAge = zero; } - const auto cacheEntry = CacheFileHash.find(relPath); - if (cacheEntry != CacheFileHash.end()) - { - socket->sendAndShutdown(*cacheEntry->second); - return; - } + // const auto cacheEntry = CacheFileHash.find(relPath); + // if (cacheEntry != CacheFileHash.end()) + // { + // socket->sendAndShutdown(*cacheEntry->second); + // return; + // } + + uriProxy.setPathEtc(relPath); + LOG_DBG("uriProxy[" << uriProxy.getPathAndQuery() << ']'); + auto protocol = uriProxy.getScheme() == "https" ? http::Session::Protocol::HttpSsl + : http::Session::Protocol::HttpUnencrypted; - uriProxy.setPath(relPath); - auto sessionProxy = http::Session::create(uriProxy.getHost(), - http::Session::Protocol::HttpSsl, + auto sessionProxy = http::Session::create(uriProxy.getHost(), protocol, uriProxy.getPort()); sessionProxy->setTimeout(std::chrono::seconds(10)); http::Request requestProxy(uriProxy.getPathAndQuery()); + requestProxy.setVerb(verb); + if (verb == http::Request::VERB_POST) + requestProxy.setBody(message); http::Session::FinishedCallback proxyCallback = [socket, zero](const std::shared_ptr& httpSession) { diff --git a/wsd/ProxyRequestHandler.hpp b/wsd/ProxyRequestHandler.hpp index 100531a1e2e14..8be8b013318a7 100644 --- a/wsd/ProxyRequestHandler.hpp +++ b/wsd/ProxyRequestHandler.hpp @@ -11,6 +11,7 @@ #pragma once +#include #include #include "Socket.hpp" @@ -19,7 +20,10 @@ class ProxyRequestHandler public: static void handleRequest(const std::string& relPath, const std::shared_ptr& socket, - const std::string& serverUri); + const std::string& serverUri, + Poco::MemoryInputStream& message, + const std::string& verb); + static std::string getProxyRatingServer() { return ProxyRatingServer; } private: