Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldebugconnection.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
7
8#include <private/qpacketprotocol_p.h>
9#include <private/qpacket_p.h>
10#include <private/qobject_p.h>
11
12#include <QtCore/qeventloop.h>
13#include <QtCore/qtimer.h>
14#include <QtCore/QHash>
15#include <QtCore/qdatastream.h>
16#include <QtNetwork/qlocalserver.h>
17#include <QtNetwork/qlocalsocket.h>
18#include <QtNetwork/qtcpsocket.h>
19
21
22static const int protocolVersion = 1;
23static const QString serverId = QLatin1String("QDeclarativeDebugServer");
24static const QString clientId = QLatin1String("QDeclarativeDebugClient");
25
49
50QQmlDebugConnectionPrivate::QQmlDebugConnectionPrivate()
51{
52 handshakeTimer.setSingleShot(true);
53 handshakeTimer.setInterval(3000);
54}
55
57{
58 Q_Q(QQmlDebugConnection);
59 if (!q->isConnected())
60 return;
61
62 QPacket pack(currentDataStreamVersion);
63 pack << serverId << 1 << plugins.keys();
64 protocol->send(pack.data());
65 flush();
66}
67
68void QQmlDebugConnection::socketConnected()
69{
70 Q_D(QQmlDebugConnection);
71 QPacket pack(d->currentDataStreamVersion);
72 pack << serverId << 0 << protocolVersion << d->plugins.keys() << d->maximumDataStreamVersion
73 << true; // We accept multiple messages per packet
74 d->protocol->send(pack.data());
75 d->flush();
76}
77
78void QQmlDebugConnection::socketDisconnected()
79{
80 Q_D(QQmlDebugConnection);
81 d->gotHello = false;
82 emit disconnected();
83}
84
85void QQmlDebugConnection::protocolReadyRead()
86{
87 Q_D(QQmlDebugConnection);
88 if (!d->gotHello) {
89 QPacket pack(d->currentDataStreamVersion, d->protocol->read());
90 QString name;
91
92 pack >> name;
93
94 bool validHello = false;
95 if (name == clientId) {
96 int op = -1;
97 pack >> op;
98 if (op == 0) {
99 int version = -1;
100 pack >> version;
101 if (version == protocolVersion) {
102 QStringList pluginNames;
103 QList<float> pluginVersions;
104 pack >> pluginNames;
105 if (!pack.atEnd())
106 pack >> pluginVersions;
107
108 const int pluginNamesSize = pluginNames.size();
109 const int pluginVersionsSize = pluginVersions.size();
110 for (int i = 0; i < pluginNamesSize; ++i) {
111 float pluginVersion = 1.0;
112 if (i < pluginVersionsSize)
113 pluginVersion = pluginVersions.at(i);
114 d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
115 }
116
117 pack >> d->currentDataStreamVersion;
118 validHello = true;
119 }
120 }
121 }
122
123 if (!validHello) {
124 qWarning("QQmlDebugConnection: Invalid hello message");
125 close();
126 return;
127 }
128 d->gotHello = true;
129 emit connected();
130
131 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.begin();
132 for (; iter != d->plugins.end(); ++iter) {
133 QQmlDebugClient::State newState = QQmlDebugClient::Unavailable;
134 if (d->serverPlugins.contains(iter.key()))
135 newState = QQmlDebugClient::Enabled;
136 iter.value()->stateChanged(newState);
137 }
138
139 d->handshakeTimer.stop();
140 d->handshakeEventLoop.quit();
141 }
142
143 while (d->protocol->packetsAvailable()) {
144 QPacket pack(d->currentDataStreamVersion, d->protocol->read());
145 QString name;
146 pack >> name;
147
148 if (name == clientId) {
149 int op = -1;
150 pack >> op;
151
152 if (op == 1) {
153 // Service Discovery
154 QHash<QString, float> oldServerPlugins = d->serverPlugins;
155 d->serverPlugins.clear();
156
157 QStringList pluginNames;
158 QList<float> pluginVersions;
159 pack >> pluginNames;
160 if (!pack.atEnd())
161 pack >> pluginVersions;
162
163 const int pluginNamesSize = pluginNames.size();
164 const int pluginVersionsSize = pluginVersions.size();
165 for (int i = 0; i < pluginNamesSize; ++i) {
166 float pluginVersion = 1.0;
167 if (i < pluginVersionsSize)
168 pluginVersion = pluginVersions.at(i);
169 d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
170 }
171
172 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.begin();
173 for (; iter != d->plugins.end(); ++iter) {
174 const QString &pluginName = iter.key();
175 QQmlDebugClient::State newState = QQmlDebugClient::Unavailable;
176 if (d->serverPlugins.contains(pluginName))
177 newState = QQmlDebugClient::Enabled;
178
179 if (oldServerPlugins.contains(pluginName)
180 != d->serverPlugins.contains(pluginName)) {
181 iter.value()->stateChanged(newState);
182 }
183 }
184 } else {
185 qWarning() << "QQmlDebugConnection: Unknown control message id" << op;
186 }
187 } else {
188 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.find(name);
189 if (iter == d->plugins.end()) {
190 // We can get more messages for plugins we have removed because it takes time to
191 // send the advertisement message but the removal is instant locally.
192 if (!d->removedPlugins.contains(name)) {
193 qWarning() << "QQmlDebugConnection: Message received for missing plugin"
194 << name;
195 }
196 } else {
197 QQmlDebugClient *client = *iter;
198 QByteArray message;
199 while (!pack.atEnd()) {
200 pack >> message;
201 client->messageReceived(message);
202 }
203 }
204 }
205 }
206}
207
208void QQmlDebugConnection::handshakeTimeout()
209{
210 Q_D(QQmlDebugConnection);
211 if (!d->gotHello) {
212 qWarning() << "QQmlDebugConnection: Did not get handshake answer in time";
213 d->handshakeEventLoop.quit();
214 }
215}
216
217QQmlDebugConnection::QQmlDebugConnection(QObject *parent) :
218 QObject(*(new QQmlDebugConnectionPrivate), parent)
219{
220 Q_D(QQmlDebugConnection);
221 connect(&d->handshakeTimer, &QTimer::timeout, this, &QQmlDebugConnection::handshakeTimeout);
222}
223
225{
226 Q_D(QQmlDebugConnection);
227 QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
228 for (; iter != d->plugins.end(); ++iter)
229 emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
230}
231
233{
234 Q_D(const QQmlDebugConnection);
235 return d->currentDataStreamVersion;
236}
237
239{
240 Q_D(QQmlDebugConnection);
241 d->maximumDataStreamVersion = maximumVersion;
242}
243
245{
246 Q_D(const QQmlDebugConnection);
247 return d->gotHello;
248}
249
251{
252 Q_D(const QQmlDebugConnection);
253 return !d->gotHello && d->device;
254}
255
257{
258 Q_D(QQmlDebugConnection);
259 if (d->gotHello) {
260 d->gotHello = false;
261 d->device->close();
262
263 QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
264 for (; iter != d->plugins.end(); ++iter)
265 emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
266 }
267
268 if (d->device) {
269 d->device->deleteLater();
270 d->device = nullptr;
271 }
272}
273
275{
276 Q_D(QQmlDebugConnection);
277 auto socket = qobject_cast<QAbstractSocket*>(d->device);
278 if (!socket) {
279 if (!d->server || (!d->server->hasPendingConnections() &&
280 !d->server->waitForNewConnection(msecs)))
281 return false;
282 } else if (!socket->waitForConnected(msecs)) {
283 return false;
284 }
285 // wait for handshake
286 d->handshakeTimer.start();
287 d->handshakeEventLoop.exec();
288 return d->gotHello;
289}
290
291QQmlDebugClient *QQmlDebugConnection::client(const QString &name) const
292{
293 Q_D(const QQmlDebugConnection);
294 return d->plugins.value(name, nullptr);
295}
296
297bool QQmlDebugConnection::addClient(const QString &name, QQmlDebugClient *client)
298{
299 Q_D(QQmlDebugConnection);
300 if (d->plugins.contains(name))
301 return false;
302 d->removedPlugins.removeAll(name);
303 d->plugins.insert(name, client);
304 d->advertisePlugins();
305 return true;
306}
307
308bool QQmlDebugConnection::removeClient(const QString &name)
309{
310 Q_D(QQmlDebugConnection);
311 if (!d->plugins.contains(name))
312 return false;
313 d->plugins.remove(name);
314 d->removedPlugins.append(name);
315 d->advertisePlugins();
316 return true;
317}
318
319float QQmlDebugConnection::serviceVersion(const QString &serviceName) const
320{
321 Q_D(const QQmlDebugConnection);
322 return d->serverPlugins.value(serviceName, -1);
323}
324
325bool QQmlDebugConnection::sendMessage(const QString &name, const QByteArray &message)
326{
327 Q_D(QQmlDebugConnection);
328 if (!isConnected() || !d->serverPlugins.contains(name))
329 return false;
330
331 QPacket pack(d->currentDataStreamVersion);
332 pack << name << message;
333 d->protocol->send(pack.data());
334 d->flush();
335
336 return true;
337}
338
340{
341 if (auto socket = qobject_cast<QAbstractSocket *>(device))
342 socket->flush();
343 else if (auto socket = qobject_cast<QLocalSocket *>(device))
344 socket->flush();
345}
346
347void QQmlDebugConnection::connectToHost(const QString &hostName, quint16 port)
348{
349 Q_D(QQmlDebugConnection);
350 if (d->gotHello)
351 close();
352 auto socket = new QTcpSocket(this);
353 d->device = socket;
354 d->createProtocol();
355 connect(socket, &QAbstractSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
356 connect(socket, &QAbstractSocket::connected, this, &QQmlDebugConnection::socketConnected);
357 connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(
358 &QAbstractSocket::errorOccurred), this, &QQmlDebugConnection::socketError);
359 connect(socket, &QAbstractSocket::stateChanged, this, &QQmlDebugConnection::socketStateChanged);
360 socket->connectToHost(hostName, port);
361}
362
363void QQmlDebugConnection::startLocalServer(const QString &fileName)
364{
365 Q_D(QQmlDebugConnection);
366 if (d->gotHello)
367 close();
368 if (d->server)
369 d->server->deleteLater();
370 d->server = new QLocalServer(this);
371 // QueuedConnection so that waitForNewConnection() returns true.
372 connect(d->server, &QLocalServer::newConnection,
373 this, &QQmlDebugConnection::newConnection, Qt::QueuedConnection);
374 d->server->listen(fileName);
375}
376
402
403void QQmlDebugConnection::newConnection()
404{
405 Q_D(QQmlDebugConnection);
406 delete d->device;
407 QLocalSocket *socket = d->server->nextPendingConnection();
408 d->server->close();
409 d->device = socket;
410 d->createProtocol();
411 connect(socket, &QLocalSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
412 auto translator = new LocalSocketSignalTranslator(socket);
413
414 connect(translator, &LocalSocketSignalTranslator::socketError,
415 this, &QQmlDebugConnection::socketError);
416 connect(translator, &LocalSocketSignalTranslator::socketStateChanged,
417 this, &QQmlDebugConnection::socketStateChanged);
418 socketConnected();
419}
420
422{
423 Q_Q(QQmlDebugConnection);
424 delete protocol;
425 protocol = new QPacketProtocol(device, q);
426 QObject::connect(protocol, &QPacketProtocol::readyRead,
427 q, &QQmlDebugConnection::protocolReadyRead);
428}
429
430QT_END_NAMESPACE
431
432#include <qqmldebugconnection.moc>
433
434#include "moc_qqmldebugconnection_p.cpp"
void socketStateChanged(QAbstractSocket::SocketState)
QHash< QString, float > serverPlugins
QHash< QString, QQmlDebugClient * > plugins
bool addClient(const QString &name, QQmlDebugClient *client)
void setMaximumDataStreamVersion(int maximumVersion)
bool sendMessage(const QString &name, const QByteArray &message)
bool removeClient(const QString &name)
QQmlDebugClient * client(const QString &name) const
float serviceVersion(const QString &serviceName) const
bool waitForConnected(int msecs=30000)
void startLocalServer(const QString &fileName)
void connectToHost(const QString &hostName, quint16 port)
Combined button and popup list for selecting options.
static const QString clientId
static const QString serverId
static QT_BEGIN_NAMESPACE const int protocolVersion