From 0eadb8dcd33a152b83f765cf03ad5d8f2c77f16a Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 30 May 2019 14:54:06 +0200 Subject: [PATCH] Fix a crash when a delayed reply would call a callback for an object that disappeared --- libnymea-app-core/connection/awsclient.cpp | 22 +++++++++++++------ libnymea-app-core/connection/awsclient.h | 6 +++-- .../connection/cloudtransport.cpp | 3 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/libnymea-app-core/connection/awsclient.cpp b/libnymea-app-core/connection/awsclient.cpp index a3a3c9ab..cc57954b 100644 --- a/libnymea-app-core/connection/awsclient.cpp +++ b/libnymea-app-core/connection/awsclient.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "sigv4utils.h" @@ -820,7 +821,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId) if (qc.method == "fetchDevices") { fetchDevices(); } else if (qc.method == "postToMQTT") { - postToMQTT(qc.arg1, qc.arg2, qc.callback); + postToMQTT(qc.arg1, qc.arg2, qc.sender, qc.callback); } else if (qc.method == "deleteAccount") { deleteAccount(); } else if (qc.method == "registerPushNotificationEndpoint") { @@ -837,7 +838,7 @@ bool AWSClient::tokensExpired() const return (m_accessTokenExpiry.addSecs(-10) < QDateTime::currentDateTime()) || (m_sessionTokenExpiry.addSecs(-10) < QDateTime::currentDateTime()); } -bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, std::function callback) +bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, QObject* sender, std::function callback) { if (!isLoggedIn()) { qWarning() << "Cannot post to MQTT. Not logged in to AWS"; @@ -846,11 +847,13 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, std:: if (tokensExpired()) { qDebug() << "Cannot post to MQTT. Need to refresh the tokens first"; refreshAccessToken(); - QueuedCall::enqueue(m_callQueue, QueuedCall("postToMQTT", boxId, timestamp, callback)); + QueuedCall::enqueue(m_callQueue, QueuedCall("postToMQTT", boxId, timestamp, sender, callback)); return true; // So far it looks we're doing ok... let's return true } QString topic = QString("%1/%2/proxy").arg(boxId).arg(QString(m_identityId)); + QPointer senderWatcher = QPointer(sender); + // This is somehow broken in AWS... // The Signature needs to be created with having the topic percentage-encoded twice // while the actual request needs to go out with it only being encoded once. @@ -882,15 +885,21 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, std:: // } // qDebug() << "Payload:" << payload; QNetworkReply *reply = m_nam->post(request, payload); - QTimer::singleShot(5000, reply, [reply, callback](){ + QTimer::singleShot(5000, reply, [reply, senderWatcher, callback](){ reply->deleteLater(); qWarning() << "Timeout posting to MQTT"; - callback(false); + if (senderWatcher) { + callback(false); + } }); - connect(reply, &QNetworkReply::finished, this, [reply, callback]() { + connect(reply, &QNetworkReply::finished, this, [reply, senderWatcher, callback]() { reply->deleteLater(); QByteArray data = reply->readAll(); // qDebug() << "MQTT post reply" << data; + if (senderWatcher.isNull()) { + qDebug() << "Request object disappeared. Discarding MQTT reply..."; + return; + } if (reply->error() != QNetworkReply::NoError) { qWarning() << "MQTT Network reply error" << reply->error() << reply->errorString(); callback(false); @@ -909,7 +918,6 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString ×tamp, std:: return; } callback(true); - }); return true; diff --git a/libnymea-app-core/connection/awsclient.h b/libnymea-app-core/connection/awsclient.h index 6184ecec..b0127d58 100644 --- a/libnymea-app-core/connection/awsclient.h +++ b/libnymea-app-core/connection/awsclient.h @@ -5,6 +5,7 @@ #include #include #include +#include class QNetworkAccessManager; @@ -117,7 +118,7 @@ public: Q_INVOKABLE void fetchDevices(); - Q_INVOKABLE bool postToMQTT(const QString &boxId, const QString ×tamp, std::function callback); + Q_INVOKABLE bool postToMQTT(const QString &boxId, const QString ×tamp, QObject* sender, std::function callback); Q_INVOKABLE void getId(); Q_INVOKABLE void registerPushNotificationEndpoint(const QString ®istrationId, const QString &deviceDisplayName, const QString mobileDeviceId, const QString &mobileDeviceManufacturer, const QString &mobileDeviceModel); @@ -184,13 +185,14 @@ private: QueuedCall(const QString &method): method(method) { } QueuedCall(const QString &method, const QString &arg1): method(method), arg1(arg1) { } QueuedCall(const QString &method, const QString &arg1, const QString &arg2, const QString &arg3, const QString &arg4, const QString &arg5): method(method), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } - QueuedCall(const QString &method, const QString &arg1, const QString &arg2, std::function callback): method(method), arg1(arg1), arg2(arg2), callback(callback) {} + QueuedCall(const QString &method, const QString &arg1, const QString &arg2, QObject* sender, std::function callback): method(method), arg1(arg1), arg2(arg2), sender(sender), callback(callback) {} QString method; QString arg1; QString arg2; QString arg3; QString arg4; QString arg5; + QPointer sender; std::function callback; static void enqueue(QList &queue, const QueuedCall &call) { diff --git a/libnymea-app-core/connection/cloudtransport.cpp b/libnymea-app-core/connection/cloudtransport.cpp index c30cac62..a071e190 100644 --- a/libnymea-app-core/connection/cloudtransport.cpp +++ b/libnymea-app-core/connection/cloudtransport.cpp @@ -5,6 +5,7 @@ #include #include +#include using namespace remoteproxyclient; @@ -52,7 +53,7 @@ bool CloudTransport::connect(const QUrl &url) m_url = url; m_timestamp = QDateTime::currentDateTime(); - bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), [this](bool success) { + bool postResult = m_awsClient->postToMQTT(url.host(), QString::number(m_timestamp.toMSecsSinceEpoch()), QPointer(this), [this](bool success) { if (success) { qDebug() << "MQTT Post done. Connecting to remote proxy"; m_remoteproxyConnection->connectServer(QUrl("wss://remoteproxy.nymea.io"));