diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2014-02-05 17:44:03 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-03-21 15:59:25 +0100 |
commit | 48e814442e6e8507aacd16362b44d5754c059228 (patch) | |
tree | 592f3916bf2e5dc18d57a62c3baf1ba0ae2bbf09 /src/webchannel | |
parent | e3e4d6a18d63537459f0e616360e53e816927f76 (diff) | |
download | qtwebchannel-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/webchannel')
-rw-r--r-- | src/webchannel/qmetaobjectpublisher.cpp | 55 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher_p.h | 10 | ||||
-rw-r--r-- | src/webchannel/qwebchannel.cpp | 28 | ||||
-rw-r--r-- | src/webchannel/qwebchannel.h | 1 | ||||
-rw-r--r-- | src/webchannel/qwebchannel.js | 15 | ||||
-rw-r--r-- | src/webchannel/qwebchannel_p.h | 4 | ||||
-rw-r--r-- | src/webchannel/qwebchanneltransportinterface.h | 7 | ||||
-rw-r--r-- | src/webchannel/qwebsockettransport.cpp | 26 | ||||
-rw-r--r-- | src/webchannel/qwebsockettransport.h | 4 | ||||
-rw-r--r-- | src/webchannel/qwebsockettransport_p.h | 6 |
10 files changed, 76 insertions, 80 deletions
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); |