summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-09-05 08:58:14 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-09-05 08:58:14 +0200
commit0665ee676b145446133f12b87ee4c621d1e8b27e (patch)
treef54332eaf8582b12e65e3c41fa333810ec6b865d
parent816c491d39d4692a769ff5701fc7c498f20a091a (diff)
parent954534fb5542dda1ab1a1730f7ef57c250d9f10c (diff)
Merge remote-tracking branch 'origin/5.14' into 5.15
-rw-r--r--src/mqtt/qmqttclient.cpp56
-rw-r--r--src/mqtt/qmqttclient.h4
-rw-r--r--src/mqtt/qmqttclient_p.h1
-rw-r--r--src/mqtt/qmqttconnection.cpp58
-rw-r--r--src/mqtt/qmqttconnection_p.h2
-rw-r--r--tests/auto/qmqttclient/tst_qmqttclient.cpp82
6 files changed, 165 insertions, 38 deletions
diff --git a/src/mqtt/qmqttclient.cpp b/src/mqtt/qmqttclient.cpp
index bd5bb12..a13ce1e 100644
--- a/src/mqtt/qmqttclient.cpp
+++ b/src/mqtt/qmqttclient.cpp
@@ -94,6 +94,8 @@ Q_LOGGING_CATEGORY(lcMqttClient, "qt.mqtt.client")
If the broker does not respond within a grace period the connection will be
closed.
+
+ \sa autoKeepAlive(), requestPing(), pingResponseReceived()
*/
/*!
@@ -157,6 +159,24 @@ Q_LOGGING_CATEGORY(lcMqttClient, "qt.mqtt.client")
*/
/*!
+ \property QMqttClient::autoKeepAlive
+ \since 5.14
+ \brief This property holds whether the client will automatically manage
+ keep alive messages to the server.
+
+ If this property is \c true, then the client will automatically send a
+ ping message to the server at the keepAlive interval.
+
+ Otherwise, a user will have to manually invoke requestPing
+ within the specified interval of the connection. If no ping has been
+ sent within the interval, the server will disconnect.
+
+ The default of this property is \c true.
+
+ \sa keepAlive(), requestPing(), serverConnectionProperties(), pingResponseReceived()
+*/
+
+/*!
\enum QMqttClient::TransportType
This enum type specifies the connection method to be used to instantiate a
@@ -471,19 +491,25 @@ qint32 QMqttClient::publish(const QMqttTopicName &topic, const QMqttPublishPrope
}
/*!
- Sends a ping message to the broker and expects a reply. If the connection
- is active, the MQTT client will automatically send a ping message at
- \l keepAlive intervals.
+ Sends a ping message to the broker and expects a reply.
+
+ If the connection is active and \l autoKeepAlive is \c true, then calling this
+ function will fail as the client is responsible for managing this process.
+
+ Using \c requestPing() manually requires a call every time within the \l keepAlive
+ interval as long as the connection is active.
To check whether the ping is successful, connect to the
\l pingResponseReceived() signal.
Returns \c true if the ping request could be sent.
+
+ \sa pingResponseReceived(), autoKeepAlive(), keepAlive()
*/
bool QMqttClient::requestPing()
{
Q_D(QMqttClient);
- return d->m_connection.sendControlPingRequest();
+ return d->m_connection.sendControlPingRequest(false);
}
QString QMqttClient::hostname() const
@@ -642,6 +668,12 @@ bool QMqttClient::willRetain() const
return d->m_willRetain;
}
+bool QMqttClient::autoKeepAlive() const
+{
+ Q_D(const QMqttClient);
+ return d->m_autoKeepAlive;
+}
+
/*!
\since 5.12
@@ -985,6 +1017,22 @@ void QMqttClient::setWillRetain(bool willRetain)
emit willRetainChanged(willRetain);
}
+void QMqttClient::setAutoKeepAlive(bool autoKeepAlive)
+{
+ Q_D(QMqttClient);
+
+ if (state() != QMqttClient::Disconnected) {
+ qCDebug(lcMqttClient) << "Changing autoKeepAlive while connected is not possible.";
+ return;
+ }
+
+ if (d->m_autoKeepAlive == autoKeepAlive)
+ return;
+
+ d->m_autoKeepAlive = autoKeepAlive;
+ emit autoKeepAliveChanged(d->m_autoKeepAlive);
+}
+
void QMqttClient::setError(ClientError e)
{
Q_D(QMqttClient);
diff --git a/src/mqtt/qmqttclient.h b/src/mqtt/qmqttclient.h
index 478c7e8..2d650a8 100644
--- a/src/mqtt/qmqttclient.h
+++ b/src/mqtt/qmqttclient.h
@@ -101,6 +101,7 @@ private:
Q_PROPERTY(QByteArray willMessage READ willMessage WRITE setWillMessage NOTIFY willMessageChanged)
Q_PROPERTY(quint8 willQoS READ willQoS WRITE setWillQoS NOTIFY willQoSChanged)
Q_PROPERTY(bool willRetain READ willRetain WRITE setWillRetain NOTIFY willRetainChanged)
+ Q_PROPERTY(bool autoKeepAlive READ autoKeepAlive WRITE setAutoKeepAlive NOTIFY autoKeepAliveChanged)
public:
explicit QMqttClient(QObject *parent = nullptr);
@@ -148,6 +149,7 @@ public:
quint8 willQoS() const;
QByteArray willMessage() const;
bool willRetain() const;
+ bool autoKeepAlive() const;
void setConnectionProperties(const QMqttConnectionProperties &prop);
QMqttConnectionProperties connectionProperties() const;
@@ -182,6 +184,7 @@ Q_SIGNALS:
void willQoSChanged(quint8 willQoS);
void willMessageChanged(QByteArray willMessage);
void willRetainChanged(bool willRetain);
+ void autoKeepAliveChanged(bool autoKeepAlive);
void authenticationRequested(const QMqttAuthenticationProperties &p);
void authenticationFinished(const QMqttAuthenticationProperties &p);
@@ -201,6 +204,7 @@ public Q_SLOTS:
void setWillQoS(quint8 willQoS);
void setWillMessage(const QByteArray &willMessage);
void setWillRetain(bool willRetain);
+ void setAutoKeepAlive(bool autoKeepAlive);
private:
void connectToHost(bool encrypted, const QString &sslPeerName);
diff --git a/src/mqtt/qmqttclient_p.h b/src/mqtt/qmqttclient_p.h
index 2ce9f48..b2343c3 100644
--- a/src/mqtt/qmqttclient_p.h
+++ b/src/mqtt/qmqttclient_p.h
@@ -71,6 +71,7 @@ public:
QByteArray m_willMessage;
quint8 m_willQoS{0};
bool m_willRetain{false};
+ bool m_autoKeepAlive{true};
QString m_username;
QString m_password;
bool m_cleanSession{true};
diff --git a/src/mqtt/qmqttconnection.cpp b/src/mqtt/qmqttconnection.cpp
index 4923e8d..1c0795b 100644
--- a/src/mqtt/qmqttconnection.cpp
+++ b/src/mqtt/qmqttconnection.cpp
@@ -47,49 +47,33 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcMqttConnection, "qt.mqtt.connection")
Q_LOGGING_CATEGORY(lcMqttConnectionVerbose, "qt.mqtt.connection.verbose");
-template<>
-quint32 QMqttConnection::readBufferTyped(qint64 *dataSize)
-{
- if (dataSize)
- *dataSize -= sizeof(quint32);
- return qFromBigEndian<quint32>(reinterpret_cast<const quint32 *>(readBuffer(4).constData()));
-}
-
-template<>
-quint16 QMqttConnection::readBufferTyped(qint64 *dataSize)
+template <typename T>
+T QMqttConnection::readBufferTyped(qint64 *dataSize)
{
- if (dataSize)
- *dataSize -= sizeof(quint16);
- return qFromBigEndian<quint16>(reinterpret_cast<const quint16 *>(readBuffer(2).constData()));
-}
+ Q_STATIC_ASSERT(std::is_integral<T>::value);
-template<>
-quint8 QMqttConnection::readBufferTyped(qint64 *dataSize)
-{
- quint8 result;
- readBuffer(reinterpret_cast<char *>(&result), 1);
- if (dataSize)
- *dataSize -= sizeof(quint8);
- return result;
+ T result;
+ readBuffer(reinterpret_cast<char *>(&result), sizeof(result));
+ if (dataSize != nullptr)
+ *dataSize -= sizeof(result);
+ return qFromBigEndian(result);
}
template<>
-QString QMqttConnection::readBufferTyped(qint64 *dataSize)
+QByteArray QMqttConnection::readBufferTyped(qint64 *dataSize)
{
const quint16 size = readBufferTyped<quint16>(dataSize);
+ QByteArray ba(int(size), Qt::Uninitialized);
+ readBuffer(ba.data(), size);
if (dataSize)
*dataSize -= size;
- const QByteArray ba = readBuffer(size);
- return QString::fromUtf8(reinterpret_cast<const char *>(ba.constData()), ba.size());
+ return ba;
}
template<>
-QByteArray QMqttConnection::readBufferTyped(qint64 *dataSize)
+QString QMqttConnection::readBufferTyped(qint64 *dataSize)
{
- const quint16 size = readBufferTyped<quint16>(dataSize);
- if (dataSize)
- *dataSize -= size;
- return readBuffer(size);
+ return QString::fromUtf8(readBufferTyped<QByteArray>(dataSize));
}
QMqttConnection::QMqttConnection(QObject *parent) : QObject(parent)
@@ -587,20 +571,27 @@ bool QMqttConnection::sendControlUnsubscribe(const QMqttTopicFilter &topic, cons
return true;
}
-bool QMqttConnection::sendControlPingRequest()
+bool QMqttConnection::sendControlPingRequest(bool isAuto)
{
qCDebug(lcMqttConnection) << Q_FUNC_INFO;
if (m_internalState != QMqttConnection::BrokerConnected)
return false;
+
+ if (!isAuto && m_clientPrivate->m_autoKeepAlive) {
+ qCDebug(lcMqttConnection) << "Requesting a manual ping while autoKeepAlive is enabled "
+ << "is not allowed.";
+ return false;
+ }
+
// 3.1.2.10 If a Client does not receive a PINGRESP packet within a reasonable amount of time
// after it has sent a PINGREQ, it SHOULD close the Network Connection to the Server
- // Consider two pending PINGRESP as reasonable.
if (m_pingTimeout > 1) {
closeConnection(QMqttClient::ServerUnavailable);
return false;
}
+
const QMqttControlPacket packet(QMqttControlPacket::PINGREQ);
if (!writePacketToTransport(packet)) {
qCDebug(lcMqttConnection) << "Failed to write PINGREQ to transport.";
@@ -1473,7 +1464,8 @@ void QMqttConnection::finalize_connack()
m_internalState = BrokerConnected;
m_clientPrivate->setStateAndError(QMqttClient::Connected);
- m_pingTimer.start(m_clientPrivate->m_keepAlive * 1000, this);
+ if (m_clientPrivate->m_autoKeepAlive)
+ m_pingTimer.start(m_clientPrivate->m_keepAlive * 1000, this);
}
void QMqttConnection::finalize_suback()
diff --git a/src/mqtt/qmqttconnection_p.h b/src/mqtt/qmqttconnection_p.h
index 91c6b77..456cd5a 100644
--- a/src/mqtt/qmqttconnection_p.h
+++ b/src/mqtt/qmqttconnection_p.h
@@ -86,7 +86,7 @@ public:
bool sendControlPublishComp(quint16 id);
QMqttSubscription *sendControlSubscribe(const QMqttTopicFilter &topic, quint8 qos, const QMqttSubscriptionProperties &properties);
bool sendControlUnsubscribe(const QMqttTopicFilter &topic, const QMqttUnsubscriptionProperties &properties);
- bool sendControlPingRequest();
+ bool sendControlPingRequest(bool isAuto = true);
bool sendControlDisconnect();
void setClientPrivate(QMqttClientPrivate *clientPrivate);
diff --git a/tests/auto/qmqttclient/tst_qmqttclient.cpp b/tests/auto/qmqttclient/tst_qmqttclient.cpp
index 21bd512..9563e7d 100644
--- a/tests/auto/qmqttclient/tst_qmqttclient.cpp
+++ b/tests/auto/qmqttclient/tst_qmqttclient.cpp
@@ -74,6 +74,8 @@ private Q_SLOTS:
void messageStatusReceive_data();
void messageStatusReceive();
void subscriptionIdsOverlap();
+ void keepAlive_data();
+ void keepAlive();
private:
QProcess m_brokerProcess;
QString m_testBroker;
@@ -147,6 +149,9 @@ void Tst_QMqttClient::getSetCheck()
QCOMPARE(client.willMessage(), QByteArray());
QCOMPARE(client.willQoS(), quint8(0));
QCOMPARE(client.willRetain(), false);
+ QCOMPARE(client.autoKeepAlive(), true);
+ client.setAutoKeepAlive(false);
+ QCOMPARE(client.autoKeepAlive(), false);
}
void Tst_QMqttClient::sendReceive_data()
@@ -827,6 +832,83 @@ void Tst_QMqttClient::subscriptionIdsOverlap()
QTRY_VERIFY2(receiveACounter == 1, "Did not receive non-overlapping message.");
}
+DefaultVersionTestData(Tst_QMqttClient::keepAlive_data)
+
+void Tst_QMqttClient::keepAlive()
+{
+ QFETCH(QMqttClient::ProtocolVersion, mqttVersion);
+ const quint16 keepAlive = 1;
+
+ VersionClient(mqttVersion, client);
+ client.setHostname(m_testBroker);
+ client.setPort(m_port);
+
+ client.setKeepAlive(keepAlive);
+ QCOMPARE(client.keepAlive(), keepAlive);
+ QCOMPARE(client.autoKeepAlive(), true); // default
+
+ client.connectToHost();
+ QTRY_COMPARE(client.state(), QMqttClient::Connected);
+
+ if (client.protocolVersion() == QMqttClient::MQTT_5_0) {
+ const auto serverProps = client.serverConnectionProperties();
+ if (serverProps.isValid() && serverProps.availableProperties() & QMqttServerConnectionProperties::ServerKeepAlive) {
+ if (serverProps.serverKeepAlive() == 0 || serverProps.serverKeepAlive() > keepAlive) {
+ qDebug() << "Server specifies keepAlive which cannot be used for this test";
+ return;
+ }
+ }
+ }
+
+ // Changing autoKeepAlive is not possible while connected
+ client.setAutoKeepAlive(false);
+ QCOMPARE(client.autoKeepAlive(), true);
+
+ // Sending a manual ping request with autoKeepAlive is not allowed
+ // and will be suppressed.
+ const bool sent = client.requestPing();
+ QCOMPARE(sent, false);
+
+ // Verify auto keep alive works
+ QSignalSpy spy(&client, &QMqttClient::pingResponseReceived);
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 5, keepAlive * 1000 * 5 * 2);
+ spy.clear();
+
+ client.disconnectFromHost();
+ QTRY_COMPARE(client.state(), QMqttClient::Disconnected);
+
+ // Set manual keepAlive
+ client.setAutoKeepAlive(false);
+ QCOMPARE(client.autoKeepAlive(), false);
+ client.setKeepAlive(keepAlive);
+
+ client.connectToHost();
+ QTRY_COMPARE(client.state(), QMqttClient::Connected);
+
+ // Check for timeout / disconnect when not ping sent
+ QTRY_COMPARE_WITH_TIMEOUT(client.state(), QMqttClient::Disconnected, keepAlive * 1000 * 5 * 2);
+
+ // Check manual ping
+ QTimer t;
+ t.setInterval(keepAlive * 1000);
+ t.setSingleShot(false);
+ t.connect(&t, &QTimer::timeout, [&client]() {
+ client.requestPing();
+ });
+
+ connect(&client, &QMqttClient::connected, [&t]() {
+ t.start();
+ });
+
+ client.connectToHost();
+ QTRY_COMPARE(client.state(), QMqttClient::Connected);
+
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 5, keepAlive * 1000 * 5 * 2);
+
+ client.disconnectFromHost();
+ QTRY_COMPARE(client.state(), QMqttClient::Disconnected);
+}
+
QTEST_MAIN(Tst_QMqttClient)
#include "tst_qmqttclient.moc"