[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-3' - 26 commits - common/Common.hpp common/Seccomp.cpp common/SigUtil.cpp configure.ac kit/ChildSession.hpp kit/Kit.cpp loleaflet/npm-shrinkwrap.json loleaflet/npm-shrinkwrap.json.in loleaflet/src loolwsd.xml.in net/Socket.cpp net/Socket.hpp net/Ssl.cpp net/SslSocket.hpp net/WebSocketHandler.hpp test/httpcrashtest.cpp test/httpwstest.cpp test/Makefile.am test/TileCacheTests.cpp test/WhiteBoxTests.cpp tools/WebSocketDump.cpp wsd/Admin.cpp wsd/Admin.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Sat May 19 21:44:35 UTC 2018


 common/Common.hpp                     |    8 
 common/Seccomp.cpp                    |    1 
 common/SigUtil.cpp                    |    1 
 configure.ac                          |    1 
 kit/ChildSession.hpp                  |   10 -
 kit/Kit.cpp                           |  334 +++++++++++++++++-----------------
 loleaflet/src/layer/tile/TileLayer.js |   88 ++++----
 loolwsd.xml.in                        |    3 
 net/Socket.cpp                        |  173 +++++++++++++++++
 net/Socket.hpp                        |  130 +++++++------
 net/Ssl.cpp                           |    4 
 net/SslSocket.hpp                     |   16 +
 net/WebSocketHandler.hpp              |  222 +++++++++++++++++-----
 test/Makefile.am                      |    8 
 test/TileCacheTests.cpp               |   12 +
 test/WhiteBoxTests.cpp                |    2 
 test/httpcrashtest.cpp                |    1 
 test/httpwstest.cpp                   |    4 
 tools/WebSocketDump.cpp               |   82 +++++++-
 wsd/Admin.cpp                         |   81 +++++++-
 wsd/Admin.hpp                         |    8 
 wsd/DocumentBroker.cpp                |    5 
 wsd/DocumentBroker.hpp                |   66 +++---
 wsd/LOOLWSD.cpp                       |  117 ++---------
 24 files changed, 902 insertions(+), 475 deletions(-)

New commits:
commit 27cc15027b59365fd4bf95c0c133541215c44771
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Mon Jan 22 14:36:16 2018 -0500

    ut: fix HTTPWSTest::testLoadTortureODP
    
    Change-Id: Ibe3bd98c1cd68da962cc8d93b837597fb7167f45
    Reviewed-on: https://gerrit.libreoffice.org/48644
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index ef1c8876a..bea861107 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -567,8 +567,8 @@ void HTTPWSTest::testLoadTortureODP()
     const auto sum_view_ids = loadTorture(testname, "empty.odp", thread_count, max_jitter_ms);
 
     // For ODP the view-id is always odd, and we expect not to skip any ids.
-    const auto number_of_loads = thread_count;
-    const int exp_sum_view_ids = number_of_loads * number_of_loads; // Odd view-ids only.
+    const int number_of_loads = thread_count;
+    const int exp_sum_view_ids = number_of_loads * (number_of_loads - 1) / 2; // 0-based view-ids.
     CPPUNIT_ASSERT_EQUAL(exp_sum_view_ids, sum_view_ids);
 }
 
commit 00c2c968cabb48ccb6ad1c5a39199d0c39a9c05e
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Fri May 18 18:32:33 2018 +0100

    Remove test monitor.
    
    Change-Id: I55f93ffec68745b194a778b541db1011962f735d

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index fea005b4f..ed275c016 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -114,7 +114,6 @@
     </admin_console>
 
     <monitors desc="Addresses of servers we connect to on start for monitoring">
-	<monitor>ws://localhost:9042/foo</monitor>
     </monitors>
 
 </config>
commit aaaf7352b429cfe0a777c8492952fc302cc763b7
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Fri May 18 16:05:36 2018 +0100

    Shutdown kit process if connection dies.
    
    Change-Id: I34e627a03a9859ccd3ae9f9925fe6ab080697b72

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index ef22214cc..83becc634 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -2034,6 +2034,12 @@ protected:
             LOG_ERR("Bad or unknown token [" << tokens[0] << "]");
         }
     }
+
+    void onDisconnect() override
+    {
+        LOG_WRN("Kit connection lost without exit arriving from wsd");
+        TerminationFlag = true;
+    }
 };
 
 void documentViewCallback(const int type, const char* payload, void* data)
commit d8ee19c7f601d1f066b8bf8115c5733d47f0f852
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Fri May 18 15:55:31 2018 +0100

    Count kits better for crash testing.
    
    Change-Id: Icf04d4124e2b538886c8568358e94d00baeefeb4

diff --git a/test/httpcrashtest.cpp b/test/httpcrashtest.cpp
index 0f8b1c3fd..29fd0c447 100644
--- a/test/httpcrashtest.cpp
+++ b/test/httpcrashtest.cpp
@@ -243,6 +243,7 @@ void killPids(const std::vector<int> &pids)
 void HTTPCrashTest::killLoKitProcesses()
 {
     killPids(getKitPids());
+    InitialLoolKitCount = 1; // non-intuitive but it will arrive soon.
 }
 
 void HTTPCrashTest::killForkitProcess()
commit 7513203daa9ab36f139f159283050632896ecb0e
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Fri May 18 14:32:26 2018 +0100

    Make PNG tile tests more robust.
    
    Change-Id: Id7afcfe9b29b2d5544e296b13f04c195d35655b5

diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp
index 2231dfa9e..cc23b49c7 100644
--- a/test/TileCacheTests.cpp
+++ b/test/TileCacheTests.cpp
@@ -694,6 +694,10 @@ void TileCacheTests::testLoad12ods()
     {
         CPPUNIT_FAIL(exc.displayText());
     }
+    catch (...)
+    {
+        CPPUNIT_FAIL("Unexpected exception thrown during ods load");
+    }
 }
 
 void TileCacheTests::checkBlackTile(std::stringstream& tile)
@@ -737,7 +741,13 @@ void TileCacheTests::checkBlackTiles(std::shared_ptr<LOOLWebSocket>& socket, con
     const auto req = "tile part=0 width=256 height=256 tileposx=0 tileposy=253440 tilewidth=3840 tileheight=3840";
     sendTextFrame(socket, req);
 
-    const auto tile = getResponseMessage(socket, "tile:", name);
+    const std::vector<char> tile = getResponseMessage(socket, "tile:", name);
+    if (!tile.size())
+    {
+        CPPUNIT_FAIL("No tile returned to checkBlackTiles - failed load ?");
+        return;
+    }
+
     const std::string firstLine = LOOLProtocol::getFirstLine(tile);
 
 #if 0
commit 72760589adeef70e484873eb08729487c7205358
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 18 13:00:16 2018 +0200

    Split close(bool) into close() and terminate().
    
    The bool flag was causing 2 complete separate code paths anyway.
    
    Also remove stop(), calling stop() followed by close() made no
    difference.
    
    Change-Id: Ica4c887b0324390d4e006a26eb4119bd5ab08723

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 2b08c4882..7654bc763 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1599,10 +1599,7 @@ void DocumentBroker::terminateChild(const std::string& closeReason)
     {
         LOG_INF("Terminating child [" << getPid() << "] of doc [" << _docKey << "].");
 
-        // First flag to stop as it might be waiting on our lock
-        // to process some incoming message.
-        _childProcess->stop();
-        _childProcess->close(false);
+        _childProcess->close();
     }
 
     stop(closeReason);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 4d7d0bd6e..92c39b2e5 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -78,63 +78,57 @@ public:
 
     ~ChildProcess()
     {
-        if (_pid > 0)
-        {
-            LOG_DBG("~ChildProcess dtor [" << _pid << "].");
-            close(true);
+        if (_pid <= 0)
+            return;
+
+        LOG_DBG("~ChildProcess dtor [" << _pid << "].");
+        terminate();
+
+        // No need for the socket anymore.
+        _ws.reset();
+        _socket.reset();
 
-            // No need for the socket anymore.
-            _ws.reset();
-            _socket.reset();
-        }
     }
 
     void setDocumentBroker(const std::shared_ptr<DocumentBroker>& docBroker);
     std::shared_ptr<DocumentBroker> getDocumentBroker() const { return _docBroker.lock(); }
 
-    void stop()
+    /// Let the child close a nice way.
+    void close()
     {
-        // Request the child to exit.
+        if (_pid < 0)
+            return;
+
         try
         {
+            LOG_DBG("Closing ChildProcess [" << _pid << "].");
+
+            // Request the child to exit
             if (isAlive())
             {
                 LOG_DBG("Stopping ChildProcess [" << _pid << "]");
                 sendTextFrame("exit");
             }
+
+            // Shutdown the socket.
+            if (_ws)
+                _ws->shutdown();
         }
-        catch (const std::exception&)
+        catch (const std::exception& ex)
         {
-            // Already logged in sendTextFrame.
+            LOG_ERR("Error while closing child process: " << ex.what());
         }
+
+        _pid = -1;
     }
 
-    void close(const bool rude)
+    /// Kill or abandon the child.
+    void terminate()
     {
         if (_pid < 0)
             return;
 
-        try
-        {
-            LOG_DBG("Closing ChildProcess [" << _pid << "].");
-
-            if (!rude)
-            {
-                // First mark to stop the thread so it knows it's intentional.
-                stop();
-
-                // Shutdown the socket.
-                if (_ws)
-                    _ws->shutdown();
-            }
-        }
-        catch (const std::exception& ex)
-        {
-            LOG_ERR("Error while closing child process: " << ex.what());
-        }
-
-        // Kill or abandon the child.
-        if (rude && _pid != -1 && kill(_pid, 0) == 0)
+        if (::kill(_pid, 0) == 0)
         {
             LOG_INF("Killing child [" << _pid << "].");
             if (!SigUtil::killChild(_pid))
@@ -179,7 +173,7 @@ public:
     {
         try
         {
-            return _pid > 1 && _ws && kill(_pid, 0) == 0;
+            return _pid > 1 && _ws && ::kill(_pid, 0) == 0;
         }
         catch (const std::exception&)
         {
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 4c5a7e3f4..4831aea33 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2816,7 +2816,7 @@ int LOOLWSD::innerMain()
     LOG_INF("Requesting child processes to terminate.");
     for (auto& child : NewChildren)
     {
-        child->close(true);
+        child->terminate();
     }
 
 #ifndef KIT_IN_PROCESS
commit 6ba88372b9cee8e2838e70d8342dbfc5dc29bfc2
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu May 17 20:47:37 2018 +0200

    Before we kill the child, check it exists, ie. kill(pid, 0) == 0.
    
    Also warn when anything was left out.
    
    Without this, we leave abandoned children around.
    
    Change-Id: I293a530ffceeb7f6bdc0cc775335c782945de6e7

diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp
index 48899f378..76e7b44f6 100644
--- a/common/SigUtil.cpp
+++ b/common/SigUtil.cpp
@@ -284,6 +284,7 @@ namespace SigUtil
         sigaction(SIGUSR1, &action, nullptr);
     }
 
+    /// Kill the given pid with SIGTERM.  Returns true when the pid does not exist any more.
     bool killChild(const int pid)
     {
         LOG_DBG("Killing PID: " << pid);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 58af3c913..4d7d0bd6e 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -134,10 +134,10 @@ public:
         }
 
         // Kill or abandon the child.
-        if (_pid != -1 && rude && kill(_pid, 0) != 0 && errno != ESRCH)
+        if (rude && _pid != -1 && kill(_pid, 0) == 0)
         {
             LOG_INF("Killing child [" << _pid << "].");
-            if (SigUtil::killChild(_pid))
+            if (!SigUtil::killChild(_pid))
             {
                 LOG_ERR("Cannot terminate lokit [" << _pid << "]. Abandoning.");
             }
commit bcc6a19c6e8bbd66c3a6f2a22d9d799384b73ddf
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Apr 25 17:38:55 2018 +0200

    Paste: Prefer text/rtf mimetype when present.
    
    Change-Id: Id4bad2d6b09b3b14e64059a942a50ce61f8f4ea4

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index ce5afa643..49aa853c6 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1982,48 +1982,51 @@ L.TileLayer = L.GridLayer.extend({
 		if (preferInternal === true) {
 			var pasteString = dataTransfer.getData('text/plain');
 			if (!pasteString) {
-				pasteString = window.clipboardData.getData('Text');
+				pasteString = dataTransfer.getData('Text'); // IE 11
 			}
 
-			if (pasteString === this._selectionTextHash) {
+			if (pasteString && pasteString === this._selectionTextHash) {
 				this._map._socket.sendMessage('uno .uno:Paste');
-				return
+				return;
 			}
 		}
 
-		// handle content
 		var types = dataTransfer.types;
-		var hasHTML = false;
-		for (var t = 0; !hasHTML && t < types.length; t++) {
-			if (types[t] === 'text/html') {
-				hasHTML = true;
-			}
-		}
 
-		var handled = false;
-		for (t = 0; !handled && t < types.length; t++) {
-			var type = types[t];
-			if (type === 'text/html') {
-				this._map._socket.sendMessage('paste mimetype=text/html\n' + dataTransfer.getData(type));
-				handled = true;
-			}
-			else if ((type === 'text/plain' || type ==='Text') && !hasHTML) {
-				this._map._socket.sendMessage('paste mimetype=text/plain;charset=utf-8\n' + dataTransfer.getData(type));
-				handled = true;
-			}
-			else if (type === 'Files') {
+		// first try to transfer images
+		// TODO if we have both Files and a normal mimetype, should we handle
+		// both, or prefer one or the other?
+		for (var t = 0; t < types.length; ++t) {
+			if (types[t] === 'Files') {
 				var files = dataTransfer.files;
-				for (var i = 0; i < files.length; ++i) {
-					var file = files[i];
+				for (var f = 0; f < files.length; ++f) {
+					var file = files[f];
 					if (file.type.match(/image.*/)) {
 						var reader = new FileReader();
 						reader.onload = this._onFileLoadFunc(file);
 						reader.readAsArrayBuffer(file);
-						handled = true;
 					}
 				}
 			}
 		}
+
+		// now try various mime types
+		var mimeTypes = [
+			['text/rtf', 'text/rtf'],
+			['text/html', 'text/html'],
+			['text/plain', 'text/plain;charset=utf-8'],
+			['Text', 'text/plain;charset=utf-8']
+		];
+
+		for (var i = 0; i < mimeTypes.length; ++i) {
+			for (t = 0; t < types.length; ++t) {
+				if (mimeTypes[i][0] === types[t]) {
+					var blob = new Blob(['paste mimetype=' + mimeTypes[i][1] + '\n', dataTransfer.getData(types[t])]);
+					this._map._socket.sendMessage(blob);
+					return;
+				}
+			}
+		}
 	},
 
 	_onFileLoadFunc: function(file) {
commit bba9e5bbfca29b5e71fa2bb258cb7cfb2bb76a99
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Apr 25 16:06:08 2018 +0200

    Paste: Share the code with Drop, to allow rich content pasting.
    
    Change-Id: I4d80421786369388b8a1a094fe7633d525fa3f08

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index c5c8bf413..ce5afa643 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -14,16 +14,6 @@ if (typeof String.prototype.startsWith !== 'function') {
 }
 
 L.Compatibility = {
-	clipboardGet: function (event) {
-		var text = null;
-		if (event.clipboardData) { // Standard
-			text = event.clipboardData.getData('text/plain');
-		}
-		else if (window.clipboardData) { // IE 11
-			text = window.clipboardData.getData('Text');
-		}
-		return text;
-	},
 	clipboardSet: function (event, text) {
 		if (event.clipboardData) { // Standard
 			event.clipboardData.setData('text/plain', text);
@@ -1956,16 +1946,12 @@ L.TileLayer = L.GridLayer.extend({
 	_onPaste: function (e) {
 		e = e.originalEvent;
 		e.preventDefault();
-		var pasteString = L.Compatibility.clipboardGet(e);
-		if (pasteString === 'false' || !pasteString || pasteString === this._selectionTextHash) {
-			// If there is nothing to paste in clipboard, no harm in
-			// issuing a .uno:Paste in case there is something internally copied in the document
-			// or if the content of the clipboard did not change, we surely must do a rich paste
-			// instead of a normal paste
-			this._map._socket.sendMessage('uno .uno:Paste');
+
+		if (e.clipboardData) { // Standard
+			this._dataTransferToDocument(e.clipboardData, /* preferInternal = */ true);
 		}
-		else {
-			this._map._socket.sendMessage('paste mimetype=text/plain;charset=utf-8\n' + pasteString);
+		else if (window.clipboardData) { // IE 11
+			this._dataTransferToDocument(window.clipboardData, /* preferInternal = */ true);
 		}
 	},
 
@@ -1988,8 +1974,25 @@ L.TileLayer = L.GridLayer.extend({
 		e = e.originalEvent;
 		e.preventDefault();
 
+		this._dataTransferToDocument(e.dataTransfer, /* preferInternal = */ false);
+	},
+
+	_dataTransferToDocument: function (dataTransfer, preferInternal) {
+		// for the paste, we might prefer the internal LOK's copy/paste
+		if (preferInternal === true) {
+			var pasteString = dataTransfer.getData('text/plain');
+			if (!pasteString) {
+				pasteString = window.clipboardData.getData('Text');
+			}
+
+			if (pasteString === this._selectionTextHash) {
+				this._map._socket.sendMessage('uno .uno:Paste');
+				return
+			}
+		}
+
 		// handle content
-		var types = e.dataTransfer.types;
+		var types = dataTransfer.types;
 		var hasHTML = false;
 		for (var t = 0; !hasHTML && t < types.length; t++) {
 			if (types[t] === 'text/html') {
@@ -2001,15 +2004,15 @@ L.TileLayer = L.GridLayer.extend({
 		for (t = 0; !handled && t < types.length; t++) {
 			var type = types[t];
 			if (type === 'text/html') {
-				this._map._socket.sendMessage('paste mimetype=text/html\n' + e.dataTransfer.getData(type));
+				this._map._socket.sendMessage('paste mimetype=text/html\n' + dataTransfer.getData(type));
 				handled = true;
 			}
-			else if (type === 'text/plain' && !hasHTML) {
-				this._map._socket.sendMessage('paste mimetype=text/plain;charset=utf-8\n' + e.dataTransfer.getData(type));
+			else if ((type === 'text/plain' || type ==='Text') && !hasHTML) {
+				this._map._socket.sendMessage('paste mimetype=text/plain;charset=utf-8\n' + dataTransfer.getData(type));
 				handled = true;
 			}
 			else if (type === 'Files') {
-				var files = e.dataTransfer.files;
+				var files = dataTransfer.files;
 				for (var i = 0; i < files.length; ++i) {
 					var file = files[i];
 					if (file.type.match(/image.*/)) {
commit 6ac56c88ebbbc11c96ae9b33001237b1604b5c3b
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Sun May 13 12:44:39 2018 +0100

    seccomp: allow socket shutdown in kit process.
    
    Change-Id: Ie11f5eb278bcba8dcf13d6f095de2ffd6d23fcb3

diff --git a/common/Seccomp.cpp b/common/Seccomp.cpp
index e49c4d5e8..fea452506 100644
--- a/common/Seccomp.cpp
+++ b/common/Seccomp.cpp
@@ -133,7 +133,6 @@ bool lockdown(Type type)
         KILL_SYSCALL(getitimer),
         KILL_SYSCALL(setitimer),
         KILL_SYSCALL(sendfile),
-        KILL_SYSCALL(shutdown),
         KILL_SYSCALL(listen),  // server sockets
         KILL_SYSCALL(accept),  // server sockets
 #if 0
commit bb508aedbce646dbff3da8c5c2b9b92d56c88ded
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 11 19:15:16 2018 +0200

    Post the message to the poll thread.
    
    Change-Id: Ibd28090a420b5396b64fdfe676bef8cf06991116

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 2bb229c62..ef22214cc 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -694,6 +694,7 @@ public:
              const std::string& docId,
              const std::string& url,
              std::shared_ptr<TileQueue> tileQueue,
+             SocketPoll& socketPoll,
              const std::shared_ptr<WebSocketHandler>& websocketHandler)
       : _loKit(loKit),
         _jailId(jailId),
@@ -701,6 +702,7 @@ public:
         _docId(docId),
         _url(url),
         _tileQueue(std::move(tileQueue)),
+        _socketPoll(socketPoll),
         _websocketHandler(websocketHandler),
         _docPassword(""),
         _haveDocPassword(false),
@@ -735,6 +737,20 @@ public:
 
     const std::string& getUrl() const { return _url; }
 
+    /// Post the message in the correct thread.
+    bool postMessage(const std::shared_ptr<std::vector<char>>& message, const WSOpCode code) const
+    {
+        LOG_TRC("postMessage called with: " << getAbbreviatedMessage(message->data(), message->size()));
+        if (!_websocketHandler)
+        {
+            LOG_ERR("Child Doc: Bad socket while sending [" << getAbbreviatedMessage(message->data(), message->size()) << "].");
+            return false;
+        }
+
+        _socketPoll.addCallback([=] { _websocketHandler->sendMessage(message->data(), message->size(), code); });
+        return true;
+    }
+
     bool createSession(const std::string& sessionId)
     {
         std::unique_lock<std::mutex> lock(_mutex);
@@ -849,9 +865,8 @@ public:
         LOG_INF("setDocumentPassword returned");
     }
 
-    void renderTile(const std::vector<std::string>& tokens, const std::shared_ptr<WebSocketHandler>& websocketHandler)
+    void renderTile(const std::vector<std::string>& tokens)
     {
-        assert(websocketHandler && "Expected a non-null websocket.");
         TileDesc tile = TileDesc::parse(tokens);
 
         size_t pixmapDataSize = 4 * tile.getWidth() * tile.getHeight();
@@ -905,12 +920,12 @@ public:
         if (_docWatermark)
             _docWatermark->blending(pixmap.data(), 0, 0, pixelWidth, pixelHeight, pixelWidth, pixelHeight, mode);
 
-        std::vector<char> output;
-        output.reserve(response.size() + pixmapDataSize);
-        output.resize(response.size());
-        std::memcpy(output.data(), response.data(), response.size());
+        std::shared_ptr<std::vector<char>> output = std::make_shared<std::vector<char>>();
+        output->reserve(response.size() + pixmapDataSize);
+        output->resize(response.size());
+        std::memcpy(output->data(), response.data(), response.size());
 
-        if (!_pngCache.encodeBufferToPNG(pixmap.data(), tile.getWidth(), tile.getHeight(), output, mode, hash, wid, oldWireId))
+        if (!_pngCache.encodeBufferToPNG(pixmap.data(), tile.getWidth(), tile.getHeight(), *output, mode, hash, wid, oldWireId))
         {
             //FIXME: Return error.
             //sendTextFrame("error: cmd=tile kind=failure");
@@ -919,13 +934,12 @@ public:
             return;
         }
 
-        LOG_TRC("Sending render-tile response (" << output.size() << " bytes) for: " << response);
-        websocketHandler->sendMessage(output.data(), output.size(), WSOpCode::Binary);
+        LOG_TRC("Sending render-tile response (" << output->size() << " bytes) for: " << response);
+        postMessage(output, WSOpCode::Binary);
     }
 
-    void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<WebSocketHandler>& websocketHandler)
+    void renderCombinedTiles(const std::vector<std::string>& tokens)
     {
-        assert(websocketHandler && "Expected a non-null websocket.");
         TileCombined tileCombined = TileCombined::parse(tokens);
         auto& tiles = tileCombined.getTiles();
 
@@ -1044,12 +1058,12 @@ public:
         const auto tileMsg = ADD_DEBUG_RENDERID(tileCombined.serialize("tilecombine:")) + "\n";
         LOG_TRC("Sending back painted tiles for " << tileMsg);
 
-        std::vector<char> response;
-        response.resize(tileMsg.size() + output.size());
-        std::copy(tileMsg.begin(), tileMsg.end(), response.begin());
-        std::copy(output.begin(), output.end(), response.begin() + tileMsg.size());
+        std::shared_ptr<std::vector<char>> response = std::make_shared<std::vector<char>>();
+        response->resize(tileMsg.size() + output.size());
+        std::copy(tileMsg.begin(), tileMsg.end(), response->begin());
+        std::copy(output.begin(), output.end(), response->begin() + tileMsg.size());
 
-        websocketHandler->sendMessage(response.data(), response.size(), WSOpCode::Binary);
+        postMessage(response, WSOpCode::Binary);
     }
 
     bool sendTextFrame(const std::string& message)
@@ -1061,14 +1075,11 @@ public:
     {
         try
         {
-            if (!_websocketHandler)
-            {
-                LOG_ERR("Child Doc: Bad socket while sending [" << getAbbreviatedMessage(buffer, length) << "].");
-                return false;
-            }
+            std::shared_ptr<std::vector<char>> message = std::make_shared<std::vector<char>>();
+            message->resize(length);
+            std::memcpy(message->data(), buffer, length);
 
-            _websocketHandler->sendMessage(buffer, length, opCode);
-            return true;
+            return postMessage(message, opCode);
         }
         catch (const Exception& exc)
         {
@@ -1768,11 +1779,11 @@ private:
 
                 if (tokens[0] == "tile")
                 {
-                    renderTile(tokens, _websocketHandler);
+                    renderTile(tokens);
                 }
                 else if (tokens[0] == "tilecombine")
                 {
-                    renderCombinedTiles(tokens, _websocketHandler);
+                    renderCombinedTiles(tokens);
                 }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
@@ -1895,6 +1906,7 @@ private:
 
     std::shared_ptr<lok::Document> _loKitDocument;
     std::shared_ptr<TileQueue> _tileQueue;
+    SocketPoll& _socketPoll;
     std::shared_ptr<WebSocketHandler> _websocketHandler;
     PngCache _pngCache;
 
@@ -1937,14 +1949,16 @@ class KitWebSocketHandler final : public WebSocketHandler, public std::enable_sh
     std::string _socketName;
     std::shared_ptr<lok::Office> _loKit;
     std::string _jailId;
+    SocketPoll& _socketPoll;
 
 public:
-    KitWebSocketHandler(const std::string& socketName, const std::shared_ptr<lok::Office>& loKit, const std::string& jailId) :
+    KitWebSocketHandler(const std::string& socketName, const std::shared_ptr<lok::Office>& loKit, const std::string& jailId, SocketPoll& socketPoll) :
         WebSocketHandler(/* isClient = */ true),
         _queue(std::make_shared<TileQueue>()),
         _socketName(socketName),
         _loKit(loKit),
-        _jailId(jailId)
+        _jailId(jailId),
+        _socketPoll(socketPoll)
     {
     }
 
@@ -1980,7 +1994,7 @@ protected:
 
             if (!document)
             {
-                document = std::make_shared<Document>(_loKit, _jailId, docKey, docId, url, _queue, shared_from_this());
+                document = std::make_shared<Document>(_loKit, _jailId, docKey, docId, url, _queue, _socketPoll, shared_from_this());
             }
 
             // Validate and create session.
@@ -2271,7 +2285,7 @@ void lokit_main(const std::string& childRoot,
 
         SocketPoll mainKit("kit");
 
-        mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId));
+        mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId, mainKit));
         LOG_INF("New kit client websocket inserted.");
 
         while (!TerminationFlag)
commit 8eb60a33eb1afcdae377c87e1db656f80a4ccf29
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed May 9 20:25:58 2018 +0200

    Use std::shared_ptr consistently.
    
    Change-Id: I6bf3ff7de47010fd78fab26a5a318bde21c1f153

diff --git a/net/Socket.hpp b/net/Socket.hpp
index a9ace0966..a3c194d83 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -704,11 +704,11 @@ protected:
 class StreamSocket : public Socket, public std::enable_shared_from_this<StreamSocket>
 {
 public:
-    /// Create a StreamSocket from native FD and take ownership of handler instance.
+    /// Create a StreamSocket from native FD.
     StreamSocket(const int fd, bool /* isClient */,
-                 std::shared_ptr<SocketHandlerInterface> socketHandler) :
+                 const std::shared_ptr<SocketHandlerInterface> socketHandler) :
         Socket(fd),
-        _socketHandler(std::move(socketHandler)),
+        _socketHandler(socketHandler),
         _bytesSent(0),
         _bytesRecvd(0),
         _wsState(WSState::HTTP),
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index c0628b456..1294a51ec 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -130,7 +130,7 @@ public:
         if (len == 0)
             return false; // avoid logging.
 
-        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << len << " bytes.");
+        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << len << " bytes: " << std::string(socket->_inBuffer.data(), socket->_inBuffer.size()));
 
         if (len < 2) // partial read
             return false;
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index 202bff3f7..777a2c82e 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -205,9 +205,9 @@ public:
     {
 #if ENABLE_SSL
         if (_isSSL)
-            return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+            return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::make_shared<ClientRequestDispatcher>());
 #endif
-        return StreamSocket::create<StreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+        return StreamSocket::create<StreamSocket>(physicalFd, false, std::make_shared<ClientRequestDispatcher>());
     }
 };
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 1f701e19a..4c5a7e3f4 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2394,8 +2394,7 @@ class PlainSocketFactory final : public SocketFactory
 
         std::shared_ptr<Socket> socket =
             StreamSocket::create<StreamSocket>(
-                fd, false, std::unique_ptr<SocketHandlerInterface>{
-                    new ClientRequestDispatcher });
+                fd, false, std::make_shared<ClientRequestDispatcher>());
 
         return socket;
     }
@@ -2412,8 +2411,7 @@ class SslSocketFactory final : public SocketFactory
             fd = Delay::create(SimulatedLatencyMs, physicalFd);
 
         return StreamSocket::create<SslStreamSocket>(
-            fd, false, std::unique_ptr<SocketHandlerInterface>{
-                new ClientRequestDispatcher });
+            fd, false, std::make_shared<ClientRequestDispatcher>());
     }
 };
 #endif
@@ -2423,7 +2421,7 @@ class PrisonerSocketFactory final : public SocketFactory
     std::shared_ptr<Socket> create(const int fd) override
     {
         // No local delay.
-        return StreamSocket::create<StreamSocket>(fd, false, std::unique_ptr<SocketHandlerInterface>{ new PrisonerRequestDispatcher });
+        return StreamSocket::create<StreamSocket>(fd, false, std::make_shared<PrisonerRequestDispatcher>());
     }
 };
 
commit 7158b2eddce4cdf236032cee194a03d7f8c086e8
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Mon May 7 15:09:40 2018 +0200

    Use correct path in the client websockets.
    
    Change-Id: Ie0bf6646ff3f6e6cf99b505143a416c86a3a33b8

diff --git a/common/Common.hpp b/common/Common.hpp
index b58c2ca75..978ef2c18 100644
--- a/common/Common.hpp
+++ b/common/Common.hpp
@@ -29,10 +29,10 @@ constexpr long READ_BUFFER_SIZE = 64 * 1024;
 /// or as intentionally flooding the server.
 constexpr int MAX_MESSAGE_SIZE = 2 * 1024 * READ_BUFFER_SIZE;
 
-constexpr auto JAILED_DOCUMENT_ROOT = "/user/docs/";
-constexpr auto CHILD_URI = "/loolws/child?";
-constexpr auto NEW_CHILD_URI = "/loolws/newchild?";
-constexpr auto LO_JAIL_SUBPATH = "lo";
+constexpr const char JAILED_DOCUMENT_ROOT[] = "/user/docs/";
+constexpr const char CHILD_URI[] = "/loolws/child?";
+constexpr const char NEW_CHILD_URI[] = "/loolws/newchild";
+constexpr const char LO_JAIL_SUBPATH[] = "lo";
 
 /// The HTTP response User-Agent.
 constexpr auto HTTP_AGENT_STRING = "LOOLWSD HTTP Agent " LOOLWSD_VERSION;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index d94166806..2bb229c62 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -2253,25 +2253,24 @@ void lokit_main(const std::string& childRoot,
 
         static const std::string pid = std::to_string(Process::id());
 
-        std::string requestUrl = NEW_CHILD_URI;
-        requestUrl += "pid=" + pid + "&jailid=" + jailId;
+        Poco::URI uri("ws://127.0.0.1");
+        uri.setPort(MasterPortNumber);
+        uri.setPath(NEW_CHILD_URI);
+        uri.addQueryParameter("pid", std::to_string(Process::id()));
+        uri.addQueryParameter("jailid", jailId);
+
         if (queryVersion)
         {
             char* versionInfo = loKit->getVersionInfo();
             std::string versionString(versionInfo);
             if (displayVersion)
                 std::cout << "office version details: " << versionString << std::endl;
-            std::string encodedVersionStr;
-            URI::encode(versionString, "", encodedVersionStr);
-            requestUrl += "&version=" + encodedVersionStr;
+            uri.addQueryParameter("version", versionString);
             free(versionInfo);
         }
 
         SocketPoll mainKit("kit");
 
-        Poco::URI uri("ws://127.0.0.1");
-        uri.setPort(MasterPortNumber);
-
         mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId));
         LOG_INF("New kit client websocket inserted.");
 
diff --git a/net/Socket.cpp b/net/Socket.cpp
index c7c265980..f9915c773 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -185,7 +185,7 @@ void SocketPoll::insertNewWebSocketSync(const Poco::URI &uri, const std::shared_
                         // send Sec-WebSocket-Key: <hmm> ... Sec-WebSocket-Protocol: chat, Sec-WebSocket-Version: 13
 
                         std::ostringstream oss;
-                        oss << "GET " << uri.getHost() << " HTTP/1.1\r\n"
+                        oss << "GET " << uri.getPathAndQuery() << " HTTP/1.1\r\n"
                             "Connection:Upgrade\r\n"
                             "User-Foo: Adminbits\r\n"
                             "Sec-WebSocket-Key: GAcwqP21iVOY2yKefQ64c0yVN5M=\r\n"
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index cccdf1a71..1f701e19a 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1613,14 +1613,15 @@ private:
                 return;
 
             LOG_TRC("Child connection with URI [" << request.getURI() << "].");
-            if (request.getURI().find(NEW_CHILD_URI) != 0)
+            Poco::URI requestURI(request.getURI());
+            if (requestURI.getPath() != NEW_CHILD_URI)
             {
                 LOG_ERR("Invalid incoming URI.");
                 return;
             }
 
             // New Child is spawned.
-            const auto params = Poco::URI(request.getURI()).getQueryParameters();
+            const Poco::URI::QueryParameters params = requestURI.getQueryParameters();
             Poco::Process::PID pid = -1;
             std::string jailId;
             for (const auto& param : params)
commit d2b0465fa51dab381ed12115870e0696d92dc92e
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 4 18:47:33 2018 +0200

    Move the functionality from connectToMonitor() to SocketPoll.
    
    Change-Id: Iab2ac09638323f5e59f7a2ea0d880f52989ad64d

diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index d8ae37b10..b1cd90133 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -70,7 +70,7 @@ public:
 
     virtual std::shared_ptr<TileQueue>& getTileQueue() = 0;
 
-    virtual bool sendFrame(const char* buffer, int length, int flags = Poco::Net::WebSocket::FRAME_TEXT) = 0;
+    virtual bool sendFrame(const char* buffer, int length, WSOpCode opCode = WSOpCode::Text) = 0;
 };
 
 struct RecordedEvent
@@ -160,15 +160,15 @@ public:
     bool sendTextFrame(const char* buffer, int length) override
     {
         const auto msg = "client-" + getId() + ' ' + std::string(buffer, length);
-        const auto lock = getLock();
-        return _docManager.sendFrame(msg.data(), msg.size(), Poco::Net::WebSocket::FRAME_TEXT);
+        const std::unique_lock<std::mutex> lock = getLock();
+        return _docManager.sendFrame(msg.data(), msg.size(), WSOpCode::Text);
     }
 
     bool sendBinaryFrame(const char* buffer, int length) override
     {
         const auto msg = "client-" + getId() + ' ' + std::string(buffer, length);
-        const auto lock = getLock();
-        return _docManager.sendFrame(msg.data(), msg.size(), Poco::Net::WebSocket::FRAME_BINARY);
+        const std::unique_lock<std::mutex> lock = getLock();
+        return _docManager.sendFrame(msg.data(), msg.size(), WSOpCode::Binary);
     }
 
     using Session::sendTextFrame;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 9cfce9b93..d94166806 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -920,7 +920,7 @@ public:
         }
 
         LOG_TRC("Sending render-tile response (" << output.size() << " bytes) for: " << response);
-        websocketHandler->sendMessage(output.data(), output.size(), WebSocket::FRAME_BINARY);
+        websocketHandler->sendMessage(output.data(), output.size(), WSOpCode::Binary);
     }
 
     void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<WebSocketHandler>& websocketHandler)
@@ -1049,7 +1049,7 @@ public:
         std::copy(tileMsg.begin(), tileMsg.end(), response.begin());
         std::copy(output.begin(), output.end(), response.begin() + tileMsg.size());
 
-        websocketHandler->sendMessage(response.data(), response.size(), WebSocket::FRAME_BINARY);
+        websocketHandler->sendMessage(response.data(), response.size(), WSOpCode::Binary);
     }
 
     bool sendTextFrame(const std::string& message)
@@ -1057,7 +1057,7 @@ public:
         return sendFrame(message.data(), message.size());
     }
 
-    bool sendFrame(const char* buffer, int length, int flags = Poco::Net::WebSocket::FRAME_TEXT) override
+    bool sendFrame(const char* buffer, int length, WSOpCode opCode = WSOpCode::Text) override
     {
         try
         {
@@ -1067,7 +1067,7 @@ public:
                 return false;
             }
 
-            _websocketHandler->sendMessage(buffer, length, flags);
+            _websocketHandler->sendMessage(buffer, length, opCode);
             return true;
         }
         catch (const Exception& exc)
@@ -2269,7 +2269,7 @@ void lokit_main(const std::string& childRoot,
 
         SocketPoll mainKit("kit");
 
-        const Poco::URI uri("ws://127.0.0.1");
+        Poco::URI uri("ws://127.0.0.1");
         uri.setPort(MasterPortNumber);
 
         mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId));
diff --git a/net/Socket.cpp b/net/Socket.cpp
index 96ad7f7c4..c7c265980 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -20,10 +20,12 @@
 #include <Poco/MemoryStream.h>
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPResponse.h>
+#include <Poco/URI.h>
 
 #include "SigUtil.hpp"
 #include "Socket.hpp"
 #include "ServerSocket.hpp"
+#include "SslSocket.hpp"
 #include "WebSocketHandler.hpp"
 
 int SocketPoll::DefaultPollTimeoutMs = 5000;
@@ -126,6 +128,98 @@ void SocketPoll::wakeupWorld()
         wakeup(fd);
 }
 
+void SocketPoll::insertNewWebSocketSync(const Poco::URI &uri, const std::shared_ptr<SocketHandlerInterface>& websocketHandler)
+{
+    LOG_INF("Connecting to " << uri.getHost() << " : " << uri.getPort() << " : " << uri.getPath());
+
+    // FIXME: put this in a ClientSocket class ?
+    // FIXME: store the address there - and ... (so on) ...
+    struct addrinfo* ainfo = nullptr;
+    struct addrinfo hints;
+    std::memset(&hints, 0, sizeof(hints));
+    int rc = getaddrinfo(uri.getHost().c_str(),
+                         std::to_string(uri.getPort()).c_str(),
+                         &hints, &ainfo);
+    std::string canonicalName;
+    bool isSSL = uri.getScheme() != "ws";
+#if !ENABLE_SSL
+    if (isSSL)
+    {
+        LOG_ERR("Error: wss for client websocket requested but SSL not compiled in.");
+        return;
+    }
+#endif
+
+    if (!rc && ainfo)
+    {
+        for (struct addrinfo* ai = ainfo; ai; ai = ai->ai_next)
+        {
+            if (ai->ai_canonname)
+                canonicalName = ai->ai_canonname;
+
+            if (ai->ai_addrlen && ai->ai_addr)
+            {
+                int fd = socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
+                int res = connect(fd, ai->ai_addr, ai->ai_addrlen);
+                // FIXME: SSL sockets presumably need some setup, checking etc. and ... =)
+                if (fd < 0 || (res < 0 && errno != EINPROGRESS))
+                {
+                    LOG_ERR("Failed to connect to " << uri.getHost());
+                    close(fd);
+                }
+                else
+                {
+                    std::shared_ptr<StreamSocket> socket;
+#if ENABLE_SSL
+                    if (isSSL)
+                        socket = StreamSocket::create<SslStreamSocket>(fd, true, websocketHandler);
+#endif
+                    if (!socket && !isSSL)
+                        socket = StreamSocket::create<StreamSocket>(fd, true, websocketHandler);
+
+                    if (socket)
+                    {
+                        LOG_DBG("Connected to client websocket " << uri.getHost() << " #" << socket->getFD());
+
+                        // cf. WebSocketHandler::upgradeToWebSocket (?)
+                        // send Sec-WebSocket-Key: <hmm> ... Sec-WebSocket-Protocol: chat, Sec-WebSocket-Version: 13
+
+                        std::ostringstream oss;
+                        oss << "GET " << uri.getHost() << " HTTP/1.1\r\n"
+                            "Connection:Upgrade\r\n"
+                            "User-Foo: Adminbits\r\n"
+                            "Sec-WebSocket-Key: GAcwqP21iVOY2yKefQ64c0yVN5M=\r\n"
+                            "Upgrade:websocket\r\n"
+                            "Accept-Encoding:gzip, deflate, br\r\n"
+                            "Accept-Language:en\r\n"
+                            "Cache-Control:no-cache\r\n"
+                            "Pragma:no-cache\r\n"
+                            "Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits\r\n"
+                            "Sec-WebSocket-Key:fxTaWTEMVhq1PkWsMoLxGw==\r\n"
+                            "Sec-WebSocket-Version:13\r\n"
+                            "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                            "\r\n";
+                        socket->send(oss.str());
+                        websocketHandler->onConnect(socket);
+                        insertNewSocket(socket);
+                    }
+                    else
+                    {
+                        LOG_ERR("Failed to allocate socket for client websocket " << uri.getHost());
+                        close(fd);
+                    }
+
+                    break;
+                }
+            }
+        }
+
+        freeaddrinfo(ainfo);
+    }
+    else
+        LOG_ERR("Failed to lookup client websocket host '" << uri.getHost() << "' skipping");
+}
+
 void ServerSocket::dumpState(std::ostream& os)
 {
     os << "\t" << getFD() << "\t<accept>\n";
diff --git a/net/Socket.hpp b/net/Socket.hpp
index d0c03071c..a9ace0966 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -46,6 +46,7 @@ namespace Poco
         class HTTPRequest;
         class HTTPResponse;
     }
+    class URI;
 }
 
 class Socket;
@@ -307,6 +308,41 @@ private:
     std::thread::id _owner;
 };
 
+class StreamSocket;
+
+/// Interface that handles the actual incoming message.
+class SocketHandlerInterface
+{
+public:
+    virtual ~SocketHandlerInterface() {}
+    /// Called when the socket is newly created to
+    /// set the socket associated with this ResponseClient.
+    /// Will be called exactly once.
+    virtual void onConnect(const std::shared_ptr<StreamSocket>& socket) = 0;
+
+    /// Called after successful socket reads.
+    virtual void handleIncomingMessage(SocketDisposition &disposition) = 0;
+
+    /// Prepare our poll record; adjust @timeoutMaxMs downwards
+    /// for timeouts, based on current time @now.
+    /// @returns POLLIN and POLLOUT if output is expected.
+    virtual int getPollEvents(std::chrono::steady_clock::time_point now,
+                              int &timeoutMaxMs) = 0;
+
+    /// Do we need to handle a timeout ?
+    virtual void checkTimeout(std::chrono::steady_clock::time_point /* now */) {}
+
+    /// Do some of the queued writing.
+    virtual void performWrites() = 0;
+
+    /// Called when the is disconnected and will be destroyed.
+    /// Will be called exactly once.
+    virtual void onDisconnect() {}
+
+    /// Append pretty printed internal state to a line
+    virtual void dumpState(std::ostream& os) { os << "\n"; }
+};
+
 /// Handles non-blocking socket event polling.
 /// Only polls on N-Sockets and invokes callback and
 /// doesn't manage buffers or client data.
@@ -534,6 +570,10 @@ public:
         }
     }
 
+    /// Inserts a new websocket to be polled.
+    /// NOTE: The DNS lookup is synchronous.
+    void insertNewWebSocketSync(const Poco::URI &uri, const std::shared_ptr<SocketHandlerInterface>& websocketHandler);
+
     typedef std::function<void()> CallbackFn;
 
     /// Add a callback to be invoked in the polling thread
@@ -660,41 +700,6 @@ protected:
     std::thread::id _owner;
 };
 
-class StreamSocket;
-
-/// Interface that handles the actual incoming message.
-class SocketHandlerInterface
-{
-public:
-    virtual ~SocketHandlerInterface() {}
-    /// Called when the socket is newly created to
-    /// set the socket associated with this ResponseClient.
-    /// Will be called exactly once.
-    virtual void onConnect(const std::shared_ptr<StreamSocket>& socket) = 0;
-
-    /// Called after successful socket reads.
-    virtual void handleIncomingMessage(SocketDisposition &disposition) = 0;
-
-    /// Prepare our poll record; adjust @timeoutMaxMs downwards
-    /// for timeouts, based on current time @now.
-    /// @returns POLLIN and POLLOUT if output is expected.
-    virtual int getPollEvents(std::chrono::steady_clock::time_point now,
-                              int &timeoutMaxMs) = 0;
-
-    /// Do we need to handle a timeout ?
-    virtual void checkTimeout(std::chrono::steady_clock::time_point /* now */) {}
-
-    /// Do some of the queued writing.
-    virtual void performWrites() = 0;
-
-    /// Called when the is disconnected and will be destroyed.
-    /// Will be called exactly once.
-    virtual void onDisconnect() {}
-
-    /// Append pretty printed internal state to a line
-    virtual void dumpState(std::ostream& os) { os << "\n"; }
-};
-
 /// A plain, non-blocking, data streaming socket.
 class StreamSocket : public Socket, public std::enable_shared_from_this<StreamSocket>
 {
diff --git a/test/Makefile.am b/test/Makefile.am
index 81346fe01..575a78ca5 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -25,6 +25,10 @@ noinst_LTLIBRARIES = \
 MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy
 AM_LDFLAGS = -pthread -module $(MAGIC_TO_FORCE_SHLIB_CREATION) $(ZLIB_LIBS)
 
+if ENABLE_SSL
+AM_LDFLAGS += -lssl -lcrypto
+endif
+
 # We work around some of the mess of using the same sources both on
 # the server side and here in unit tests with conditional compilation
 # based on BUILDING_TESTS
@@ -47,6 +51,10 @@ wsd_sources = \
             ../common/Unit.cpp \
             ../net/Socket.cpp
 
+if ENABLE_SSL
+wsd_sources += ../net/Ssl.cpp
+endif
+
 test_base_source = \
 	TileQueueTests.cpp \
 	WhiteBoxTests.cpp \
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index ba8b1603b..a05d34f89 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -387,7 +387,7 @@ public:
         return _tileQueue;
     }
 
-    bool sendFrame(const char* /*buffer*/, int /*length*/, int /*flags*/) override
+    bool sendFrame(const char* /*buffer*/, int /*length*/, WSOpCode /*opCode*/) override
     {
         return true;
     }
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index c34a98aa9..0c195e419 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -658,95 +658,7 @@ public:
 
 void Admin::connectToMonitor(const Poco::URI &uri)
 {
-    LOG_INF("Connecting to monitor " << uri.getHost() << " : " << uri.getPort() << " : " << uri.getPath());
-
-    // FIXME: put this in a ClientSocket class ?
-    // FIXME: store the address there - and ... (so on) ...
-    struct addrinfo* ainfo = nullptr;
-    struct addrinfo hints;
-    std::memset(&hints, 0, sizeof(hints));
-    int rc = getaddrinfo(uri.getHost().c_str(),
-                         std::to_string(uri.getPort()).c_str(),
-                         &hints, &ainfo);
-    std::string canonicalName;
-    bool isSSL = uri.getScheme() != "ws";
-#if !ENABLE_SSL
-    if (isSSL)
-    {
-        LOG_ERR("Error: wss for monitor requested but SSL not compiled in.");
-        return;
-    }
-#endif
-
-    if (!rc && ainfo)
-    {
-        for (struct addrinfo* ai = ainfo; ai; ai = ai->ai_next)
-        {
-            if (ai->ai_canonname)
-                canonicalName = ai->ai_canonname;
-
-            if (ai->ai_addrlen && ai->ai_addr)
-            {
-                int fd = socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
-                int res = connect(fd, ai->ai_addr, ai->ai_addrlen);
-                // FIXME: SSL sockets presumably need some setup, checking etc. and ... =)
-                if (fd < 0 || (res < 0 && errno != EINPROGRESS))
-                {
-                    LOG_ERR("Failed to connect to " << uri.getHost());
-                    close(fd);
-                }
-                else
-                {
-                    std::shared_ptr<StreamSocket> socket;
-                    std::shared_ptr<SocketHandlerInterface> handler = std::make_shared<MonitorSocketHandler>(this);
-#if ENABLE_SSL
-                    if (isSSL)
-                        socket = StreamSocket::create<SslStreamSocket>(fd, true, handler);
-#endif
-                    if (!socket && !isSSL)
-                        socket = StreamSocket::create<StreamSocket>(fd, true, handler);
-
-                    if (socket)
-                    {
-                        LOG_DBG("Connected to monitor " << uri.getHost() << " #" << socket->getFD());
-
-                        // cf. WebSocketHandler::upgradeToWebSocket (?)
-                        // send Sec-WebSocket-Key: <hmm> ... Sec-WebSocket-Protocol: chat, Sec-WebSocket-Version: 13
-
-                        std::ostringstream oss;
-                        oss << "GET " << uri.getHost() << " HTTP/1.1\r\n"
-                            "Connection:Upgrade\r\n"
-                            "User-Foo: Adminbits\r\n"
-                            "Sec-WebSocket-Key: GAcwqP21iVOY2yKefQ64c0yVN5M=\r\n"
-                            "Upgrade:websocket\r\n"
-                            "Accept-Encoding:gzip, deflate, br\r\n"
-                            "Accept-Language:en\r\n"
-                            "Cache-Control:no-cache\r\n"
-                            "Pragma:no-cache\r\n"
-                            "Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits\r\n"
-                            "Sec-WebSocket-Key:fxTaWTEMVhq1PkWsMoLxGw==\r\n"
-                            "Sec-WebSocket-Version:13\r\n"
-                            "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
-                            "\r\n";
-                        socket->send(oss.str());
-                        handler->onConnect(socket);
-                        insertNewSocket(socket);
-                    }
-                    else
-                    {
-                        LOG_ERR("Failed to allocate socket for monitor " << uri.getHost());
-                        close(fd);
-                    }
-
-                    break;
-                }
-            }
-        }
-
-        freeaddrinfo(ainfo);
-    }
-    else
-        LOG_ERR("Failed to lookup monitor host '" << uri.getHost() << "' skipping");
+    insertNewWebSocketSync(uri, std::make_shared<MonitorSocketHandler>(this));
 }
 
 void Admin::start()
commit 16e2998111d0b6b0b9fa2d919c707f2623c94e34
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 4 18:32:42 2018 +0200

    Change Kit to use the new client websocket code.
    
    Change-Id: Ib4e62ea618da5bd8992b51165b0d7ee955c61637

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 779deb151..9cfce9b93 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -57,18 +57,17 @@
 #include "IoUtil.hpp"
 #include "KitHelper.hpp"
 #include "Kit.hpp"
-#include "Protocol.hpp"
-#include "LOOLWebSocket.hpp"
-#include "Log.hpp"
-#include "Png.hpp"
-#include "Rectangle.hpp"
-#include "TileDesc.hpp"
-#include "Unit.hpp"
-#include "UserMessages.hpp"
-#include "Util.hpp"
-
-#include "common/SigUtil.hpp"
-#include "common/Seccomp.hpp"
+#include <Protocol.hpp>
+#include <Log.hpp>
+#include <Png.hpp>
+#include <Rectangle.hpp>
+#include <TileDesc.hpp>
+#include <Unit.hpp>
+#include <UserMessages.hpp>
+#include <Util.hpp>
+
+#include <common/SigUtil.hpp>
+#include <common/Seccomp.hpp>
 
 #ifdef FUZZER
 #include <kit/DummyLibreOfficeKit.hpp>
@@ -695,14 +694,14 @@ public:
              const std::string& docId,
              const std::string& url,
              std::shared_ptr<TileQueue> tileQueue,
-             const std::shared_ptr<LOOLWebSocket>& ws)
+             const std::shared_ptr<WebSocketHandler>& websocketHandler)
       : _loKit(loKit),
         _jailId(jailId),
         _docKey(docKey),
         _docId(docId),
         _url(url),
         _tileQueue(std::move(tileQueue)),
-        _ws(ws),
+        _websocketHandler(websocketHandler),
         _docPassword(""),
         _haveDocPassword(false),
         _isDocPasswordProtected(false),
@@ -850,10 +849,10 @@ public:
         LOG_INF("setDocumentPassword returned");
     }
 
-    void renderTile(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    void renderTile(const std::vector<std::string>& tokens, const std::shared_ptr<WebSocketHandler>& websocketHandler)
     {
-        assert(ws && "Expected a non-null websocket.");
-        auto tile = TileDesc::parse(tokens);
+        assert(websocketHandler && "Expected a non-null websocket.");
+        TileDesc tile = TileDesc::parse(tokens);
 
         size_t pixmapDataSize = 4 * tile.getWidth() * tile.getHeight();
         std::vector<unsigned char> pixmap;
@@ -921,13 +920,13 @@ public:
         }
 
         LOG_TRC("Sending render-tile response (" << output.size() << " bytes) for: " << response);
-        ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
+        websocketHandler->sendMessage(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
-    void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<WebSocketHandler>& websocketHandler)
     {
-        assert(ws && "Expected a non-null websocket.");
-        auto tileCombined = TileCombined::parse(tokens);
+        assert(websocketHandler && "Expected a non-null websocket.");
+        TileCombined tileCombined = TileCombined::parse(tokens);
         auto& tiles = tileCombined.getTiles();
 
         Util::Rectangle renderArea;
@@ -1050,7 +1049,7 @@ public:
         std::copy(tileMsg.begin(), tileMsg.end(), response.begin());
         std::copy(output.begin(), output.end(), response.begin() + tileMsg.size());
 
-        ws->sendFrame(response.data(), response.size(), WebSocket::FRAME_BINARY);
+        websocketHandler->sendMessage(response.data(), response.size(), WebSocket::FRAME_BINARY);
     }
 
     bool sendTextFrame(const std::string& message)
@@ -1062,13 +1061,13 @@ public:
     {
         try
         {
-            if (!_ws || _ws->poll(Poco::Timespan(0), Poco::Net::Socket::SelectMode::SELECT_ERROR))
+            if (!_websocketHandler)
             {
                 LOG_ERR("Child Doc: Bad socket while sending [" << getAbbreviatedMessage(buffer, length) << "].");
                 return false;
             }
 
-            _ws->sendFrame(buffer, length, flags);
+            _websocketHandler->sendMessage(buffer, length, flags);
             return true;
         }
         catch (const Exception& exc)
@@ -1769,11 +1768,11 @@ private:
 
                 if (tokens[0] == "tile")
                 {
-                    renderTile(tokens, _ws);
+                    renderTile(tokens, _websocketHandler);
                 }
                 else if (tokens[0] == "tilecombine")
                 {
-                    renderCombinedTiles(tokens, _ws);
+                    renderCombinedTiles(tokens, _websocketHandler);
                 }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
@@ -1896,7 +1895,7 @@ private:
 
     std::shared_ptr<lok::Document> _loKitDocument;
     std::shared_ptr<TileQueue> _tileQueue;
-    std::shared_ptr<LOOLWebSocket> _ws;
+    std::shared_ptr<WebSocketHandler> _websocketHandler;
     PngCache _pngCache;
 
     // Document password provided
@@ -1932,6 +1931,97 @@ private:
     Poco::Thread _callbackThread;
 };
 
+class KitWebSocketHandler final : public WebSocketHandler, public std::enable_shared_from_this<KitWebSocketHandler>
+{
+    std::shared_ptr<TileQueue> _queue;
+    std::string _socketName;
+    std::shared_ptr<lok::Office> _loKit;
+    std::string _jailId;
+
+public:
+    KitWebSocketHandler(const std::string& socketName, const std::shared_ptr<lok::Office>& loKit, const std::string& jailId) :
+        WebSocketHandler(/* isClient = */ true),
+        _queue(std::make_shared<TileQueue>()),
+        _socketName(socketName),
+        _loKit(loKit),
+        _jailId(jailId)
+    {
+    }
+
+protected:
+    void handleMessage(bool /*fin*/, WSOpCode /*code*/, std::vector<char>& data) override
+    {
+        std::string message(data.data(), data.size());
+
+#if 0 // FIXME might be needed for unit tests #ifndef KIT_IN_PROCESS
+        if (UnitKit::get().filterKitMessage(ws, message))
+        {
+            return;
+        }
+#endif
+
+        LOG_DBG(_socketName << ": recv [" << LOOLProtocol::getAbbreviatedMessage(message) << "].");
+        std::vector<std::string> tokens = LOOLProtocol::tokenize(message);
+
+        // Note: Syntax or parsing errors here are unexpected and fatal.
+        if (TerminationFlag)
+        {
+            LOG_DBG("Too late, we're going down");
+        }
+        else if (tokens[0] == "session")
+        {
+            const std::string& sessionId = tokens[1];
+            const std::string& docKey = tokens[2];
+            const std::string& docId = tokens[3];
+
+            std::string url;
+            URI::decode(docKey, url);
+            LOG_INF("New session [" << sessionId << "] request on url [" << url << "].");
+
+            if (!document)
+            {
+                document = std::make_shared<Document>(_loKit, _jailId, docKey, docId, url, _queue, shared_from_this());
+            }
+
+            // Validate and create session.
+            if (!(url == document->getUrl() && document->createSession(sessionId)))
+            {
+                LOG_DBG("CreateSession failed.");
+            }
+        }
+        else if (tokens[0] == "exit")
+        {
+            LOG_TRC("Setting TerminationFlag due to 'exit' command from parent.");
+            TerminationFlag = true;
+        }
+        else if (tokens[0] == "tile" || tokens[0] == "tilecombine" || tokens[0] == "canceltiles" ||
+                tokens[0] == "paintwindow" ||
+                LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
+        {
+            if (document)
+            {
+                _queue->put(message);
+            }
+            else
+            {
+                LOG_WRN("No document while processing " << tokens[0] << " request.");
+            }
+        }
+        else if (tokens.size() == 3 && tokens[0] == "setconfig")
+        {
+            // Currently onlly rlimit entries are supported.
+            if (!Rlimit::handleSetrlimitCommand(tokens))
+            {
+                LOG_ERR("Unknown setconfig command: " << message);
+            }
+        }
+        else
+        {
+            LOG_ERR("Bad or unknown token [" << tokens[0] << "]");
+        }
+    }
+};
+
 void documentViewCallback(const int type, const char* payload, void* data)
 {
     Document::ViewCallback(type, payload, data);
@@ -2177,125 +2267,26 @@ void lokit_main(const std::string& childRoot,
             free(versionInfo);
         }
 
-        // Open websocket connection between the child process and WSD.
-        HTTPClientSession cs("127.0.0.1", MasterPortNumber);
-        cs.setTimeout(Poco::Timespan(10, 0)); // 10 second
-        LOG_DBG("Connecting to Master " << cs.getHost() << ':' << cs.getPort());
-        HTTPRequest request(HTTPRequest::HTTP_GET, requestUrl);
-        HTTPResponse response;
-        auto ws = std::make_shared<LOOLWebSocket>(cs, request, response);
-        ws->setReceiveTimeout(0);
+        SocketPoll mainKit("kit");
 
-        auto queue = std::make_shared<TileQueue>();
+        const Poco::URI uri("ws://127.0.0.1");
+        uri.setPort(MasterPortNumber);
 
-        const std::string socketName = "child_ws_" + pid;
-        IoUtil::SocketProcessor(ws, socketName,
-                [&socketName, &ws, &loKit, &jailId, &queue](const std::vector<char>& data)
-                {
-                    std::string message(data.data(), data.size());
-
-#ifndef KIT_IN_PROCESS
-                    if (UnitKit::get().filterKitMessage(ws, message))
-                    {
-                        return true;
-                    }
-#endif
-
-                    LOG_DBG(socketName << ": recv [" << LOOLProtocol::getAbbreviatedMessage(message) << "].");
-                    std::vector<std::string> tokens = LOOLProtocol::tokenize(message);
-
-                    // Note: Syntax or parsing errors here are unexpected and fatal.
-                    if (TerminationFlag)
-                    {
-                        LOG_DBG("Too late, we're going down");
-                    }
-                    else if (tokens[0] == "session")
-                    {
-                        const std::string& sessionId = tokens[1];
-                        const std::string& docKey = tokens[2];
-                        const std::string& docId = tokens[3];
-
-                        std::string url;
-                        URI::decode(docKey, url);
-                        LOG_INF("New session [" << sessionId << "] request on url [" << url << "].");
-
-                        if (!document)
-                        {
-                            document = std::make_shared<Document>(loKit, jailId, docKey, docId, url, queue, ws);
-                        }
-
-                        // Validate and create session.
-                        if (!(url == document->getUrl() &&
-                              document->createSession(sessionId)))
-                        {
-                            LOG_DBG("CreateSession failed.");
-                        }
-                    }
-                    else if (tokens[0] == "exit")
-                    {
-                        LOG_TRC("Setting TerminationFlag due to 'exit' command from parent.");
-                        TerminationFlag = true;
-                    }
-                    else if (tokens[0] == "tile" || tokens[0] == "tilecombine" || tokens[0] == "canceltiles" ||
-                             tokens[0] == "paintwindow" ||
-                             LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
-                    {
-                        if (document)
-                        {
-                            queue->put(message);
-                        }
-                        else
-                        {
-                            LOG_WRN("No document while processing " << tokens[0] << " request.");
-                        }
-                    }
-                    else if (tokens.size() == 3 && tokens[0] == "setconfig")
-                    {
-                        // Currently onlly rlimit entries are supported.
-                        if (!Rlimit::handleSetrlimitCommand(tokens))
-                        {
-                            LOG_ERR("Unknown setconfig command: " << message);
-                        }
-                    }
-                    else
-                    {
-                        LOG_ERR("Bad or unknown token [" << tokens[0] << "]");
-                    }
+        mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId));
+        LOG_INF("New kit client websocket inserted.");
 
-                    return true;
-                },
-                []() {},
-                []()
-                {
-                    if (document && document->purgeSessions() == 0)
-                    {
-                        LOG_INF("Last session discarded. Terminating.");
-                        TerminationFlag = true;
-                    }
-
-                    return TerminationFlag.load();
-                });
-
-#if 0
-        std::string uri = "file://$HOME/docs/basic-presentation.pptx";
-        std::shared_ptr<lok::Document> loKitDoc;
-
-        const auto flags = LOK_FEATURE_DOCUMENT_PASSWORD
-                           | LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
-                           | LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK
-                           | LOK_FEATURE_NO_TILED_ANNOTATIONS
-                           | LOK_FEATURE_RANGE_HEADERS;
-        loKit->setOptionalFeatures(flags);
-        loKitDoc.reset(loKit->documentLoad(uri.c_str()));
-        if (!loKitDoc || !loKitDoc->get())
+        while (!TerminationFlag)
         {
-            LOG_ERR("Failed to load: " << uri << ", error: " << loKit->getError());
-            std::_Exit(Application::EXIT_OK);
+            mainKit.poll(SocketPoll::DefaultPollTimeoutMs);
+
+            if (document && document->purgeSessions() == 0)
+            {
+                LOG_INF("Last session discarded. Terminating.");
+                TerminationFlag = true;
+            }
         }
 
-        // specific case to debug
-        // ...
-#endif
+        LOG_INF("Kit poll terminated.");
 
         // Let forkit handle the jail cleanup.
     }
commit 43522af092b30d9d7b6267a007772fcb2169dd13
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 4 16:08:32 2018 +0200

    websocketdump: Read the port and ssl support from the config.
    
    Change-Id: Ifc4566d5e1f2cdba1fd4bd7d53b359d81604083b

diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index 7f13012dd..202bff3f7 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -18,6 +18,7 @@
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPResponse.h>
 #include <Poco/StringTokenizer.h>
+#include <Poco/Util/XMLConfiguration.h>
 
 #include <Log.hpp>
 #include <Util.hpp>
@@ -194,13 +195,19 @@ private:
 
 class DumpSocketFactory final : public SocketFactory
 {
+private:
+    bool _isSSL = false;
+
+public:
+    DumpSocketFactory(bool isSSL) : _isSSL(isSSL) {}
+
     std::shared_ptr<Socket> create(const int physicalFd) override
     {
 #if ENABLE_SSL
-        return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
-#else
-        return StreamSocket::create<StreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+        if (_isSSL)
+            return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
 #endif
+        return StreamSocket::create<StreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
     }
 };
 
@@ -212,9 +219,15 @@ namespace Util
     }
 }
 
+class LoolConfig final: public Poco::Util::XMLConfiguration
+{
+public:
+    LoolConfig()
+        {}
+};
+
 int main (int argc, char **argv)
 {
-    int port = 9042;
     (void) argc; (void) argv;
 
     if (!UnitWSD::init(UnitWSD::UnitType::Wsd, ""))
@@ -225,6 +238,20 @@ int main (int argc, char **argv)
     Log::initialize("WebSocketDump", "trace", true, false,
                     std::map<std::string, std::string>());
 
+    LoolConfig config;
+    config.load("loolwsd.xml");
+
+    // read the port & ssl support
+    int port = 9042;
+    bool isSSL = false;
+    std::string monitorAddress = config.getString("monitors.monitor");
+    if (!monitorAddress.empty())
+    {
+        Poco::URI monitorURI(monitorAddress);
+        port = monitorURI.getPort();
+        isSSL = (monitorURI.getScheme() == "wss");
+    }
+
 #if ENABLE_SSL
     // hard coded but easy for now.
     const std::string ssl_cert_file_path = "etc/cert.pem";
@@ -233,10 +260,11 @@ int main (int argc, char **argv)
     const std::string ssl_cipher_list = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
 
     // Initialize the non-blocking socket SSL.
-    SslContext::initialize(ssl_cert_file_path,
-                           ssl_key_file_path,
-                           ssl_ca_file_path,
-                           ssl_cipher_list);
+    if (isSSL)
+        SslContext::initialize(ssl_cert_file_path,
+                               ssl_key_file_path,
+                               ssl_ca_file_path,
+                               ssl_cipher_list);
 #endif
 
     SocketPoll acceptPoll("accept");
@@ -244,7 +272,7 @@ int main (int argc, char **argv)
     // Setup listening socket with a factory for connected sockets.
     auto serverSocket = std::make_shared<ServerSocket>(
         Socket::Type::All, DumpSocketPoll,
-        std::make_shared<DumpSocketFactory>());
+        std::make_shared<DumpSocketFactory>(isSSL));
 
     if (!serverSocket->bind(ServerSocket::Type::Public, port))
     {
commit a88a48b5f3ce2f0ed85f3854933ee1db7d1640b3
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu May 3 18:03:56 2018 +0100

    Enable SSL in outbound, client websockets ...
    
    Switch SSL context creation to be generic rather than pure server.
    
    Change-Id: I1b750b4ddc8c607381f5541a4f4412fa16e457d4

diff --git a/net/Ssl.cpp b/net/Ssl.cpp
index a16caa258..0af675688 100644
--- a/net/Ssl.cpp
+++ b/net/Ssl.cpp
@@ -66,9 +66,9 @@ SslContext::SslContext(const std::string& certFilePath,
     // Create the Context. We only have one,
     // as we don't expect/support different servers in same process.
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
-    _ctx = SSL_CTX_new(TLS_server_method());
+    _ctx = SSL_CTX_new(TLS_method());
 #else
-    _ctx = SSL_CTX_new(SSLv23_server_method());
+    _ctx = SSL_CTX_new(SSLv23_method());
 #endif
 
     // SSL_CTX_set_default_passwd_cb(_ctx, &privateKeyPassphraseCallback);
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index c19fedea4..44a2fa382 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -46,7 +46,12 @@ public:
         SSL_set_bio(_ssl, bio, bio);
 
         if (isClient)
+        {
             SSL_set_connect_state(_ssl);
+            if (SSL_connect(_ssl) == 0)
+                LOG_DBG("SslStreamSocket connect #" << getFD() << " failed ");
+            // else -1 is quite possibly SSL_ERROR_WANT_READ
+        }
         else // We are a server-side socket.
             SSL_set_accept_state(_ssl);
     }
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index 74faa310e..7f13012dd 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -196,7 +196,7 @@ class DumpSocketFactory final : public SocketFactory
 {
     std::shared_ptr<Socket> create(const int physicalFd) override
     {
-#if 0 && ENABLE_SSL
+#if ENABLE_SSL
         return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
 #else
         return StreamSocket::create<StreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
@@ -225,6 +225,20 @@ int main (int argc, char **argv)
     Log::initialize("WebSocketDump", "trace", true, false,
                     std::map<std::string, std::string>());
 
+#if ENABLE_SSL
+    // hard coded but easy for now.
+    const std::string ssl_cert_file_path = "etc/cert.pem";
+    const std::string ssl_key_file_path = "etc/key.pem";
+    const std::string ssl_ca_file_path = "etc/ca-chain.cert.pem";
+    const std::string ssl_cipher_list = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
+
+    // Initialize the non-blocking socket SSL.
+    SslContext::initialize(ssl_cert_file_path,
+                           ssl_key_file_path,
+                           ssl_ca_file_path,
+                           ssl_cipher_list);
+#endif
+
     SocketPoll acceptPoll("accept");
 
     // Setup listening socket with a factory for connected sockets.
commit 177f253561e594c24f0b50e586f4cfb3d6ce96c8
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Thu May 3 17:52:35 2018 +0100

    re-factor socket factories to take a client parameter.
    
    Change-Id: I0be98eb583b4f8081dd8ad23e688e93c55220367

diff --git a/net/Socket.hpp b/net/Socket.hpp
index 9c3f7fe48..d0c03071c 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -700,7 +700,8 @@ class StreamSocket : public Socket, public std::enable_shared_from_this<StreamSo
 {
 public:
     /// Create a StreamSocket from native FD and take ownership of handler instance.
-    StreamSocket(const int fd, std::shared_ptr<SocketHandlerInterface> socketHandler) :
+    StreamSocket(const int fd, bool /* isClient */,
+                 std::shared_ptr<SocketHandlerInterface> socketHandler) :
         Socket(fd),
         _socketHandler(std::move(socketHandler)),
         _bytesSent(0),
@@ -827,10 +828,10 @@ public:
     /// but we can't have a shared_ptr in the ctor.
     template <typename TSocket>
     static
-    std::shared_ptr<TSocket> create(const int fd, std::shared_ptr<SocketHandlerInterface> handler)
+    std::shared_ptr<TSocket> create(const int fd, bool isClient, std::shared_ptr<SocketHandlerInterface> handler)
     {
         SocketHandlerInterface* pHandler = handler.get();
-        auto socket = std::make_shared<TSocket>(fd, std::move(handler));
+        auto socket = std::make_shared<TSocket>(fd, isClient, std::move(handler));
         pHandler->onConnect(socket);
         return socket;
     }
diff --git a/net/SslSocket.hpp b/net/SslSocket.hpp
index 2f8d45cb6..c19fedea4 100644
--- a/net/SslSocket.hpp
+++ b/net/SslSocket.hpp
@@ -19,8 +19,9 @@
 class SslStreamSocket final : public StreamSocket
 {
 public:
-    SslStreamSocket(const int fd, std::shared_ptr<SocketHandlerInterface> responseClient) :
-        StreamSocket(fd, std::move(responseClient)),
+    SslStreamSocket(const int fd, bool isClient,
+                    std::shared_ptr<SocketHandlerInterface> responseClient) :
+        StreamSocket(fd, isClient, std::move(responseClient)),
         _ssl(nullptr),
         _sslWantsTo(SslWantsTo::Neither),
         _doHandshake(true)
@@ -44,8 +45,10 @@ public:
 
         SSL_set_bio(_ssl, bio, bio);
 
-        // We are a server-side socket.
-        SSL_set_accept_state(_ssl);
+        if (isClient)
+            SSL_set_connect_state(_ssl);
+        else // We are a server-side socket.
+            SSL_set_accept_state(_ssl);
     }
 
     ~SslStreamSocket()
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index bc7a04781..74faa310e 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -140,7 +140,10 @@ private:
 
             if (request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
             {
-                socket->setHandler(std::make_shared<DumpSocketHandler>(_socket, request));
+                auto dumpHandler = std::make_shared<DumpSocketHandler>(_socket, request);
+                socket->setHandler(dumpHandler);
+                dumpHandler->sendMessage("version");
+                dumpHandler->sendMessage("documents");
             }
             else
             {
@@ -194,9 +197,9 @@ class DumpSocketFactory final : public SocketFactory
     std::shared_ptr<Socket> create(const int physicalFd) override
     {
 #if 0 && ENABLE_SSL
-        return StreamSocket::create<SslStreamSocket>(physicalFd, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+        return StreamSocket::create<SslStreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
 #else
-        return StreamSocket::create<StreamSocket>(physicalFd, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+        return StreamSocket::create<StreamSocket>(physicalFd, false, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
 #endif
     }
 };
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index ce1322877..c34a98aa9 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -701,10 +701,10 @@ void Admin::connectToMonitor(const Poco::URI &uri)
                     std::shared_ptr<SocketHandlerInterface> handler = std::make_shared<MonitorSocketHandler>(this);
 #if ENABLE_SSL
                     if (isSSL)
-                        socket = StreamSocket::create<SslStreamSocket>(fd, handler);
+                        socket = StreamSocket::create<SslStreamSocket>(fd, true, handler);
 #endif
                     if (!socket && !isSSL)
-                        socket = StreamSocket::create<StreamSocket>(fd, handler);
+                        socket = StreamSocket::create<StreamSocket>(fd, true, handler);
 
                     if (socket)
                     {
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 86bccc768..cccdf1a71 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2393,7 +2393,7 @@ class PlainSocketFactory final : public SocketFactory
 
         std::shared_ptr<Socket> socket =
             StreamSocket::create<StreamSocket>(
-                fd, std::unique_ptr<SocketHandlerInterface>{
+                fd, false, std::unique_ptr<SocketHandlerInterface>{
                     new ClientRequestDispatcher });
 
         return socket;
@@ -2410,7 +2410,9 @@ class SslSocketFactory final : public SocketFactory
         if (SimulatedLatencyMs > 0)
             fd = Delay::create(SimulatedLatencyMs, physicalFd);
 
-        return StreamSocket::create<SslStreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
+        return StreamSocket::create<SslStreamSocket>(
+            fd, false, std::unique_ptr<SocketHandlerInterface>{
+                new ClientRequestDispatcher });
     }
 };
 #endif
@@ -2420,7 +2422,7 @@ class PrisonerSocketFactory final : public SocketFactory
     std::shared_ptr<Socket> create(const int fd) override
     {
         // No local delay.
-        return StreamSocket::create<StreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new PrisonerRequestDispatcher });
+        return StreamSocket::create<StreamSocket>(fd, false, std::unique_ptr<SocketHandlerInterface>{ new PrisonerRequestDispatcher });
     }
 };
 
commit 4e7cb3e4a5c3753436b4b02ffd4dfbd2104b1960
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu May 3 17:32:31 2018 +0200

    The WebSocketHandler::handleClientUpgrade() needs to handle a Response,
    
    not a request.
    
    This commit includes some fixes from Michael Meeks too.
    
    Change-Id: I25198ded9d354a44d7718071394bcccdcabcdd94

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 3380764b5..96ad7f7c4 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -297,8 +297,16 @@ bool StreamSocket::parseHeader(const char *clientName,
             return false;
         }
     }
+    catch (const Poco::Exception& exc)
+    {
+        LOG_DBG("parseHeader exception caught: " << exc.displayText());
+        // Probably don't have enough data just yet.
+        // TODO: timeout if we never get enough.
+        return false;
+    }
     catch (const std::exception& exc)
     {
+        LOG_DBG("parseHeader exception caught.");
         // Probably don't have enough data just yet.
         // TODO: timeout if we never get enough.
         return false;
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 47d917077..c0628b456 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -21,6 +21,7 @@
 
 #include <Poco/MemoryStream.h>
 #include <Poco/Net/HTTPRequest.h>
+#include <Poco/Net/HTTPResponse.h>
 #include <Poco/Net/WebSocket.h>
 
 class WebSocketHandler : public SocketHandlerInterface
@@ -494,52 +495,64 @@ protected:
     {
         std::shared_ptr<StreamSocket> socket = _socket.lock();
 
-        LOG_TRC("Incoming client websocket upgrade request");
-
-        Poco::MemoryInputStream message(&socket->_inBuffer[0],
-                                        socket->_inBuffer.size());;
-        Poco::Net::HTTPRequest req;
-        size_t requestSize = 0;
+        LOG_TRC("Incoming client websocket upgrade response: " << std::string(&socket->_inBuffer[0], socket->_inBuffer.size()));
 
         bool bOk = false;
-        if (!socket->parseHeader("Monitor", message, req, &requestSize))
-        {
-// FIXME: grim hack [!] we can't parse the response for some strange reason ...
-//        we get an exception inside Poco ...
-//            return;
-            bOk = true;
-        }
-        else if (req.find("Upgrade") != req.end() && Poco::icompare(req["Upgrade"], "websocket") == 0)
+        size_t responseSize = 0;
+
+        try
         {
-            const std::string wsKey = req.get("Sec-WebSocket-Accept", "");
-            const std::string wsProtocol = req.get("Sec-WebSocket-Protocol", "");
-            if (Poco::icompare(wsProtocol, "chat") != 0)
-                LOG_ERR("Unknown websocket protocol " << wsProtocol);
-            else
+            Poco::MemoryInputStream message(&socket->_inBuffer[0], socket->_inBuffer.size());;
+            Poco::Net::HTTPResponse response;
+
+            response.read(message);
+
             {
-                LOG_TRC("Accepted incoming websocket request");
-                // FIXME: validate Sec-WebSocket-Accept vs. Sec-WebSocket-Key etc.
-                bOk = true;
+                static const std::string marker("\r\n\r\n");
+                auto itBody = std::search(socket->_inBuffer.begin(),
+                                          socket->_inBuffer.end(),
+                                          marker.begin(), marker.end());
+
+                if (itBody != socket->_inBuffer.end())
+                    responseSize = itBody - socket->_inBuffer.begin() + marker.size();
             }
+
+            if (response.getStatus() == Poco::Net::HTTPResponse::HTTP_SWITCHING_PROTOCOLS &&
+                    response.has("Upgrade") && Poco::icompare(response.get("Upgrade"), "websocket") == 0)
+            {
+#if 0 // SAL_DEBUG ...
+                const std::string wsKey = response.get("Sec-WebSocket-Accept", "");
+                const std::string wsProtocol = response.get("Sec-WebSocket-Protocol", "");
+                if (Poco::icompare(wsProtocol, "chat") != 0)
+                    LOG_ERR("Unknown websocket protocol " << wsProtocol);
+                else
+#endif
+                {
+                    LOG_TRC("Accepted incoming websocket response");
+                    // FIXME: validate Sec-WebSocket-Accept vs. Sec-WebSocket-Key etc.
+                    bOk = true;
+                }
+            }
+        }
+        catch (const Poco::Exception& exc)
+        {
+            LOG_DBG("handleClientUpgrade exception caught: " << exc.displayText());
+        }
+        catch (const std::exception& exc)
+        {
+            LOG_DBG("handleClientUpgrade exception caught.");
         }
 
-        if (!bOk)
+        if (!bOk || responseSize == 0)
         {
-            LOG_ERR("Bad websocker server reply: " << req.getURI());
-
-            // Bad request.
-            std::ostringstream oss;
-            oss << "HTTP/1.1 400\r\n"
-                << "Date: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
-                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
-                << "Content-Length: 0\r\n"
-                << "\r\n";
-            socket->send(oss.str());
+            LOG_ERR("Bad websocker server response.");
+
             socket->shutdown();
+            return;
         }
 
         setWebSocket();
-        socket->eraseFirstInputBytes(requestSize);
+        socket->eraseFirstInputBytes(responseSize);
     }
 
     void setWebSocket()
commit fc59ea70085e770144adf61236ddd6c14941488c
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Tue May 1 14:57:17 2018 +0100

    Share HTTP header parsing inside the StreamSocket.
    
    Change-Id: Id98e895a939d931ac10b7cd7403da4cbe822ee82

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 3098ec1f4..3380764b5 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -17,6 +17,8 @@
 #include <Poco/DateTime.h>
 #include <Poco/DateTimeFormat.h>
 #include <Poco/DateTimeFormatter.h>
+#include <Poco/MemoryStream.h>
+#include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPResponse.h>
 
 #include "SigUtil.hpp"
@@ -241,6 +243,70 @@ bool ServerSocket::bind(Type type, int port)
     return rc == 0;
 }
 
+bool StreamSocket::parseHeader(const char *clientName,
+                               Poco::MemoryInputStream &message,
+                               Poco::Net::HTTPRequest &request,
+                               size_t *requestSize)
+{
+    LOG_TRC("#" << getFD() << " handling incoming " << _inBuffer.size() << " bytes.");
+
+    assert(!requestSize || *requestSize == 0);
+
+    // Find the end of the header, if any.
+    static const std::string marker("\r\n\r\n");
+    auto itBody = std::search(_inBuffer.begin(), _inBuffer.end(),
+                              marker.begin(), marker.end());
+    if (itBody == _inBuffer.end())
+    {
+        LOG_TRC("#" << getFD() << " doesn't have enough data yet.");
+        return false;
+    }
+
+    // Skip the marker.
+    itBody += marker.size();
+    if (requestSize)
+        *requestSize = static_cast<size_t>(itBody - _inBuffer.begin());
+
+    try
+    {
+        request.read(message);
+
+        Log::StreamLogger logger = Log::info();
+        if (logger.enabled())
+        {
+            logger << "#" << getFD() << ": " << clientName << " HTTP Request: "
+                   << request.getMethod() << ' '
+                   << request.getURI() << ' '
+                   << request.getVersion();
+
+            for (const auto& it : request)
+            {
+                logger << " / " << it.first << ": " << it.second;
+            }
+
+            LOG_END(logger);
+        }
+
+        const std::streamsize contentLength = request.getContentLength();
+        const auto offset = itBody - _inBuffer.begin();
+        const std::streamsize available = _inBuffer.size() - offset;
+
+        if (contentLength != Poco::Net::HTTPMessage::UNKNOWN_CONTENT_LENGTH && available < contentLength)
+        {
+            LOG_DBG("Not enough content yet: ContentLength: " << contentLength << ", available: " << available);
+            return false;
+        }
+    }
+    catch (const std::exception& exc)
+    {
+        // Probably don't have enough data just yet.
+        // TODO: timeout if we never get enough.
+        return false;
+    }
+
+    return true;
+}
+
 
 namespace HttpHelper
 {
diff --git a/net/Socket.hpp b/net/Socket.hpp
index beb8b85d3..9c3f7fe48 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -40,8 +40,10 @@
 
 namespace Poco
 {
+    class MemoryInputStream;
     namespace Net
     {
+        class HTTPRequest;
         class HTTPResponse;
     }
 }
@@ -833,6 +835,20 @@ public:
         return socket;
     }
 
+    /// Remove the first @count bytes from input buffer
+    void eraseFirstInputBytes(size_t count)
+    {
+        _inBuffer.erase(_inBuffer.begin(), _inBuffer.begin() + count);
+    }
+
+    /// Detects if we have an HTTP header in the provided message and
+    /// populates a request for that.
+    bool parseHeader(const char *clientLoggingName,
+                     Poco::MemoryInputStream &message,
+                     Poco::Net::HTTPRequest &request,
+                     size_t *requestSize = nullptr);
+
+    /// Get input/output statistics on this stream
     void getIOStats(uint64_t &sent, uint64_t &recv)
     {
         sent = _bytesSent;
@@ -1016,8 +1032,7 @@ namespace HttpHelper
     void sendFile(const std::shared_ptr<StreamSocket>& socket, const std::string& path, const std::string& mediaType,
                   Poco::Net::HTTPResponse& response, bool noCache = false, bool deflate = false,
                   const bool headerOnly = false);
-};
-
+}
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index d329c4e3f..86bccc768 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1600,43 +1600,17 @@ private:
             return;
         }
 
-        auto socket = _socket.lock();
-        std::vector<char>& in = socket->_inBuffer;
-
-        // Find the end of the header, if any.
-        static const std::string marker("\r\n\r\n");
-        auto itBody = std::search(in.begin(), in.end(),
-                                  marker.begin(), marker.end());
-        if (itBody == in.end())
-        {
-            LOG_TRC("#" << socket->getFD() << " doesn't have enough data yet.");
-            return;
-        }
-
-        // Skip the marker.
-        itBody += marker.size();
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
 
-        Poco::MemoryInputStream message(&in[0], in.size());
+        Poco::MemoryInputStream message(&socket->_inBuffer[0],
+                                        socket->_inBuffer.size());;
         Poco::Net::HTTPRequest request;
+        size_t requestSize = 0;
+
         try
         {
-            request.read(message);
-
-            auto logger = Log::info();
-            if (logger.enabled())
-            {
-                logger << "#" << socket->getFD() << ": Prisoner HTTP Request: "
-                       << request.getMethod() << ' '
-                       << request.getURI() << ' '
-                       << request.getVersion();
-
-                for (const auto& it : request)
-                {
-                    logger << " / " << it.first << ": " << it.second;
-                }
-
-                LOG_END(logger);
-            }
+            if (!socket->parseHeader("Prisoner", message, request, &requestSize))
+                return;
 
             LOG_TRC("Child connection with URI [" << request.getURI() << "].");
             if (request.getURI().find(NEW_CHILD_URI) != 0)
@@ -1677,7 +1651,7 @@ private:
                 return;
             }
 
-            in.clear();
+            socket->_inBuffer.clear();
 
             LOG_INF("New child [" << pid << "], jailId: " << jailId << ".");
 
@@ -1760,61 +1734,15 @@ private:
     /// Called after successful socket reads.
     void handleIncomingMessage(SocketDisposition &disposition) override
     {
-        auto socket = _socket.lock();
-        std::vector<char>& in = socket->_inBuffer;
-        LOG_TRC("#" << socket->getFD() << " handling incoming " << in.size() << " bytes.");
-
-        // Find the end of the header, if any.
-        static const std::string marker("\r\n\r\n");
-        auto itBody = std::search(in.begin(), in.end(),
-                                  marker.begin(), marker.end());
-        if (itBody == in.end())
-        {
-            LOG_DBG("#" << socket->getFD() << " doesn't have enough data yet.");
-            return;
-        }
-
-        // Skip the marker.
-        itBody += marker.size();
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
 
-        Poco::MemoryInputStream message(&in[0], in.size());
+        Poco::MemoryInputStream message(&socket->_inBuffer[0],
+                                        socket->_inBuffer.size());;
         Poco::Net::HTTPRequest request;
-        try
-        {
-            request.read(message);
-
-            auto logger = Log::info();
-            if (logger.enabled())
-            {
-                logger << "#" << socket->getFD() << ": Client HTTP Request: "
-                       << request.getMethod() << ' '
-                       << request.getURI() << ' '
-                       << request.getVersion();
-
-                for (const auto& it : request)
-                {
-                    logger << " / " << it.first << ": " << it.second;
-                }
-
-                LOG_END(logger);
-            }
+        size_t requestSize = 0;
 
-            const std::streamsize contentLength = request.getContentLength();
-            const auto offset = itBody - in.begin();
-            const std::streamsize available = in.size() - offset;
-
-            if (contentLength != Poco::Net::HTTPMessage::UNKNOWN_CONTENT_LENGTH && available < contentLength)
-            {
-                LOG_DBG("Not enough content yet: ContentLength: " << contentLength << ", available: " << available);
-                return;
-            }
-        }
-        catch (const std::exception& exc)
-        {
-            // Probably don't have enough data just yet.
-            // TODO: timeout if we never get enough.
+        if (!socket->parseHeader("Client", message, request, &requestSize))
             return;
-        }
 
         try
         {
@@ -1908,12 +1836,12 @@ private:
 
             // NOTE: Check _wsState to choose between HTTP response or WebSocket (app-level) error.
             LOG_INF("#" << socket->getFD() << " Exception while processing incoming request: [" <<
-                    LOOLProtocol::getAbbreviatedMessage(in) << "]: " << exc.what());
+                    LOOLProtocol::getAbbreviatedMessage(socket->_inBuffer) << "]: " << exc.what());
         }
 
         // if we succeeded - remove the request from our input buffer
         // we expect one request per socket
-        in.erase(in.begin(), itBody);
+        socket->eraseFirstInputBytes(requestSize);
     }
 
     int getPollEvents(std::chrono::steady_clock::time_point /* now */,
commit 5e079e160a0667e14c47707e3d385fd8b1c5c000
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Wed May 2 15:40:16 2018 +0100

    Get ping/pong handling sorted with more isClient conditionality.
    
    Change-Id: I859ed5b5bcc302304e23ad3554247af920de2421

diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 5663cab2c..47d917077 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -197,24 +197,51 @@ public:
                 ", fin? " << fin << ", mask? " << hasMask << ", payload length: " << _wsPayload.size() <<
                 ", residual socket data: " << socket->_inBuffer.size() << " bytes.");
 
+        bool doClose = false;
+
         switch (code)
         {
         case WSOpCode::Pong:
         {
-            _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds>
-                                        (std::chrono::steady_clock::now() - _lastPingSentTime).count();
-            LOG_TRC("#" << socket->getFD() << ": Pong received: " << _pingTimeUs << " microseconds");
-            break;
+            if (_isClient)
+            {
+                LOG_ERR("#" << socket->getFD() << ": Servers should not send pongs, only clients");
+                doClose = true;
+                break;
+            }
+            else
+            {
+                _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds>
+                    (std::chrono::steady_clock::now() - _lastPingSentTime).count();
+                LOG_TRC("#" << socket->getFD() << ": Pong received: " << _pingTimeUs << " microseconds");
+                break;
+            }
         }
         case WSOpCode::Ping:
-            LOG_ERR("#" << socket->getFD() << ": Clients should not send pings, only servers");
-            // drop through
-#if defined __clang__
-            [[clang::fallthrough]];
-#elif defined __GNUC__ && __GNUC__ >= 7
-            [[fallthrough]];
-#endif
+            if (_isClient)
+            {
+                auto now = std::chrono::steady_clock::now();
+                _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds>
+                                        (now - _lastPingSentTime).count();
+                sendPong(now, &_wsPayload[0], payloadLen, socket);
+                break;
+            }
+            else
+            {
+                LOG_ERR("#" << socket->getFD() << ": Clients should not send pings, only servers");
+                doClose = true;
+            }
+            break;
         case WSOpCode::Close:
+            doClose = true;
+            break;
+        default:
+            handleMessage(fin, code, _wsPayload);
+            break;
+        }
+
+        if (doClose)
+        {
             if (!_shuttingDown)
             {
                 // Peer-initiated shutdown must be echoed.
@@ -239,10 +266,6 @@ public:
 
             // TCP Close.
             socket->closeConnection();
-            break;
-        default:
-            handleMessage(fin, code, _wsPayload);
-            break;
         }
 
         _wsPayload.clear();
@@ -270,15 +293,20 @@ public:
     int getPollEvents(std::chrono::steady_clock::time_point now,
                       int & timeoutMaxMs) override
     {
-        const int timeSincePingMs =
-            std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastPingSentTime).count();
-        timeoutMaxMs = std::min(timeoutMaxMs, PingFrequencyMs - timeSincePingMs);
+        if (!_isClient)
+        {
+            const int timeSincePingMs =
+                std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastPingSentTime).count();
+            timeoutMaxMs = std::min(timeoutMaxMs, PingFrequencyMs - timeSincePingMs);
+        }
         return POLLIN;
     }
 
     /// Send a ping message
-    void sendPing(std::chrono::steady_clock::time_point now,
-                  const std::shared_ptr<StreamSocket>& socket)
+    void sendPingOrPong(std::chrono::steady_clock::time_point now,
+                        const char* data, const size_t len,
+                        const WSOpCode code,
+                        const std::shared_ptr<StreamSocket>& socket)
     {
         assert(socket && "Expected a valid socket instance.");
 
@@ -290,15 +318,34 @@ public:
             return;
         }
 
-        LOG_TRC("#" << socket->getFD() << ": Sending ping.");
+        LOG_TRC("#" << socket->getFD() << ": Sending " <<
+                (const char *)(code == WSOpCode::Ping ? " ping." : "pong."));
         // FIXME: allow an empty payload.
-        sendMessage("", 1, WSOpCode::Ping, false);
+        sendMessage(data, len, code, false);
         _lastPingSentTime = now;
     }
 
+    void sendPing(std::chrono::steady_clock::time_point now,
+                  const std::shared_ptr<StreamSocket>& socket)
+    {
+        assert(!_isClient);
+        sendPingOrPong(now, "", 1, WSOpCode::Ping, socket);
+    }
+
+    void sendPong(std::chrono::steady_clock::time_point now,
+                  const char* data, const size_t len,
+                  const std::shared_ptr<StreamSocket>& socket)
+    {
+        assert(_isClient);
+        sendPingOrPong(now, data, len, WSOpCode::Pong, socket);
+    }
+
     /// Do we need to handle a timeout ?
     void checkTimeout(std::chrono::steady_clock::time_point now) override
     {
+        if (_isClient)
+            return;
+
         const int timeSincePingMs =
             std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastPingSentTime).count();
         if (timeSincePingMs >= PingFrequencyMs)
commit a631f129ba5918a11488808dd90dd30f508a7bcf
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Tue May 1 17:50:13 2018 +0100

    More work on client / Monitor websocket connections.
    
    Change-Id: Ic70fe522e24f2b1863c2d9d1dd6941785510758a

diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index dd5c4be08..5663cab2c 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -19,6 +19,7 @@
 #include "common/Unit.hpp"
 #include "Socket.hpp"
 
+#include <Poco/MemoryStream.h>
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/WebSocket.h>
 
@@ -33,6 +34,7 @@ protected:
 
     std::vector<char> _wsPayload;
     std::atomic<bool> _shuttingDown;
+    bool _isClient;
 
     struct WSFrameMask
     {
@@ -44,10 +46,12 @@ protected:
     static const int PingFrequencyMs;
 
 public:
-    WebSocketHandler() :
+    /// Perform upgrade ourselves, or select a client web socket.
+    WebSocketHandler(bool isClient = false) :
         _lastPingSentTime(std::chrono::steady_clock::now()),
         _pingTimeUs(0),
-        _shuttingDown(false)
+        _shuttingDown(false),
+        _isClient(isClient)
     {
     }
 
@@ -59,7 +63,8 @@ public:
                   std::chrono::milliseconds(PingFrequencyMs) -
                   std::chrono::milliseconds(InitialPingDelayMs)),
         _pingTimeUs(0),
-        _shuttingDown(false)
+        _shuttingDown(false),
+        _isClient(false)
     {
         upgradeToWebSocket(request);
     }
@@ -253,6 +258,8 @@ public:
         {
             LOG_ERR("No socket associated with WebSocketHandler 0x" << std::hex << this << std::dec);
         }

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list