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>
Mårten Nordheim 2019-04-25 17:25:22 +02:00
parent 15d3a52d4e
commit 927eba344c
4 changed files with 85 additions and 1 deletions

View File

@ -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.

View File

@ -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

View File

@ -98,6 +98,7 @@ public:
RedirectPolicyAttribute,
Http2DirectAttribute,
ResourceTypeAttribute, // internal
AutoDeleteReplyOnFinishAttribute,
User = 1000,
UserMax = 32767

View File

@ -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()
{