choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qnetworkreplyimpl.cpp
index 78e2463..d039347 100644 (file)
@@ -1,34 +1,34 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
 **
 ** This file is part of the QtNetwork module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
 ** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
-** rights.  These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
 **
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
 **
 **
 **
@@ -42,6 +42,7 @@
 #include "qnetworkreplyimpl_p.h"
 #include "qnetworkaccessbackend_p.h"
 #include "qnetworkcookie.h"
+#include "qnetworkcookiejar.h"
 #include "qabstractnetworkcache.h"
 #include "QtCore/qcoreapplication.h"
 #include "QtCore/qdatetime.h"
@@ -72,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");
@@ -90,30 +93,61 @@ void QNetworkReplyImplPrivate::_q_startOperation()
     }
 
 #ifndef QT_NO_BEARERMANAGEMENT
-    if (!backend->start()) { // ### we should call that method even if bearer is not used
+    // Do not start background requests if they are not allowed by session policy
+    QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
+    QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
+    if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
+        error(QNetworkReply::BackgroundRequestNotAllowedError,
+            QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
+        finished();
+        return;
+    }
+#endif
+
+    if (!backend->start()) {
+#ifndef QT_NO_BEARERMANAGEMENT
         // backend failed to start because the session state is not Connected.
-        // QNetworkAccessManager will call reply->backend->start() again for us when the session
+        // QNetworkAccessManager will call _q_startOperation again for us when the session
         // state changes.
         state = WaitingForSession;
 
-        QNetworkSession *session = manager->d_func()->networkSession.data();
-
         if (session) {
-            Q_Q(QNetworkReplyImpl);
-
-            QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
+            QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
                              q, SLOT(_q_networkSessionFailed()));
 
-            if (!session->isOpen())
+            if (!session->isOpen()) {
+                session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
                 session->open();
+            }
         } else {
             qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
+            state = Working;
+            error(QNetworkReplyImpl::NetworkSessionFailedError,
+                  QCoreApplication::translate("QNetworkReply", "Network session error."));
+            finished();
         }
-
+#else
+        qWarning("Backend start failed");
+        state = Working;
+        error(QNetworkReplyImpl::UnknownNetworkError,
+              QCoreApplication::translate("QNetworkReply", "backend start error."));
+        finished();
+#endif
         return;
     }
+
+#ifndef QT_NO_BEARERMANAGEMENT
+    if (session) {
+        //get notification of policy changes.
+        QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
+                    q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
+    }
 #endif
 
+    // Prepare timer for progress notifications
+    downloadProgressSignalChoke.start();
+    uploadProgressSignalChoke.invalidate();
+
     if (backend && backend->isSynchronous()) {
         state = Finished;
         q_func()->setFinished(true);
@@ -180,8 +214,11 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
     // emit readyRead before downloadProgress incase this will cause events to be
     // processed and we get into a recursive call (as in QProgressDialog).
     emit q->readyRead();
-    emit q->downloadProgress(bytesDownloaded,
+    if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
+        downloadProgressSignalChoke.restart();
+        emit q->downloadProgress(bytesDownloaded,
                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+    }
     resumeNotificationHandling();
 }
 
@@ -258,7 +295,7 @@ void QNetworkReplyImplPrivate::_q_networkSessionConnected()
     if (manager.isNull())
         return;
 
-    QNetworkSession *session = manager->d_func()->networkSession.data();
+    QSharedPointer<QNetworkSession> session = manager->d_func()->getNetworkSession();
     if (!session)
         return;
 
@@ -286,11 +323,32 @@ void QNetworkReplyImplPrivate::_q_networkSessionFailed()
     // Abort waiting and working replies.
     if (state == WaitingForSession || state == Working) {
         state = Working;
-        error(QNetworkReplyImpl::UnknownNetworkError,
-              QCoreApplication::translate("QNetworkReply", "Network session error."));
+        QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
+        QString errorStr;
+        if (session)
+            errorStr = session->errorString();
+        else
+            errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
+        error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
         finished();
     }
 }
+
+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
 
 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
@@ -441,10 +499,7 @@ void QNetworkReplyImplPrivate::createCache()
 {
     // check if we can save and if we're allowed to
     if (!networkCache()
-        || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
-        || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
-                             QNetworkRequest::PreferNetwork).toInt()
-            == QNetworkRequest::AlwaysNetwork)
+        || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
         return;
     cacheEnabled = true;
 }
@@ -496,6 +551,17 @@ void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytes
 {
     Q_Q(QNetworkReplyImpl);
     bytesUploaded = bytesSent;
+
+    //choke signal emissions, except the first and last signals which are unconditional
+    if (uploadProgressSignalChoke.isValid()) {
+        if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
+            return;
+        }
+        uploadProgressSignalChoke.restart();
+    } else {
+        uploadProgressSignalChoke.start();
+    }
+
     pauseNotificationHandling();
     emit q->uploadProgress(bytesSent, bytesTotal);
     resumeNotificationHandling();
@@ -592,8 +658,11 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
     emit q->readyRead();
     // emit readyRead before downloadProgress incase this will cause events to be
     // processed and we get into a recursive call (as in QProgressDialog).
-    emit q->downloadProgress(bytesDownloaded,
+    if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
+        downloadProgressSignalChoke.restart();
+        emit q->downloadProgress(bytesDownloaded,
                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+    }
 
     resumeNotificationHandling();
     // do we still have room in the buffer?
@@ -652,7 +721,7 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
             downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
             downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
 
-            q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+            q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
         }
     }
 
@@ -667,7 +736,7 @@ void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64
     downloadBuffer = downloadBufferPointer.data();
     downloadBufferCurrentSize = 0;
     downloadBufferMaximumSize = size;
-    q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+    q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
 }
 
 
@@ -699,7 +768,10 @@ void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesRe
     // processed and we get into a recursive call (as in QProgressDialog).
     if (bytesDownloaded > 0)
         emit q->readyRead();
-    emit q->downloadProgress(bytesDownloaded, bytesTotal);
+    if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
+        downloadProgressSignalChoke.restart();
+        emit q->downloadProgress(bytesDownloaded, bytesTotal);
+    }
 }
 
 void QNetworkReplyImplPrivate::finished()
@@ -716,7 +788,7 @@ void QNetworkReplyImplPrivate::finished()
 
     if (!manager.isNull()) {
 #ifndef QT_NO_BEARERMANAGEMENT
-        QNetworkSession *session = manager->d_func()->networkSession.data();
+        QSharedPointer<QNetworkSession> session (manager->d_func()->getNetworkSession());
         if (session && session->state() == QNetworkSession::Roaming &&
             state == Working && errorCode != QNetworkReply::OperationCanceledError) {
             // only content with a known size will fail with a temporary network failure error
@@ -747,6 +819,8 @@ void QNetworkReplyImplPrivate::finished()
     pauseNotificationHandling();
     if (totalSize.isNull() || totalSize == -1) {
         emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
+    } else {
+        emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
     }
 
     if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
@@ -771,7 +845,7 @@ void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const
     Q_Q(QNetworkReplyImpl);
     // Can't set and emit multiple errors.
     if (errorCode != QNetworkReply::NoError) {
-        qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
+        qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
         return;
     }
 
@@ -809,7 +883,7 @@ void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
 
 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
 {
-#ifndef QT_NO_OPENSSL
+#ifndef QT_NO_SSL
     Q_Q(QNetworkReplyImpl);
     emit q->sslErrors(errors);
 #else
@@ -850,6 +924,8 @@ void QNetworkReplyImpl::abort()
     if (d->state != QNetworkReplyImplPrivate::Finished) {
         // call finished which will emit signals
         d->error(OperationCanceledError, tr("Operation canceled"));
+        if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
+            d->state = QNetworkReplyImplPrivate::Working;
         d->finished();
     }
     d->state = QNetworkReplyImplPrivate::Aborted;
@@ -918,14 +994,12 @@ void QNetworkReplyImpl::setReadBufferSize(qint64 size)
         d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
 }
 
-#ifndef QT_NO_OPENSSL
-QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
+#ifndef QT_NO_SSL
+void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
 {
     Q_D(const QNetworkReplyImpl);
-    QSslConfiguration config;
     if (d->backend)
-        d->backend->fetchSslConfiguration(config);
-    return config;
+        d->backend->fetchSslConfiguration(configuration);
 }
 
 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
@@ -948,7 +1022,7 @@ void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &er
     if (d->backend)
         d->backend->ignoreSslErrors(errors);
 }
-#endif  // QT_NO_OPENSSL
+#endif  // QT_NO_SSL
 
 /*!
     \internal
@@ -963,7 +1037,7 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
         if (maxAvail == 0)
             return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
         // FIXME what about "Aborted" state?
-        qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
+        memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
         d->downloadBufferReadPosition += maxAvail;
         return maxAvail;
     }
@@ -1009,10 +1083,6 @@ bool QNetworkReplyImplPrivate::migrateBackend()
     if (state == Finished || state == Aborted)
         return true;
 
-    // Backend does not support resuming download.
-    if (!backend->canResume())
-        return false;
-
     // Request has outgoing data, not migrating.
     if (outgoingData)
         return false;
@@ -1021,6 +1091,10 @@ bool QNetworkReplyImplPrivate::migrateBackend()
     if (copyDevice)
         return true;
 
+    // Backend does not support resuming download.
+    if (!backend->canResume())
+        return false;
+
     state = QNetworkReplyImplPrivate::Reconnecting;
 
     if (backend) {