summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2019-04-25 17:25:22 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2019-05-15 12:47:55 +0000
commit927eba344c2dbabc7275a4f561dd97f0e30760a2 (patch)
treec2fb4057dd74745f971bf4de152fa4ec6452aa78
parent15d3a52d4eb0f07a95f9f59895ba438e3b9cf45a (diff)
Introduce AutoDeleteReplyOnFinishAttribute for QNetworkRequest
[ChangeLog][QtNetwork][QNetworkRequest] Added the AutoDeleteReplyOnFinishAttribute attribute to QNetworkRequest, which makes QNetworkAccessManager delete the QNetworkReply after it has emitted the "finished" signal. Change-Id: I03d4ac0830137882e51dd28795a8ec817762a300 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp5
-rw-r--r--src/network/access/qnetworkrequest.cpp6
-rw-r--r--src/network/access/qnetworkrequest.h1
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp74
4 files changed, 85 insertions, 1 deletions
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index d201b9e211..f1f63ad00a 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1677,8 +1677,11 @@ void QNetworkAccessManagerPrivate::_q_replyFinished()
Q_Q(QNetworkAccessManager);
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
- if (reply)
+ if (reply) {
emit q->finished(reply);
+ if (reply->request().attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, false).toBool())
+ QMetaObject::invokeMethod(reply, [reply] { reply->deleteLater(); }, Qt::QueuedConnection);
+ }
#ifndef QT_NO_BEARERMANAGEMENT
// If there are no active requests, release our reference to the network session.
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index f15c43cdd8..deb0792938 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -331,6 +331,12 @@ QT_BEGIN_NAMESPACE
\omitvalue ResourceTypeAttribute
+ \value AutoDeleteReplyOnFinishAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ If set, this attribute will make QNetworkAccessManager delete
+ the QNetworkReply after having emitted "finished".
+ (This value was introduced in 5.14.)
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index de27b9fede..846ead1592 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -98,6 +98,7 @@ public:
RedirectPolicyAttribute,
Http2DirectAttribute,
ResourceTypeAttribute, // internal
+ AutoDeleteReplyOnFinishAttribute,
User = 1000,
UserMax = 32767
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index eb956bec30..5e3018366c 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -503,6 +503,9 @@ private Q_SLOTS:
void putWithServerClosingConnectionImmediately();
#endif
+ void autoDeleteRepliesAttribute_data();
+ void autoDeleteRepliesAttribute();
+
// NOTE: This test must be last!
void parentingRepliesToTheApp();
private:
@@ -9167,6 +9170,77 @@ void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
#endif
+void tst_QNetworkReply::autoDeleteRepliesAttribute_data()
+{
+ QTest::addColumn<QUrl>("destination");
+
+ QTest::newRow("http") << QUrl("http://QInvalidDomain.qt/test");
+ QTest::newRow("https") << QUrl("https://QInvalidDomain.qt/test");
+ QTest::newRow("ftp") << QUrl("ftp://QInvalidDomain.qt/test");
+ QTest::newRow("file") << QUrl("file:///thisfolderdoesn'texist/probably.txt");
+#ifdef Q_OS_WIN
+ // Only supported on windows.
+ QTest::newRow("remote-file") << QUrl("file://QInvalidHost/thisfolderdoesn'texist/probably.txt");
+#endif
+ QTest::newRow("qrc") << QUrl("qrc:///path/to/nowhere");
+ QTest::newRow("data") << QUrl("data:,Some%20plaintext%20data");
+}
+
+void tst_QNetworkReply::autoDeleteRepliesAttribute()
+{
+ QFETCH(QUrl, destination);
+ {
+ // Get
+ QNetworkRequest request(destination);
+ request.setAttribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, true);
+ QNetworkReply *reply = manager.get(request);
+ QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
+ QSignalSpy destroyedSpy(reply, &QObject::destroyed);
+ QVERIFY(finishedSpy.wait());
+ QCOMPARE(destroyedSpy.count(), 0);
+ QVERIFY(destroyedSpy.wait());
+ }
+ {
+ // Post
+ QNetworkRequest request(destination);
+ request.setAttribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, true);
+ QNetworkReply *reply = manager.post(request, QByteArrayLiteral("datastring"));
+ QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
+ QSignalSpy destroyedSpy(reply, &QObject::destroyed);
+ QVERIFY(finishedSpy.wait());
+ QCOMPARE(destroyedSpy.count(), 0);
+ QVERIFY(destroyedSpy.wait());
+ }
+ // Now repeated, but without the attribute to make sure it does not get deleted automatically.
+ // We need two calls to processEvents to test that the QNetworkReply doesn't get deleted.
+ // The first call executes a metacall event which adds the deleteLater meta event which
+ // would be executed in the second call. But that shouldn't happen without the attribute.
+ {
+ // Get
+ QNetworkRequest request(destination);
+ QScopedPointer<QNetworkReply> reply(manager.get(request));
+ QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
+ QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
+ QVERIFY(finishedSpy.wait());
+ QCOMPARE(destroyedSpy.count(), 0);
+ QCoreApplication::processEvents();
+ QCoreApplication::processEvents();
+ QCOMPARE(destroyedSpy.count(), 0);
+ }
+ {
+ // Post
+ QNetworkRequest request(destination);
+ QScopedPointer<QNetworkReply> reply(manager.post(request, QByteArrayLiteral("datastring")));
+ QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
+ QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
+ QVERIFY(finishedSpy.wait());
+ QCOMPARE(destroyedSpy.count(), 0);
+ QCoreApplication::processEvents();
+ QCoreApplication::processEvents();
+ QCOMPARE(destroyedSpy.count(), 0);
+ }
+}
+
// NOTE: This test must be last testcase in tst_qnetworkreply!
void tst_QNetworkReply::parentingRepliesToTheApp()
{