Delete QNetworkReply correctly when destroying SyncthingFileModel

When disconnecting the callback we need to destroy the QNetworkReply
manually (as this is no longer done by the usual handler). This will also
close any network connections (if still open).
This commit is contained in:
Martchus 2024-05-05 12:48:01 +02:00
parent 5f995055b0
commit 506f2a295c
4 changed files with 46 additions and 30 deletions

View File

@ -137,6 +137,11 @@ public:
SyncthingConnectionLoggingFlags loggingFlags = SyncthingConnectionLoggingFlags::FromEnvironment, QObject *parent = nullptr);
~SyncthingConnection() override;
struct QueryResult {
QNetworkReply *reply = nullptr;
QMetaObject::Connection connection;
};
// getter/setter for
const QString &syncthingUrl() const;
void setSyncthingUrl(const QString &url);
@ -275,10 +280,10 @@ public Q_SLOTS:
public:
// methods to GET or POST information from/to Syncthing (non-slots)
QMetaObject::Connection browse(const QString &dirId, const QString &prefix, int level,
QueryResult browse(const QString &dirId, const QString &prefix, int level,
std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback);
QMetaObject::Connection ignores(const QString &dirId, std::function<void(SyncthingIgnores &&, QString &&)> &&callback);
QMetaObject::Connection setIgnores(const QString &dirId, const SyncthingIgnores &ignores, std::function<void(QString &&)> &&callback);
QueryResult ignores(const QString &dirId, std::function<void(SyncthingIgnores &&, QString &&)> &&callback);
QueryResult setIgnores(const QString &dirId, const SyncthingIgnores &ignores, std::function<void(QString &&)> &&callback);
Q_SIGNALS:
void newConfig(const QJsonObject &rawConfig);

View File

@ -1590,7 +1590,7 @@ void SyncthingConnection::readRevert()
* to consume results of a specific request. Errors are still reported via the error() signal so there's no extra error handling
* required. Note that in case of an error \a callback is invoked with a non-empty string containing the error message.
*/
QMetaObject::Connection SyncthingConnection::browse(const QString &dirId, const QString &prefix, int levels,
SyncthingConnection::QueryResult SyncthingConnection::browse(const QString &dirId, const QString &prefix, int levels,
std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback)
{
auto query = QUrlQuery();
@ -1601,9 +1601,11 @@ QMetaObject::Connection SyncthingConnection::browse(const QString &dirId, const
if (levels > 0) {
query.addQueryItem(QStringLiteral("levels"), QString::number(levels));
}
return QObject::connect(
requestData(QStringLiteral("db/browse"), query), &QNetworkReply::finished, this,
[this, id = dirId, l = levels, cb = std::move(callback)]() mutable { readBrowse(id, l, std::move(cb)); }, Qt::QueuedConnection);
auto *const reply = requestData(QStringLiteral("db/browse"), query);
return { reply,
QObject::connect(
reply, &QNetworkReply::finished, this,
[this, id = dirId, l = levels, cb = std::move(callback)]() mutable { readBrowse(id, l, std::move(cb)); }, Qt::QueuedConnection) };
}
/*!
@ -1614,13 +1616,15 @@ QMetaObject::Connection SyncthingConnection::browse(const QString &dirId, const
* to consume results of a specific request. Errors are still reported via the error() signal so there's no extra error handling
* required. Note that in case of an error \a callback is invoked with a non-empty string containing the error message.
*/
QMetaObject::Connection SyncthingConnection::ignores(const QString &dirId, std::function<void(SyncthingIgnores &&, QString &&)> &&callback)
SyncthingConnection::QueryResult SyncthingConnection::ignores(const QString &dirId, std::function<void(SyncthingIgnores &&, QString &&)> &&callback)
{
auto query = QUrlQuery();
query.addQueryItem(QStringLiteral("folder"), formatQueryItem(dirId));
return QObject::connect(
requestData(QStringLiteral("db/ignores"), query), &QNetworkReply::finished, this,
[this, id = dirId, cb = std::move(callback)]() mutable { readIgnores(id, std::move(cb)); }, Qt::QueuedConnection);
auto *const reply = requestData(QStringLiteral("db/ignores"), query);
return { reply,
QObject::connect(
reply, &QNetworkReply::finished, this, [this, id = dirId, cb = std::move(callback)]() mutable { readIgnores(id, std::move(cb)); },
Qt::QueuedConnection) };
}
/*!
@ -1631,7 +1635,7 @@ QMetaObject::Connection SyncthingConnection::ignores(const QString &dirId, std::
* to consume results of a specific request. Errors are still reported via the error() signal so there's no extra error handling
* required. Note that in case of an error \a callback is invoked with a non-empty string containing the error message.
*/
QMetaObject::Connection SyncthingConnection::setIgnores(
SyncthingConnection::QueryResult SyncthingConnection::setIgnores(
const QString &dirId, const SyncthingIgnores &ignores, std::function<void(QString &&)> &&callback)
{
auto query = QUrlQuery();
@ -1644,9 +1648,11 @@ QMetaObject::Connection SyncthingConnection::setIgnores(
jsonObj.insert(QLatin1String("ignore"), ignoreArray);
auto jsonDoc = QJsonDocument();
jsonDoc.setObject(jsonObj);
return QObject::connect(
postData(QStringLiteral("db/ignores"), query, jsonDoc.toJson(QJsonDocument::Compact)), &QNetworkReply::finished, this,
[this, id = dirId, cb = std::move(callback)]() mutable { readSetIgnores(id, std::move(cb)); }, Qt::QueuedConnection);
auto *const reply = postData(QStringLiteral("db/ignores"), query, jsonDoc.toJson(QJsonDocument::Compact));
return { reply,
QObject::connect(
reply, &QNetworkReply::finished, this, [this, id = dirId, cb = std::move(callback)]() mutable { readSetIgnores(id, std::move(cb)); },
Qt::QueuedConnection) };
}
/// \cond

View File

@ -10,6 +10,7 @@
#include <QClipboard>
#include <QGuiApplication>
#include <QNetworkReply>
#include <QStringBuilder>
using namespace std;
@ -46,25 +47,28 @@ SyncthingFileModel::SyncthingFileModel(SyncthingConnection &connection, const Sy
m_root->size = dir.globalStats.bytes;
m_root->type = SyncthingItemType::Directory;
m_fetchQueue.append(QString());
m_connection.browse(m_dirId, QString(), 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) {
Q_UNUSED(errorMessage)
m_pendingRequest
= m_connection.browse(m_dirId, QString(), 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) {
m_pendingRequest.reply = nullptr;
Q_UNUSED(errorMessage)
m_fetchQueue.removeAll(QString());
if (items.empty()) {
return;
}
const auto last = items.size() - 1;
beginInsertRows(index(0, 0), 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
populatePath(QString(), items);
m_root->children = std::move(items);
m_root->childrenPopulated = true;
endInsertRows();
});
m_fetchQueue.removeAll(QString());
if (items.empty()) {
return;
}
const auto last = items.size() - 1;
beginInsertRows(index(0, 0), 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
populatePath(QString(), items);
m_root->children = std::move(items);
m_root->childrenPopulated = true;
endInsertRows();
});
}
SyncthingFileModel::~SyncthingFileModel()
{
QObject::disconnect(m_pendingRequest);
QObject::disconnect(m_pendingRequest.connection);
delete m_pendingRequest.reply;
}
QHash<int, QByteArray> SyncthingFileModel::roleNames() const
@ -384,6 +388,7 @@ void SyncthingFileModel::processFetchQueue()
const auto &path = m_fetchQueue.front();
m_pendingRequest = m_connection.browse(
m_dirId, path, 1, [this, p = path](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) mutable {
m_pendingRequest.reply = nullptr;
Q_UNUSED(errorMessage)
m_fetchQueue.removeAll(p);

View File

@ -55,7 +55,7 @@ private:
QString m_dirId;
QString m_localPath;
QStringList m_fetchQueue;
QMetaObject::Connection m_pendingRequest;
SyncthingConnection::QueryResult m_pendingRequest;
std::unique_ptr<SyncthingItem> m_root;
};