loleaflet/src/core/Socket.js | 46 +++++++++++++++++++------------------ loleaflet/src/map/Map.js | 8 +++--- loolwsd/LOOLWSD.cpp | 53 +++++++++++++++++++++++++++++++++++++++---- loolwsd/UserMessages.hpp | 3 +- loolwsd/test/helpers.hpp | 2 - loolwsd/test/httpwserror.cpp | 33 +++++++++++--------------- 6 files changed, 94 insertions(+), 51 deletions(-)
New commits: commit e766e82f3d118ef0c5f2d8dac9a14255232b2dd8 Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 29 13:48:08 2016 -0400 loleaflet: fix undefined variables diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js index f456ce6..5110c60 100644 --- a/loleaflet/src/map/Map.js +++ b/loleaflet/src/map/Map.js @@ -754,7 +754,7 @@ L.Map = L.Evented.extend({ // A dialog is already dimming the screen and probably // shows an error message. Leave it alone. this._active = false; - this._docLayer._onMessage('textselection:', null); + this._docLayer && this._docLayer._onMessage('textselection:', null); if (this._socket.connected()) { this._socket.sendMessage('userinactive'); } @@ -798,7 +798,7 @@ L.Map = L.Evented.extend({ $(options.appendLocation).append(options.$vex); vex.setupBodyClassName(options.$vex); - map._docLayer._onMessage('textselection:', null); + map._doclayer && map._docLayer._onMessage('textselection:', null); map._socket.sendMessage('userinactive'); }, 30 * 1000); // Dim in 30 seconds. @@ -808,7 +808,7 @@ L.Map = L.Evented.extend({ if (!this._loaded) { return; } var doclayer = this._docLayer; - if (doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) { + if (doclayer && doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) { doclayer._visibleCursorOnLostFocus = doclayer._visibleCursor; doclayer._isCursorOverlayVisibleOnLostFocus = doclayer._isCursorVisibleOnLostFocus = true; doclayer._isCursorOverlayVisible = false; @@ -822,7 +822,7 @@ L.Map = L.Evented.extend({ if (!this._loaded) { return; } var doclayer = this._docLayer; - if (doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) { + if (doclayer && doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) { // we restore the old cursor position by a small delay, so that if the user clicks // inside the document we skip to restore it, so that the user does not see the cursor // jumping from the old position to the new one commit 3fcde8be836da5b54a110f43f888030fd749b093 Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 29 09:50:00 2016 -0400 loleaflet: fix show dialog error Conflicts: loleaflet/src/core/Socket.js diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index d66c578..937af32 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -12,17 +12,17 @@ L.Socket = L.Class.extend({ if (map.options.permission) { map.options.docParams['permission'] = map.options.permission; } - this.socket = new WebSocket(map.options.server + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws'); + this.socket = new WebSocket(map.options.server + '/lool/ws/' + map.options.doc + '?' + $.param(map.options.docParams)); + this.socket.onerror = L.bind(this._onSocketError, this); + this.socket.onclose = L.bind(this._onSocketClose, this); + this.socket.onopen = L.bind(this._onSocketOpen, this); + this.socket.onmessage = L.bind(this._onMessage, this); + this.socket.binaryType = 'arraybuffer'; } catch (e) { - this.fire('error', {msg: _('Oops, there is a problem connecting to LibreOffice Online : ' + e), cmd: 'socket', kind: 'failed', id: 3}); + this._map.fire('error', {msg: _('Oops, there is a problem connecting to LibreOffice Online : ' + e), cmd: 'socket', kind: 'failed', id: 3}); return null; } this._msgQueue = []; - this.socket.onerror = L.bind(this._onSocketError, map); - this.socket.onclose = L.bind(this._onSocketClose, map); - this.socket.onopen = L.bind(this._onOpen, this); - this.socket.onmessage = L.bind(this._onMessage, this); - this.socket.binaryType = 'arraybuffer'; }, close: function () { @@ -69,7 +69,7 @@ L.Socket = L.Class.extend({ this.socket.send(msg); }, - _onOpen: function () { + _onSocketOpen: function () { // Always send the protocol version number. // TODO: Move the version number somewhere sensible. this._doSend('loolclient ' + this.ProtocolVersionNumber); @@ -134,7 +134,7 @@ L.Socket = L.Class.extend({ // TODO: For now we expect perfect match in protocol versions if (loolwsdVersionObj.Protocol !== this.ProtocolVersionNumber) { - this.fire('error', {msg: _('Unsupported server version.')}); + this._map.fire('error', {msg: _('Unsupported server version.')}); } } else if (textMsg.startsWith('lokitversion ')) { @@ -186,7 +186,11 @@ L.Socket = L.Class.extend({ } } else if (textMsg.startsWith('error:') && !this._map._docLayer) { - this.fail = true; + textMsg = textMsg.substring(6); + vex.dialog.alert({ + message: textMsg, + css: {'font-size': 'small'} + }); } else if (textMsg === 'pong' && this._map._docLayer && this._map._docLayer._debug) { var times = this._map._docLayer._debugTimePING; @@ -301,26 +305,24 @@ L.Socket = L.Class.extend({ }, _onSocketError: function () { - this.hideBusy(); + this._map.hideBusy(); // Let onclose (_onSocketClose) report errors. }, - _onSocketClose: function () { - this.hideBusy(); - if (this) { - this._active = false; - } + _onSocketClose: function (e) { + this._map.hideBusy(); + this._map._active = false; - if (this._docLayer) { - this._docLayer.removeAllViews(); + if (this._map._docLayer) { + this._map._docLayer.removeAllViews(); } - if (this.fail) { - this.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4}); + + if (e.code && e.reason) { + this._map.fire('error', {msg: e.reason}); } else { - this.fire('error', {msg: _('We are sorry, this is an unexpected connection error. Please try again.'), cmd: 'socket', kind: 'closed', id: 4}); + this._map.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4}); } - this.fail = false; }, parseServerCmd: function (msg) { commit 6f7d67a20472f16a78c58e0dd8017195b0d4bb5d Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 29 09:48:09 2016 -0400 loolwsd: control frames must have a payload of 125 bytes diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index 460a6c9..146449c 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -180,6 +180,50 @@ static std::map<std::string, std::shared_ptr<PrisonerSession>> AvailableChildSes static int careerSpanSeconds = 0; #endif +namespace { + +static inline +void lcl_shutdownLimitReached(WebSocket& ws) +{ + const std::string msg = std::string("error: ") + Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS, + std::string(LOOLWSD_PRODUCT), + std::string(LOOLWSD_URL), + std::string(LOOLWSD_URL)); + + /* loleaflet sends loolclient, load and partrectangles message immediately + after web socket handshake, so closing web socket fails loading page in + some sensible browsers. Ignore handshake messages and gracefully + close in order to send error messages. + */ + try + { + int flags = 0; + int handshake = 3; + std::vector<char> buffer(READ_BUFFER_SIZE * 100); + + // 5 seconds timeout + ws.setReceiveTimeout(5000000); + do + { + // ignore loolclient, load and partpagerectangles + ws.receiveFrame(buffer.data(), buffer.capacity(), flags); + if (--handshake == 0) + { + ws.sendFrame(msg.data(), msg.size()); + ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS)); + } + } + while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE); + } + catch (Exception& e) + { + ws.sendFrame(msg.data(), msg.size()); + ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS)); + } +} + +} + static void forkChildren(const int number) { Util::assertIsLocked(newChildrenMutex); @@ -657,7 +701,8 @@ private: { --LOOLWSD::NumDocBrokers; Log::error("Maximum number of open documents reached."); - throw WebSocketErrorMessageException(Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS, std::string(LOOLWSD_PRODUCT), std::string(LOOLWSD_URL), std::string(LOOLWSD_URL))); + lcl_shutdownLimitReached(*ws); + return; } #endif @@ -882,9 +927,9 @@ public: { --LOOLWSD::NumConnections; Log::error("Maximum number of connections reached."); - response.setStatusAndReason(HTTPResponse::HTTP_NOT_ACCEPTABLE, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS, std::string(LOOLWSD_PRODUCT), std::string(LOOLWSD_URL), std::string(LOOLWSD_URL))); - response.setContentLength(0); - response.send(); + // accept hand shake + WebSocket ws(request, response); + lcl_shutdownLimitReached(ws); return; } #endif diff --git a/loolwsd/UserMessages.hpp b/loolwsd/UserMessages.hpp index 8d1dee7..7aa7aa4 100644 --- a/loolwsd/UserMessages.hpp +++ b/loolwsd/UserMessages.hpp @@ -15,7 +15,8 @@ //NOTE: For whatever reason Poco seems to trim the first character. constexpr auto SERVICE_UNAVALABLE_INTERNAL_ERROR = " Service is unavailable. Please try again later and report to your administrator if the issue persists."; -constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %s checkout - <a href=\"%s\">%s</a>."; +constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections"; +constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %s checkout: <br/><a href=\"%s\">%s</a>."; #endif diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp index fd9c572..01922e3 100644 --- a/loolwsd/test/helpers.hpp +++ b/loolwsd/test/helpers.hpp @@ -198,7 +198,7 @@ int getErrorCode(Poco::Net::WebSocket& ws, std::string& message) Poco::Buffer<char> buffer(READ_BUFFER_SIZE); message.clear(); - Poco::Timespan timeout(250000); + Poco::Timespan timeout(5000000); ws.setReceiveTimeout(timeout); do { diff --git a/loolwsd/test/httpwserror.cpp b/loolwsd/test/httpwserror.cpp index b8b8afb..a7f8385 100644 --- a/loolwsd/test/httpwserror.cpp +++ b/loolwsd/test/httpwserror.cpp @@ -97,6 +97,10 @@ void HTTPWSError::testMaxDocuments() Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL); std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri)); Poco::Net::WebSocket socket(*session, request, _response); + // send loolclient, load and partpagerectangles + sendTextFrame(socket, "loolclient "); + sendTextFrame(socket, "load "); + sendTextFrame(socket, "partpagerectangles "); statusCode = getErrorCode(socket, message); CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode); CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos); @@ -117,6 +121,7 @@ void HTTPWSError::testMaxConnections() std::string docPath; std::string docURL; std::string message; + Poco::UInt16 statusCode; std::vector<std::shared_ptr<Poco::Net::WebSocket>> views; getDocumentPathAndURL("empty.odt", docPath, docURL); @@ -125,31 +130,21 @@ void HTTPWSError::testMaxConnections() for(int it = 1; it < MAX_CONNECTIONS; it++) { - std::cerr << it << std::endl; std::unique_ptr<Poco::Net::HTTPClientSession> session(createSession(_uri)); auto ws = std::make_shared<Poco::Net::WebSocket>(*session, request, _response); views.emplace_back(ws); } // try to connect MAX_CONNECTIONS + 1 - { - // Load a document and get its status. - const std::string documentURL = "lool/ws/file:///fake.doc"; - - Poco::Net::HTTPResponse response; - Poco::Net::HTTPRequest requestN(Poco::Net::HTTPRequest::HTTP_GET, documentURL); - std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri)); - requestN.set("Connection", "Upgrade"); - requestN.set("Upgrade", "websocket"); - requestN.set("Sec-WebSocket-Version", "13"); - requestN.set("Sec-WebSocket-Key", ""); - requestN.setChunkedTransferEncoding(false); - session->setKeepAlive(true); - session->sendRequest(requestN); - session->receiveResponse(response); - CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTPResponse::HTTP_NOT_ACCEPTABLE, response.getStatus()); - CPPUNIT_ASSERT_MESSAGE("Wrong error message ", response.getReason().find("This development build") != std::string::npos); - } + std::unique_ptr<Poco::Net::HTTPClientSession> session(createSession(_uri)); + auto socketN = std::make_shared<Poco::Net::WebSocket>(*session, request, _response); + // send loolclient, load and partpagerectangles + sendTextFrame(socketN, "loolclient "); + sendTextFrame(socketN, "load "); + sendTextFrame(socketN, "partpagerectangles "); + statusCode = getErrorCode(*socketN, message); + CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode); + CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos); } catch (const Poco::Exception& exc) { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits