From af100dbd7e23076f8873f9ee8847d1ac90bb1700 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Fri, 13 Apr 2012 18:05:33 +0100 Subject: Abort background requests if policy changes to disallow them Using the policy change signal from QNetworkSession. If the new policy disallows background requests and this is a background request, then generate an error. This results in a TCP RST on the socket, and a BackgroundRequestNotAllowedError on the QNetworkReply. If the reply is already finished, no action is taken. Change-Id: I4ff5c681a8b7b852727bb95f03664d666f4efe07 Reviewed-by: Martin Petersson --- src/network/access/qnetworkreplyhttpimpl.cpp | 19 +++++- src/network/access/qnetworkreplyimpl.cpp | 27 ++++++-- .../access/qnetworkreply/tst_qnetworkreply.cpp | 73 ++++++++++++++++++++++ 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 624c39f292..4796e4bb86 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1521,6 +1521,9 @@ bool QNetworkReplyHttpImplPrivate::start() if (managerPrivate->networkSession->isOpen() && managerPrivate->networkSession->state() == QNetworkSession::Connected) { + Q_Q(QNetworkReplyHttpImpl); + QObject::connect(managerPrivate->networkSession.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)), + q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))); postRequest(); return true; } @@ -1564,8 +1567,6 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation() if (session) { QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)), q, SLOT(_q_networkSessionFailed())); - QObject::connect(session, SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)), - q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))); if (!session->isOpen()) { session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground); @@ -1767,8 +1768,20 @@ void QNetworkReplyHttpImplPrivate::_q_networkSessionFailed() } } -void QNetworkReplyHttpImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies) +void QNetworkReplyHttpImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies) { + if (request.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { + if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) { + // Abort waiting and working replies. + if (state == WaitingForSession || state == Working) { + state = Working; + error(QNetworkReply::BackgroundRequestNotAllowedError, + QCoreApplication::translate("QNetworkReply", "Background request not allowed.")); + finished(); + } + // ### if canResume(), then we could resume automatically + } + } } #endif diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index c9efcbb1fe..d535cbda65 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -73,6 +73,8 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() void QNetworkReplyImplPrivate::_q_startOperation() { + Q_Q(QNetworkReplyImpl); + // ensure this function is only being called once if (state == Working || state == Finished) { qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); @@ -110,12 +112,8 @@ void QNetworkReplyImplPrivate::_q_startOperation() state = WaitingForSession; if (session) { - Q_Q(QNetworkReplyImpl); - QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)), q, SLOT(_q_networkSessionFailed())); - QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)), - q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))); if (!session->isOpen()) { session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground); @@ -138,6 +136,12 @@ void QNetworkReplyImplPrivate::_q_startOperation() return; } + if (session) { + //get notification of policy changes. + QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)), + q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))); + } + if (backend && backend->isSynchronous()) { state = Finished; q_func()->setFinished(true); @@ -321,9 +325,20 @@ void QNetworkReplyImplPrivate::_q_networkSessionFailed() } } -void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies) +void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies) { - + if (backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { + if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) { + // Abort waiting and working replies. + if (state == WaitingForSession || state == Working) { + state = Working; + error(QNetworkReply::BackgroundRequestNotAllowedError, + QCoreApplication::translate("QNetworkReply", "Background request not allowed.")); + finished(); + } + // ### if backend->canResume(), then we could resume automatically, however no backend supports resuming + } + } } #endif diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 35d4add0f8..aede0d290b 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -405,6 +405,9 @@ private Q_SLOTS: void backgroundRequest_data(); void backgroundRequest(); + void backgroundRequestInterruption_data(); + void backgroundRequestInterruption(); + // NOTE: This test must be last! void parentingRepliesToTheApp(); @@ -6844,6 +6847,7 @@ void tst_QNetworkReply::backgroundRequest_data() } +//test purpose: background requests can't be started when not allowed void tst_QNetworkReply::backgroundRequest() { #ifdef QT_BUILD_INTERNAL @@ -6883,6 +6887,75 @@ void tst_QNetworkReply::backgroundRequest() #endif } +void tst_QNetworkReply::backgroundRequestInterruption_data() +{ + QTest::addColumn("url"); + QTest::addColumn("background"); + QTest::addColumn("error"); + + QUrl httpurl("http://" + QtNetworkSettings::serverName() + "/qtest/mediumfile"); + QUrl httpsurl("https://" + QtNetworkSettings::serverName() + "/qtest/mediumfile"); + QUrl ftpurl("ftp://" + QtNetworkSettings::serverName() + "/qtest/bigfile"); + + QTest::newRow("http, fg, nobg") << httpurl << false << QNetworkReply::NoError; + QTest::newRow("http, bg, nobg") << httpurl << true << QNetworkReply::BackgroundRequestNotAllowedError; + +#ifndef QT_NO_SSL + QTest::newRow("https, fg, nobg") << httpsurl << false << QNetworkReply::NoError; + QTest::newRow("https, bg, nobg") << httpsurl << true << QNetworkReply::BackgroundRequestNotAllowedError; +#endif + + QTest::newRow("ftp, fg, nobg") << ftpurl << false << QNetworkReply::NoError; + QTest::newRow("ftp, bg, nobg") << ftpurl << true << QNetworkReply::BackgroundRequestNotAllowedError; + +} + +//test purpose: background requests in progress are aborted when policy changes to disallow them +void tst_QNetworkReply::backgroundRequestInterruption() +{ +#ifdef QT_BUILD_INTERNAL +#ifndef QT_NO_BEARERMANAGEMENT + QFETCH(QUrl, url); + QFETCH(bool, background); + QFETCH(QNetworkReply::NetworkError, error); + + QNetworkRequest request(url); + + if (background) + request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(true)); + + //this preconstructs the session so we can change policies in advance + manager.setConfiguration(networkConfiguration); + +#ifndef QT_NO_SSL + connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + SLOT(sslErrors(QNetworkReply*,QList))); +#endif + + const QWeakPointer session = QNetworkAccessManagerPrivate::getNetworkSession(&manager); + QVERIFY(session); + QNetworkSession::UsagePolicies original = session.data()->usagePolicies(); + QNetworkSessionPrivate::setUsagePolicies(*const_cast(session.data()), QNetworkSession::NoPolicy); + + request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 8192); + QNetworkReplyPtr reply(manager.get(request)); + reply->setReadBufferSize(1024); + + QSignalSpy spy(reply.data(), SIGNAL(readyRead())); + QTRY_VERIFY(spy.count() > 0); + + QNetworkSessionPrivate::setUsagePolicies(*const_cast(session.data()), QNetworkSession::NoBackgroundTrafficPolicy); + + QVERIFY(waitForFinish(reply) != Timeout); + if (session) + QNetworkSessionPrivate::setUsagePolicies(*const_cast(session.data()), original); + + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), error); +#endif +#endif +} + // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() { -- cgit v1.2.3