Fix a crash when a delayed reply would call a callback for an object that disappeared

This commit is contained in:
Michael Zanetti 2019-05-30 14:54:06 +02:00
parent 4e11026ea4
commit 0eadb8dcd3
3 changed files with 21 additions and 10 deletions

View File

@ -8,6 +8,7 @@
#include <QSettings> #include <QSettings>
#include <QUuid> #include <QUuid>
#include <QTimer> #include <QTimer>
#include <QPointer>
#include "sigv4utils.h" #include "sigv4utils.h"
@ -820,7 +821,7 @@ void AWSClient::getCredentialsForIdentity(const QString &identityId)
if (qc.method == "fetchDevices") { if (qc.method == "fetchDevices") {
fetchDevices(); fetchDevices();
} else if (qc.method == "postToMQTT") { } 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") { } else if (qc.method == "deleteAccount") {
deleteAccount(); deleteAccount();
} else if (qc.method == "registerPushNotificationEndpoint") { } 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()); return (m_accessTokenExpiry.addSecs(-10) < QDateTime::currentDateTime()) || (m_sessionTokenExpiry.addSecs(-10) < QDateTime::currentDateTime());
} }
bool AWSClient::postToMQTT(const QString &boxId, const QString &timestamp, std::function<void(bool)> callback) bool AWSClient::postToMQTT(const QString &boxId, const QString &timestamp, QObject* sender, std::function<void (bool)> callback)
{ {
if (!isLoggedIn()) { if (!isLoggedIn()) {
qWarning() << "Cannot post to MQTT. Not logged in to AWS"; qWarning() << "Cannot post to MQTT. Not logged in to AWS";
@ -846,11 +847,13 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString &timestamp, std::
if (tokensExpired()) { if (tokensExpired()) {
qDebug() << "Cannot post to MQTT. Need to refresh the tokens first"; qDebug() << "Cannot post to MQTT. Need to refresh the tokens first";
refreshAccessToken(); 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 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)); QString topic = QString("%1/%2/proxy").arg(boxId).arg(QString(m_identityId));
QPointer<QObject> senderWatcher = QPointer<QObject>(sender);
// This is somehow broken in AWS... // This is somehow broken in AWS...
// The Signature needs to be created with having the topic percentage-encoded twice // 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. // 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 &timestamp, std::
// } // }
// qDebug() << "Payload:" << payload; // qDebug() << "Payload:" << payload;
QNetworkReply *reply = m_nam->post(request, payload); QNetworkReply *reply = m_nam->post(request, payload);
QTimer::singleShot(5000, reply, [reply, callback](){ QTimer::singleShot(5000, reply, [reply, senderWatcher, callback](){
reply->deleteLater(); reply->deleteLater();
qWarning() << "Timeout posting to MQTT"; 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(); reply->deleteLater();
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
// qDebug() << "MQTT post reply" << data; // qDebug() << "MQTT post reply" << data;
if (senderWatcher.isNull()) {
qDebug() << "Request object disappeared. Discarding MQTT reply...";
return;
}
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
qWarning() << "MQTT Network reply error" << reply->error() << reply->errorString(); qWarning() << "MQTT Network reply error" << reply->error() << reply->errorString();
callback(false); callback(false);
@ -909,7 +918,6 @@ bool AWSClient::postToMQTT(const QString &boxId, const QString &timestamp, std::
return; return;
} }
callback(true); callback(true);
}); });
return true; return true;

View File

@ -5,6 +5,7 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDate> #include <QDate>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QPointer>
class QNetworkAccessManager; class QNetworkAccessManager;
@ -117,7 +118,7 @@ public:
Q_INVOKABLE void fetchDevices(); Q_INVOKABLE void fetchDevices();
Q_INVOKABLE bool postToMQTT(const QString &boxId, const QString &timestamp, std::function<void(bool)> callback); Q_INVOKABLE bool postToMQTT(const QString &boxId, const QString &timestamp, QObject* sender, std::function<void(bool)> callback);
Q_INVOKABLE void getId(); Q_INVOKABLE void getId();
Q_INVOKABLE void registerPushNotificationEndpoint(const QString &registrationId, const QString &deviceDisplayName, const QString mobileDeviceId, const QString &mobileDeviceManufacturer, const QString &mobileDeviceModel); Q_INVOKABLE void registerPushNotificationEndpoint(const QString &registrationId, 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): method(method) { }
QueuedCall(const QString &method, const QString &arg1): method(method), arg1(arg1) { } 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, 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<void(bool)> callback): method(method), arg1(arg1), arg2(arg2), callback(callback) {} QueuedCall(const QString &method, const QString &arg1, const QString &arg2, QObject* sender, std::function<void(bool)> callback): method(method), arg1(arg1), arg2(arg2), sender(sender), callback(callback) {}
QString method; QString method;
QString arg1; QString arg1;
QString arg2; QString arg2;
QString arg3; QString arg3;
QString arg4; QString arg4;
QString arg5; QString arg5;
QPointer<QObject> sender;
std::function<void(bool)> callback; std::function<void(bool)> callback;
static void enqueue(QList<QueuedCall> &queue, const QueuedCall &call) { static void enqueue(QList<QueuedCall> &queue, const QueuedCall &call) {

View File

@ -5,6 +5,7 @@
#include <QUrlQuery> #include <QUrlQuery>
#include <QHostInfo> #include <QHostInfo>
#include <QPointer>
using namespace remoteproxyclient; using namespace remoteproxyclient;
@ -52,7 +53,7 @@ bool CloudTransport::connect(const QUrl &url)
m_url = url; m_url = url;
m_timestamp = QDateTime::currentDateTime(); 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<QObject>(this), [this](bool success) {
if (success) { if (success) {
qDebug() << "MQTT Post done. Connecting to remote proxy"; qDebug() << "MQTT Post done. Connecting to remote proxy";
m_remoteproxyConnection->connectServer(QUrl("wss://remoteproxy.nymea.io")); m_remoteproxyConnection->connectServer(QUrl("wss://remoteproxy.nymea.io"));