common/Common.hpp                     |    8 
 common/Seccomp.cpp                    |    1 
 common/SigUtil.cpp                    |    1 
 kit/ChildSession.hpp                  |    6 
 kit/Kit.cpp                           |  287 ++++++++++++++++++----------------
 loleaflet/src/layer/tile/TileLayer.js |   88 +++++-----
 loolwsd.xml.in                        |    4 
 net/Socket.cpp                        |  102 ++++++++++++
 net/Socket.hpp                        |   88 +++++-----
 net/Ssl.cpp                           |    4 
 net/SslSocket.hpp                     |   16 +
 net/WebSocketHandler.hpp              |  177 +++++++++++++++++---
 test/Makefile.am                      |    8 
 test/WhiteBoxTests.cpp                |    2 
 test/countloolkits.hpp                |    4 
 tools/WebSocketDump.cpp               |   82 ++++++++-
 wsd/Admin.cpp                         |   71 ++++++++
 wsd/Admin.hpp                         |    5 
 wsd/DocumentBroker.cpp                |    5 
 wsd/DocumentBroker.hpp                |   66 +++----
 wsd/LOOLWSD.cpp                       |   15 -
 21 files changed, 723 insertions(+), 317 deletions(-)

New commits:
commit 432a1c7d78d027f9986b91dc9e48edd33902a046
Author: Jan Holesovsky <ke...@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 e972459af..1006dc2dc 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1588,10 +1588,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 b88e37af6..fc3cb095d 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 9e48426c8..d7cb6a473 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2885,7 +2885,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 088acf5e98c32228960b81b43f447de6c384cd17
Author: Jan Holesovsky <ke...@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 856e89fd5..ae6d6b18e 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 443d8ab52..b88e37af6 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 9c790d1796c5564875d38d6109ac883a30d3bf2a
Author: Jan Holesovsky <ke...@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 9080acb77..1deb1425a 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2078,48 +2078,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 4e4d9719d4bb3144d5385be7be104eefa3390f11
Author: Jan Holesovsky <ke...@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 06a04f5d8..9080acb77 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -26,16 +26,6 @@ function hex2string(inData)
 }
 
 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);
@@ -2052,16 +2042,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);
                }
        },
 
@@ -2084,8 +2070,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') {
@@ -2097,15 +2100,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 fde34f6e173ceba82c7db40fa36bd38efb368638
Author: Michael Meeks <michael.me...@collabora.com>
Date:   Mon May 14 10:35:56 2018 +0100

    Better debugging of tests.
    
    Change-Id: If3b3d2ad862526238bee3c092389c8cc266b24e6

diff --git a/test/countloolkits.hpp b/test/countloolkits.hpp
index 90b89e12a..0f7010ff0 100644
--- a/test/countloolkits.hpp
+++ b/test/countloolkits.hpp
@@ -76,9 +76,11 @@ static std::chrono::steady_clock::time_point TestStartTime;
 
 static void testCountHowManyLoolkits()
 {
+    const char testname[] = "countHowManyLoolkits ";
     TestStartTime = std::chrono::steady_clock::now();
 
     InitialLoolKitCount = countLoolKitProcesses(InitialLoolKitCount);
+    TST_LOG("Initial loolkit count is " << InitialLoolKitCount);
     CPPUNIT_ASSERT(InitialLoolKitCount > 0);
 
     TestStartTime = std::chrono::steady_clock::now();
@@ -86,7 +88,7 @@ static void testCountHowManyLoolkits()
 
 static void testNoExtraLoolKitsLeft()
 {
-    const char* testname = "noExtraLoolKitsLeft ";
+    const char testname[] = "noExtraLoolKitsLeft ";
     const int countNow = countLoolKitProcesses(InitialLoolKitCount);
     CPPUNIT_ASSERT_EQUAL(InitialLoolKitCount, countNow);
 
commit 071079a6a9aba7ecb7b7da2b2c65d30b58c2f23a
Author: Michael Meeks <michael.me...@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 6ae0b2899..fc276a8aa 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 6c31b7793c03da7d38754dbef6a9b1f32489c00c
Author: Jan Holesovsky <ke...@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 59b7dec74..d7f4cc05b 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -753,6 +753,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),
@@ -760,6 +761,7 @@ public:
         _docId(docId),
         _url(url),
         _tileQueue(std::move(tileQueue)),
+        _socketPoll(socketPoll),
         _websocketHandler(websocketHandler),
         _docPassword(""),
         _haveDocPassword(false),
@@ -794,6 +796,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);
@@ -908,9 +924,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();
@@ -964,12 +979,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");
@@ -978,13 +993,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();
 
@@ -1103,12 +1117,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)
@@ -1120,14 +1134,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)
         {
@@ -1826,11 +1837,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")
                 {
@@ -1953,6 +1964,7 @@ private:
 
     std::shared_ptr<lok::Document> _loKitDocument;
     std::shared_ptr<TileQueue> _tileQueue;
+    SocketPoll& _socketPoll;
     std::shared_ptr<WebSocketHandler> _websocketHandler;
     PngCache _pngCache;
 
@@ -1995,14 +2007,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)
     {
     }
 
@@ -2038,7 +2052,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.
@@ -2336,7 +2350,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 93cc4b4548e3abcefc282e05ddf46677f8dd2897
Author: Jan Holesovsky <ke...@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 cb146f62c..6cd2190d4 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 856c1721a..59814b6d5 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 7c44bab3a..9e48426c8 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2443,8 +2443,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;
     }
@@ -2461,8 +2460,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
@@ -2472,7 +2470,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 6996139e5fba0310ddc9d97f2005a3383818ec7a
Author: Jan Holesovsky <ke...@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 b73e98249..42e8dd450 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 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";
+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 const char* HTTP_AGENT_STRING = "LOOLWSD HTTP Agent " 
LOOLWSD_VERSION;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 88a58e198..59b7dec74 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -2318,25 +2318,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 17ac9166d..4d58ff949 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 8a99428a5..7c44bab3a 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1620,14 +1620,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 Poco::URI::QueryParameters 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 98ed24257b505b10ac48b2159d12f82cb81b3559
Author: Jan Holesovsky <ke...@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 093ca4e66..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
@@ -161,14 +161,14 @@ public:
     {
         const auto msg = "client-" + getId() + ' ' + std::string(buffer, 
length);
         const std::unique_lock<std::mutex> lock = getLock();
-        return _docManager.sendFrame(msg.data(), msg.size(), 
Poco::Net::WebSocket::FRAME_TEXT);
+        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 std::unique_lock<std::mutex> lock = getLock();
-        return _docManager.sendFrame(msg.data(), msg.size(), 
Poco::Net::WebSocket::FRAME_BINARY);
+        return _docManager.sendFrame(msg.data(), msg.size(), WSOpCode::Binary);
     }
 
     using Session::sendTextFrame;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 37c62f10c..88a58e198 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -979,7 +979,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)
@@ -1108,7 +1108,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)
@@ -1116,7 +1116,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
         {
@@ -1126,7 +1126,7 @@ public:
                 return false;
             }
 
-            _websocketHandler->sendMessage(buffer, length, flags);
+            _websocketHandler->sendMessage(buffer, length, opCode);
             return true;
         }
         catch (const Exception& exc)
@@ -2334,7 +2334,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 c9a2968f6..17ac9166d 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 373fcc79c..cb146f62c 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 5b17f54a2..7e59fa621 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 f910aac3d..580df11e3 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -390,7 +390,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 d0e11aeb8..0350c714d 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -656,95 +656,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 35ec3649bbf98fc79d28530a0bb8e082156ba831
Author: Jan Holesovsky <ke...@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 1451778b3..37c62f10c 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -57,7 +57,6 @@
 #include "KitHelper.hpp"
 #include "Kit.hpp"
 #include <Protocol.hpp>
-#include <LOOLWebSocket.hpp>
 #include <Log.hpp>
 #include <Png.hpp>
 #include <Rectangle.hpp>
@@ -754,14 +753,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),
@@ -909,9 +908,9 @@ 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.");
+        assert(websocketHandler && "Expected a non-null websocket.");
         TileDesc tile = TileDesc::parse(tokens);
 
         size_t pixmapDataSize = 4 * tile.getWidth() * tile.getHeight();
@@ -980,12 +979,12 @@ 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.");
+        assert(websocketHandler && "Expected a non-null websocket.");
         TileCombined tileCombined = TileCombined::parse(tokens);
         auto& tiles = tileCombined.getTiles();
 
@@ -1109,7 +1108,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)
@@ -1121,13 +1120,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)
@@ -1827,11 +1826,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")
                 {
@@ -1954,7 +1953,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
@@ -1990,6 +1989,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);
@@ -2242,110 +2332,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);
 
-        if (bTraceStartup && LogLevel != "trace")
-        {
-            LOG_INF("Setting log-level to [" << LogLevel << "].");
-            Log::logger().setLevel(LogLevel);
-        }
-
-        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);
+        mainKit.insertNewWebSocketSync(uri, 
std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId));
+        LOG_INF("New kit client websocket inserted.");
 
-                    // 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] << "]");
-                    }
+        while (!TerminationFlag)
+        {
+            mainKit.poll(SocketPoll::DefaultPollTimeoutMs);
 
-                    return true;
-                },
-                []() {},
-                []()
-                {
-                    if (document && document->purgeSessions() == 0)
-                    {
-                        LOG_INF("Last session discarded. Terminating.");
-                        TerminationFlag = true;
-                    }
+            if (document && document->purgeSessions() == 0)
+            {
+                LOG_INF("Last session discarded. Terminating.");
+                TerminationFlag = true;
+            }
+        }
 
-                    return TerminationFlag.load();
-                });
+        LOG_INF("Kit poll terminated.");
 
         // Let forkit handle the jail cleanup.
     }
commit 82f2f2711b2d2b3e1f8099c0a8e8305bc516a303
Author: Jan Holesovsky <ke...@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 5a94614217b1b577dce071f6fbe797cb7f930cbc
Author: Michael Meeks <michael.me...@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 b56b73524..74fd75ada 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 9e7dff79f3cc9140f1a81bca2ce6cf8ddd46f8f6
Author: Michael Meeks <michael.me...@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 36d1a0168..373fcc79c 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 26f9243b7..d0e11aeb8 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -699,10 +699,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 4ffd8c9df..8a99428a5 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2442,7 +2442,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;
@@ -2459,7 +2459,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
@@ -2469,7 +2471,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 ca587f407004848937b6c8036aa21b438569b44a
Author: Jan Holesovsky <ke...@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 4faa4f753..c9a2968f6 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 cea442e73..856c1721a 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 4cf2ee4fab2ffa86e5047c78bb99a6e408786847
Author: Michael Meeks <michael.me...@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 7679e50ce..cea442e73 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 80a13a1e7bddbcd52486994009afaa980838992d
Author: Michael Meeks <michael.me...@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 f157720b0..7679e50ce 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);
         }
+        else if (_isClient && !socket->isWebSocket())
+            handleClientUpgrade();
         else
         {
             while (handleOneIncomingMessage(socket))
@@ -435,6 +442,59 @@ protected:
         setWebSocket();
     }
 
+    // Handle incoming upgrade to full socket as client WS.
+    void handleClientUpgrade()
+    {
+        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;
+
+        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)
+        {
+            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
+            {
+                LOG_TRC("Accepted incoming websocket request");
+                // FIXME: validate Sec-WebSocket-Accept vs. Sec-WebSocket-Key 
etc.
+                bOk = true;
+            }
+        }
+
+        if (!bOk)
+        {
+            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());
+            socket->shutdown();
+        }
+
+        setWebSocket();
+        socket->eraseFirstInputBytes(requestSize);
+    }
+
     void setWebSocket()
     {
         std::shared_ptr<StreamSocket> socket = _socket.lock();
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index 87c76048b..bc7a04781 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -214,6 +214,11 @@ int main (int argc, char **argv)
     int port = 9042;
     (void) argc; (void) argv;
 
+    if (!UnitWSD::init(UnitWSD::UnitType::Wsd, ""))
+    {
+        throw std::runtime_error("Failed to load wsd unit test library.");
+    }
+
     Log::initialize("WebSocketDump", "trace", true, false,
                     std::map<std::string, std::string>());
 
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index a4d2baec5..26f9243b7 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -269,6 +269,7 @@ void AdminSocketHandler::handleMessage(bool /* fin */, 
WSOpCode /* code */,
     }
 }
 
+/// Connection from remote admin socket
 AdminSocketHandler::AdminSocketHandler(Admin* adminManager,
                                        const std::weak_ptr<StreamSocket>& 
socket,
                                        const Poco::Net::HTTPRequest& request)
@@ -279,8 +280,9 @@ AdminSocketHandler::AdminSocketHandler(Admin* adminManager,
 {
 }
 
+/// Client connection to remote amdin socket
 AdminSocketHandler::AdminSocketHandler(Admin* adminManager)
-    : WebSocketHandler(),
+    : WebSocketHandler(true),
       _admin(adminManager),
       _sessionId(0),
       _isAuthenticated(true)
@@ -648,7 +650,6 @@ public:
     {
         LOG_TRC("Outbound monitor - connected");
         _connecting = false;
-        setWebSocket();
         return AdminSocketHandler::performWrites();
     }
 };
@@ -713,7 +714,8 @@ void Admin::connectToMonitor(const Poco::URI &uri)
                         std::ostringstream oss;
                         oss << "GET " << uri.getHost() << " HTTP/1.1\r\n"
                             "Connection:Upgrade\r\n"
-                            
"Sec-WebSocket-Accept:GAcwqP21iVOY2yKefQ64c0yVN5M=\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"
commit b483f477ddfc20d936a7e202449911c4ec6b525b
Author: Michael Meeks <michael.me...@collabora.com>
Date:   Wed Apr 18 19:20:54 2018 +0100

    Allow a 'monitor' to be connected to remotely if configured.
    
    So far monitors have the access an permissions of an
    authenticated admin.
    
    Change-Id: I59dfa8a646a60584a5c113ee0521e9afba4f6b76

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 33fd52c1b..4937055a3 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -116,4 +116,8 @@
         <password desc="The password of the admin console. Deprecated on most 
platforms. Instead, use PAM or loolconfig to set up a secure 
password."></password>
     </admin_console>
 
+    <monitors desc="Addresses of servers we connect to on start for 
monitoring">
+       <monitor>ws://localhost:9042/foo</monitor>
+    </monitors>
+
 </config>
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 84632a4f2..36d1a0168 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -118,7 +118,7 @@ public:
     }
 
     /// Create socket of the given type.
-    int createSocket(Type type);
+    static int createSocket(Type type);
 
     /// Returns the OS native socket fd.
     int getFD() const { return _fd; }
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index b363364fd..f157720b0 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -432,7 +432,12 @@ protected:
         const std::string res = oss.str();
         LOG_TRC("#" << socket->getFD() << ": Sending WS Upgrade response: " << 
res);
         socket->send(res);
+        setWebSocket();
+    }
 
+    void setWebSocket()
+    {
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
         socket->setWebSocket();
 
         // No need to ping right upon connection/upgrade,
diff --git a/tools/WebSocketDump.cpp b/tools/WebSocketDump.cpp
index a72e646c4..87c76048b 100644
--- a/tools/WebSocketDump.cpp
+++ b/tools/WebSocketDump.cpp
@@ -28,11 +28,15 @@
 #  include <SslSocket.hpp>
 #endif
 
+SocketPoll DumpSocketPoll("websocket");
+
 // Dumps incoming websocket messages and doesn't respond.
 class DumpSocketHandler : public WebSocketHandler
 {
 public:
-    DumpSocketHandler()
+    DumpSocketHandler(const std::weak_ptr<StreamSocket>& socket,
+                      const Poco::Net::HTTPRequest& request) :
+        WebSocketHandler(socket, request)
     {
     }
 
@@ -63,7 +67,7 @@ private:
     }
 
     /// Called after successful socket reads.
-    void handleIncomingMessage(SocketDisposition & /* disposition */) override
+    void handleIncomingMessage(SocketDisposition &disposition) override
     {
         std::shared_ptr<StreamSocket> socket = _socket.lock();
         std::vector<char>& in = socket->_inBuffer;
@@ -135,7 +139,9 @@ private:
                                                               
Poco::StringTokenizer::TOK_TRIM);
 
             if (request.find("Upgrade") != request.end() && 
Poco::icompare(request["Upgrade"], "websocket") == 0)
-                socket->setHandler(std::make_shared<DumpSocketHandler>());
+            {
+                
socket->setHandler(std::make_shared<DumpSocketHandler>(_socket, request));
+            }
             else
             {
                 Poco::Net::HTTPResponse response;
@@ -143,7 +149,7 @@ private:
                 response.setContentLength(0);
                 LOG_INF("DumpWebSockets bad request");
                 socket->send(response);
-                socket->shutdown();
+                disposition.setClosed();
             }
         }
         catch (const std::exception& exc)
@@ -187,7 +193,7 @@ class DumpSocketFactory final : public SocketFactory
 {
     std::shared_ptr<Socket> create(const int physicalFd) override
     {
-#if ENABLE_SSL
+#if 0 && ENABLE_SSL
         return StreamSocket::create<SslStreamSocket>(physicalFd, 
std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
 #else
         return StreamSocket::create<StreamSocket>(physicalFd, 
std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
@@ -208,12 +214,14 @@ int main (int argc, char **argv)
     int port = 9042;
     (void) argc; (void) argv;
 
+    Log::initialize("WebSocketDump", "trace", true, false,
+                    std::map<std::string, std::string>());
+
     SocketPoll acceptPoll("accept");
-    SocketPoll DumpSocketPoll("websocket");
 
     // Setup listening socket with a factory for connected sockets.
     auto serverSocket = std::make_shared<ServerSocket>(
-        Socket::Type::IPv4, DumpSocketPoll,
+        Socket::Type::All, DumpSocketPoll,
         std::make_shared<DumpSocketFactory>());
 
     if (!serverSocket->bind(ServerSocket::Type::Public, port))
@@ -233,7 +241,7 @@ int main (int argc, char **argv)
 
     while (true)
     {
-        DumpSocketPoll.poll(1000);
+        DumpSocketPoll.poll(100 * 1000);
     }
 }
 
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index f05a80281..a4d2baec5 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -34,6 +34,7 @@
 #include <Util.hpp>
 
 #include <net/Socket.hpp>
+#include <net/SslSocket.hpp>
 #include <net/WebSocketHandler.hpp>
 
 #include <common/SigUtil.hpp>
@@ -278,6 +279,14 @@ AdminSocketHandler::AdminSocketHandler(Admin* adminManager,
 {
 }
 
+AdminSocketHandler::AdminSocketHandler(Admin* adminManager)
+    : WebSocketHandler(),
+      _admin(adminManager),
+      _sessionId(0),
+      _isAuthenticated(true)
+{
+}
+
 void AdminSocketHandler::sendTextFrame(const std::string& message)
 {
     UnitWSD::get().onAdminQueryMessage(message);
@@ -613,8 +622,156 @@ void Admin::dumpState(std::ostream& os)
     SocketPoll::dumpState(os);
 }
 
+class MonitorSocketHandler : public AdminSocketHandler
+{
+    bool _connecting;
+public:
+
+    MonitorSocketHandler(Admin *admin) :
+        AdminSocketHandler(admin),
+        _connecting(true)
+    {
+    }
+    int getPollEvents(std::chrono::steady_clock::time_point now,
+                      int &timeoutMaxMs) override
+    {
+        if (_connecting)
+        {
+            LOG_TRC("Waiting for outbound connection to complete");
+            return POLLOUT;
+        }
+        else
+            return AdminSocketHandler::getPollEvents(now, timeoutMaxMs);
+    }
+
+    void performWrites() override
+    {
+        LOG_TRC("Outbound monitor - connected");
+        _connecting = false;
+        setWebSocket();
+        return AdminSocketHandler::performWrites();
+    }
+};
+
+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, 
handler);
+#endif
+                    if (!socket && !isSSL)
+                        socket = StreamSocket::create<StreamSocket>(fd, 
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"
+                            
"Sec-WebSocket-Accept: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());

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to