[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-1-0' - 3 commits - loleaflet/dist loleaflet/src loolwsd/LOOLWSD.cpp loolwsd/protocol.txt loolwsd/test loolwsd/UserMessages.hpp
Henry Castro
hcastro at collabora.com
Tue Oct 4 00:01:55 UTC 2016
loleaflet/dist/errormessages.js | 1
loleaflet/src/control/Control.Dialog.js | 3
loleaflet/src/core/Socket.js | 49 +++++----
loleaflet/src/map/Map.js | 8 -
loolwsd/LOOLWSD.cpp | 50 +++++++++-
loolwsd/UserMessages.hpp | 7 +
loolwsd/protocol.txt | 2
loolwsd/test/Makefile.am | 2
loolwsd/test/data/empty.odt |binary
loolwsd/test/helpers.hpp | 28 +++++
loolwsd/test/httpwserror.cpp | 158 ++++++++++++++++++++++++++++++++
11 files changed, 277 insertions(+), 31 deletions(-)
New commits:
commit 21ab135ec4b96a948913c67d13e0f8d7f84895b7
Author: Henry Castro <hcastro at collabora.com>
Date: Mon Oct 3 18:30:18 2016 -0400
loleaflet: fix show dialog error
diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index 6fa628b..0ea91ff 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -1,2 +1,3 @@
var wrongwopisrc = _('Wrong WOPISrc, usage: WOPISrc=valid encoded URI, or file_path, usage: file_path=/path/to/doc/');
var emptyhosturl = _('The host URL is empty. The loolwsd server is probably misconfigured, please contact the administrator.');
+var limitreached = _('This development build is limited to %0 documents, and %1 connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploy ing and scaling %2 checkout: <br/><a href=\"%3\">%3</a>.');
diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.Dialog.js
index 9417a8b..41836ae 100644
--- a/loleaflet/src/control/Control.Dialog.js
+++ b/loleaflet/src/control/Control.Dialog.js
@@ -10,7 +10,8 @@ L.Control.Dialog = L.Control.extend({
},
_onError: function (e) {
- if (vex.dialogID > 0) {
+ if (vex.dialogID > 0 && !this._map._fatal) {
+ // TODO. queue message errors and pop-up dialogs
// Close other dialogs before presenting a new one.
vex.close(vex.dialogID);
}
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index abf9165..59ceb10 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -10,16 +10,16 @@ L.Socket = L.Class.extend({
this._map = map;
try {
this.socket = new WebSocket(map.options.server + '/lool/' + encodeURIComponent(map.options.doc) + '/ws');
+ 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 () {
@@ -66,7 +66,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);
@@ -132,7 +132,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 ')) {
@@ -184,11 +184,20 @@ L.Socket = L.Class.extend({
}
}
else if (textMsg.startsWith('error:') && !this._map._docLayer) {
- this.fail = true;
+ textMsg = textMsg.substring(6);
+ if (command.errorKind === 'limitreached') {
+ this._map._fatal = true;
+ textMsg = limitreached;
+ textMsg = textMsg.replace(/%0/g, command.params[0]);
+ textMsg = textMsg.replace(/%1/g, command.params[1]);
+ textMsg = textMsg.replace(/%2/g, (typeof brandProductName !== 'undefined' ? brandProductName : 'LibreOffice Online'));
+ textMsg = textMsg.replace(/%3/g, (typeof brandProductURL !== 'undefined' ? brandProductURL : 'https://wiki.documentfoundation.org/Development/LibreOffice_Online'));
+ }
+ this._map.fire('error', {msg: textMsg});
}
else if (textMsg.startsWith('statusindicator:')) {
//FIXME: We should get statusindicator when saving too, no?
- this._map.showBusy('Connecting...', false);
+ this._map.showBusy(_('Connecting...'), false);
}
else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:')) {
// log the tile msg separately as we need the tile coordinates
@@ -291,23 +300,20 @@ L.Socket = L.Class.extend({
},
_onSocketError: function () {
- this.hideBusy();
+ this._map.hideBusy();
// Let onclose (_onSocketClose) report errors.
},
- _onSocketClose: function () {
- this.hideBusy();
- if (this._map) {
- this._map._active = false;
- }
+ _onSocketClose: function (e) {
+ this._map.hideBusy();
+ this._map._active = false;
- 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) {
@@ -382,6 +388,9 @@ L.Socket = L.Class.extend({
else if (tokens[i].substring(0, 5) === 'font=') {
command.font = window.decodeURIComponent(tokens[i].substring(5));
}
+ else if (tokens[i].substring(0, 7) === 'params=') {
+ command.params = tokens[i].substring(7).split(',');
+ }
}
if (command.tileWidth && command.tileHeight && this._map._docLayer) {
var defaultZoom = this._map.options.zoom;
commit 8cd78ef851840e74e266a8e5f8c17c7cd645d171
Author: Henry Castro <hcastro at 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 ea3f06f..3bcc9a2 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -736,7 +736,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');
}
@@ -776,7 +776,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.
@@ -784,7 +784,7 @@ L.Map = L.Evented.extend({
_onLostFocus: function () {
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;
@@ -796,7 +796,7 @@ L.Map = L.Evented.extend({
_onGotFocus: function () {
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 41605abcd28ff4abaca81ad64ce1071c0420d08c
Author: Henry Castro <hcastro at collabora.com>
Date: Mon Oct 3 16:57:59 2016 -0400
loolwsd: bccu#2022, User warning on hitting limit
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 00e9471..94ae192 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -174,6 +174,48 @@ static std::map<std::string, std::shared_ptr<MasterProcessSession>> AvailableChi
static int careerSpanSeconds = 0;
#endif
+namespace {
+
+static inline
+void lcl_shutdownLimitReached(WebSocket& ws)
+{
+ const std::string error = Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS);
+ const std::string close = Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, static_cast<int>(WebSocket::WS_POLICY_VIOLATION));
+
+ /* 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 retries = 7;
+ 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 (--retries == 4)
+ {
+ ws.sendFrame(error.data(), error.size());
+ ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
+ }
+ }
+ while (retries > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
+ }
+ catch (Exception&)
+ {
+ ws.sendFrame(error.data(), error.size());
+ ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
+ }
+}
+
+}
+
static void forkChildren(const int number)
{
Util::assertIsLocked(newChildrenMutex);
@@ -620,7 +662,8 @@ private:
{
--LOOLWSD::NumDocBrokers;
Log::error("Maximum number of open documents reached.");
- throw WebSocketErrorMessageException(SERVICE_UNAVALABLE_LIMIT_REACHED);
+ lcl_shutdownLimitReached(*ws);
+ return;
}
#endif
@@ -841,7 +884,10 @@ public:
{
--LOOLWSD::NumConnections;
Log::error("Maximum number of connections reached.");
- throw WebSocketErrorMessageException(SERVICE_UNAVALABLE_LIMIT_REACHED);
+ // accept hand shake
+ WebSocket ws(request, response);
+ lcl_shutdownLimitReached(ws);
+ return;
}
#endif
diff --git a/loolwsd/UserMessages.hpp b/loolwsd/UserMessages.hpp
index 5208b09..36e6ad3 100644
--- a/loolwsd/UserMessages.hpp
+++ b/loolwsd/UserMessages.hpp
@@ -12,8 +12,11 @@
#ifndef INCLUDED_USERMESSAGES_HPP
#define INCLUDED_USERMESSAGES_HPP
-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 server has reached the number of connections or documents it supports at a given time.";
+//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 = "error: cmd=socket kind=close code=%d";
+constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "error: cmd=socket kind=limitreached params=%d,%d";
#endif
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 82819ea..a3b1c82 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -220,7 +220,7 @@ editlock: <1 or 0>
Note that only one client can have the editlock at a time and
others can only view.
-error: cmd=<command> kind=<kind> [code=<error_code>]
+error: cmd=<command> kind=<kind> [code=<error_code>] [params=1,2,3,...,N]
<freeErrorText>
<command> is the command part of the corresponding client->server
diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index 9ce7c17..4f28533 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -28,7 +28,7 @@ wsd_sources = \
test_CPPFLAGS = -DTDOC=\"$(top_srcdir)/test/data\" -I$(top_srcdir)
test_SOURCES = TileCacheTests.cpp WhiteBoxTests.cpp integration-http-server.cpp \
- httpwstest.cpp httpcrashtest.cpp test.cpp $(wsd_sources)
+ httpwstest.cpp httpcrashtest.cpp httpwserror.cpp test.cpp $(wsd_sources)
test_LDADD = $(CPPUNIT_LIBS)
# unit test modules:
diff --git a/loolwsd/test/data/empty.odt b/loolwsd/test/data/empty.odt
new file mode 100755
index 0000000..6b07475
Binary files /dev/null and b/loolwsd/test/data/empty.odt differ
diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp
index e676ff1..7e570d3 100644
--- a/loolwsd/test/helpers.hpp
+++ b/loolwsd/test/helpers.hpp
@@ -15,6 +15,7 @@
#include <thread>
#include <regex>
+#include <Poco/BinaryReader.h>
#include <Poco/DirectoryIterator.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/FileStream.h>
@@ -156,6 +157,33 @@ std::string getTestServerURI()
}
inline
+int getErrorCode(Poco::Net::WebSocket& ws, std::string& message)
+{
+ int flags = 0;
+ int bytes = 0;
+ Poco::UInt16 statusCode = -1;
+ Poco::Buffer<char> buffer(READ_BUFFER_SIZE);
+
+ message.clear();
+ Poco::Timespan timeout(5000000);
+ ws.setReceiveTimeout(timeout);
+ do
+ {
+ bytes = ws.receiveFrame(buffer.begin(), READ_BUFFER_SIZE, flags);
+ }
+ while ((flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE);
+
+ if (bytes > 0)
+ {
+ Poco::MemoryBinaryReader reader(buffer, Poco::BinaryReader::NETWORK_BYTE_ORDER);
+ reader >> statusCode;
+ message.append(buffer.begin() + 2, bytes - 2);
+ }
+
+ return statusCode;
+}
+
+inline
void getResponseMessage(Poco::Net::WebSocket& ws, const std::string& prefix, std::string& response, const bool isLine)
{
try
diff --git a/loolwsd/test/httpwserror.cpp b/loolwsd/test/httpwserror.cpp
new file mode 100644
index 0000000..8e7769d
--- /dev/null
+++ b/loolwsd/test/httpwserror.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "config.h"
+
+#include <vector>
+#include <string>
+
+#include <Poco/Net/HTTPClientSession.h>
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/Net/HTTPResponse.h>
+#include <Poco/Net/HTTPSClientSession.h>
+#include <Poco/Net/NetException.h>
+#include <Poco/Net/WebSocket.h>
+#include <Poco/URI.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Common.hpp"
+#include "LOOLProtocol.hpp"
+#include "helpers.hpp"
+
+using namespace helpers;
+
+class HTTPWSError : public CPPUNIT_NS::TestFixture
+{
+ const Poco::URI _uri;
+ Poco::Net::HTTPResponse _response;
+
+ CPPUNIT_TEST_SUITE(HTTPWSError);
+
+ CPPUNIT_TEST(testMaxDocuments);
+ CPPUNIT_TEST(testMaxConnections);
+
+ CPPUNIT_TEST_SUITE_END();
+
+ void testMaxDocuments();
+ void testMaxConnections();
+
+public:
+ HTTPWSError()
+ : _uri(helpers::getTestServerURI())
+ {
+#if ENABLE_SSL
+ Poco::Net::initializeSSL();
+ // Just accept the certificate anyway for testing purposes
+ Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> invalidCertHandler = new Poco::Net::AcceptCertificateHandler(false);
+ Poco::Net::Context::Params sslParams;
+ Poco::Net::Context::Ptr sslContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, sslParams);
+ Poco::Net::SSLManager::instance().initializeClient(0, invalidCertHandler, sslContext);
+#endif
+ }
+
+#if ENABLE_SSL
+ ~HTTPWSError()
+ {
+ Poco::Net::uninitializeSSL();
+ }
+#endif
+
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+};
+
+void HTTPWSError::testMaxDocuments()
+{
+#if MAX_DOCUMENTS > 0
+ try
+ {
+ // Load a document.
+ std::string docPath;
+ std::string docURL;
+ std::string message;
+ Poco::UInt16 statusCode;
+ std::vector<std::shared_ptr<Poco::Net::WebSocket>> docs;
+
+ for(int it = 1; it <= MAX_DOCUMENTS; it++)
+ {
+ getDocumentPathAndURL("empty.odt", docPath, docURL);
+ Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL);
+ docs.emplace_back(connectLOKit(_uri, request, _response));
+ }
+
+ // try to open MAX_DOCUMENTS + 1
+ getDocumentPathAndURL("empty.odt", docPath, docURL);
+ 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_POLICY_VIOLATION), statusCode);
+ CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
+ }
+ catch (const Poco::Exception& exc)
+ {
+ CPPUNIT_FAIL(exc.displayText());
+ }
+#endif
+}
+
+void HTTPWSError::testMaxConnections()
+{
+#if MAX_CONNECTIONS > 0
+ try
+ {
+ // Load a document.
+ 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);
+ Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL);
+ auto socket = loadDocAndGetSocket(_uri, docURL, "testMaxConnections ");
+
+ for(int it = 1; it < MAX_CONNECTIONS; it++)
+ {
+ 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
+ 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_POLICY_VIOLATION), statusCode);
+ CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
+ }
+ catch (const Poco::Exception& exc)
+ {
+ CPPUNIT_FAIL(exc.displayText());
+ }
+#endif
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSError);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
More information about the Libreoffice-commits
mailing list