summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/remoteobjects/doc/src/remoteobjects-external-schemas.qdoc137
-rw-r--r--src/remoteobjects/doc/src/remoteobjects-index.qdoc1
-rw-r--r--src/remoteobjects/qconnectionfactories.cpp38
-rw-r--r--src/remoteobjects/qconnectionfactories_p.h22
-rw-r--r--src/remoteobjects/qremoteobjectnode.cpp148
-rw-r--r--src/remoteobjects/qremoteobjectnode.h14
-rw-r--r--src/remoteobjects/qremoteobjectnode_p.h5
-rw-r--r--src/remoteobjects/qremoteobjectreplica.cpp17
-rw-r--r--src/remoteobjects/qremoteobjectreplica_p.h6
-rw-r--r--src/remoteobjects/qremoteobjectsource.cpp8
-rw-r--r--src/remoteobjects/qremoteobjectsource_p.h8
-rw-r--r--src/remoteobjects/qremoteobjectsourceio.cpp51
-rw-r--r--src/remoteobjects/qremoteobjectsourceio_p.h7
-rw-r--r--src/remoteobjects/qtremoteobjectglobal.h1
14 files changed, 404 insertions, 59 deletions
diff --git a/src/remoteobjects/doc/src/remoteobjects-external-schemas.qdoc b/src/remoteobjects/doc/src/remoteobjects-external-schemas.qdoc
new file mode 100644
index 0000000..fdc8e4f
--- /dev/null
+++ b/src/remoteobjects/doc/src/remoteobjects-external-schemas.qdoc
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page qtremoteobjects-external-schemas.html
+\title Qt Remote Objects - External QIODevices
+\brief Describes how Qt Remote Objects supports custom QIODevice channels.
+
+\section1 External QIODevices
+
+Qt Remote Objects supports several communications channels out-of-the-box, such
+as the \l QTcpServer and \l QTcpSocket pair. Given the desired \l QUrl for tcp,
+or the desired name (for the \l QLocalServer and \l QLocalSocket pair), the
+code needed to listen and connect are boilerplate and handled internally by Qt.
+Qt Remote Objects supports other types of \l QIODevice as well, and the \l
+QRemoteObjectNode classes provide additional methods to support cases where
+custom code is needed.
+
+A contrived example with TCP/IP is shown below. A more realistic example would
+use an SSL connection, which would require configuration of certificates, etc.
+
+\code
+ // Create the server and listen outside of QtRO
+ QTcpServer tcpServer;
+ tcpServer.listen(QHostAddress(127.0.0.1), 65213);
+
+ // Create the host node. We don't need a hostUrl unless we want to take
+ // advantage of external schemas (see next example).
+ QRemoteObjectHost srcNode();
+
+ // Make sure any connections are handed to QtRO
+ QObject::connect(&tcpServer, &QTcpServer::newConnection, &srcNode,
+ [&srcNode, &tcpServer]() {
+ srcNode.addHostSideConnection(tcpServer.nextPendingConnection());
+ });
+\endcode
+
+The Replica side code needs to manually connect to the Host
+\code
+ QRemoteObjectNode repNode();
+ QTcpSocket *socket = new QTcpSocket(&repNode);
+ connect(socket, &QTcpSocket::connected, &repNode,
+ [socket, &repNode]() {
+ repNode.addClientSideConnection(socket);
+ });
+ socket->connectToHost(QHostAddress(127.0.0.1), 65213);
+ };
+\endcode
+
+\section1 External Schemas
+
+It is possible to create each side of the QIODevice and call \l
+{QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)} and \l
+{QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)} as shown
+above. This is fully supported, but requires the client know how to establish
+the connection or have a way to discover that information. This is exactly the
+problem the registry was designed to solve.
+
+Qt Remote Objects also allows "External Schemas" to be used with the registry,
+which helps with connection setup. On the \l QRemoteObjectHostNode side, the
+user must set the hostUrl with the desired schema.
+
+\code
+ // Use standard tcp url for the registry
+ const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+ // Use "exttcp" for the "external" interface
+ const QUrl extUrl = QUrl(QStringLiteral("exttcp://127.0.0.1:65213"));
+
+ // Create the server and listen outside of QtRO
+ QTcpServer tcpServer;
+ tcpServer.listen(QHostAddress(extUrl.host()), extUrl.port());
+
+ // We need a registry for everyone to connect to
+ QRemoteObjectRegistryHost registry(registryUrl);
+
+ // Finally, we create our host node and register "exttcp" as our schema.
+ // We need the AllowExternalRegistration parameter to prevent the node from
+ // setting a hostUrlInvalid error.
+ QRemoteObjectHost srcNode(extUrl, registryUrl, QRemoteObjectHost::AllowExternalRegistration);
+ // From now on, when we call enableRemoting() from this node, the registry
+ // will be updated to show the Source object at extUrl.
+\endcode
+
+On the \l Replica side, the \l QRemoteObjectNode needs to register a callback
+to be used when the external schema is detected. The callback must be a \l
+RemoteObjectSchemaHandler.
+
+\code
+ // Use standard tcp url for the registry
+ const QUrl registryUrl = QUrl(QStringLiteral("tcp://127.0.0.1:65212"));
+
+ // This time create the node connected to the registry
+ QRemoteObjectNode repNode(registryUrl);
+
+ // Create the RemoteObjectSchemaHandler callback
+ QRemoteObjectNode::RemoteObjectSchemaHandler setupTcp = [&repNode](QUrl url) {
+ QTcpSocket *socket = new QTcpSocket(&repNode);
+ connect(socket, &QTcpSocket::connected,
+ [socket, &repNode]() {
+ repNode.addClientSideConnection(socket);
+ });
+ connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QSslSocket::error),
+ [socket](QAbstractSocket::SocketError error) {
+ delete socket;
+ });
+ socket->connectToHost(url.host(), url.port());
+ };
+
+ // Once we call registerExternalSchema, the above method will be called
+ // whenever the registry sees an object we are interested in on "exttcp"
+ repNode.registerExternalSchema(QStringLiteral("exttcp"), setupTcp);
+\endcode
+*/
diff --git a/src/remoteobjects/doc/src/remoteobjects-index.qdoc b/src/remoteobjects/doc/src/remoteobjects-index.qdoc
index d483271..7b008bf 100644
--- a/src/remoteobjects/doc/src/remoteobjects-index.qdoc
+++ b/src/remoteobjects/doc/src/remoteobjects-index.qdoc
@@ -96,6 +96,7 @@ See \l{Qt Licensing} for further details.
\li \l {Source Objects}{Qt Remote Objects Source Objects}
\li \l {Replica Objects}{Qt Remote Objects Replica Objects}
\li \l {Qt Remote Objects Registry}
+ \li \l {Qt Remote Objects - External QIODevices}
\li \l {Qt Remote Objects Compiler}
\li \l {Remote Object Interaction}
\li \l {Using Qt Remote Objects}
diff --git a/src/remoteobjects/qconnectionfactories.cpp b/src/remoteobjects/qconnectionfactories.cpp
index 317d2fa..0ca2fa8 100644
--- a/src/remoteobjects/qconnectionfactories.cpp
+++ b/src/remoteobjects/qconnectionfactories.cpp
@@ -79,13 +79,14 @@ inline bool fromDataStream(QDataStream &in, QRemoteObjectPacketTypeEnum &type, Q
case Ping: type = Ping; break;
case Pong: type = Pong; break;
default:
- qCWarning(QT_REMOTEOBJECT_IO) << "Invalid packet received" << type;
+ qCWarning(QT_REMOTEOBJECT_IO) << "Invalid packet received" << _type;
}
if (type == Invalid)
return false;
if (type == ObjectList)
return true;
in >> name;
+ qCDebug(QT_REMOTEOBJECT_IO) << "Packet received of type" << type << "for object" << name;
return true;
}
@@ -225,6 +226,41 @@ ServerIoDevice *QConnectionAbstractServer::nextPendingConnection()
return iodevice;
}
+ExternalIoDevice::ExternalIoDevice(QIODevice *device, QObject *parent)
+ : IoDeviceBase(parent)
+ , m_device(device)
+{
+ initializeDataStream();
+ connect(m_device, &QIODevice::aboutToClose, this, [this]() { this->m_isClosing = true; });
+ connect(m_device, &QIODevice::readyRead, this, &ExternalIoDevice::readyRead);
+ auto meta = device->metaObject();
+ if (-1 == meta->indexOfSignal(SIGNAL(disconnected())))
+ connect(m_device, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
+}
+
+QIODevice *ExternalIoDevice::connection() const
+{
+ return m_device;
+}
+
+bool ExternalIoDevice::isOpen() const
+{
+ if (!m_device)
+ return false;
+ return m_device->isOpen() && IoDeviceBase::isOpen();
+}
+
+void ExternalIoDevice::doClose()
+{
+ if (isOpen())
+ m_device->close();
+}
+
+QString ExternalIoDevice::deviceType() const
+{
+ return QStringLiteral("ExternalIoDevice");
+}
+
/*!
\class QtROServerFactory
\inmodule QtRemoteObjects
diff --git a/src/remoteobjects/qconnectionfactories_p.h b/src/remoteobjects/qconnectionfactories_p.h
index 56283be..84bc917 100644
--- a/src/remoteobjects/qconnectionfactories_p.h
+++ b/src/remoteobjects/qconnectionfactories_p.h
@@ -53,6 +53,8 @@
#include <QAbstractSocket>
#include <QDataStream>
+#include <QIODevice>
+#include <QPointer>
#include <QtRemoteObjects/qtremoteobjectglobal.h>
@@ -91,6 +93,7 @@ public:
Q_SIGNALS:
void readyRead();
+ void disconnected();
protected:
virtual QString deviceType() const = 0;
@@ -111,9 +114,6 @@ class Q_REMOTEOBJECTS_EXPORT ServerIoDevice : public IoDeviceBase
public:
explicit ServerIoDevice(QObject *parent = nullptr);
-Q_SIGNALS:
- void disconnected();
-
protected:
QString deviceType() const override;
};
@@ -156,7 +156,6 @@ public:
QUrl url() const;
Q_SIGNALS:
- void disconnected();
void shouldReconnect(ClientIoDevice*);
protected:
@@ -169,6 +168,21 @@ private:
QUrl m_url;
};
+class ExternalIoDevice : public IoDeviceBase
+{
+ Q_OBJECT
+
+public:
+ explicit ExternalIoDevice(QIODevice *device, QObject *parent=nullptr);
+ QIODevice *connection() const override;
+ bool isOpen() const override;
+
+protected:
+ void doClose() override;
+ QString deviceType() const override;
+ QPointer<QIODevice> m_device;
+};
+
class QtROServerFactory
{
public:
diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp
index 5173c45..2dd7086 100644
--- a/src/remoteobjects/qremoteobjectnode.cpp
+++ b/src/remoteobjects/qremoteobjectnode.cpp
@@ -163,6 +163,44 @@ void QRemoteObjectNode::setHeartbeatInterval(int interval)
}
/*!
+ \since 5.12
+ \typedef QRemoteObjectNode::RemoteObjectSchemaHandler
+
+ Typedef for a std::function method that can take a QUrl input and is
+ responsible for creating the communications channel between this node and
+ the node hosting the desired \l Source. As some types of QIODevices (e.g.,
+ QSslSocket) require additional steps before the device is ready for use,
+ the method is responsible for calling \l addClientSideConnection once the
+ connection is fully established.
+*/
+
+/*!
+ \since 5.12
+ \brief Provide a custom method to handle externally provided schemas
+
+ This method is tied to the \l Registry and \l {External Schemas}. By
+ registering a std::function handler for an external schema, the registered
+ method will be called when the registry is notified of a \l Source you've
+ acquired being available. Without this registration, QtRO would only be
+ able to handle the "built-in" schemas.
+
+ The provided method, \a handler, will be called when the registry sees a \l
+ Source object on a new (not yet connected) Node with a {QUrl::schema()} of
+ \a schema. The \a handler, of type \l
+ QRemoteObjectNode::RemoteObjectSchemaHandler will get the \l QUrl of the
+ Node providing the \l Source as an input parameter, and is responsible for
+ establishing the communications channel (a \l QIODevice of some sort) and
+ calling \l addClientSideConnection with it.
+
+ \sa RemoteObjectSchemaHandler
+*/
+void QRemoteObjectNode::registerExternalSchema(const QString &schema, QRemoteObjectNode::RemoteObjectSchemaHandler handler)
+{
+ Q_D(QRemoteObjectNode);
+ d->schemaHandlers.insert(schema, handler);
+}
+
+/*!
\since 5.11
\brief Forward Remote Objects from another network
@@ -642,21 +680,26 @@ bool QRemoteObjectNodePrivate::initConnection(const QUrl &address)
requestedUrls.insert(address);
+ if (schemaHandlers.contains(address.scheme())) {
+ schemaHandlers[address.scheme()](address);
+ return true;
+ }
+
ClientIoDevice *connection = QtROClientFactory::instance()->create(address, q);
if (!connection) {
qROPrivWarning() << "Could not create ClientIoDevice for client. Invalid url/scheme provided?" << address;
return false;
}
-
qROPrivDebug() << "Opening connection to" << address.toString();
qROPrivDebug() << "Replica Connection isValid" << connection->isOpen();
QObject::connect(connection, &ClientIoDevice::shouldReconnect, q, [this, connection]() {
onShouldReconnect(connection);
});
- QObject::connect(connection, &ClientIoDevice::readyRead, q, [this, connection]() {
+ QObject::connect(connection, &IoDeviceBase::readyRead, q, [this, connection]() {
onClientRead(connection);
});
connection->connectToServer();
+
return true;
}
@@ -791,7 +834,7 @@ void QRemoteObjectNodePrivate::handleReplicaConnection(const QString &name)
}
}
-void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, ClientIoDevice *connection)
+void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, IoDeviceBase *connection)
{
if (!checkSignatures(rep->m_objectSignature, sourceSignature)) {
qROPrivWarning() << "Signature mismatch for" << rep->m_metaObject->className() << (rep->m_objectName.isEmpty() ? QLatin1String("(unnamed)") : rep->m_objectName);
@@ -820,7 +863,7 @@ QReplicaImplementationInterface *QRemoteObjectHostBasePrivate::handleNewAcquire(
void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
{
using namespace QRemoteObjectPackets;
- ClientIoDevice *connection = qobject_cast<ClientIoDevice*>(obj);
+ IoDeviceBase *connection = qobject_cast<IoDeviceBase*>(obj);
QRemoteObjectPacketTypeEnum packetType;
Q_ASSERT(connection);
@@ -913,7 +956,7 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
qROPrivDebug() << "RemoveObject-->" << rxName << this;
connectedSources.remove(rxName);
connection->removeSource(rxName);
- if (replicas.contains(rxName)) { //We have a replica waiting on this remoteObject
+ if (replicas.contains(rxName)) { //We have a replica using the removed source
QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
if (rep && !rep->connectionToSource.isNull()) {
rep->connectionToSource.clear();
@@ -1105,6 +1148,26 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
*/
/*!
+ \enum QRemoteObjectHostBase::AllowedSchemas
+
+ This enum is used to specify whether a Node will accept a url with an
+ unrecognized schema for the hostUrl. By default only urls with known
+ schemas are accepted, but using \c AllowExternalRegistration will enable
+ the \l Registry to pass your external (to QtRO) url to client Nodes.
+
+ \value BuiltInSchemasOnly Only allow the hostUrl to be set to a QtRO
+ supported schema. This is the default value, and causes a Node error to be
+ set if an unrecognized schema is provided.
+ \value AllowExternalRegistration The provided schema is registered as an
+ \l {External Schemas} {External Schema}
+
+ \sa QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress =
+ QUrl(), AllowedSchemas allowedSchemas=BuiltInSchemasOnly, QObject *parent =
+ nullptr), setHostUrl(const QUrl &hostAddress, AllowedSchemas
+ allowedSchemas=BuiltInSchemasOnly)
+*/
+
+/*!
\fn ObjectType *QRemoteObjectNode::acquire(const QString &name)
Returns a pointer to a Replica of type ObjectType (which is a template
@@ -1217,15 +1280,20 @@ QRemoteObjectHost::QRemoteObjectHost(QObject *parent)
Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
exposing \l Source objects on the QtRO network) with address \a address. If
set, \a registryAddress will be used to connect to the \l
- QRemoteObjectRegistry at the provided address.
+ QRemoteObjectRegistry at the provided address. The \a allowedSchemas
+ parameter is only needed (and should be set to \l
+ {QRemoteObjectHostBase::AllowExternalRegistration}
+ {AllowExternalRegistration}) if the schema of the url should be used as an
+ \l {External Schemas} {External Schema} by the registry.
\sa setHostUrl(), setRegistryUrl()
*/
-QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress, QObject *parent)
+QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress,
+ AllowedSchemas allowedSchemas, QObject *parent)
: QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
{
if (!address.isEmpty()) {
- if (!setHostUrl(address))
+ if (!setHostUrl(address, allowedSchemas))
return;
}
@@ -1325,7 +1393,7 @@ QUrl QRemoteObjectHostBase::hostUrl() const
\internal The HostBase version of this method is protected so the method
isn't exposed on RegistryHost nodes.
*/
-bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress)
+bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
{
Q_D(QRemoteObjectHostBase);
if (d->remoteObjectIo) {
@@ -1333,13 +1401,11 @@ bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress)
return false;
}
- d->remoteObjectIo = new QRemoteObjectSourceIo(hostAddress, this);
- if (d->remoteObjectIo->m_server.isNull()) { //Invalid url/scheme
+ if (allowedSchemas == AllowedSchemas::BuiltInSchemasOnly && !QtROServerFactory::instance()->isValid(hostAddress)) {
d->setLastError(HostUrlInvalid);
- delete d->remoteObjectIo;
- d->remoteObjectIo = 0;
return false;
}
+ d->remoteObjectIo = new QRemoteObjectSourceIo(hostAddress, this);
//If we've given a name to the node, set it on the sourceIo as well
if (!objectName().isEmpty())
@@ -1368,10 +1434,15 @@ QUrl QRemoteObjectHost::hostUrl() const
Sets the \a hostAddress for a host QRemoteObjectNode.
Returns \c true if the Host address is set, otherwise \c false.
+
+ The \a allowedSchemas parameter is only needed (and should be set to \l
+ {QRemoteObjectHostBase::AllowExternalRegistration}
+ {AllowExternalRegistration}) if the schema of the url should be used as an
+ \l {External Schemas} {External Schema} by the registry.
*/
-bool QRemoteObjectHost::setHostUrl(const QUrl &hostAddress)
+bool QRemoteObjectHost::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
{
- return QRemoteObjectHostBase::setHostUrl(hostAddress);
+ return QRemoteObjectHostBase::setHostUrl(hostAddress, allowedSchemas);
}
/*!
@@ -1568,6 +1639,30 @@ bool QRemoteObjectNode::connectToNode(const QUrl &address)
}
/*!
+ \since 5.12
+
+ In order to \l QRemoteObjectNode::acquire() \l Replica objects over \l
+ {External QIODevices}, Qt Remote Objects needs access to the communications
+ channel (a \l QIODEvice) between the respective nodes. It is the
+ addClientSideConnection() call that enables this, taking the \a ioDevice as
+ input. Any acquire() call made without calling addClientSideConnection will
+ still work, but the Node will not be able to initialize the \l Replica
+ without being provided the connection to the Host node.
+
+ \sa {QRemoteObjectHostBase::addHostSideConnection}
+*/
+void QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)
+{
+ Q_D(QRemoteObjectNode);
+ ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
+ connect(device, &IoDeviceBase::readyRead, this, [d, device]() {
+ d->onClientRead(device);
+ });
+ if (device->bytesAvailable())
+ d->onClientRead(device);
+}
+
+/*!
\fn void QRemoteObjectNode::remoteObjectAdded(const QRemoteObjectSourceLocation &loc)
This signal is emitted whenever a new \l {Source} object is added to
@@ -1784,6 +1879,29 @@ bool QRemoteObjectHostBase::disableRemoting(QObject *remoteObject)
}
/*!
+ \since 5.12
+
+ In order to \l QRemoteObjectHost::enableRemoting() \l Source objects over
+ \l {External QIODevices}, Qt Remote Objects needs access to the
+ communications channel (a \l QIODEvice) between the respective nodes. It is
+ the addHostSideConnection() call that enables this on the \l Source side,
+ taking the \a ioDevice as input. Any enableRemoting() call will still work
+ without calling addHostSideConnection, but the Node will not be able to
+ share the \l Source objects without being provided the connection to the
+ Replica node.
+
+ \sa addClientSideConnection
+*/
+void QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!d->remoteObjectIo)
+ d->remoteObjectIo = new QRemoteObjectSourceIo(this);
+ ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
+ return d->remoteObjectIo->newConnection(device);
+}
+
+/*!
Returns a pointer to a Replica which is specifically derived from \l
QAbstractItemModel. The \a name provided must match the name used with the
matching \l {QRemoteObjectHostBase::enableRemoting} {enableRemoting} that put
diff --git a/src/remoteobjects/qremoteobjectnode.h b/src/remoteobjects/qremoteobjectnode.h
index a8c4d8f..35d0d9d 100644
--- a/src/remoteobjects/qremoteobjectnode.h
+++ b/src/remoteobjects/qremoteobjectnode.h
@@ -105,6 +105,7 @@ public:
~QRemoteObjectNode() override;
Q_INVOKABLE bool connectToNode(const QUrl &address);
+ void addClientSideConnection(QIODevice *ioDevice);
virtual void setName(const QString &name);
template < class ObjectType >
ObjectType *acquire(const QString &name = QString())
@@ -140,6 +141,9 @@ public:
int heartbeatInterval() const;
void setHeartbeatInterval(int interval);
+ typedef std::function<void (QUrl)> RemoteObjectSchemaHandler;
+ void registerExternalSchema(const QString &schema, RemoteObjectSchemaHandler handler);
+
Q_SIGNALS:
void remoteObjectAdded(const QRemoteObjectSourceLocation &);
void remoteObjectRemoved(const QRemoteObjectSourceLocation &);
@@ -165,6 +169,8 @@ class Q_REMOTEOBJECTS_EXPORT QRemoteObjectHostBase : public QRemoteObjectNode
{
Q_OBJECT
public:
+ enum AllowedSchemas { BuiltInSchemasOnly, AllowExternalRegistration };
+ Q_ENUM(AllowedSchemas)
~QRemoteObjectHostBase() override;
void setName(const QString &name) override;
@@ -177,6 +183,7 @@ public:
bool enableRemoting(QObject *object, const QString &name = QString());
bool enableRemoting(QAbstractItemModel *model, const QString &name, const QVector<int> roles, QItemSelectionModel *selectionModel = nullptr);
bool disableRemoting(QObject *remoteObject);
+ void addHostSideConnection(QIODevice *ioDevice);
typedef std::function<bool(const QString &, const QString &)> RemoteObjectNameFilter;
bool proxy(const QUrl &registryUrl, const QUrl &hostUrl={},
@@ -185,7 +192,7 @@ public:
protected:
virtual QUrl hostUrl() const;
- virtual bool setHostUrl(const QUrl &hostAddress);
+ virtual bool setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas=BuiltInSchemasOnly);
QRemoteObjectHostBase(QRemoteObjectHostBasePrivate &, QObject *);
private:
@@ -198,11 +205,12 @@ class Q_REMOTEOBJECTS_EXPORT QRemoteObjectHost : public QRemoteObjectHostBase
Q_OBJECT
public:
QRemoteObjectHost(QObject *parent = nullptr);
- QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress = QUrl(), QObject *parent = nullptr);
+ QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress = QUrl(),
+ AllowedSchemas allowedSchemas=BuiltInSchemasOnly, QObject *parent = nullptr);
QRemoteObjectHost(const QUrl &address, QObject *parent);
~QRemoteObjectHost() override;
QUrl hostUrl() const override;
- bool setHostUrl(const QUrl &hostAddress) override;
+ bool setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas=BuiltInSchemasOnly) override;
protected:
QRemoteObjectHost(QRemoteObjectHostPrivate &, QObject *);
diff --git a/src/remoteobjects/qremoteobjectnode_p.h b/src/remoteobjects/qremoteobjectnode_p.h
index efcf936..1a8ebb6 100644
--- a/src/remoteobjects/qremoteobjectnode_p.h
+++ b/src/remoteobjects/qremoteobjectnode_p.h
@@ -154,7 +154,7 @@ public:
virtual QReplicaImplementationInterface *handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name);
void handleReplicaConnection(const QString &name);
- void handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, ClientIoDevice *connection);
+ void handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, IoDeviceBase *connection);
void initialize();
private:
bool checkSignatures(const QByteArray &a, const QByteArray &b);
@@ -162,7 +162,7 @@ private:
public:
struct SourceInfo
{
- ClientIoDevice* device;
+ IoDeviceBase* device;
QString typeName;
QByteArray objectSignature;
};
@@ -171,6 +171,7 @@ public:
QUrl registryAddress;
QHash<QString, QWeakPointer<QReplicaImplementationInterface> > replicas;
QMap<QString, SourceInfo> connectedSources;
+ QMap<QString, QRemoteObjectNode::RemoteObjectSchemaHandler> schemaHandlers;
QSet<ClientIoDevice*> pendingReconnect;
QSet<QUrl> requestedUrls;
QRemoteObjectRegistry *registry;
diff --git a/src/remoteobjects/qremoteobjectreplica.cpp b/src/remoteobjects/qremoteobjectreplica.cpp
index 6cfec3c..dc2f926 100644
--- a/src/remoteobjects/qremoteobjectreplica.cpp
+++ b/src/remoteobjects/qremoteobjectreplica.cpp
@@ -104,16 +104,23 @@ QConnectedReplicaImplementation::QConnectedReplicaImplementation(const QString &
m_heartbeatTimer.start();
});
connect(&m_heartbeatTimer, &QTimer::timeout, this, [this] {
+ // TODO: Revisit if a baseclass method can be used to avoid specialized cast
+ // conditional logic.
+ auto clientIo = qobject_cast<ClientIoDevice *>(connectionToSource);
if (m_pendingCalls.contains(0)) {
// The source didn't respond in time, disconnect the connection
- if (connectionToSource)
- connectionToSource->disconnectFromServer();
+ if (clientIo)
+ clientIo->disconnectFromServer();
+ else if (connectionToSource)
+ connectionToSource->close();
} else {
serializePingPacket(m_packet, m_objectName);
if (sendCommandWithReply(0).d->serialId == -1) {
m_heartbeatTimer.stop();
- if (connectionToSource)
- connectionToSource->disconnectFromServer();
+ if (clientIo)
+ clientIo->disconnectFromServer();
+ else if (connectionToSource)
+ connectionToSource->close();
}
}
});
@@ -449,7 +456,7 @@ void QConnectedReplicaImplementation::setProperty(int i, const QVariant &prop)
m_propertyStorage[i] = prop;
}
-void QConnectedReplicaImplementation::setConnection(ClientIoDevice *conn)
+void QConnectedReplicaImplementation::setConnection(IoDeviceBase *conn)
{
if (connectionToSource.isNull()) {
connectionToSource = conn;
diff --git a/src/remoteobjects/qremoteobjectreplica_p.h b/src/remoteobjects/qremoteobjectreplica_p.h
index 6b64b67..471964c 100644
--- a/src/remoteobjects/qremoteobjectreplica_p.h
+++ b/src/remoteobjects/qremoteobjectreplica_p.h
@@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE
class QRemoteObjectReplica;
class QRemoteObjectSource;
-class ClientIoDevice;
+class IoDeviceBase;
class QReplicaImplementationInterface
{
@@ -165,7 +165,7 @@ public:
QRemoteObjectPendingCall sendCommandWithReply(int serialId);
bool waitForFinished(const QRemoteObjectPendingCall &call, int timeout) override;
void notifyAboutReply(int ackedSerialId, const QVariant &value) override;
- void setConnection(ClientIoDevice *conn);
+ void setConnection(IoDeviceBase *conn);
void setDisconnected();
void _q_send(QMetaObject::Call call, int index, const QVariantList &args) override;
@@ -176,7 +176,7 @@ public:
QVector<QRemoteObjectReplica *> m_parentsNeedingConnect;
QVariantList m_propertyStorage;
QVector<int> m_childIndices;
- QPointer<ClientIoDevice> connectionToSource;
+ QPointer<IoDeviceBase> connectionToSource;
// pending call data
int m_curSerialId = 1; // 0 is reserved for heartbeat signals
diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp
index 5c9f7f9..36bee20 100644
--- a/src/remoteobjects/qremoteobjectsource.cpp
+++ b/src/remoteobjects/qremoteobjectsource.cpp
@@ -228,7 +228,7 @@ QRemoteObjectRootSource::~QRemoteObjectRootSource()
delete it;
}
d->m_sourceIo->unregisterSource(this);
- Q_FOREACH (ServerIoDevice *io, d->m_listeners) {
+ Q_FOREACH (IoDeviceBase *io, d->m_listeners) {
removeListener(io, true);
}
delete d;
@@ -323,11 +323,11 @@ void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call,
serializeInvokePacket(d->m_packet, name(), call, index, *marshalArgs(index, a), -1, propertyIndex);
d->m_packet.baseAddress = 0;
- Q_FOREACH (ServerIoDevice *io, d->m_listeners)
+ Q_FOREACH (IoDeviceBase *io, d->m_listeners)
io->write(d->m_packet.array, d->m_packet.size);
}
-void QRemoteObjectRootSource::addListener(ServerIoDevice *io, bool dynamic)
+void QRemoteObjectRootSource::addListener(IoDeviceBase *io, bool dynamic)
{
d->m_listeners.append(io);
d->isDynamic = dynamic;
@@ -347,7 +347,7 @@ void QRemoteObjectRootSource::addListener(ServerIoDevice *io, bool dynamic)
d->isDynamic = false;
}
-int QRemoteObjectRootSource::removeListener(ServerIoDevice *io, bool shouldSendRemove)
+int QRemoteObjectRootSource::removeListener(IoDeviceBase *io, bool shouldSendRemove)
{
d->m_listeners.removeAll(io);
if (shouldSendRemove)
diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h
index 86c1c5e..2961e5c 100644
--- a/src/remoteobjects/qremoteobjectsource_p.h
+++ b/src/remoteobjects/qremoteobjectsource_p.h
@@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
class QRemoteObjectSourceIo;
-class ServerIoDevice;
+class IoDeviceBase;
class QRemoteObjectSourceBase : public QObject
{
@@ -87,7 +87,7 @@ public:
struct Private {
Private(QRemoteObjectSourceIo *io) : m_sourceIo(io), isDynamic(false) {}
QRemoteObjectSourceIo *m_sourceIo;
- QVector<ServerIoDevice*> m_listeners;
+ QVector<IoDeviceBase*> m_listeners;
QRemoteObjectPackets::DataStreamPacket m_packet;
// Types needed during recursively sending a root to a new listener
@@ -122,8 +122,8 @@ public:
bool isRoot() const override { return true; }
QString name() const override { return m_name; }
- void addListener(ServerIoDevice *io, bool dynamic = false);
- int removeListener(ServerIoDevice *io, bool shouldSendRemove = false);
+ void addListener(IoDeviceBase *io, bool dynamic = false);
+ int removeListener(IoDeviceBase *io, bool shouldSendRemove = false);
QString m_name;
};
diff --git a/src/remoteobjects/qremoteobjectsourceio.cpp b/src/remoteobjects/qremoteobjectsourceio.cpp
index 2ba2043..d864ed5 100644
--- a/src/remoteobjects/qremoteobjectsourceio.cpp
+++ b/src/remoteobjects/qremoteobjectsourceio.cpp
@@ -52,22 +52,31 @@ using namespace QtRemoteObjects;
QRemoteObjectSourceIo::QRemoteObjectSourceIo(const QUrl &address, QObject *parent)
: QObject(parent)
- , m_server(QtROServerFactory::instance()->create(address, this))
+ , m_server(QtROServerFactory::instance()->isValid(address) ?
+ QtROServerFactory::instance()->create(address, this) : nullptr)
{
if (m_server && m_server->listen(address)) {
qRODebug(this) << "QRemoteObjectSourceIo is Listening" << address;
} else {
- qROWarning(this) << "Listen failed for URL:" << address;
- if (m_server)
+ if (m_server) {
+ qROWarning(this) << "Listen failed for URL:" << address;
qROWarning(this) << m_server->serverError();
- else
- qROWarning(this) << "Most likely an unrecognized scheme was used.";
+ } else {
+ m_address = address;
+ qRODebug(this) << "Using" << address << "as external url.";
+ }
return;
}
connect(m_server.data(), &QConnectionAbstractServer::newConnection, this, &QRemoteObjectSourceIo::handleConnection);
}
+QRemoteObjectSourceIo::QRemoteObjectSourceIo(QObject *parent)
+ : QObject(parent)
+ , m_server(nullptr)
+{
+}
+
QRemoteObjectSourceIo::~QRemoteObjectSourceIo()
{
qDeleteAll(m_sourceRoots.values());
@@ -93,7 +102,7 @@ bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const SourceApiMap *
new QRemoteObjectRootSource(object, api, adapter, this);
QRemoteObjectPackets::serializeObjectListPacket(m_packet, {QRemoteObjectPackets::ObjectInfo{api->name(), api->typeName(), api->objectSignature()}});
- foreach (ServerIoDevice *conn, m_connections)
+ for (auto conn : m_connections)
conn->write(m_packet.array, m_packet.size);
if (const int count = m_connections.size())
qRODebug(this) << "Wrote new QObjectListPacket for" << api->name() << "to" << count << "connections";
@@ -120,8 +129,10 @@ void QRemoteObjectSourceIo::registerSource(QRemoteObjectSourceBase *source)
qRODebug(this) << "Registering" << name;
m_sourceRoots[name] = root;
m_objectToSourceMap[source->m_object] = root;
- const auto &type = source->m_api->typeName();
- emit remoteObjectAdded(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
+ if (serverAddress().isValid()) {
+ const auto &type = source->m_api->typeName();
+ emit remoteObjectAdded(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
+ }
}
}
@@ -134,13 +145,14 @@ void QRemoteObjectSourceIo::unregisterSource(QRemoteObjectSourceBase *source)
const auto type = source->m_api->typeName();
m_objectToSourceMap.remove(source->m_object);
m_sourceRoots.remove(name);
- emit remoteObjectRemoved(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
+ if (serverAddress().isValid())
+ emit remoteObjectRemoved(qMakePair(name, QRemoteObjectSourceLocationInfo(type, serverAddress())));
}
}
void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn)
{
- ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn);
+ IoDeviceBase *connection = qobject_cast<IoDeviceBase*>(conn);
m_connections.remove(connection);
qRODebug(this) << "OnServerDisconnect";
@@ -158,7 +170,7 @@ void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn)
void QRemoteObjectSourceIo::onServerRead(QObject *conn)
{
// Assert the invariant here conn is of type QIODevice
- ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn);
+ IoDeviceBase *connection = qobject_cast<IoDeviceBase*>(conn);
QRemoteObjectPacketTypeEnum packetType;
do {
@@ -258,13 +270,18 @@ void QRemoteObjectSourceIo::handleConnection()
qRODebug(this) << "handleConnection" << m_connections;
ServerIoDevice *conn = m_server->nextPendingConnection();
+ newConnection(conn);
+}
+
+void QRemoteObjectSourceIo::newConnection(IoDeviceBase *conn)
+{
m_connections.insert(conn);
- connect(conn, &ServerIoDevice::disconnected, this, [this, conn]() {
- onServerDisconnect(conn);
- });
- connect(conn, &ServerIoDevice::readyRead, this, [this, conn]() {
+ connect(conn, &IoDeviceBase::readyRead, this, [this, conn]() {
onServerRead(conn);
});
+ connect(conn, &IoDeviceBase::disconnected, this, [this, conn]() {
+ onServerDisconnect(conn);
+ });
serializeHandshakePacket(m_packet);
conn->write(m_packet.array, m_packet.size);
@@ -280,7 +297,9 @@ void QRemoteObjectSourceIo::handleConnection()
QUrl QRemoteObjectSourceIo::serverAddress() const
{
- return m_server->address();
+ if (m_server)
+ return m_server->address();
+ return m_address;
}
QT_END_NAMESPACE
diff --git a/src/remoteobjects/qremoteobjectsourceio_p.h b/src/remoteobjects/qremoteobjectsourceio_p.h
index c9adde7..4cc045c 100644
--- a/src/remoteobjects/qremoteobjectsourceio_p.h
+++ b/src/remoteobjects/qremoteobjectsourceio_p.h
@@ -70,12 +70,14 @@ class QRemoteObjectSourceIo : public QObject
Q_OBJECT
public:
explicit QRemoteObjectSourceIo(const QUrl &address, QObject *parent = nullptr);
+ explicit QRemoteObjectSourceIo(QObject *parent = nullptr);
~QRemoteObjectSourceIo() override;
bool enableRemoting(QObject *object, const QMetaObject *meta, const QString &name,
const QString &typeName);
bool enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter = nullptr);
bool disableRemoting(QObject *object);
+ void newConnection(IoDeviceBase *conn);
QUrl serverAddress() const;
@@ -94,15 +96,16 @@ public:
void unregisterSource(QRemoteObjectSourceBase *source);
QHash<QIODevice*, quint32> m_readSize;
- QSet<ServerIoDevice*> m_connections;
+ QSet<IoDeviceBase*> m_connections;
QHash<QObject *, QRemoteObjectRootSource*> m_objectToSourceMap;
QMap<QString, QRemoteObjectSourceBase*> m_sourceObjects;
QMap<QString, QRemoteObjectRootSource*> m_sourceRoots;
- QHash<ServerIoDevice*, QUrl> m_registryMapping;
+ QHash<IoDeviceBase*, QUrl> m_registryMapping;
QScopedPointer<QConnectionAbstractServer> m_server;
QRemoteObjectPackets::DataStreamPacket m_packet;
QString m_rxName;
QVariantList m_rxArgs;
+ QUrl m_address;
};
QT_END_NAMESPACE
diff --git a/src/remoteobjects/qtremoteobjectglobal.h b/src/remoteobjects/qtremoteobjectglobal.h
index 7e6dfb9..eb23b44 100644
--- a/src/remoteobjects/qtremoteobjectglobal.h
+++ b/src/remoteobjects/qtremoteobjectglobal.h
@@ -173,6 +173,7 @@ enum QRemoteObjectPacketTypeEnum
Ping,
Pong
};
+Q_ENUM_NS(QRemoteObjectPacketTypeEnum)
enum InitialAction {
FetchRootSize,