[Libreoffice-commits] online.git: Branch 'feature/proxyhack' - 216 commits - android/app android/lib common/Authorization.hpp common/Clipboard.hpp common/Common.hpp common/Crypto.hpp common/FileUtil.cpp common/FileUtil.hpp common/IoUtil.cpp common/IoUtil.hpp common/JsonUtil.hpp common/Log.hpp common/LOOLWebSocket.hpp common/Message.hpp common/MessageQueue.cpp common/MessageQueue.hpp common/Png.hpp common/Protocol.hpp common/Rectangle.hpp common/Seccomp.hpp common/security.h common/Session.cpp common/Session.hpp common/SigUtil.cpp common/SigUtil.hpp common/SpookyV2.h common/StringVector.hpp common/Unit.hpp common/UnitHTTP.hpp common/Util.cpp common/Util.hpp configure.ac cypress_test/cypress.json cypress_test/data cypress_test/integration_tests cypress_test/Makefile.am cypress_test/package.json cypress_test/run_iteratively.sh docker/l10n-docker-nightly.sh fuzzer/Admin.cpp fuzzer/admin-data fuzzer/ClientSession.cpp .gitignore ios/config.h.in ios/Mobile ios/Mobile.xcodeproj kit/ChildSession.cpp kit/C hildSession.hpp kit/Delta.hpp kit/DummyLibreOfficeKit.hpp kit/ForKit.cpp kit/Kit.cpp kit/KitHelper.hpp kit/Kit.hpp kit/Watermark.hpp loleaflet/admin loleaflet/css loleaflet/.gitignore loleaflet/html loleaflet/images loleaflet/js loleaflet/Makefile.am loleaflet/po loleaflet/README loleaflet/spec loleaflet/src loleaflet/welcome loolwsd.xml.in Makefile.am net/DelaySocket.cpp net/DelaySocket.hpp net/FakeSocket.hpp net/ServerSocket.hpp net/Socket.cpp net/Socket.hpp net/Ssl.hpp net/SslSocket.hpp net/WebSocketHandler.hpp test/countloolkits.hpp test/helpers.hpp test/httpcrashtest.cpp test/lokassert.hpp test/Makefile.am test/test.hpp test/UnitConvert.cpp test/UnitFuzz.cpp test/UnitOOB.cpp test/UnitSession.cpp test/UnitWOPIDocumentConflict.cpp test/WopiProofTests.cpp tools/Connect.cpp tools/Replay.hpp tools/Stress.cpp tools/Tool.cpp tools/WebSocketDump.cpp wsd/Admin.cpp wsd/Admin.hpp wsd/AdminModel.cpp wsd/AdminModel.hpp wsd/Auth.cpp wsd/Auth.hpp wsd/ClientSession.cpp wsd/ClientSession.hpp ws d/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/Exceptions.hpp wsd/FileServer.cpp wsd/FileServer.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/metrics.txt wsd/ProofKey.cpp wsd/ProofKey.hpp wsd/protocol.txt wsd/ProxyProtocol.cpp wsd/ProxyProtocol.hpp wsd/QueueHandler.hpp wsd/SenderQueue.hpp wsd/Storage.hpp wsd/TileCache.hpp wsd/TileDesc.hpp wsd/TraceFile.hpp wsd/UserMessages.hpp

Michael Meeks (via logerrit) logerrit at kemper.freedesktop.org
Sat Apr 18 16:48:40 UTC 2020


Rebased ref, commits from common ancestor:
commit c90bf00077d4091ca49c6ade6ffaf86d0603a0b3
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Tue Apr 14 17:01:41 2020 +0100
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Sat Mar 21 20:03:37 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Sat Mar 21 15:07:10 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Sat Mar 21 14:27:15 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Sat Mar 21 14:19:49 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Fri Mar 20 20:45:38 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Fri Mar 20 20:15:08 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Fri Mar 20 19:05:48 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Fri Mar 20 16:38:14 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Thu Mar 19 15:54:28 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Wed Mar 4 13:54:04 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Wed Mar 4 13:52:51 2020 +0000
Commit:     Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Sat Apr 18 11:55:50 2020 +0100
Commit:     Michael Meeks <michael.meeks at 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 <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Michael Meeks <michael.meeks at 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 <tml at collabora.com>
AuthorDate: Sat Apr 18 16:47:09 2020 +0300
Commit:     Tor Lillqvist <tml at 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 <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tor Lillqvist <tml at 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 <tml at collabora.com>
AuthorDate: Sat Apr 18 16:44:50 2020 +0300
Commit:     Tor Lillqvist <tml at 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 <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tor Lillqvist <tml at 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 <tml at collabora.com>
AuthorDate: Sat Apr 18 16:20:17 2020 +0300
Commit:     Tor Lillqvist <tml at 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 <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tor Lillqvist <tml at 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 <tml at collabora.com>
AuthorDate: Sat Apr 18 11:39:50 2020 +0300
Commit:     Tor Lillqvist <tml at 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 <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tor Lillqvist <tml at 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


More information about the Libreoffice-commits mailing list