fuzzer/ClientSession.cpp | 4 ++-- loleaflet/js/global.js | 36 +++++++++++++++++++++++++----------- loleaflet/src/core/Socket.js | 5 +++++ loleaflet/src/main.js | 2 ++ net/Socket.hpp | 2 +- wsd/ClientSession.cpp | 4 ++-- wsd/ClientSession.hpp | 2 +- wsd/DocumentBroker.cpp | 8 ++++---- wsd/DocumentBroker.hpp | 10 ++++------ wsd/LOOLWSD.cpp | 24 +++++++----------------- wsd/ProxyProtocol.cpp | 43 +++++++++++++++++++++++++++++++++++-------- wsd/ProxyProtocol.hpp | 9 ++++++--- 12 files changed, 94 insertions(+), 55 deletions(-)
New commits: commit 99d6282dde64b9528eec54bcd960ea547823571a Author: Michael Meeks <[email protected]> AuthorDate: Sat May 9 19:41:40 2020 +0100 Commit: Michael Meeks <[email protected]> CommitDate: Wed May 13 00:53:36 2020 +0200 Proxy: re-arrange URL structure & document it better. Also implement 'close' during browser unload. Change-Id: Ie2072c14cf863876c633b3371b86016367ad4992 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94089 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Meeks <[email protected]> diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index c5546f7c2..f0556a5fb 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -213,10 +213,11 @@ this.binaryType = 'arraybuffer'; this.bufferedAmount = 0; this.extensions = ''; + this.unloading = false; this.protocol = ''; this.connected = true; this.readyState = 0; // connecting - this.sessionId = 'fetchsession'; + this.sessionId = 'open'; this.id = window.proxySocketCounter++; this.sendCounter = 0; this.readWaiting = 0; @@ -299,9 +300,8 @@ console.debug('send msg "' + that.sendQueue + '"'); var req = new XMLHttpRequest(); req.open('POST', that.getEndPoint('write')); - req.setRequestHeader('SessionId', that.sessionId); - if (that.sessionId === 'fetchsession') - console.debug('session fetch not completed'); + if (that.sessionId === 'open') + console.debug('new session not completed'); else { req.responseType = 'arraybuffer'; @@ -318,8 +318,7 @@ }; this.getSessionId = function() { var req = new XMLHttpRequest(); - req.open('POST', that.getEndPoint('write')); - req.setRequestHeader('SessionId', that.sessionId); + req.open('POST', that.getEndPoint('open')); req.responseType = 'text'; req.addEventListener('load', function() { console.debug('got session: ' + this.responseText); @@ -344,19 +343,35 @@ 'B0x' + this.outSerial.toString(16) + '\n' + '0x' + msg.length.toString(16) + '\n' + msg + '\n'); this.outSerial++; - if (this.sessionId !== 'fetchsession' && this.sendTimeout === undefined) + if (this.sessionId !== 'open' && this.sendTimeout === undefined) this.sendTimeout = setTimeout(this.doSend, 2 /* ms */); }; + this.sendCloseMsg = function(beacon) { + var url = that.getEndPoint('close'); + if (!beacon) + { + var req = new XMLHttpRequest(); + req.open('POST', url); + req.send(''); + } + else + navigator.sendBeacon(url, ''); + }; this.close = function() { console.debug('proxy: close socket'); this.readyState = 3; this.onclose(); clearInterval(this.waitInterval); this.waitInterval = undefined; + this.sendCloseMsg(this.unloading); + this.sessionId = 'open'; + }; + this.setUnloading = function() { + this.unloading = true; }; - this.getEndPoint = function(type) { + this.getEndPoint = function(command) { var base = this.uri; - return base + '/' + type + '/' + this.outSerial; + return base + '/' + this.sessionId + '/' + command + '/' + this.outSerial; }; console.debug('proxy: new socket ' + this.id + ' ' + this.uri); @@ -368,7 +383,7 @@ console.debug('proxy: waiting - ' + that.readWaiting + ' on session ' + that.sessionId); if (that.readWaiting >= 4) // max 4 waiting connections concurrently. return; - if (that.sessionId == 'fetchsession') + if (that.sessionId == 'open') return; // waiting for our session id. var req = new XMLHttpRequest(); // fetch session id: @@ -384,7 +399,6 @@ that.waitConnect(); }); req.open('GET', that.getEndPoint('wait')); - req.setRequestHeader('SessionId', that.sessionId); req.responseType = 'arraybuffer'; req.send(''); that.readWaiting++; diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index f6ba2a5c1..a5e394c67 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -104,6 +104,11 @@ L.Socket = L.Class.extend({ 120 * 1000); }, + setUnloading: function() { + if (this.socket.setUnloading) + this.socket.setUnloading(); + }, + close: function () { this.socket.onerror = function () {}; this.socket.onclose = function () {}; diff --git a/loleaflet/src/main.js b/loleaflet/src/main.js index 7f8fbb267..6b9390058 100644 --- a/loleaflet/src/main.js +++ b/loleaflet/src/main.js @@ -79,6 +79,8 @@ map.loadDocument(global.socket); global.socket = map._socket; window.addEventListener('beforeunload', function () { if (map && map._socket) { + if (map._socket.setUnloading) + map._socket.setUnloading(); map._socket.close(); } }); diff --git a/net/Socket.hpp b/net/Socket.hpp index c004a1748..967e7b8c6 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -385,7 +385,7 @@ public: /// Do some of the queued writing. virtual void performWrites() = 0; - /// Called when the is disconnected and will be destroyed. + /// Called when the socket is disconnected and will be destroyed. /// Will be called exactly once. virtual void onDisconnect() {} diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 939743fe4..17300d0cb 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -158,13 +158,11 @@ public: /// Find or create a new client session for the PHP proxy void handleProxyRequest( - const std::string& sessionId, const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, const RequestDetails &requestDetails, - const std::shared_ptr<StreamSocket> &socket, - bool isWaiting); + const std::shared_ptr<StreamSocket> &socket); /// Thread safe termination of this broker if it has a lingering thread void joinThread(); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 600c1e369..b03574724 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2909,11 +2909,7 @@ private: Poco::MemoryInputStream& message, SocketDisposition &disposition) { - if (!request.has("SessionId")) - throw BadRequestException("No session id header on proxied request"); - std::string url = requestDetails.getDocumentURI(); - std::string sessionId = request.get("SessionId"); LOG_INF("URL [" << url << "]."); const auto uriPublic = DocumentBroker::sanitizeURI(url); @@ -2943,15 +2939,12 @@ private: // Request a kit process for this doc. std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( none, DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic); - - std::string fullURL = request.getURI(); - std::string ending = "/ws/wait"; - bool isWaiting = fullURL.find(ending) != std::string::npos; if (docBroker) { // need to move into the DocumentBroker context before doing session lookup / creation etc. std::string id = _id; - disposition.setMove([docBroker, id, uriPublic, isReadOnly, requestDetails, sessionId, isWaiting] + disposition.setMove([docBroker, id, uriPublic, + isReadOnly, requestDetails] (const std::shared_ptr<Socket> &moveSocket) { LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey()); @@ -2961,8 +2954,8 @@ private: // We no longer own this socket. moveSocket->setThreadOwner(std::thread::id()); - docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, requestDetails, - sessionId, moveSocket, isWaiting]() + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, + requestDetails, moveSocket]() { // Now inside the document broker thread ... LOG_TRC("In the docbroker thread for " << docBroker->getDocKey()); @@ -2971,8 +2964,8 @@ private: try { docBroker->handleProxyRequest( - sessionId, id, uriPublic, isReadOnly, - requestDetails, streamSocket, isWaiting); + id, uriPublic, isReadOnly, + requestDetails, streamSocket); return; } catch (const UnauthorizedRequestException& exc) diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 8784c39b1..7a5ef1b4c 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -7,6 +7,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * The ProxyProtocol creates a web-socket like connection over HTTP + * requests. URLs are formed like this: + * 0 1 2 3 4 5 + * /lool/<encoded-document-url>/ws/<session-id>/<command>/<serial> + * <session-id> can be 'unknown' + * <command> can be 'open', 'write', 'wait', or 'close' + */ + #include <config.h> #include "DocumentBroker.hpp" @@ -20,18 +29,18 @@ #include <cassert> void DocumentBroker::handleProxyRequest( - const std::string& sessionId, const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, const RequestDetails &requestDetails, - const std::shared_ptr<StreamSocket> &socket, - bool isWaiting) + const std::shared_ptr<StreamSocket> &socket) { - std::shared_ptr<ClientSession> clientSession; - + const size_t session = 3; + const size_t command = 4; + const size_t serial = 5; - if (sessionId == "fetchsession") + std::shared_ptr<ClientSession> clientSession; + if (requestDetails.equals(command, "open")) { bool isLocal = socket->isLocal(); LOG_TRC("proxy: validate that socket is from localhost: " << isLocal); @@ -64,6 +73,7 @@ void DocumentBroker::handleProxyRequest( } else { + std::string sessionId = requestDetails[session]; LOG_TRC("proxy: find session for " << _docKey << " with id " << sessionId); for (const auto &it : _sessions) { @@ -87,9 +97,16 @@ void DocumentBroker::handleProxyRequest( // this DocumentBroker's poll handles reading & writing addSocketToPoll(socket); - auto proxy = std::static_pointer_cast<ProxyProtocolHandler>( - protocol); + auto proxy = std::static_pointer_cast<ProxyProtocolHandler>(protocol); + if (requestDetails.equals(command, "close")) + { + LOG_TRC("Close session"); + proxy->notifyDisconnected(); + return; + } + (void)serial; // in URL for logging, debugging, and uniqueness. + bool isWaiting = requestDetails.equals(command, "wait"); proxy->handleRequest(isWaiting, socket); } @@ -98,9 +115,11 @@ bool ProxyProtocolHandler::parseEmitIncoming( { std::vector<char> &in = socket->getInBuffer(); +#if 0 // protocol debugging. std::stringstream oss; socket->dumpState(oss); LOG_TRC("Parse message:\n" << oss.str()); +#endif while (in.size() > 0) { @@ -219,6 +238,12 @@ void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition) LOG_ERR("If you got here, it means we failed to parse this properly in handleRequest: " << oss.str()); } +void ProxyProtocolHandler::notifyDisconnected() +{ + if (_msgHandler) + _msgHandler->onDisconnect(); +} + int ProxyProtocolHandler::sendMessage(const char *msg, const size_t len, bool text, bool flush) { _writeQueue.push_back(std::make_shared<Message>(msg, len, text, _outSerial++)); diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index 7bca66600..4a890d8f6 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -30,9 +30,7 @@ public: virtual ~ProxyProtocolHandler() { } /// Will be called exactly once by setHandler - void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override - { - } + void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override {} /// Called after successful socket reads. void handleIncomingMessage(SocketDisposition &/* disposition */) override; @@ -61,10 +59,15 @@ public: void getIOStats(uint64_t &sent, uint64_t &recv) override; void dumpState(std::ostream& os) override; bool parseEmitIncoming(const std::shared_ptr<StreamSocket> &socket); + void handleRequest(bool isWaiting, const std::shared_ptr<Socket> &socket); + /// tell our handler we've received a close. + void notifyDisconnected(); + private: std::shared_ptr<StreamSocket> popOutSocket(); + /// can we find anything to send back if we try ? bool slurpHasMessages(); int sendMessage(const char *msg, const size_t len, bool text, bool flush); bool flushQueueTo(const std::shared_ptr<StreamSocket> &socket); commit 34fc7fb6b726dcdf0c40f19ed402c3728581d1a2 Author: Michael Meeks <[email protected]> AuthorDate: Tue May 12 21:10:56 2020 +0100 Commit: Michael Meeks <[email protected]> CommitDate: Wed May 13 00:53:24 2020 +0200 Proxy: move requestDetails closer to ProxyProtocol. Change-Id: I07c00ea1dad15fd70b658a04f722cbd516fd5c18 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94088 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Meeks <[email protected]> diff --git a/fuzzer/ClientSession.cpp b/fuzzer/ClientSession.cpp index edd4053d1..e24647315 100644 --- a/fuzzer/ClientSession.cpp +++ b/fuzzer/ClientSession.cpp @@ -22,9 +22,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) std::shared_ptr<ProtocolHandlerInterface> ws; std::string id; bool isReadOnly = false; - const ServerURL serverURL; + const RequestDetails requestDetails("fuzzer"); auto session - = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, serverURL); + = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, requestDetails); std::string input(reinterpret_cast<const char*>(data), size); std::stringstream ss(input); diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 41d9bbcd0..3991c3d92 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -44,7 +44,7 @@ ClientSession::ClientSession( const std::shared_ptr<DocumentBroker>& docBroker, const Poco::URI& uriPublic, const bool readOnly, - const ServerURL &serverURL) : + const RequestDetails &requestDetails) : Session(ws, "ToClient-" + id, id, readOnly), _docBroker(docBroker), _uriPublic(uriPublic), @@ -58,7 +58,7 @@ ClientSession::ClientSession( _tileWidthTwips(0), _tileHeightTwips(0), _kitViewId(-1), - _serverURL(serverURL), + _serverURL(requestDetails), _isTextDocument(false) { const size_t curConnections = ++LOOLWSD::NumConnections; diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index a99832560..865649c30 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -34,7 +34,7 @@ public: const std::shared_ptr<DocumentBroker>& docBroker, const Poco::URI& uriPublic, const bool isReadOnly, - const ServerURL &serverURL); + const RequestDetails &requestDetails); void construct(); virtual ~ClientSession(); diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 1cbfcac13..3293dea94 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -1526,7 +1526,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession( const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const ServerURL &serverURL) + const RequestDetails &requestDetails) { try { @@ -1541,7 +1541,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession( // In case of WOPI, if this session is not set as readonly, it might be set so // later after making a call to WOPI host which tells us the permission on files // (UserCanWrite param). - auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, serverURL); + auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, requestDetails); session->construct(); return session; @@ -2269,8 +2269,8 @@ bool ConvertToBroker::startConversion(SocketDisposition &disposition, const std: const bool isReadOnly = true; // FIXME: associate this with moveSocket (?) std::shared_ptr<ProtocolHandlerInterface> nullPtr; - ServerURL serverURL; - _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, serverURL); + RequestDetails requestDetails("convert-to"); + _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, requestDetails); _clientSession->construct(); if (!_clientSession) diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index f086ddea6..939743fe4 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -97,7 +97,7 @@ private: int _smapsFD; }; -class ServerURL; +class RequestDetails; class ClientSession; /// DocumentBroker is responsible for setting up a document in jail and brokering loading it from @@ -154,7 +154,7 @@ public: const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const ServerURL &serverURL); + const RequestDetails &requestDetails); /// Find or create a new client session for the PHP proxy void handleProxyRequest( @@ -162,7 +162,7 @@ public: const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const ServerURL &serverURL, + const RequestDetails &requestDetails, const std::shared_ptr<StreamSocket> &socket, bool isWaiting); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 7f455e93a..600c1e369 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2936,8 +2936,6 @@ private: } } - ServerURL serverURL(requestDetails); - LOG_INF("URL [" << LOOLWSD::anonymizeUrl(url) << "] is " << (isReadOnly ? "readonly" : "writable") << "."); (void)request; (void)message; (void)disposition; @@ -2953,7 +2951,7 @@ private: { // need to move into the DocumentBroker context before doing session lookup / creation etc. std::string id = _id; - disposition.setMove([docBroker, id, uriPublic, isReadOnly, serverURL, sessionId, isWaiting] + disposition.setMove([docBroker, id, uriPublic, isReadOnly, requestDetails, sessionId, isWaiting] (const std::shared_ptr<Socket> &moveSocket) { LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey()); @@ -2963,7 +2961,7 @@ private: // We no longer own this socket. moveSocket->setThreadOwner(std::thread::id()); - docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, serverURL, + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, requestDetails, sessionId, moveSocket, isWaiting]() { // Now inside the document broker thread ... @@ -2974,7 +2972,7 @@ private: { docBroker->handleProxyRequest( sessionId, id, uriPublic, isReadOnly, - serverURL, streamSocket, isWaiting); + requestDetails, streamSocket, isWaiting); return; } catch (const UnauthorizedRequestException& exc) @@ -3066,9 +3064,8 @@ private: DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic); if (docBroker) { - ServerURL serverURL(requestDetails); std::shared_ptr<ClientSession> clientSession = - docBroker->createNewClientSession(ws, _id, uriPublic, isReadOnly, serverURL); + docBroker->createNewClientSession(ws, _id, uriPublic, isReadOnly, requestDetails); if (clientSession) { // Transfer the client socket to the DocumentBroker when we get back to the poll: diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 7c03d65e8..8784c39b1 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -24,11 +24,13 @@ void DocumentBroker::handleProxyRequest( const std::string& id, const Poco::URI& uriPublic, const bool isReadOnly, - const ServerURL &serverURL, + const RequestDetails &requestDetails, const std::shared_ptr<StreamSocket> &socket, bool isWaiting) { std::shared_ptr<ClientSession> clientSession; + + if (sessionId == "fetchsession") { bool isLocal = socket->isLocal(); @@ -39,7 +41,7 @@ void DocumentBroker::handleProxyRequest( LOG_TRC("proxy: Create session for " << _docKey); clientSession = createNewClientSession( std::make_shared<ProxyProtocolHandler>(), - id, uriPublic, isReadOnly, serverURL); + id, uriPublic, isReadOnly, requestDetails); addSession(clientSession); LOOLWSD::checkDiskSpaceAndWarnClients(true); LOOLWSD::checkSessionLimitsAndWarnClients(); _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
