Rebased ref, commits from common ancestor: commit c90bf00077d4091ca49c6ade6ffaf86d0603a0b3 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Tue Apr 14 17:01:41 2020 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100
Proof: improve debugging and connection handling. Change-Id: I1d48c4ec7fb80eaab1aabc83b0c210b7cf138ef2 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 2268af83f..17fe03c9a 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -260,7 +260,6 @@ this.sendQueue = ''; this.sendTimeout = undefined; this.doSend = function () { - that.sendTimeout = undefined; console.debug('send msg "' + that.sendQueue + '"'); var req = new XMLHttpRequest(); req.open('POST', that.getEndPoint('write')); @@ -279,6 +278,7 @@ } req.send(that.sendQueue); that.sendQueue = ''; + that.sendTimeout = undefined; }; this.getSessionId = function() { var req = new XMLHttpRequest(); @@ -318,7 +318,7 @@ // horrors ... this.waitConnect = function() { console.debug('proxy: waiting - ' + that.readWaiting + ' on session ' + that.sessionId); - if (that.readWaiting > 4) // max 4 waiting connections concurrently. + if (that.readWaiting >= 4) // max 4 waiting connections concurrently. return; if (that.sessionId == 'fetchsession') return; // waiting for our session id. diff --git a/net/Socket.cpp b/net/Socket.cpp index 24041bc4b..0b7a2f2ff 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -219,7 +219,6 @@ int SocketPoll::poll(int64_t timeoutMaxMicroS) timeout.tv_sec = timeoutMaxMicroS / (1000 * 1000); timeout.tv_nsec = (timeoutMaxMicroS % (1000 * 1000)) * 1000; rc = ::ppoll(&_pollFds[0], size + 1, &timeout, nullptr); - LOG_TRC("ppoll result " << rc << " errno " << strerror(errno)); # else int timeoutMaxMs = (timeoutMaxMicroS + 9999) / 1000; LOG_TRC("Legacy Poll start, timeoutMs: " << timeoutMaxMs); diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 3ed43e979..c8a259abe 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -219,6 +219,13 @@ void ProxyProtocolHandler::getIOStats(uint64_t &sent, uint64_t &recv) void ProxyProtocolHandler::dumpState(std::ostream& os) { os << "proxy protocol sockets: " << _outSockets.size() << " writeQueue: " << _writeQueue.size() << ":\n"; + os << "\t"; + for (auto &it : _outSockets) + { + auto sock = it.lock(); + os << "#" << (sock ? sock->getFD() : -2) << " "; + } + os << "\n"; for (auto it : _writeQueue) Util::dumpHex(os, "\twrite queue entry:", "\t\t", *it); if (_msgHandler) commit 13a09ec86c013d53acab6b3ec6beff0b110e2c80 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Mar 21 20:03:37 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: don't leave out sockets lingering around for the !flush case. Change-Id: I13ad123a6c3a068a676eae5e509367e727e9ac06 diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 89b3d9ba9..3ed43e979 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -181,11 +181,14 @@ void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition) int ProxyProtocolHandler::sendMessage(const char *msg, const size_t len, bool text, bool flush) { _writeQueue.push_back(std::make_shared<Message>(msg, len, text)); - auto sock = popOutSocket(); - if (sock && flush) + if (flush) { - flushQueueTo(sock); - sock->shutdown(); + auto sock = popOutSocket(); + if (sock) + { + flushQueueTo(sock); + sock->shutdown(); + } } return len; commit e955a70b2db062fc20407171cba603468aad5390 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Mar 21 15:07:10 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: improve debugging & naming. Change-Id: Ifba669a33855a67c9a4e968db42ef1a2cb301d63 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index c5dff015e..2268af83f 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -216,7 +216,7 @@ }; this.parseIncomingArray = function(arr) { var decoder = new TextDecoder(); - console.debug('Parse incoming array of length ' + arr.length); + console.debug('proxy: parse incoming array of length ' + arr.length); for (var i = 0; i < arr.length; ++i) { var left = arr.length - i; @@ -274,7 +274,7 @@ if (this.status == 200) that.parseIncomingArray(new Uint8Array(this.response)); else - console.debug('Error on incoming response'); + console.debug('proxy: error on incoming response'); }); } req.send(that.sendQueue); @@ -300,21 +300,24 @@ this.sendTimeout = setTimeout(this.doSend, 2 /* ms */); }; this.close = function() { - console.debug('close socket'); + console.debug('proxy: close socket'); this.readyState = 3; this.onclose(); + clearInterval(this.waitInterval); + this.waitInterval = undefined; }; this.getEndPoint = function(type) { var base = this.uri; return base.replace(/^ws/, 'http') + '/' + type; }; - console.debug('New proxy socket ' + this.id + ' ' + this.uri); + console.debug('proxy: new socket ' + this.id + ' ' + this.uri); // queue fetch of session id. this.getSessionId(); // horrors ... - this.readInterval = setInterval(function() { + this.waitConnect = function() { + console.debug('proxy: waiting - ' + that.readWaiting + ' on session ' + that.sessionId); if (that.readWaiting > 4) // max 4 waiting connections concurrently. return; if (that.sessionId == 'fetchsession') @@ -329,13 +332,16 @@ }); req.addEventListener('loadend', function() { that.readWaiting--; + console.debug('proxy: wait ended, re-issue'); + that.waitConnect(); }); - req.open('GET', that.getEndPoint('read')); + req.open('GET', that.getEndPoint('wait')); req.setRequestHeader('SessionId', that.sessionId); req.responseType = 'arraybuffer'; req.send(''); that.readWaiting++; - }, 250); + }; + this.waitInterval = setInterval(this.waitConnect, 250); }; if (global.socketProxy) diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index badf0b05f..541640608 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2911,7 +2911,7 @@ private: none, url, docKey, _id, uriPublic); std::string fullURL = request.getURI(); - std::string ending = "/ws/read"; + std::string ending = "/ws/wait"; bool isWaiting = (fullURL.size() > ending.size() && std::equal(ending.rbegin(), ending.rend(), fullURL.rbegin())); if (docBroker) diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index c8f578559..89b3d9ba9 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -31,7 +31,7 @@ void DocumentBroker::handleProxyRequest( std::shared_ptr<ClientSession> clientSession; if (sessionId == "fetchsession") { - LOG_TRC("Create session for " << _docKey); + LOG_TRC("proxy: Create session for " << _docKey); clientSession = createNewClientSession( std::make_shared<ProxyProtocolHandler>(), id, uriPublic, isReadOnly, hostNoTrust); @@ -39,7 +39,7 @@ void DocumentBroker::handleProxyRequest( LOOLWSD::checkDiskSpaceAndWarnClients(true); LOOLWSD::checkSessionLimitsAndWarnClients(); - LOG_TRC("Returning id " << clientSession->getId()); + LOG_TRC("proxy: Returning sessionId " << clientSession->getId()); std::ostringstream oss; oss << "HTTP/1.1 200 OK\r\n" @@ -57,7 +57,7 @@ void DocumentBroker::handleProxyRequest( } else { - LOG_TRC("Find session for " << _docKey << " with id " << sessionId); + LOG_TRC("proxy: find session for " << _docKey << " with id " << sessionId); for (const auto &it : _sessions) { if (it.second->getId() == sessionId) @@ -133,28 +133,29 @@ void ProxyProtocolHandler::handleRequest(bool isWaiting, const std::shared_ptr<S { auto streamSocket = std::static_pointer_cast<StreamSocket>(socket); - LOG_INF("Proxy handle request type: " << (isWaiting ? "wait" : "respond")); + LOG_INF("proxy: handle request type: " << (isWaiting ? "wait" : "respond") << + " on socket #" << socket->getFD()); if (!isWaiting) { if (!_msgHandler) - LOG_WRN("unusual - incoming message with no-one to handle it"); + LOG_WRN("proxy: unusual - incoming message with no-one to handle it"); else if (!parseEmitIncoming(streamSocket)) { std::stringstream oss; streamSocket->dumpState(oss); - LOG_ERR("bad socket structure " << oss.str()); + LOG_ERR("proxy: bad socket structure " << oss.str()); } } if (!flushQueueTo(streamSocket) && isWaiting) { - LOG_TRC("Queue a waiting socket"); + LOG_TRC("proxy: queue a waiting out socket #" << streamSocket->getFD()); // longer running 'write socket' (marked 'read' by the client) _outSockets.push_back(streamSocket); if (_outSockets.size() > 16) { - LOG_ERR("Unexpected - client opening many concurrent waiting connections " << _outSockets.size()); + LOG_ERR("proxy: Unexpected - client opening many concurrent waiting connections " << _outSockets.size()); // cleanup older waiting sockets. auto sockWeak = _outSockets.front(); _outSockets.erase(_outSockets.begin()); @@ -180,7 +181,7 @@ void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition) int ProxyProtocolHandler::sendMessage(const char *msg, const size_t len, bool text, bool flush) { _writeQueue.push_back(std::make_shared<Message>(msg, len, text)); - auto sock = popWriteSocket(); + auto sock = popOutSocket(); if (sock && flush) { flushQueueTo(sock); @@ -230,16 +231,24 @@ int ProxyProtocolHandler::getPollEvents(std::chrono::steady_clock::time_point /* return events; } -void ProxyProtocolHandler::performWrites() +/// slurp from the core to us, @returns true if there are messages to send +bool ProxyProtocolHandler::slurpHasMessages() { - if (_msgHandler) + if (_msgHandler && _msgHandler->hasQueuedMessages()) _msgHandler->writeQueuedMessages(); - if (_writeQueue.size() <= 0) + + return _writeQueue.size() > 0; +} + +void ProxyProtocolHandler::performWrites() +{ + if (!slurpHasMessages()) return; - auto sock = popWriteSocket(); + auto sock = popOutSocket(); if (sock) { + LOG_TRC("proxy: performWrites"); flushQueueTo(sock); sock->shutdown(); } @@ -247,9 +256,8 @@ void ProxyProtocolHandler::performWrites() bool ProxyProtocolHandler::flushQueueTo(const std::shared_ptr<StreamSocket> &socket) { - // slurp from the core to us. - if (_msgHandler && _msgHandler->hasQueuedMessages()) - _msgHandler->writeQueuedMessages(); + if (!slurpHasMessages()) + return false; size_t totalSize = 0; for (auto it : _writeQueue) @@ -258,6 +266,8 @@ bool ProxyProtocolHandler::flushQueueTo(const std::shared_ptr<StreamSocket> &soc if (!totalSize) return false; + LOG_TRC("proxy: flushQueue of size " << totalSize << " to socket #" << socket->getFD() << " & close"); + std::ostringstream oss; oss << "HTTP/1.1 200 OK\r\n" "Last-Modified: " << Util::getHttpTimeNow() << "\r\n" @@ -276,7 +286,7 @@ bool ProxyProtocolHandler::flushQueueTo(const std::shared_ptr<StreamSocket> &soc } // LRU-ness ... -std::shared_ptr<StreamSocket> ProxyProtocolHandler::popWriteSocket() +std::shared_ptr<StreamSocket> ProxyProtocolHandler::popOutSocket() { std::weak_ptr<StreamSocket> sock; while (!_outSockets.empty()) @@ -285,8 +295,12 @@ std::shared_ptr<StreamSocket> ProxyProtocolHandler::popWriteSocket() _outSockets.erase(_outSockets.begin()); auto realSock = sock.lock(); if (realSock) + { + LOG_TRC("proxy: popped an out socket #" << realSock->getFD() << " leaving: " << _outSockets.size()); return realSock; + } } + LOG_TRC("proxy: no out sockets to pop."); return std::shared_ptr<StreamSocket>(); } diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index 090d4331e..692fd903c 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -60,7 +60,8 @@ public: void handleRequest(bool isWaiting, const std::shared_ptr<Socket> &socket); private: - std::shared_ptr<StreamSocket> popWriteSocket(); + std::shared_ptr<StreamSocket> popOutSocket(); + bool slurpHasMessages(); int sendMessage(const char *msg, const size_t len, bool text, bool flush); bool flushQueueTo(const std::shared_ptr<StreamSocket> &socket); commit 2471f338ccff14d63a5d3af4edb2dd03fc55ca5b Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Mar 21 14:27:15 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: ensure dumpState dumps via the MessageHandlerInterface too. Change-Id: If514e2359188d56bbf7ddef6e04f9d8bf5c50910 diff --git a/net/Socket.cpp b/net/Socket.cpp index 372f9ea1a..24041bc4b 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -524,6 +524,8 @@ void WebSocketHandler::dumpState(std::ostream& os) if (_wsPayload.size() > 0) Util::dumpHex(os, "\t\tws queued payload:\n", "\t\t", _wsPayload); os << "\n"; + if (_msgHandler) + _msgHandler->dumpState(os); } void StreamSocket::dumpState(std::ostream& os) diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 6279682f2..c8f578559 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -217,6 +217,8 @@ void ProxyProtocolHandler::dumpState(std::ostream& os) os << "proxy protocol sockets: " << _outSockets.size() << " writeQueue: " << _writeQueue.size() << ":\n"; for (auto it : _writeQueue) Util::dumpHex(os, "\twrite queue entry:", "\t\t", *it); + if (_msgHandler) + _msgHandler->dumpState(os); } int ProxyProtocolHandler::getPollEvents(std::chrono::steady_clock::time_point /* now */, commit 134beda7984af0195b32bc4927274bc4fc453dd0 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Mar 21 14:19:49 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: make eslint happier. Change-Id: I9ecec787a9a69633a015459eaf39d4b8bd5bb61d diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index babbd5f02..c5dff015e 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -1,4 +1,5 @@ /* -*- js-indent-level: 8 -*- */ +/* global Uint8Array */ (function (global) { var ua = navigator.userAgent.toLowerCase(), @@ -312,8 +313,6 @@ // queue fetch of session id. this.getSessionId(); - var that = this; - // horrors ... this.readInterval = setInterval(function() { if (that.readWaiting > 4) // max 4 waiting connections concurrently. commit 831b763474b581b7d398025e4fede0eb1dd6f262 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Mar 20 20:45:38 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: poll for output space if we need waking. Change-Id: I18a5e71bd3342eea7992672d9be1f5518ea008e3 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 146083d71..babbd5f02 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -215,6 +215,7 @@ }; this.parseIncomingArray = function(arr) { var decoder = new TextDecoder(); + console.debug('Parse incoming array of length ' + arr.length); for (var i = 0; i < arr.length; ++i) { var left = arr.length - i; diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 25602f146..6279682f2 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -219,6 +219,15 @@ void ProxyProtocolHandler::dumpState(std::ostream& os) Util::dumpHex(os, "\twrite queue entry:", "\t\t", *it); } +int ProxyProtocolHandler::getPollEvents(std::chrono::steady_clock::time_point /* now */, + int64_t &/* timeoutMaxMs */) +{ + int events = POLLIN; + if (_msgHandler && _msgHandler->hasQueuedMessages()) + events |= POLLOUT; + return events; +} + void ProxyProtocolHandler::performWrites() { if (_msgHandler) diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index dda24328a..090d4331e 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -34,11 +34,7 @@ public: void handleIncomingMessage(SocketDisposition &/* disposition */) override; int getPollEvents(std::chrono::steady_clock::time_point /* now */, - int &/* timeoutMaxMs */) override - { - // underlying buffer based polling is fine. - return POLLIN; - } + int64_t &/* timeoutMaxMs */) override; void checkTimeout(std::chrono::steady_clock::time_point /* now */) override { commit 89db64728d453b313926724582ac848de26db950 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Mar 20 20:15:08 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: open four wait sockets concurrently. Change-Id: I08b85677be528b7aa77272a8527c9bacf3f7c336 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 28def7224..146083d71 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -206,7 +206,7 @@ this.sessionId = 'fetchsession'; this.id = window.proxySocketCounter++; this.sendCounter = 0; - this.readWaiting = false; + this.readWaiting = 0; this.onclose = function() { }; this.onerror = function() { @@ -315,9 +315,9 @@ // horrors ... this.readInterval = setInterval(function() { - if (this.readWaiting) // one at a time for now + if (that.readWaiting > 4) // max 4 waiting connections concurrently. return; - if (this.sessionId == 'fetchsession') + if (that.sessionId == 'fetchsession') return; // waiting for our session id. var req = new XMLHttpRequest(); // fetch session id: @@ -326,13 +326,15 @@ that.parseIncomingArray(new Uint8Array(this.response)); else console.debug('Handle error ' + this.status); - that.readWaiting = false; + }); + req.addEventListener('loadend', function() { + that.readWaiting--; }); req.open('GET', that.getEndPoint('read')); req.setRequestHeader('SessionId', that.sessionId); req.responseType = 'arraybuffer'; req.send(''); - that.readWaiting = true; + that.readWaiting++; }, 250); }; diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 4e854d3fb..6e265c3dc 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -147,7 +147,8 @@ public: const Poco::URI& uriPublic, const bool isReadOnly, const std::string& hostNoTrust, - const std::shared_ptr<StreamSocket> &socket); + const std::shared_ptr<StreamSocket> &socket, + bool isWaiting); /// Thread safe termination of this broker if it has a lingering thread void joinThread(); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index e07758269..badf0b05f 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2909,11 +2909,16 @@ private: // Request a kit process for this doc. std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( none, url, docKey, _id, uriPublic); + + std::string fullURL = request.getURI(); + std::string ending = "/ws/read"; + bool isWaiting = (fullURL.size() > ending.size() && + std::equal(ending.rbegin(), ending.rend(), fullURL.rbegin())); if (docBroker) { // need to move into the DocumentBroker context before doing session lookup / creation etc. std::string id = _id; - disposition.setMove([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId] + disposition.setMove([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId, isWaiting] (const std::shared_ptr<Socket> &moveSocket) { LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey()); @@ -2923,7 +2928,8 @@ private: // We no longer own this socket. moveSocket->setThreadOwner(std::thread::id()); - docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId, moveSocket]() + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, hostNoTrust, + sessionId, moveSocket, isWaiting]() { // Now inside the document broker thread ... LOG_TRC("In the docbroker thread for " << docBroker->getDocKey()); @@ -2933,7 +2939,7 @@ private: { docBroker->handleProxyRequest( sessionId, id, uriPublic, isReadOnly, - hostNoTrust, streamSocket); + hostNoTrust, streamSocket, isWaiting); return; } catch (const UnauthorizedRequestException& exc) diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 8aaff0131..25602f146 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -25,7 +25,8 @@ void DocumentBroker::handleProxyRequest( const Poco::URI& uriPublic, const bool isReadOnly, const std::string& hostNoTrust, - const std::shared_ptr<StreamSocket> &socket) + const std::shared_ptr<StreamSocket> &socket, + bool isWaiting) { std::shared_ptr<ClientSession> clientSession; if (sessionId == "fetchsession") @@ -82,7 +83,7 @@ void DocumentBroker::handleProxyRequest( auto proxy = std::static_pointer_cast<ProxyProtocolHandler>( protocol); - proxy->handleRequest(uriPublic.toString(), socket); + proxy->handleRequest(isWaiting, socket); } bool ProxyProtocolHandler::parseEmitIncoming( @@ -128,16 +129,13 @@ bool ProxyProtocolHandler::parseEmitIncoming( return true; } -void ProxyProtocolHandler::handleRequest(const std::string &uriPublic, - const std::shared_ptr<Socket> &socket) +void ProxyProtocolHandler::handleRequest(bool isWaiting, const std::shared_ptr<Socket> &socket) { auto streamSocket = std::static_pointer_cast<StreamSocket>(socket); - bool bRead = uriPublic.find("/write") == std::string::npos; - LOG_INF("Proxy handle request " << uriPublic << " type: " << - (bRead ? "read" : "write")); + LOG_INF("Proxy handle request type: " << (isWaiting ? "wait" : "respond")); - if (bRead) + if (!isWaiting) { if (!_msgHandler) LOG_WRN("unusual - incoming message with no-one to handle it"); @@ -149,13 +147,27 @@ void ProxyProtocolHandler::handleRequest(const std::string &uriPublic, } } - if (!flushQueueTo(streamSocket) && !bRead) + if (!flushQueueTo(streamSocket) && isWaiting) { - // longer running 'write socket' - _writeSockets.push_back(streamSocket); + LOG_TRC("Queue a waiting socket"); + // longer running 'write socket' (marked 'read' by the client) + _outSockets.push_back(streamSocket); + if (_outSockets.size() > 16) + { + LOG_ERR("Unexpected - client opening many concurrent waiting connections " << _outSockets.size()); + // cleanup older waiting sockets. + auto sockWeak = _outSockets.front(); + _outSockets.erase(_outSockets.begin()); + auto sock = sockWeak.lock(); + if (sock) + sock->shutdown(); + } } else + { + LOG_TRC("Return a reply immediately"); socket->shutdown(); + } } void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition) @@ -202,7 +214,7 @@ void ProxyProtocolHandler::getIOStats(uint64_t &sent, uint64_t &recv) void ProxyProtocolHandler::dumpState(std::ostream& os) { - os << "proxy protocol sockets: " << _writeSockets.size() << " writeQueue: " << _writeQueue.size() << ":\n"; + os << "proxy protocol sockets: " << _outSockets.size() << " writeQueue: " << _writeQueue.size() << ":\n"; for (auto it : _writeQueue) Util::dumpHex(os, "\twrite queue entry:", "\t\t", *it); } @@ -256,10 +268,10 @@ bool ProxyProtocolHandler::flushQueueTo(const std::shared_ptr<StreamSocket> &soc std::shared_ptr<StreamSocket> ProxyProtocolHandler::popWriteSocket() { std::weak_ptr<StreamSocket> sock; - while (!_writeSockets.empty()) + while (!_outSockets.empty()) { - sock = _writeSockets.front(); - _writeSockets.erase(_writeSockets.begin()); + sock = _outSockets.front(); + _outSockets.erase(_outSockets.begin()); auto realSock = sock.lock(); if (realSock) return realSock; diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index 0b20900b1..dda24328a 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -60,11 +60,8 @@ public: void shutdown(bool goingAway = false, const std::string &statusMessage = "") override; void getIOStats(uint64_t &sent, uint64_t &recv) override; void dumpState(std::ostream& os); - bool parseEmitIncoming(const std::shared_ptr<StreamSocket> &socket); - - void handleRequest(const std::string &uriPublic, - const std::shared_ptr<Socket> &socket); + void handleRequest(bool isWaiting, const std::shared_ptr<Socket> &socket); private: std::shared_ptr<StreamSocket> popWriteSocket(); @@ -88,7 +85,7 @@ private: }; /// queue things when we have no socket to hand. std::vector<std::shared_ptr<Message>> _writeQueue; - std::vector<std::weak_ptr<StreamSocket>> _writeSockets; + std::vector<std::weak_ptr<StreamSocket>> _outSockets; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 6c59f6af49dd3bf755daa4a8dfa38b54cc3c4249 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Mar 20 19:05:48 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: re-write css image URLs to handle the proxy. Change-Id: I09f3dea2f5e3a51869d5b0aa3f473d8f3ba75f44 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 0f773fbe6..28def7224 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -336,6 +336,33 @@ }, 250); }; + if (global.socketProxy) + { + // re-write relative URLs in CSS - somewhat grim. + window.addEventListener('load', function() { + var sheets = document.styleSheets; + for (var i = 0; i < sheets.length; ++i) { + var relBases = sheets[i].href.split('/'); + relBases.pop(); // bin last - css name. + var replaceBase = 'url("' + relBases.join('/') + '/images/'; + + var rules = sheets[i].cssRules || sheets[i].rules; + for (var r = 0; r < rules.length; ++r) { + if (!rules[r] || !rules[r].style) + continue; + var img = rules[r].style.backgroundImage; + if (img === '' || img === undefined) + continue; + if (img.startsWith('url("images/')) + { + rules[r].style.backgroundImage = + img.replace('url("images/', replaceBase); + } + } + } + }, false); + } + global.createWebSocket = function(uri) { if (global.socketProxy) { return new global.ProxySocket(uri); commit 15777d733aca9cb9c90a716130344aad1f967b38 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Mar 20 16:38:14 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy: send multiple messages in a single request. Change-Id: Ic0a303979478801bd23941e8893ce5721cf3e732 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index d0d1f6ad6..0f773fbe6 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -195,6 +195,7 @@ global.proxySocketCounter = 0; global.ProxySocket = function (uri) { + var that = this; this.uri = uri; this.binaryType = 'arraybuffer'; this.bufferedAmount = 0; @@ -254,56 +255,48 @@ i += size; // skip trailing '\n' in loop-increment } }; - this.parseIncoming = function(type, msg) { - if (type === 'blob') - { - var fileReader = new FileReader(); - var that = this; - fileReader.onload = function(event) { - that.parseIncomingArray(event.target.result); - }; - fileReader.readAsArrayBuffer(msg); - } - else if (type === 'arraybuffer') - { - this.parseIncomingArray(new Uint8Array(msg)); - } - else if (type === 'text' || type === '') - { - const encoder = new TextEncoder() - const arr = encoder.encode(msg); - this.parseIncomingArray(arr); - } - else - console.debug('Unknown encoding type: ' + type); - }; - this.send = function(msg) { - console.debug('send msg "' + msg + '"'); + this.sendQueue = ''; + this.sendTimeout = undefined; + this.doSend = function () { + that.sendTimeout = undefined; + console.debug('send msg "' + that.sendQueue + '"'); var req = new XMLHttpRequest(); - req.open('POST', this.getEndPoint('write')); - req.setRequestHeader('SessionId', this.sessionId); - if (this.sessionId === 'fetchsession') - { - req.responseType = 'text'; - req.addEventListener('load', function() { - console.debug('got session: ' + this.responseText); - that.sessionId = this.responseText; - that.readyState = 1; - that.onopen(); - }); - } + req.open('POST', that.getEndPoint('write')); + req.setRequestHeader('SessionId', that.sessionId); + if (that.sessionId === 'fetchsession') + console.debug('session fetch not completed'); else { req.responseType = 'arraybuffer'; req.addEventListener('load', function() { if (this.status == 200) - that.parseIncoming(this.responseType, this.response); + that.parseIncomingArray(new Uint8Array(this.response)); else console.debug('Error on incoming response'); }); } - req.send('B0x' + msg.length.toString(16) + '\n' + msg + '\n'); - }, + req.send(that.sendQueue); + that.sendQueue = ''; + }; + this.getSessionId = function() { + var req = new XMLHttpRequest(); + req.open('POST', that.getEndPoint('write')); + req.setRequestHeader('SessionId', that.sessionId); + req.responseType = 'text'; + req.addEventListener('load', function() { + console.debug('got session: ' + this.responseText); + that.sessionId = this.responseText; + that.readyState = 1; + that.onopen(); + }); + req.send(''); + }; + this.send = function(msg) { + this.sendQueue = this.sendQueue.concat( + 'B0x' + msg.length.toString(16) + '\n' + msg + '\n'); + if (this.sessionId !== 'fetchsession' && this.sendTimeout === undefined) + this.sendTimeout = setTimeout(this.doSend, 2 /* ms */); + }; this.close = function() { console.debug('close socket'); this.readyState = 3; @@ -315,7 +308,9 @@ }; console.debug('New proxy socket ' + this.id + ' ' + this.uri); - this.send('fetchsession'); + // queue fetch of session id. + this.getSessionId(); + var that = this; // horrors ... @@ -328,7 +323,7 @@ // fetch session id: req.addEventListener('load', function() { if (this.status == 200) - that.parseIncoming(this.responseType, this.response); + that.parseIncomingArray(new Uint8Array(this.response)); else console.debug('Handle error ' + this.status); that.readWaiting = false; commit 68f1017717d95ff050a066da13bf232c77c41499 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Thu Mar 19 15:54:28 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:17 2020 +0100 Proxy protocol bits. For now very silly: [T|B] + hex length + \n + content + \n Change-Id: I256b834a23cca975a705da2c569887665ac6be02 diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 3becdd706..d0d1f6ad6 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -212,19 +212,97 @@ }; this.onmessage = function() { }; + this.parseIncomingArray = function(arr) { + var decoder = new TextDecoder(); + for (var i = 0; i < arr.length; ++i) + { + var left = arr.length - i; + if (left < 4) + { + console.debug('no data left'); + break; + } + var type = String.fromCharCode(arr[i+0]); + if (type != 'T' && type != 'B') + { + console.debug('wrong data type: ' + type); + break; + } + if (arr[i+1] !== 48 && arr[i+2] !== 120) // '0x' + { + console.debug('missing hex preamble'); + break; + } + i += 3; + var numStr = ''; + var start = i; + while (arr[i] != 10) // '\n' + i++; + numStr = decoder.decode(arr.slice(start, i)); // FIXME: IE11 + var size = parseInt(numStr, 16); + + i++; // skip \n + + var data; + if (type == 'T') // FIXME: IE11 + data = decoder.decode(arr.slice(i, i + size)); + else + data = arr.slice(i, i + size); + + this.onmessage({ data: data }); + + i += size; // skip trailing '\n' in loop-increment + } + }; + this.parseIncoming = function(type, msg) { + if (type === 'blob') + { + var fileReader = new FileReader(); + var that = this; + fileReader.onload = function(event) { + that.parseIncomingArray(event.target.result); + }; + fileReader.readAsArrayBuffer(msg); + } + else if (type === 'arraybuffer') + { + this.parseIncomingArray(new Uint8Array(msg)); + } + else if (type === 'text' || type === '') + { + const encoder = new TextEncoder() + const arr = encoder.encode(msg); + this.parseIncomingArray(arr); + } + else + console.debug('Unknown encoding type: ' + type); + }; this.send = function(msg) { console.debug('send msg "' + msg + '"'); var req = new XMLHttpRequest(); req.open('POST', this.getEndPoint('write')); req.setRequestHeader('SessionId', this.sessionId); if (this.sessionId === 'fetchsession') + { + req.responseType = 'text'; req.addEventListener('load', function() { console.debug('got session: ' + this.responseText); that.sessionId = this.responseText; that.readyState = 1; that.onopen(); }); - req.send(msg); + } + else + { + req.responseType = 'arraybuffer'; + req.addEventListener('load', function() { + if (this.status == 200) + that.parseIncoming(this.responseType, this.response); + else + console.debug('Error on incoming response'); + }); + } + req.send('B0x' + msg.length.toString(16) + '\n' + msg + '\n'); }, this.close = function() { console.debug('close socket'); @@ -237,7 +315,6 @@ }; console.debug('New proxy socket ' + this.id + ' ' + this.uri); - // FIXME: perhaps a little risky. this.send('fetchsession'); var that = this; @@ -250,20 +327,16 @@ var req = new XMLHttpRequest(); // fetch session id: req.addEventListener('load', function() { - console.debug('read: ' + this.responseText); if (this.status == 200) - { - that.onmessage({ data: this.response }); - } + that.parseIncoming(this.responseType, this.response); else - { console.debug('Handle error ' + this.status); - } that.readWaiting = false; }); req.open('GET', that.getEndPoint('read')); - req.setRequestHeader('SessionId', this.sessionId); - req.send(that.sessionId); + req.setRequestHeader('SessionId', that.sessionId); + req.responseType = 'arraybuffer'; + req.send(''); that.readWaiting = true; }, 250); }; diff --git a/net/Socket.hpp b/net/Socket.hpp index f98e53dbb..07895d0bf 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -85,6 +85,10 @@ public: { _disposition = Type::CLOSED; } + std::shared_ptr<Socket> getSocket() const + { + return _socket; + } bool isMove() { return _disposition == Type::MOVE; } bool isClosed() { return _disposition == Type::CLOSED; } @@ -923,6 +927,12 @@ public: std::vector<std::pair<size_t, size_t>> _spans; }; + /// remove all queued input bytes + void clearInput() + { + _inBuffer.clear(); + } + /// Remove the first @count bytes from input buffer void eraseFirstInputBytes(const MessageMap &map) { @@ -1086,6 +1096,8 @@ public: /// Does it look like we have some TLS / SSL where we don't expect it ? bool sniffSSL() const; + void dumpState(std::ostream& os) override; + protected: /// Override to handle reading of socket data differently. virtual int readData(char* buf, int len) @@ -1109,8 +1121,6 @@ protected: #endif } - void dumpState(std::ostream& os) override; - void setShutdownSignalled(bool shutdownSignalled) { _shutdownSignalled = shutdownSignalled; diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 7d7b5e67d..4e854d3fb 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -147,7 +147,7 @@ public: const Poco::URI& uriPublic, const bool isReadOnly, const std::string& hostNoTrust, - const std::shared_ptr<Socket> &moveSocket); + const std::shared_ptr<StreamSocket> &socket); /// Thread safe termination of this broker if it has a lingering thread void joinThread(); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 6d5c39238..e07758269 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2933,7 +2933,7 @@ private: { docBroker->handleProxyRequest( sessionId, id, uriPublic, isReadOnly, - hostNoTrust, moveSocket); + hostNoTrust, streamSocket); return; } catch (const UnauthorizedRequestException& exc) diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 41043a57a..8aaff0131 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -25,7 +25,7 @@ void DocumentBroker::handleProxyRequest( const Poco::URI& uriPublic, const bool isReadOnly, const std::string& hostNoTrust, - const std::shared_ptr<Socket> &socket) + const std::shared_ptr<StreamSocket> &socket) { std::shared_ptr<ClientSession> clientSession; if (sessionId == "fetchsession") @@ -37,6 +37,22 @@ void DocumentBroker::handleProxyRequest( addSession(clientSession); LOOLWSD::checkDiskSpaceAndWarnClients(true); LOOLWSD::checkSessionLimitsAndWarnClients(); + + LOG_TRC("Returning id " << clientSession->getId()); + + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + "Last-Modified: " << Util::getHttpTimeNow() << "\r\n" + "User-Agent: " WOPI_AGENT_STRING "\r\n" + "Content-Length: " << clientSession->getId().size() << "\r\n" + "Content-Type: application/json\r\n" + "X-Content-Type-Options: nosniff\r\n" + "\r\n" + << clientSession->getId(); + + socket->send(oss.str()); + socket->shutdown(); + return; } else { @@ -69,13 +85,186 @@ void DocumentBroker::handleProxyRequest( proxy->handleRequest(uriPublic.toString(), socket); } +bool ProxyProtocolHandler::parseEmitIncoming( + const std::shared_ptr<StreamSocket> &socket) +{ + std::vector<char> &in = socket->getInBuffer(); + + std::stringstream oss; + socket->dumpState(oss); + LOG_TRC("Parse message:\n" << oss.str()); + + while (in.size() > 0) + { + if (in[0] != 'T' && in[0] != 'B') + { + LOG_ERR("Invalid message type " << in[0]); + return false; + } + auto it = in.begin() + 1; + for (; it != in.end() && *it != '\n'; ++it); + *it = '\0'; + uint64_t len = strtoll( &in[1], nullptr, 16 ); + in.erase(in.begin(), it + 1); + if (len > in.size()) + { + LOG_ERR("Invalid message length " << len << " vs " << in.size()); + return false; + } + // far from efficient: + std::vector<char> data; + data.insert(data.begin(), in.begin(), in.begin() + len + 1); + in.erase(in.begin(), in.begin() + len); + + if (in.size() < 1 || in[0] != '\n') + { + LOG_ERR("Missing final newline"); + return false; + } + in.erase(in.begin(), in.begin() + 1); + + _msgHandler->handleMessage(data); + } + return true; +} + void ProxyProtocolHandler::handleRequest(const std::string &uriPublic, const std::shared_ptr<Socket> &socket) { + auto streamSocket = std::static_pointer_cast<StreamSocket>(socket); + bool bRead = uriPublic.find("/write") == std::string::npos; LOG_INF("Proxy handle request " << uriPublic << " type: " << (bRead ? "read" : "write")); - (void)socket; + + if (bRead) + { + if (!_msgHandler) + LOG_WRN("unusual - incoming message with no-one to handle it"); + else if (!parseEmitIncoming(streamSocket)) + { + std::stringstream oss; + streamSocket->dumpState(oss); + LOG_ERR("bad socket structure " << oss.str()); + } + } + + if (!flushQueueTo(streamSocket) && !bRead) + { + // longer running 'write socket' + _writeSockets.push_back(streamSocket); + } + else + socket->shutdown(); +} + +void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition) +{ + std::stringstream oss; + disposition.getSocket()->dumpState(oss); + LOG_ERR("If you got here, it means we failed to parse this properly in handleRequest: " << oss.str()); +} + +int ProxyProtocolHandler::sendMessage(const char *msg, const size_t len, bool text, bool flush) +{ + _writeQueue.push_back(std::make_shared<Message>(msg, len, text)); + auto sock = popWriteSocket(); + if (sock && flush) + { + flushQueueTo(sock); + sock->shutdown(); + } + + return len; +} + +int ProxyProtocolHandler::sendTextMessage(const char *msg, const size_t len, bool flush) const +{ + LOG_TRC("ProxyHack - send text msg " + std::string(msg, len)); + return const_cast<ProxyProtocolHandler *>(this)->sendMessage(msg, len, true, flush); +} + +int ProxyProtocolHandler::sendBinaryMessage(const char *data, const size_t len, bool flush) const +{ + LOG_TRC("ProxyHack - send binary msg len " << len); + return const_cast<ProxyProtocolHandler *>(this)->sendMessage(data, len, false, flush); +} + +void ProxyProtocolHandler::shutdown(bool goingAway, const std::string &statusMessage) +{ + LOG_TRC("ProxyHack - shutdown " << goingAway << ": " << statusMessage); +} + +void ProxyProtocolHandler::getIOStats(uint64_t &sent, uint64_t &recv) +{ + sent = recv = 0; +} + +void ProxyProtocolHandler::dumpState(std::ostream& os) +{ + os << "proxy protocol sockets: " << _writeSockets.size() << " writeQueue: " << _writeQueue.size() << ":\n"; + for (auto it : _writeQueue) + Util::dumpHex(os, "\twrite queue entry:", "\t\t", *it); +} + +void ProxyProtocolHandler::performWrites() +{ + if (_msgHandler) + _msgHandler->writeQueuedMessages(); + if (_writeQueue.size() <= 0) + return; + + auto sock = popWriteSocket(); + if (sock) + { + flushQueueTo(sock); + sock->shutdown(); + } +} + +bool ProxyProtocolHandler::flushQueueTo(const std::shared_ptr<StreamSocket> &socket) +{ + // slurp from the core to us. + if (_msgHandler && _msgHandler->hasQueuedMessages()) + _msgHandler->writeQueuedMessages(); + + size_t totalSize = 0; + for (auto it : _writeQueue) + totalSize += it->size(); + + if (!totalSize) + return false; + + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + "Last-Modified: " << Util::getHttpTimeNow() << "\r\n" + "User-Agent: " WOPI_AGENT_STRING "\r\n" + "Content-Length: " << totalSize << "\r\n" + "Content-Type: application/json\r\n" + "X-Content-Type-Options: nosniff\r\n" + "\r\n"; + socket->send(oss.str()); + + for (auto it : _writeQueue) + socket->send(it->data(), it->size(), false); + _writeQueue.clear(); + + return true; +} + +// LRU-ness ... +std::shared_ptr<StreamSocket> ProxyProtocolHandler::popWriteSocket() +{ + std::weak_ptr<StreamSocket> sock; + while (!_writeSockets.empty()) + { + sock = _writeSockets.front(); + _writeSockets.erase(_writeSockets.begin()); + auto realSock = sock.lock(); + if (realSock) + return realSock; + } + return std::shared_ptr<StreamSocket>(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index bd6beac3d..0b20900b1 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -9,19 +9,21 @@ #pragma once +#include <memory> #include <net/Socket.hpp> -/// Interface for building a websocket from this ... +/** + * Implementation that builds a websocket like protocol from many + * individual proxied HTTP requests back to back. + * + * we use a trivial framing: <hex-length>\r\n<content>\r\n + */ class ProxyProtocolHandler : public ProtocolHandlerInterface { public: - ProxyProtocolHandler() - { - } + ProxyProtocolHandler() { } - virtual ~ProxyProtocolHandler() - { - } + virtual ~ProxyProtocolHandler() { } /// Will be called exactly once by setHandler void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override @@ -29,10 +31,7 @@ public: } /// Called after successful socket reads. - void handleIncomingMessage(SocketDisposition &/* disposition */) override - { - assert("we get our data a different way" && false); - } + void handleIncomingMessage(SocketDisposition &/* disposition */) override; int getPollEvents(std::chrono::steady_clock::time_point /* now */, int &/* timeoutMaxMs */) override @@ -45,9 +44,7 @@ public: { } - void performWrites() override - { - } + void performWrites() override; void onDisconnect() override { @@ -58,40 +55,40 @@ public: /// Clear all external references virtual void dispose() { _msgHandler.reset(); } - int sendTextMessage(const char *msg, const size_t len, bool flush = false) const override - { - LOG_TRC("ProxyHack - send text msg " + std::string(msg, len)); - (void) flush; - return len; - } + int sendTextMessage(const char *msg, const size_t len, bool flush = false) const override; + int sendBinaryMessage(const char *data, const size_t len, bool flush = false) const override; + void shutdown(bool goingAway = false, const std::string &statusMessage = "") override; + void getIOStats(uint64_t &sent, uint64_t &recv) override; + void dumpState(std::ostream& os); - int sendBinaryMessage(const char *data, const size_t len, bool flush = false) const override - { - (void) data; (void) flush; - LOG_TRC("ProxyHack - send binary msg len " << len); - return len; - } - - void shutdown(bool goingAway = false, const std::string &statusMessage = "") override - { - LOG_TRC("ProxyHack - shutdown " << goingAway << ": " << statusMessage); - } - - void getIOStats(uint64_t &sent, uint64_t &recv) override - { - sent = recv = 0; - } - - void dumpState(std::ostream& os) - { - os << "proxy protocol\n"; - } + bool parseEmitIncoming(const std::shared_ptr<StreamSocket> &socket); void handleRequest(const std::string &uriPublic, const std::shared_ptr<Socket> &socket); private: - std::vector<std::weak_ptr<StreamSocket>> _sockets; + std::shared_ptr<StreamSocket> popWriteSocket(); + int sendMessage(const char *msg, const size_t len, bool text, bool flush); + bool flushQueueTo(const std::shared_ptr<StreamSocket> &socket); + + struct Message : public std::vector<char> + { + Message(const char *msg, const size_t len, bool text) + { + const char *type = text ? "T" : "B"; + insert(end(), type, type + 1); + std::ostringstream os; + os << std::hex << "0x" << len << "\n"; + std::string str = os.str(); + insert(end(), str.c_str(), str.c_str() + str.size()); + insert(end(), msg, msg + len); + const char *terminator = "\n"; + insert(end(), terminator, terminator + 1); + } + }; + /// queue things when we have no socket to hand. + std::vector<std::shared_ptr<Message>> _writeQueue; + std::vector<std::weak_ptr<StreamSocket>> _writeSockets; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit b75148c54b474127f853336ad0a7c66366f01c38 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed Mar 4 13:54:04 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:38:16 2020 +0100 Proxy websocket prototype. Try to read/write avoiding a websocket. Change-Id: I382039fa88f1030a63df1e47f687df2ee5a6055b diff --git a/Makefile.am b/Makefile.am index ff7ad0b5f..b6cd106f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -113,6 +113,7 @@ loolwsd_sources = common/Crypto.cpp \ wsd/AdminModel.cpp \ wsd/Auth.cpp \ wsd/DocumentBroker.cpp \ + wsd/ProxyProtocol.cpp \ wsd/LOOLWSD.cpp \ wsd/ClientSession.cpp \ wsd/FileServer.cpp \ @@ -219,6 +220,7 @@ wsd_headers = wsd/Admin.hpp \ wsd/Auth.hpp \ wsd/ClientSession.hpp \ wsd/DocumentBroker.hpp \ + wsd/ProxyProtocol.hpp \ wsd/Exceptions.hpp \ wsd/FileServer.hpp \ wsd/LOOLWSD.hpp \ diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index 3022d94a3..3becdd706 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -185,16 +185,97 @@ }; this.onopen = function() { }; + this.close = function() { + }; }; - - global.FakeWebSocket.prototype.close = function() { - }; - global.FakeWebSocket.prototype.send = function(data) { this.sendCounter++; window.postMobileMessage(data); }; + global.proxySocketCounter = 0; + global.ProxySocket = function (uri) { + this.uri = uri; + this.binaryType = 'arraybuffer'; + this.bufferedAmount = 0; + this.extensions = ''; + this.protocol = ''; + this.connected = true; + this.readyState = 0; // connecting + this.sessionId = 'fetchsession'; + this.id = window.proxySocketCounter++; + this.sendCounter = 0; + this.readWaiting = false; + this.onclose = function() { + }; + this.onerror = function() { + }; + this.onmessage = function() { + }; + this.send = function(msg) { + console.debug('send msg "' + msg + '"'); + var req = new XMLHttpRequest(); + req.open('POST', this.getEndPoint('write')); + req.setRequestHeader('SessionId', this.sessionId); + if (this.sessionId === 'fetchsession') + req.addEventListener('load', function() { + console.debug('got session: ' + this.responseText); + that.sessionId = this.responseText; + that.readyState = 1; + that.onopen(); + }); + req.send(msg); + }, + this.close = function() { + console.debug('close socket'); + this.readyState = 3; + this.onclose(); + }; + this.getEndPoint = function(type) { + var base = this.uri; + return base.replace(/^ws/, 'http') + '/' + type; + }; + console.debug('New proxy socket ' + this.id + ' ' + this.uri); + + // FIXME: perhaps a little risky. + this.send('fetchsession'); + var that = this; + + // horrors ... + this.readInterval = setInterval(function() { + if (this.readWaiting) // one at a time for now + return; + if (this.sessionId == 'fetchsession') + return; // waiting for our session id. + var req = new XMLHttpRequest(); + // fetch session id: + req.addEventListener('load', function() { + console.debug('read: ' + this.responseText); + if (this.status == 200) + { + that.onmessage({ data: this.response }); + } + else + { + console.debug('Handle error ' + this.status); + } + that.readWaiting = false; + }); + req.open('GET', that.getEndPoint('read')); + req.setRequestHeader('SessionId', this.sessionId); + req.send(that.sessionId); + that.readWaiting = true; + }, 250); + }; + + global.createWebSocket = function(uri) { + if (global.socketProxy) { + return new global.ProxySocket(uri); + } else { + return new WebSocket(uri); + } + }; + // If not debug, don't print anything on the console // except in tile debug mode (Ctrl-Shift-Alt-d) console.log2 = console.log; @@ -219,7 +300,8 @@ window.postMobileError(log); } else if (global.socket && (global.socket instanceof WebSocket) && global.socket.readyState === 1) { global.socket.send(log); - } else if (global.socket && (global.socket instanceof global.L.Socket) && global.socket.connected()) { + } else if (global.socket && global.L && global.L.Socket && + (global.socket instanceof global.L.Socket) && global.socket.connected()) { global.socket.sendMessage(log); } else { var req = new XMLHttpRequest(); @@ -294,7 +376,7 @@ var websocketURI = global.host + global.serviceRoot + '/lool/' + encodeURIComponent(global.docURL + (docParams ? '?' + docParams : '')) + '/ws' + wopiSrc; try { - global.socket = new WebSocket(websocketURI); + global.socket = global.createWebSocket(websocketURI); } catch (err) { console.log(err); } diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index a4aa948c5..fdd0b2db6 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -49,7 +49,7 @@ L.Socket = L.Class.extend({ } try { - this.socket = new WebSocket(this.getWebSocketBaseURI(map) + wopiSrc); + this.socket = window.createWebSocket(this.getWebSocketBaseURI(map) + wopiSrc); } catch (e) { // On IE 11 there is a limitation on the number of WebSockets open to a single domain (6 by default and can go to 128). // Detect this and hint the user. diff --git a/net/Socket.hpp b/net/Socket.hpp index 5155a7bc8..f98e53dbb 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -445,6 +445,11 @@ public: } } + std::shared_ptr<ProtocolHandlerInterface> getProtocol() const + { + return _protocol; + } + /// Do we have something to send ? virtual bool hasQueuedMessages() const = 0; /// Please send them to me then. diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index 0f425b117..f2bd63adc 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -37,9 +37,6 @@ public: void construct(); virtual ~ClientSession(); - /// Lookup any session by id. - static std::shared_ptr<ClientSession> getById(const std::string &id); - void setReadOnly() override; enum SessionState { diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 329bece7f..7d7b5e67d 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -140,6 +140,15 @@ public: const bool isReadOnly, const std::string& hostNoTrust); + /// Find or create a new client session for the PHP proxy + void handleProxyRequest( + const std::string& sessionId, + const std::string& id, + const Poco::URI& uriPublic, + const bool isReadOnly, + const std::string& hostNoTrust, + const std::shared_ptr<Socket> &moveSocket); + /// Thread safe termination of this broker if it has a lingering thread void joinThread(); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 9ab86355a..6d5c39238 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -236,6 +236,9 @@ namespace #if ENABLE_SUPPORT_KEY inline void shutdownLimitReached(const std::shared_ptr<ProtocolHandlerInterface>& proto) { + if (!proto) + return; + const std::string error = Poco::format(PAYLOAD_UNAVAILABLE_LIMIT_REACHED, LOOLWSD::MaxDocuments, LOOLWSD::MaxConnections); LOG_INF("Sending client 'hardlimitreached' message: " << error); @@ -1828,9 +1831,12 @@ static std::shared_ptr<DocumentBroker> if (docBroker->isMarkedToDestroy()) { LOG_WRN("DocBroker with docKey [" << docKey << "] that is marked to be destroyed. Rejecting client request."); - std::string msg("error: cmd=load kind=docunloading"); - proto->sendTextMessage(msg.data(), msg.size()); - proto->shutdown(true, "error: cmd=load kind=docunloading"); + if (proto) + { + std::string msg("error: cmd=load kind=docunloading"); + proto->sendTextMessage(msg.data(), msg.size()); + proto->shutdown(true, "error: cmd=load kind=docunloading"); + } return nullptr; } } @@ -1846,9 +1852,12 @@ static std::shared_ptr<DocumentBroker> } // Indicate to the client that we're connecting to the docbroker. - const std::string statusConnect = "statusindicator: connect"; - LOG_TRC("Sending to Client [" << statusConnect << "]."); - proto->sendTextMessage(statusConnect.data(), statusConnect.size()); + if (proto) + { + const std::string statusConnect = "statusindicator: connect"; + LOG_TRC("Sending to Client [" << statusConnect << "]."); + proto->sendTextMessage(statusConnect.data(), statusConnect.size()); + } if (!docBroker) { @@ -2320,6 +2329,13 @@ private: // Util::dumpHex(std::cerr, "clipboard:\n", "", socket->getInBuffer()); // lots of data ... handleClipboardRequest(request, message, disposition); } + else if (request.has("ProxyPrefix") && reqPathTokens.count() > 2 && + (reqPathTokens[reqPathTokens.count()-2] == "ws")) + { + std::string decodedUri; // WOPISrc + Poco::URI::decode(reqPathTokens[1], decodedUri); + handleClientProxyRequest(request, decodedUri, message, disposition); + } else if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) && reqPathTokens.count() > 0 && reqPathTokens[0] == "lool") { @@ -2853,6 +2869,99 @@ private: } #endif + void handleClientProxyRequest(const Poco::Net::HTTPRequest& request, + std::string url, + Poco::MemoryInputStream& message, + SocketDisposition &disposition) + { + if (!request.has("SessionId")) + throw BadRequestException("No session id header on proxied request"); + + std::string sessionId = request.get("SessionId"); + + LOG_INF("URL [" << url << "]."); + const auto uriPublic = DocumentBroker::sanitizeURI(url); + LOG_INF("URI [" << uriPublic.getPath() << "]."); + const auto docKey = DocumentBroker::getDocKey(uriPublic); + LOG_INF("DocKey [" << docKey << "]."); + const std::string fileId = Util::getFilenameFromURL(docKey); + Util::mapAnonymized(fileId, fileId); // Identity mapping, since fileId is already obfuscated + + LOG_INF("Starting Proxy request handler for session [" << _id << "] on url [" << LOOLWSD::anonymizeUrl(url) << "]."); + + // Check if readonly session is required + bool isReadOnly = false; + for (const auto& param : uriPublic.getQueryParameters()) + { + LOG_DBG("Query param: " << param.first << ", value: " << param.second); + if (param.first == "permission" && param.second == "readonly") + { + isReadOnly = true; + } + } + + const std::string hostNoTrust = (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); + + LOG_INF("URL [" << LOOLWSD::anonymizeUrl(url) << "] is " << (isReadOnly ? "readonly" : "writable") << "."); + (void)request; (void)message; (void)disposition; + + std::shared_ptr<ProtocolHandlerInterface> none; + // Request a kit process for this doc. + std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( + none, url, docKey, _id, uriPublic); + if (docBroker) + { + // need to move into the DocumentBroker context before doing session lookup / creation etc. + std::string id = _id; + disposition.setMove([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId] + (const std::shared_ptr<Socket> &moveSocket) + { + LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey()); + // Make sure the thread is running before adding callback. + docBroker->startThread(); + + // We no longer own this socket. + moveSocket->setThreadOwner(std::thread::id()); + + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId, moveSocket]() + { + // Now inside the document broker thread ... + LOG_TRC("In the docbroker thread for " << docBroker->getDocKey()); + + auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket); + try + { + docBroker->handleProxyRequest( + sessionId, id, uriPublic, isReadOnly, + hostNoTrust, moveSocket); + return; + } + catch (const UnauthorizedRequestException& exc) + { + LOG_ERR("Unauthorized Request while loading session for " << docBroker->getDocKey() << ": " << exc.what()); + } + catch (const StorageConnectionException& exc) + { + LOG_ERR("Error while loading : " << exc.what()); + } + catch (const std::exception& exc) + { + LOG_ERR("Error while loading : " << exc.what()); + } + // badness occured: + std::ostringstream oss; + oss << "HTTP/1.1 400\r\n" + << "Date: " << Util::getHttpTimeNow() << "\r\n" + << "User-Agent: LOOLWSD WOPI Agent\r\n" + << "Content-Length: 0\r\n" + << "\r\n"; + streamSocket->send(oss.str()); + streamSocket->shutdown(); + }); + }); + } + } + void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url, SocketDisposition &disposition) { @@ -2915,7 +3024,7 @@ private: // Request a kit process for this doc. std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( std::static_pointer_cast<ProtocolHandlerInterface>(ws), url, docKey, _id, uriPublic); - if (docBroker) + if (docBroker) { #if MOBILEAPP const std::string hostNoTrust; diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp new file mode 100644 index 000000000..41043a57a --- /dev/null +++ b/wsd/ProxyProtocol.cpp @@ -0,0 +1,81 @@ +/* -*- 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/. + */ + +#include <config.h> + +#include "DocumentBroker.hpp" +#include "ClientSession.hpp" +#include "ProxyProtocol.hpp" +#include "Exceptions.hpp" +#include "LOOLWSD.hpp" +#include <Socket.hpp> + +#include <atomic> +#include <cassert> + +void DocumentBroker::handleProxyRequest( + const std::string& sessionId, + const std::string& id, + const Poco::URI& uriPublic, + const bool isReadOnly, + const std::string& hostNoTrust, + const std::shared_ptr<Socket> &socket) +{ + std::shared_ptr<ClientSession> clientSession; + if (sessionId == "fetchsession") + { + LOG_TRC("Create session for " << _docKey); + clientSession = createNewClientSession( + std::make_shared<ProxyProtocolHandler>(), + id, uriPublic, isReadOnly, hostNoTrust); + addSession(clientSession); + LOOLWSD::checkDiskSpaceAndWarnClients(true); + LOOLWSD::checkSessionLimitsAndWarnClients(); + } + else + { + LOG_TRC("Find session for " << _docKey << " with id " << sessionId); + for (const auto &it : _sessions) + { + if (it.second->getId() == sessionId) + { + clientSession = it.second; + break; + } + } + if (!clientSession) + { + LOG_ERR("Invalid session id used " << sessionId); + throw BadRequestException("invalid session id"); + } + } + + auto protocol = clientSession->getProtocol(); + auto streamSocket = std::static_pointer_cast<StreamSocket>(socket); + streamSocket->setHandler(protocol); + + // this DocumentBroker's poll handles reading & writing + addSocketToPoll(socket); + + auto proxy = std::static_pointer_cast<ProxyProtocolHandler>( + protocol); + + proxy->handleRequest(uriPublic.toString(), socket); +} + +void ProxyProtocolHandler::handleRequest(const std::string &uriPublic, + const std::shared_ptr<Socket> &socket) +{ + bool bRead = uriPublic.find("/write") == std::string::npos; + LOG_INF("Proxy handle request " << uriPublic << " type: " << + (bRead ? "read" : "write")); + (void)socket; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp new file mode 100644 index 000000000..bd6beac3d --- /dev/null +++ b/wsd/ProxyProtocol.hpp @@ -0,0 +1,97 @@ +/* -*- 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/. + */ + +#pragma once + +#include <net/Socket.hpp> + +/// Interface for building a websocket from this ... +class ProxyProtocolHandler : public ProtocolHandlerInterface +{ +public: + ProxyProtocolHandler() + { + } + + virtual ~ProxyProtocolHandler() + { + } + + /// Will be called exactly once by setHandler + void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override + { + } + + /// Called after successful socket reads. + void handleIncomingMessage(SocketDisposition &/* disposition */) override + { + assert("we get our data a different way" && false); + } + + int getPollEvents(std::chrono::steady_clock::time_point /* now */, + int &/* timeoutMaxMs */) override + { + // underlying buffer based polling is fine. + return POLLIN; + } + + void checkTimeout(std::chrono::steady_clock::time_point /* now */) override + { + } + + void performWrites() override + { + } + + void onDisconnect() override + { + // connections & sockets come and go a lot. + } + +public: + /// Clear all external references + virtual void dispose() { _msgHandler.reset(); } + + int sendTextMessage(const char *msg, const size_t len, bool flush = false) const override + { + LOG_TRC("ProxyHack - send text msg " + std::string(msg, len)); + (void) flush; + return len; + } + + int sendBinaryMessage(const char *data, const size_t len, bool flush = false) const override + { + (void) data; (void) flush; + LOG_TRC("ProxyHack - send binary msg len " << len); + return len; + } + + void shutdown(bool goingAway = false, const std::string &statusMessage = "") override + { + LOG_TRC("ProxyHack - shutdown " << goingAway << ": " << statusMessage); + } + + void getIOStats(uint64_t &sent, uint64_t &recv) override + { + sent = recv = 0; + } + + void dumpState(std::ostream& os) + { + os << "proxy protocol\n"; + } + + void handleRequest(const std::string &uriPublic, + const std::shared_ptr<Socket> &socket); + +private: + std::vector<std::weak_ptr<StreamSocket>> _sockets; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit f261ae77409697a4e7d8a78bc45d81240e9856e5 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed Mar 4 13:52:51 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 17:37:23 2020 +0100 ProxyPrefix: allow the user to specify a custom prefix. This allows us to re-direct web traffic via a proxy quite simply during fetch, instead of changing the service root. Change-Id: I28d348467e48394d581fca4da4c199348a2ca8e0 diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4 index c8d51f0f0..bcfe6ebdb 100644 --- a/loleaflet/html/loleaflet.html.m4 +++ b/loleaflet/html/loleaflet.html.m4 @@ -240,6 +240,7 @@ m4_ifelse(MOBILEAPP,[true], window.reuseCookies = ''; window.protocolDebug = false; window.frameAncestors = ''; + window.socketProxy = false; window.tileSize = 256;], [window.host = '%HOST%'; window.serviceRoot = '%SERVICE_ROOT%'; @@ -253,6 +254,7 @@ m4_ifelse(MOBILEAPP,[true], window.reuseCookies = '%REUSE_COOKIES%'; window.protocolDebug = %PROTOCOL_DEBUG%; window.frameAncestors = '%FRAME_ANCESTORS%'; + window.socketProxy = %SOCKET_PROXY%; window.tileSize = 256;]) m4_syscmd([cat ]GLOBAL_JS)m4_dnl diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp index 5a973bc01..95f7826e4 100644 --- a/wsd/FileServer.cpp +++ b/wsd/FileServer.cpp @@ -640,6 +640,17 @@ constexpr char BRANDING[] = "branding"; constexpr char BRANDING_UNSUPPORTED[] = "branding-unsupported"; #endif +namespace { + // The user can override the ServerRoot with a new prefix. + std::string getResponseRoot(const HTTPRequest &request) + { + if (!request.has("ProxyPrefix")) + return LOOLWSD::ServiceRoot; + std::string proxyPrefix = request.get("ProxyPrefix", ""); + return proxyPrefix; + } +} + void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::MemoryInputStream& message, const std::shared_ptr<StreamSocket>& socket) { @@ -686,15 +697,21 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco:: } } - const auto& config = Application::instance().config(); + std::string socketProxy = "false"; + if (request.has("ProxyPrefix")) + socketProxy = "true"; + Poco::replaceInPlace(preprocess, std::string("%SOCKET_PROXY%"), socketProxy); + + std::string responseRoot = getResponseRoot(request); Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN%"), escapedAccessToken); Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN_TTL%"), std::to_string(tokenTtl)); Poco::replaceInPlace(preprocess, std::string("%ACCESS_HEADER%"), escapedAccessHeader); Poco::replaceInPlace(preprocess, std::string("%HOST%"), host); Poco::replaceInPlace(preprocess, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); - Poco::replaceInPlace(preprocess, std::string("%SERVICE_ROOT%"), LOOLWSD::ServiceRoot); + Poco::replaceInPlace(preprocess, std::string("%SERVICE_ROOT%"), responseRoot); + const auto& config = Application::instance().config(); std::string protocolDebug = "false"; if (config.getBool("logging.protocol")) protocolDebug = "true"; @@ -703,16 +720,16 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco:: static const std::string linkCSS("<link rel=\"stylesheet\" href=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.css\">"); static const std::string scriptJS("<script src=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.js\"></script>"); - std::string brandCSS(Poco::format(linkCSS, LOOLWSD::ServiceRoot, std::string(BRANDING))); - std::string brandJS(Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING))); + std::string brandCSS(Poco::format(linkCSS, responseRoot, std::string(BRANDING))); + std::string brandJS(Poco::format(scriptJS, responseRoot, std::string(BRANDING))); #if ENABLE_SUPPORT_KEY const std::string keyString = config.getString("support_key", ""); SupportKey key(keyString); if (!key.verify() || key.validDaysRemaining() <= 0) { - brandCSS = Poco::format(linkCSS, LOOLWSD::ServiceRoot, std::string(BRANDING_UNSUPPORTED)); - brandJS = Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING_UNSUPPORTED)); + brandCSS = Poco::format(linkCSS, responseRoot, std::string(BRANDING_UNSUPPORTED)); + brandJS = Poco::format(scriptJS, responseRoot, std::string(BRANDING_UNSUPPORTED)); } #endif @@ -900,13 +917,15 @@ void FileServerRequestHandler::preprocessAdminFile(const HTTPRequest& request,co if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) throw Poco::Net::NotAuthenticatedException("Invalid admin login"); + std::string responseRoot = getResponseRoot(request); + static const std::string scriptJS("<script src=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.js\"></script>"); static const std::string footerPage("<div class=\"footer navbar-fixed-bottom text-info text-center\"><strong>Key:</strong> %s <strong>Expiry Date:</strong> %s</div>"); const std::string relPath = getRequestPathname(request); LOG_DBG("Preprocessing file: " << relPath); std::string adminFile = *getUncompressedFile(relPath); - std::string brandJS(Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING))); + std::string brandJS(Poco::format(scriptJS, responseRoot, std::string(BRANDING))); std::string brandFooter; #if ENABLE_SUPPORT_KEY @@ -924,7 +943,7 @@ void FileServerRequestHandler::preprocessAdminFile(const HTTPRequest& request,co Poco::replaceInPlace(adminFile, std::string("<!--%BRANDING_JS%-->"), brandJS); Poco::replaceInPlace(adminFile, std::string("<!--%FOOTER%-->"), brandFooter); Poco::replaceInPlace(adminFile, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); - Poco::replaceInPlace(adminFile, std::string("%SERVICE_ROOT%"), LOOLWSD::ServiceRoot); + Poco::replaceInPlace(adminFile, std::string("%SERVICE_ROOT%"), responseRoot); // Ask UAs to block if they detect any XSS attempt response.add("X-XSS-Protection", "1; mode=block"); commit 3e4ac31c7c6ba95eab12585e0550372b0a39973f Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Apr 18 11:55:50 2020 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Apr 18 18:29:04 2020 +0200 killpoco: remove WebSocket includes from a couple of places. Change-Id: I06740cd978bec8e6a74beb8ed9ef8f4f970a2535 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92470 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Michael Meeks <michael.me...@collabora.com> diff --git a/common/LOOLWebSocket.hpp b/common/LOOLWebSocket.hpp index a99c3afb0..627b89a5e 100644 --- a/common/LOOLWebSocket.hpp +++ b/common/LOOLWebSocket.hpp @@ -62,7 +62,7 @@ public: if (n <= 0) LOG_TRC("Got nothing (" << n << ")"); else - LOG_TRC("Got frame: " << LOOLProtocol::getAbbreviatedFrameDump(buffer, n, flags)); + LOG_TRC("Got frame: " << getAbbreviatedFrameDump(buffer, n, flags)); if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_CLOSE) { @@ -103,11 +103,11 @@ public: if (result != length) { LOG_ERR("Sent incomplete message, expected " << length << " bytes but sent " << result << - " for: " << LOOLProtocol::getAbbreviatedFrameDump(buffer, length, flags)); + " for: " << getAbbreviatedFrameDump(buffer, length, flags)); } else { - LOG_TRC("Sent frame: " << LOOLProtocol::getAbbreviatedFrameDump(buffer, length, flags)); + LOG_TRC("Sent frame: " << getAbbreviatedFrameDump(buffer, length, flags)); } return result; @@ -154,6 +154,38 @@ public: } } } + + // Return a string dump of a WebSocket frame: Its opcode, length, first line (if present), + // flags. For human-readable logging purposes. Format not guaranteed to be stable. Not to be + // inspected programmatically. + static inline + std::string getAbbreviatedFrameDump(const char *message, const int length, const int flags) + { + std::ostringstream result; + switch (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) + { +#define CASE(x) case Poco::Net::WebSocket::FRAME_OP_##x: result << #x; break + CASE(CONT); + CASE(TEXT); + CASE(BINARY); + CASE(CLOSE); + CASE(PING); + CASE(PONG); +#undef CASE + default: + result << Poco::format("%#x", flags); + break; + } + result << " " << std::setw(3) << length << " bytes"; + + if (length > 0 && + ((flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_TEXT || + (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_BINARY || + (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_PING || + (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_PONG)) + result << ": '" << LOOLProtocol::getAbbreviatedMessage(message, length) << "'"; + return result.str(); + } }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/MessageQueue.cpp b/common/MessageQueue.cpp index 5482b4f79..3e5cb08d2 100644 --- a/common/MessageQueue.cpp +++ b/common/MessageQueue.cpp @@ -10,7 +10,7 @@ #include <config.h> #include "MessageQueue.hpp" - +#include <climits> #include <algorithm> #include <Poco/JSON/JSON.h> diff --git a/common/Protocol.hpp b/common/Protocol.hpp index c10e9ae52..56cff7896 100644 --- a/common/Protocol.hpp +++ b/common/Protocol.hpp @@ -19,8 +19,6 @@ #include <string> #include <vector> -#include <Poco/Net/WebSocket.h> - #include <Util.hpp> #define LOK_USE_UNSTABLE_API @@ -311,38 +309,6 @@ namespace LOOLProtocol { return getAbbreviatedMessage(message.data(), message.size()); } - - // Return a string dump of a WebSocket frame: Its opcode, length, first line (if present), - // flags. For human-readable logging purposes. Format not guaranteed to be stable. Not to be - // inspected programmatically. - inline - std::string getAbbreviatedFrameDump(const char *message, const int length, const int flags) - { - std::ostringstream result; - switch (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) - { -#define CASE(x) case Poco::Net::WebSocket::FRAME_OP_##x: result << #x; break - CASE(CONT); - CASE(TEXT); - CASE(BINARY); - CASE(CLOSE); - CASE(PING); - CASE(PONG); -#undef CASE - default: - result << Poco::format("%#x", flags); - break; - } - result << " " << std::setw(3) << length << " bytes"; - - if (length > 0 && - ((flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_TEXT || - (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_BINARY || - (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_PING || - (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_PONG)) - result << ": '" << getAbbreviatedMessage(message, length) << "'"; - return result.str(); - } }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Util.cpp b/common/Util.cpp index 7b71da598..e4d19adb1 100644 --- a/common/Util.cpp +++ b/common/Util.cpp @@ -25,6 +25,7 @@ #include <sys/types.h> #include <unistd.h> #include <dirent.h> +#include <fcntl.h> #include <atomic> #include <cassert> @@ -51,7 +52,6 @@ #include <Poco/JSON/JSON.h> #include <Poco/JSON/Object.h> #include <Poco/JSON/Parser.h> -#include <Poco/Net/WebSocket.h> #include <Poco/Process.h> #include <Poco/RandomStream.h> #include <Poco/TemporaryFile.h> diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index 780efb18f..cfc7c8baa 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -11,6 +11,7 @@ #include "ChildSession.hpp" +#include <climits> #include <fstream> #include <sstream> diff --git a/test/UnitSession.cpp b/test/UnitSession.cpp index 7e11c2128..f8b859fa1 100644 --- a/test/UnitSession.cpp +++ b/test/UnitSession.cpp @@ -110,18 +110,18 @@ UnitBase::TestResult UnitSession::testHandshake() int flags = 0; char buffer[1024] = { 0 }; int bytes = socket.receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); LOK_ASSERT_EQUAL(std::string("statusindicator: find"), std::string(buffer, bytes)); bytes = socket.receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); if (bytes > 0 && !std::strstr(buffer, "error:")) { LOK_ASSERT_EQUAL(std::string("statusindicator: connect"), std::string(buffer, bytes)); bytes = socket.receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); if (!std::strstr(buffer, "error:")) { LOK_ASSERT_EQUAL(std::string("statusindicator: ready"), @@ -134,7 +134,7 @@ UnitBase::TestResult UnitSession::testHandshake() // close frame message bytes = socket.receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); LOK_ASSERT((flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_CLOSE); } @@ -146,7 +146,7 @@ UnitBase::TestResult UnitSession::testHandshake() // close frame message bytes = socket.receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); LOK_ASSERT((flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) == Poco::Net::WebSocket::FRAME_OP_CLOSE); } diff --git a/test/helpers.hpp b/test/helpers.hpp index 3322fc5b0..341c5621c 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -273,7 +273,7 @@ int getErrorCode(LOOLWebSocket& ws, std::string& message, const std::string& tes do { bytes = ws.receiveFrame(buffer.begin(), READ_BUFFER_SIZE, flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer.begin(), bytes, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer.begin(), bytes, flags)); std::this_thread::sleep_for(std::chrono::microseconds(POLL_TIMEOUT_MICRO_S)); } while (bytes > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE); @@ -325,7 +325,7 @@ std::vector<char> getResponseMessage(LOOLWebSocket& ws, const std::string& prefi if (LOOLProtocol::matchPrefix(prefix, message)) { TST_LOG("[" << prefix << "] Matched " << - LOOLProtocol::getAbbreviatedFrameDump(response.data(), bytes, flags)); + LOOLWebSocket::getAbbreviatedFrameDump(response.data(), bytes, flags)); return response; } } @@ -349,7 +349,7 @@ std::vector<char> getResponseMessage(LOOLWebSocket& ws, const std::string& prefi } TST_LOG("[" << prefix << "] Ignored " << - LOOLProtocol::getAbbreviatedFrameDump(response.data(), bytes, flags)); + LOOLWebSocket::getAbbreviatedFrameDump(response.data(), bytes, flags)); } } } @@ -539,7 +539,7 @@ void SocketProcessor(const std::string& testname, } n = socket->receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG("Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, n, flags)); + TST_LOG("Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, n, flags)); if (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE) { if (!handler(std::string(buffer, n))) diff --git a/test/httpcrashtest.cpp b/test/httpcrashtest.cpp index e824c1cb9..3a69a96ba 100644 --- a/test/httpcrashtest.cpp +++ b/test/httpcrashtest.cpp @@ -166,7 +166,7 @@ void HTTPCrashTest::testCrashKit() int flags; char buffer[READ_BUFFER_SIZE]; const int bytes = socket->receiveFrame(buffer, sizeof(buffer), flags); - TST_LOG(testname << "Got " << LOOLProtocol::getAbbreviatedFrameDump(buffer, bytes, flags)); + TST_LOG(testname << "Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, bytes, flags)); // While we expect no more messages after shutdown call, apparently // sometimes we _do_ get data. Even when the receiveFrame in the loop diff --git a/tools/Connect.cpp b/tools/Connect.cpp index a150bdf99..0a115960b 100644 --- a/tools/Connect.cpp +++ b/tools/Connect.cpp @@ -84,7 +84,7 @@ public: { { std::unique_lock<std::mutex> lock(coutMutex); - std::cout << "Got " << getAbbreviatedFrameDump(buffer, n, flags) << std::endl; + std::cout << "Got " << LOOLWebSocket::getAbbreviatedFrameDump(buffer, n, flags) << std::endl; } std::string firstLine = getFirstLine(buffer, n); commit 378a2b5295b7b4d005107e4638ac2e8a61518b7e Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Sat Apr 18 16:47:09 2020 +0300 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Sat Apr 18 16:35:15 2020 +0200 Bin a couple of unintentionally committed LOG_TRC() calls Change-Id: Ieadc06a38d71aed723191f1738f8f39154978091 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92476 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp index dcca2d295..cd4c779dc 100644 --- a/common/SigUtil.cpp +++ b/common/SigUtil.cpp @@ -56,13 +56,11 @@ namespace SigUtil bool getTerminationFlag() { - LOG_TRC("getTerminationFlag: " << (TerminationFlag ? "YES" : "NO")); return TerminationFlag; } void setTerminationFlag() { - LOG_TRC("setTerminationFlag"); TerminationFlag = true; } diff --git a/kit/ForKit.cpp b/kit/ForKit.cpp index aecf96b64..993794bc7 100644 --- a/kit/ForKit.cpp +++ b/kit/ForKit.cpp @@ -82,7 +82,6 @@ public: WebSocketHandler(/* isClient = */ true, /* isMasking */ false), _socketName(socketName) { - LOG_TRC("===> Here is ServerWSHandler"); } protected: diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 99391c0d0..329bece7f 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -75,7 +75,6 @@ public: WSProcess("ChildProcess", pid, socket, std::make_shared<WebSocketHandler>(socket, request)), _jailId(jailId) { - LOG_TRC("==> new ChildProcess"); } commit 7a416cbf0a3cf6724d5ff21128ed46cb82c42a1a Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Sat Apr 18 16:44:50 2020 +0300 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Sat Apr 18 16:34:49 2020 +0200 Clarify what ChildProcess and WSProcess are Change-Id: I13dd37c840d2c8b811bb56614fbc9f8ee2e44d40 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92475 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index d161be4aa..99391c0d0 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -58,8 +58,10 @@ public: #include "LOOLWSD.hpp" -/// Represents a new LOK child that is read -/// to host a document. +/// A ChildProcess object represents a KIT process that hosts a document and manipulates the +/// document using the LibreOfficeKit API. It isn't actually a child of the WSD process, but a +/// grandchild. The comments loosely talk about "child" anyway. + class ChildProcess : public WSProcess { public: diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp index 368b58076..c391cc055 100644 --- a/wsd/LOOLWSD.hpp +++ b/wsd/LOOLWSD.hpp @@ -38,10 +38,10 @@ std::shared_ptr<ChildProcess> getNewChild_Blocks( const std::string& uri #endif ); -// This is common code used to setup as socket to both -// forkit and child document processes via a websocket. -// In general, a WSProcess instance represents a child -// process with which we can communicate through websocket. +// A WSProcess object in the WSD process represents a descendant process, either the direct child +// process FORKIT or a grandchild KIT process, with which the WSD process communicates through a +// WebSocket. + class WSProcess { public: commit 2a16f34812cf69bbe0f21b7e7d048fdb3271fa9d Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Sat Apr 18 16:20:17 2020 +0300 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Sat Apr 18 16:33:56 2020 +0200 Don't use "kitbroker" as part of the thread name in a 'kit' process It just causes confusion with the name "DocumentBroker". DocumentBroker objects exist only in the WSD process. Instead just use "kit". Change-Id: I3d9915c4759899ea6ed9084cf3ec6dc0f3b88ee5 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92474 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 5e28c0ca0..539615da5 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2154,7 +2154,7 @@ protected: std::string url; URI::decode(docKey, url); LOG_INF("New session [" << sessionId << "] request on url [" << url << "]."); - Util::setThreadName("kitbroker_" + docId); + Util::setThreadName("kit_" + docId); if (!document) document = std::make_shared<Document>( commit 4eb598711cd6162452a28285abbd65c6397f58bf Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Sat Apr 18 11:39:50 2020 +0300 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Sat Apr 18 15:00:18 2020 +0200 Use #pragma once LibreOffice core uses that, too, and we support an even more restricted set of compilers. Change-Id: I0d0e2c8608e323eb5ef0f35ee8c46d02ab49a745 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92467 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/common/Authorization.hpp b/common/Authorization.hpp index c8c594988..4fd2f3671 100644 --- a/common/Authorization.hpp +++ b/common/Authorization.hpp @@ -9,8 +9,7 @@ // WOPI Authorization -#ifndef INCLUDED_AUTHORIZATION_HPP -#define INCLUDED_AUTHORIZATION_HPP +#pragma once #include <string> @@ -51,6 +50,4 @@ public: void authorizeRequest(Poco::Net::HTTPRequest& request) const; }; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Clipboard.hpp b/common/Clipboard.hpp index 0b4bee438..15e5e58e2 100644 --- a/common/Clipboard.hpp +++ b/common/Clipboard.hpp @@ -8,8 +8,8 @@ */ // Clipboard abstraction. -#ifndef INCLUDED_CLIPBOARD_HPP -#define INCLUDED_CLIPBOARD_HPP + +#pragma once #include <string> #include <vector> @@ -142,6 +142,4 @@ public: } }; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Common.hpp b/common/Common.hpp index ab9989e73..b12cc67b5 100644 --- a/common/Common.hpp +++ b/common/Common.hpp @@ -7,11 +7,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + #include <string> // Default values and other shared data between processes. -#ifndef INCLUDED_COMMON_HPP -#define INCLUDED_COMMON_HPP constexpr int DEFAULT_CLIENT_PORT_NUMBER = 9980; @@ -50,6 +50,4 @@ constexpr const char CAPABILITIES_END_POINT[] = "/hosting/capabilities"; extern int ClientPortNumber; extern std::string MasterLocation; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Crypto.hpp b/common/Crypto.hpp index 079a4f742..270e74e0e 100644 --- a/common/Crypto.hpp +++ b/common/Crypto.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_CRYPTO_HPP -#define INCLUDED_CRYPTO_HPP +#pragma once #if ENABLE_SUPPORT_KEY @@ -39,6 +38,4 @@ public: #endif -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/FileUtil.hpp b/common/FileUtil.hpp index 864188308..29426e16c 100644 --- a/common/FileUtil.hpp +++ b/common/FileUtil.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_FILEUTIL_HPP -#define INCLUDED_FILEUTIL_HPP +#pragma once #include <string> @@ -98,6 +97,4 @@ namespace FileUtil } // end namespace FileUtil -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/JsonUtil.hpp b/common/JsonUtil.hpp index c7fa20ac2..42cd4e915 100644 --- a/common/JsonUtil.hpp +++ b/common/JsonUtil.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_JSONUTIL_HPP -#define INCLUDED_JSONUTIL_HPP +#pragma once #include <cstddef> #include <set> @@ -140,7 +139,5 @@ bool findJSONValue(Poco::JSON::Object::Ptr &object, const std::string& key, T& v } // end namespace JsonUtil -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/LOOLWebSocket.hpp b/common/LOOLWebSocket.hpp index 2c0559593..a99c3afb0 100644 --- a/common/LOOLWebSocket.hpp +++ b/common/LOOLWebSocket.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_LOOLWEBSOCKET_HPP -#define INCLUDED_LOOLWEBSOCKET_HPP +#pragma once #include <cstdlib> #include <mutex> @@ -157,6 +156,4 @@ public: } }; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Log.hpp b/common/Log.hpp index aa83bc171..bc96035e9 100644 --- a/common/Log.hpp +++ b/common/Log.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_LOG_HPP -#define INCLUDED_LOG_HPP +#pragma once #include <sys/syscall.h> #include <unistd.h> @@ -409,6 +408,4 @@ namespace Log } \ } while (false) -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Message.hpp b/common/Message.hpp index 9320037c7..8fc894b96 100644 --- a/common/Message.hpp +++ b/common/Message.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_MESSAGE_HPP -#define INCLUDED_MESSAGE_HPP +#pragma once #include <atomic> #include <string> @@ -184,6 +183,4 @@ private: const Type _type; }; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/MessageQueue.hpp b/common/MessageQueue.hpp index bf5f6c950..f315de558 100644 --- a/common/MessageQueue.hpp +++ b/common/MessageQueue.hpp @@ -7,8 +7,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_MESSAGEQUEUE_HPP -#define INCLUDED_MESSAGEQUEUE_HPP +#pragma once #include <algorithm> #include <condition_variable> @@ -253,6 +252,4 @@ private: std::vector<int> _viewOrder; }; -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Png.hpp b/common/Png.hpp index c0a99c74e..496d293df 100644 --- a/common/Png.hpp +++ b/common/Png.hpp @@ -7,6 +7,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + /* cairo - a vector graphics library with display and print output * ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits