summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMilian Wolff <milian.wolff@kdab.com>2014-02-05 17:44:03 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-03-21 15:59:25 +0100
commit48e814442e6e8507aacd16362b44d5754c059228 (patch)
tree592f3916bf2e5dc18d57a62c3baf1ba0ae2bbf09 /src
parente3e4d6a18d63537459f0e616360e53e816927f76 (diff)
downloadqtwebchannel-48e814442e6e8507aacd16362b44d5754c059228.tar.gz
Send response data only to target client.
Before, the response was sent to all clients in a broad-cast and had to be filtered on the client-side. This required additional client identification data to be added to all requests and responses. Now, we keep track of the transport and transport-internal client and only send the response to that client. This is very benefitial for multi-client setups but also reduces traffic for single-client setups and thus their performance. Change-Id: Ia1ef5e031b0058222083d352a8aa28a7d566a6ca Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/imports/webchannel/qmlwebviewtransport.cpp8
-rw-r--r--src/imports/webchannel/qmlwebviewtransport.h4
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp55
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h10
-rw-r--r--src/webchannel/qwebchannel.cpp28
-rw-r--r--src/webchannel/qwebchannel.h1
-rw-r--r--src/webchannel/qwebchannel.js15
-rw-r--r--src/webchannel/qwebchannel_p.h4
-rw-r--r--src/webchannel/qwebchanneltransportinterface.h7
-rw-r--r--src/webchannel/qwebsockettransport.cpp26
-rw-r--r--src/webchannel/qwebsockettransport.h4
-rw-r--r--src/webchannel/qwebsockettransport_p.h6
12 files changed, 82 insertions, 86 deletions
diff --git a/src/imports/webchannel/qmlwebviewtransport.cpp b/src/imports/webchannel/qmlwebviewtransport.cpp
index 7fb9d16..3d4e2ed 100644
--- a/src/imports/webchannel/qmlwebviewtransport.cpp
+++ b/src/imports/webchannel/qmlwebviewtransport.cpp
@@ -74,7 +74,7 @@ QObject *QmlWebViewTransport::webViewExperimental() const
return m_webViewExperimental;
}
-void QmlWebViewTransport::sendMessage(const QString &message) const
+void QmlWebViewTransport::sendMessage(const QString &message, int /*clientId*/) const
{
if (!m_webViewExperimental) {
qWarning("Cannot send message - did you forget to set the webViewExperimental property?");
@@ -83,16 +83,16 @@ void QmlWebViewTransport::sendMessage(const QString &message) const
QMetaObject::invokeMethod(m_webViewExperimental, "postMessage", Q_ARG(QString, message));
}
-void QmlWebViewTransport::sendMessage(const QByteArray &message) const
+void QmlWebViewTransport::sendMessage(const QByteArray &message, int clientId) const
{
- sendMessage(QString::fromUtf8(message));
+ sendMessage(QString::fromUtf8(message), clientId);
}
void QmlWebViewTransport::handleWebViewMessage(const QVariantMap &message)
{
if (m_handler) {
const QString &data = message[QStringLiteral("data")].toString();
- m_handler->handleMessage(data);
+ m_handler->handleMessage(data, this, -1);
emit messageReceived(data);
}
}
diff --git a/src/imports/webchannel/qmlwebviewtransport.h b/src/imports/webchannel/qmlwebviewtransport.h
index 98dc504..bb1e27e 100644
--- a/src/imports/webchannel/qmlwebviewtransport.h
+++ b/src/imports/webchannel/qmlwebviewtransport.h
@@ -55,8 +55,8 @@ public:
explicit QmlWebViewTransport(QObject *parent = 0);
~QmlWebViewTransport() Q_DECL_OVERRIDE;
- void sendMessage(const QString &message) const Q_DECL_OVERRIDE;
- void sendMessage(const QByteArray &message) const Q_DECL_OVERRIDE;
+ void sendMessage(const QString &message, int clientId) const Q_DECL_OVERRIDE;
+ void sendMessage(const QByteArray &message, int clientId) const Q_DECL_OVERRIDE;
void setMessageHandler(QWebChannelMessageHandlerInterface *handler) Q_DECL_OVERRIDE;
void setWebViewExperimental(QObject *webViewExperimental);
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index d06fe98..2914714 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -41,6 +41,7 @@
#include "qmetaobjectpublisher_p.h"
#include "qwebchannel.h"
+#include "qwebchannel_p.h"
#include <QEvent>
#include <QJsonDocument>
@@ -336,27 +337,27 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
setClientIsIdle(false);
}
-bool QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
- const QJsonArray &args, const QJsonValue &id)
+QByteArray QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
+ const QJsonArray &args, const QJsonValue &id)
{
const QMetaMethod &method = object->metaObject()->method(methodIndex);
if (method.name() == QByteArrayLiteral("deleteLater")) {
// invoke `deleteLater` on wrapped QObject indirectly
deleteWrappedObject(object);
- return true;
+ return QByteArray();
} else if (!method.isValid()) {
qWarning() << "Cannot invoke unknown method of index" << methodIndex << "on object" << object << '.';
- return false;
+ return QByteArray();
} else if (method.access() != QMetaMethod::Public) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
- return false;
+ return QByteArray();
} else if (method.methodType() != QMetaMethod::Method && method.methodType() != QMetaMethod::Slot) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
- return false;
+ return QByteArray();
} else if (args.size() > 10) {
qWarning() << "Cannot invoke method" << method.name() << "on object" << object << "with more than 10 arguments, as that is not supported by QMetaMethod::invoke.";
- return false;
+ return QByteArray();
} else if (args.size() > method.parameterCount()) {
qWarning() << "Ignoring additional arguments while invoking method" << method.name() << "on object" << object << ':'
<< args.size() << "arguments given, but method only takes" << method.parameterCount() << '.';
@@ -388,9 +389,7 @@ bool QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodI
arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
// and send the return value to the client
- webChannel->respond(id, wrapResult(returnValue));
-
- return true;
+ return generateJSONMessage(id, wrapResult(returnValue), true);
}
void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
@@ -486,70 +485,74 @@ void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
object->deleteLater();
}
-bool QMetaObjectPublisher::handleRequest(const QJsonObject &message)
+QByteArray QMetaObjectPublisher::handleRequest(const QJsonObject &message)
{
if (!message.contains(KEY_DATA)) {
- return false;
+ return QByteArray();
}
const QJsonObject &payload = message.value(KEY_DATA).toObject();
if (!payload.contains(KEY_TYPE)) {
- return false;
+ return QByteArray();
}
const Type type = toType(payload.value(KEY_TYPE));
if (type == TypeIdle) {
setClientIsIdle(true);
- return true;
+ return QByteArray();
} else if (type == TypeInit) {
if (!blockUpdates) {
initializeClients();
} else {
pendingInit = true;
}
- return true;
+ return QByteArray();
} else if (type == TypeDebug) {
static QTextStream out(stdout);
out << "DEBUG: " << payload.value(KEY_MESSAGE).toString() << endl;
- return true;
+ return QByteArray();
} else if (payload.contains(KEY_OBJECT)) {
const QString &objectName = payload.value(KEY_OBJECT).toString();
QObject *object = registeredObjects.value(objectName);
if (!object) {
qWarning() << "Unknown object encountered" << objectName;
- return false;
+ return QByteArray();
}
if (type == TypeInvokeMethod) {
return invokeMethod(object, payload.value(KEY_METHOD).toInt(-1), payload.value(KEY_ARGS).toArray(), message.value(KEY_ID));
} else if (type == TypeConnectToSignal) {
signalHandler.connectTo(object, payload.value(KEY_SIGNAL).toInt(-1));
- return true;
+ return QByteArray();
} else if (type == TypeDisconnectFromSignal) {
signalHandler.disconnectFrom(object, payload.value(KEY_SIGNAL).toInt(-1));
- return true;
+ return QByteArray();
} else if (type == TypeSetProperty) {
const int propertyIdx = payload.value(KEY_PROPERTY).toInt(-1);
QMetaProperty property = object->metaObject()->property(propertyIdx);
if (!property.isValid()) {
qWarning() << "Cannot set unknown property" << payload.value(KEY_PROPERTY) << "of object" << objectName;
- return false;
+ return QByteArray();
} else if (!object->metaObject()->property(propertyIdx).write(object, payload.value(KEY_VALUE).toVariant())) {
qWarning() << "Could not write value " << payload.value(KEY_VALUE)
<< "to property" << property.name() << "of object" << objectName;
- return false;
+ return QByteArray();
}
- return true;
+ return QByteArray();
}
}
- return false;
+ return QByteArray();
}
-void QMetaObjectPublisher::handleMessage(const QString &message)
+void QMetaObjectPublisher::handleMessage(const QString &message, QWebChannelTransportInterface *transport, int clientId)
{
const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
- if (doc.isObject()) {
- handleRequest(doc.object());
+ if (!doc.isObject()) {
+ return;
+ }
+ const QByteArray &response = handleRequest(doc.object());
+ if (!response.isEmpty()) {
+ transport->sendMessage(response, clientId);
}
}
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 3047548..4413df4 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -81,7 +81,7 @@ public:
*
* @return true if the request was handled, false otherwise.
*/
- bool handleRequest(const QJsonObject &message);
+ QByteArray handleRequest(const QJsonObject &message);
/**
* Serialize the QMetaObject of @p object and return it in JSON form.
@@ -124,10 +124,10 @@ public:
/**
* Invoke the method of index @p methodIndex on @p object with the arguments @p args.
*
- * The return value of the method invocation is then transmitted to the calling client
- * via a webchannel response to the message identified by @p id.
+ * The return value of the method invocation is then serialized and a response message
+ * is returned.
*/
- bool invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args, const QJsonValue &id);
+ QByteArray invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args, const QJsonValue &id);
/**
* Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
@@ -164,7 +164,7 @@ public:
/**
* Parse the message as JSON and if it succeeds, call handleRequest with the obtained JSON object.
*/
- void handleMessage(const QString &message) Q_DECL_OVERRIDE;
+ void handleMessage(const QString &message, QWebChannelTransportInterface* transport, int clientId) Q_DECL_OVERRIDE;
signals:
void blockUpdatesChanged(bool block);
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index 5e8e320..75d1a4b 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -50,13 +50,8 @@
QT_BEGIN_NAMESPACE
-void QWebChannelPrivate::sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const
+QByteArray generateJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response)
{
- if (transports.isEmpty()) {
- qWarning("QWebChannel is not connected to any transports, cannot send messages.");
- return;
- }
-
QJsonObject obj;
if (response) {
obj[QStringLiteral("response")] = true;
@@ -66,11 +61,7 @@ void QWebChannelPrivate::sendJSONMessage(const QJsonValue &id, const QJsonValue
obj[QStringLiteral("data")] = data;
}
QJsonDocument doc(obj);
- const QByteArray &message = doc.toJson(QJsonDocument::Compact);
-
- foreach (QWebChannelTransportInterface *transport, transports) {
- transport->sendMessage(message);
- }
+ return doc.toJson(QJsonDocument::Compact);
}
QWebChannel::QWebChannel(QObject *parent)
@@ -136,14 +127,17 @@ void QWebChannel::disconnectFrom(QWebChannelTransportInterface *transport)
}
}
-void QWebChannel::respond(const QJsonValue &messageId, const QJsonValue &data) const
-{
- d->sendJSONMessage(messageId, data, true);
-}
-
void QWebChannel::sendMessage(const QJsonValue &id, const QJsonValue &data) const
{
- d->sendJSONMessage(id, data, false);
+ if (d->transports.isEmpty()) {
+ qWarning("QWebChannel is not connected to any transports, cannot send messages.");
+ return;
+ }
+
+ const QByteArray &message = generateJSONMessage(id, data, false);
+ foreach (QWebChannelTransportInterface *transport, d->transports) {
+ transport->sendMessage(message);
+ }
}
QT_END_NAMESPACE
diff --git a/src/webchannel/qwebchannel.h b/src/webchannel/qwebchannel.h
index 9139a15..ec3f72b 100644
--- a/src/webchannel/qwebchannel.h
+++ b/src/webchannel/qwebchannel.h
@@ -97,7 +97,6 @@ signals:
public slots:
void sendMessage(const QJsonValue &id, const QJsonValue &data = QJsonValue()) const;
- void respond(const QJsonValue &messageId, const QJsonValue &data = QJsonValue()) const;
private:
QScopedPointer<QWebChannelPrivate> d;
diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js
index 33f4cf5..9529c0b 100644
--- a/src/webchannel/qwebchannel.js
+++ b/src/webchannel/qwebchannel.js
@@ -57,13 +57,6 @@ var QWebChannelMessageTypes = {
var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
{
var channel = this;
- // support multiple channels listening to the same socket
- // the responses to channel.exec must be distinguishable
- // see: http://stackoverflow.com/a/2117523/35250
- this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
- return v.toString(16);
- });
this.send = function(data)
{
if (typeof(data) !== "string") {
@@ -82,10 +75,8 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
jsonData.data = {};
}
if (jsonData.response) {
- if (jsonData.id[0] === channel.id) {
- channel.execCallbacks[jsonData.id[1]](jsonData.data);
- delete channel.execCallbacks[jsonData.id];
- }
+ channel.execCallbacks[jsonData.id](jsonData.data);
+ delete channel.execCallbacks[jsonData.id];
} else if (channel.subscriptions[jsonData.id]) {
channel.subscriptions[jsonData.id].forEach(function(callback) {
(callback)(jsonData.data); }
@@ -153,7 +144,7 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
}
var id = channel.execId++;
channel.execCallbacks[id] = callback;
- channel.send({"id": [channel.id, id], "data": data});
+ channel.send({"id": id, "data": data});
};
this.objectMap = {};
diff --git a/src/webchannel/qwebchannel_p.h b/src/webchannel/qwebchannel_p.h
index 954134e..b81640e 100644
--- a/src/webchannel/qwebchannel_p.h
+++ b/src/webchannel/qwebchannel_p.h
@@ -53,12 +53,12 @@ class QJsonValue;
class QWebChannelTransportInterface;
class QMetaObjectPublisher;
+Q_WEBCHANNEL_EXPORT QByteArray generateJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response);
+
struct Q_WEBCHANNEL_EXPORT QWebChannelPrivate
{
QVector<QWebChannelTransportInterface*> transports;
QMetaObjectPublisher *publisher;
-
- void sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const;
};
QT_END_NAMESPACE
diff --git a/src/webchannel/qwebchanneltransportinterface.h b/src/webchannel/qwebchanneltransportinterface.h
index 9044c68..c532732 100644
--- a/src/webchannel/qwebchanneltransportinterface.h
+++ b/src/webchannel/qwebchanneltransportinterface.h
@@ -48,6 +48,7 @@
QT_BEGIN_NAMESPACE
+class QWebChannelTransportInterface;
class Q_WEBCHANNEL_EXPORT QWebChannelMessageHandlerInterface
{
public:
@@ -56,7 +57,7 @@ public:
/**
* Handle a text message from a web channel client.
*/
- virtual void handleMessage(const QString &message) = 0;
+ virtual void handleMessage(const QString &message, QWebChannelTransportInterface* transport, int clientId) = 0;
};
#define QWebChannelMessageHandlerInterface_iid "org.qt-project.Qt.QWebChannelMessageHandlerInterface"
@@ -71,12 +72,12 @@ public:
/**
* Send a text message to all web channel clients.
*/
- virtual void sendMessage(const QString &message) const = 0;
+ virtual void sendMessage(const QString &message, int clientId = -1) const = 0;
/**
* Send a binary message to all web channel clients.
*/
- virtual void sendMessage(const QByteArray &message) const = 0;
+ virtual void sendMessage(const QByteArray &message, int clientId = -1) const = 0;
/**
* Sets the message handler that will be called on incoming messages from web channel clients.
diff --git a/src/webchannel/qwebsockettransport.cpp b/src/webchannel/qwebsockettransport.cpp
index 9a432fe..8c25a9b 100644
--- a/src/webchannel/qwebsockettransport.cpp
+++ b/src/webchannel/qwebsockettransport.cpp
@@ -50,9 +50,10 @@ QT_USE_NAMESPACE
//BEGIN QWebSocketTransportPrivate
-QWebSocketTransportPrivate::QWebSocketTransportPrivate(QObject *parent)
+QWebSocketTransportPrivate::QWebSocketTransportPrivate(QWebSocketTransport *transport, QObject *parent)
: QWebSocketServer(QStringLiteral("QWebChannel Server"), NonSecureMode, parent)
, m_messageHandler(Q_NULLPTR)
+ , m_transport(transport)
, m_useSecret(true)
, m_starting(false)
{
@@ -76,10 +77,14 @@ void QWebSocketTransportPrivate::initLater()
m_starting = true;
}
-void QWebSocketTransportPrivate::sendMessage(const QString &message)
+void QWebSocketTransportPrivate::sendMessage(const QString &message, int clientId)
{
- foreach (QWebSocket *client, m_clients) {
- client->sendTextMessage(message);
+ if (clientId == -1) {
+ foreach (QWebSocket *client, m_clients) {
+ client->sendTextMessage(message);
+ }
+ } else {
+ m_clients.at(clientId)->sendTextMessage(message);
}
}
@@ -132,7 +137,8 @@ void QWebSocketTransportPrivate::socketError()
void QWebSocketTransportPrivate::messageReceived(const QString &message)
{
if (m_messageHandler) {
- m_messageHandler->handleMessage(message);
+ QWebSocket *client = qobject_cast<QWebSocket*>(sender());
+ m_messageHandler->handleMessage(message, m_transport, m_clients.indexOf(client));
}
emit textDataReceived(message);
}
@@ -153,7 +159,7 @@ void QWebSocketTransportPrivate::clientDisconnected()
QWebSocketTransport::QWebSocketTransport(QObject *parent)
: QObject(parent)
- , d(new QWebSocketTransportPrivate)
+ , d(new QWebSocketTransportPrivate(this))
{
connect(d.data(), SIGNAL(textDataReceived(QString)),
SIGNAL(messageReceived(QString)));
@@ -172,14 +178,14 @@ QWebSocketTransport::~QWebSocketTransport()
}
-void QWebSocketTransport::sendMessage(const QByteArray &message) const
+void QWebSocketTransport::sendMessage(const QByteArray &message, int clientId) const
{
- d->sendMessage(QString::fromUtf8(message));
+ d->sendMessage(QString::fromUtf8(message), clientId);
}
-void QWebSocketTransport::sendMessage(const QString &message) const
+void QWebSocketTransport::sendMessage(const QString &message, int clientId) const
{
- d->sendMessage(message);
+ d->sendMessage(message, clientId);
}
void QWebSocketTransport::setMessageHandler(QWebChannelMessageHandlerInterface *handler)
diff --git a/src/webchannel/qwebsockettransport.h b/src/webchannel/qwebsockettransport.h
index 6b1cb13..b3e484f 100644
--- a/src/webchannel/qwebsockettransport.h
+++ b/src/webchannel/qwebsockettransport.h
@@ -57,8 +57,8 @@ public:
explicit QWebSocketTransport(QObject *parent = 0);
~QWebSocketTransport() Q_DECL_OVERRIDE;
- void sendMessage(const QByteArray &message) const Q_DECL_OVERRIDE;
- void sendMessage(const QString &message) const Q_DECL_OVERRIDE;
+ void sendMessage(const QByteArray &message, int clientId = -1) const Q_DECL_OVERRIDE;
+ void sendMessage(const QString &message, int clientId = -1) const Q_DECL_OVERRIDE;
void setMessageHandler(QWebChannelMessageHandlerInterface *handler) Q_DECL_OVERRIDE;
QString baseUrl() const;
diff --git a/src/webchannel/qwebsockettransport_p.h b/src/webchannel/qwebsockettransport_p.h
index 1a95787..95a431d 100644
--- a/src/webchannel/qwebsockettransport_p.h
+++ b/src/webchannel/qwebsockettransport_p.h
@@ -48,6 +48,7 @@
QT_BEGIN_NAMESPACE
+class QWebSocketTransport;
class QWebSocketTransportPrivate : public QWebSocketServer
{
Q_OBJECT
@@ -55,15 +56,16 @@ public:
QString m_secret;
QString m_baseUrl;
QWebChannelMessageHandlerInterface *m_messageHandler;
+ QWebSocketTransport *m_transport;
bool m_useSecret;
bool m_starting;
- explicit QWebSocketTransportPrivate(QObject *parent = 0);
+ explicit QWebSocketTransportPrivate(QWebSocketTransport* transport, QObject *parent = 0);
virtual ~QWebSocketTransportPrivate();
void initLater();
- void sendMessage(const QString &message);
+ void sendMessage(const QString &message, int clientId);
signals:
void failed(const QString &reason);