summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVille Voutilainen <ville.voutilainen@qt.io>2017-10-27 18:00:00 +0300
committerVille Voutilainen <ville.voutilainen@qt.io>2017-11-03 07:44:14 +0000
commit6f187699dea7a2844c7a26631da632b47c649df6 (patch)
treeb75f08509d6e98feea73263477604c4a14c88877 /src
parentd40c73e51cac7332bca5af37d460b1f0a37b12a1 (diff)
Implement package download resume
This doesn't implement a resume for checksum files and resource files; there's no pressing need for those. Task-number: QTIFW-5 Change-Id: Id14a3d9b46f2e16a15f52ae536d060d505b2f4f0 Reviewed-by: Katja Marttila <katja.marttila@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/libs/kdtools/filedownloader.cpp232
-rw-r--r--src/libs/kdtools/filedownloader.h13
-rw-r--r--src/libs/kdtools/filedownloader_p.h3
3 files changed, 225 insertions, 23 deletions
diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp
index 1a0a8f1c2..2732e4980 100644
--- a/src/libs/kdtools/filedownloader.cpp
+++ b/src/libs/kdtools/filedownloader.cpp
@@ -181,8 +181,13 @@ struct KDUpdater::FileDownloader::Private
, m_assumedSha1Sum("")
, autoRemove(true)
, m_speedTimerInterval(100)
+ , m_downloadDeadlineTimerInterval(30000)
+ , m_downloadPaused(false)
+ , m_downloadResumed(false)
, m_bytesReceived(0)
, m_bytesToReceive(0)
+ , m_bytesBeforeResume(0)
+ , m_totalBytesBeforeResume(0)
, m_currentSpeedBin(0)
, m_sampleIndex(0)
, m_downloadSpeed(0)
@@ -207,11 +212,18 @@ struct KDUpdater::FileDownloader::Private
bool autoRemove;
bool followRedirect;
- QBasicTimer m_timer;
+ QBasicTimer m_speedIntervalTimer;
int m_speedTimerInterval;
+ QBasicTimer m_downloadDeadlineTimer;
+ int m_downloadDeadlineTimerInterval;
+ bool m_downloadPaused;
+ bool m_downloadResumed;
+
qint64 m_bytesReceived;
qint64 m_bytesToReceive;
+ qint64 m_bytesBeforeResume;
+ qint64 m_totalBytesBeforeResume;
mutable qint64 m_samples[50];
mutable qint64 m_currentSpeedBin;
@@ -382,8 +394,8 @@ void KDUpdater::FileDownloader::cancelDownload()
*/
void KDUpdater::FileDownloader::runDownloadSpeedTimer()
{
- if (!d->m_timer.isActive())
- d->m_timer.start(d->m_speedTimerInterval, this);
+ if (!d->m_speedIntervalTimer.isActive())
+ d->m_speedIntervalTimer.start(d->m_speedTimerInterval, this);
}
/*!
@@ -391,7 +403,97 @@ void KDUpdater::FileDownloader::runDownloadSpeedTimer()
*/
void KDUpdater::FileDownloader::stopDownloadSpeedTimer()
{
- d->m_timer.stop();
+ d->m_speedIntervalTimer.stop();
+}
+
+/*!
+ Restarts the download deadline timer.
+*/
+void KDUpdater::FileDownloader::runDownloadDeadlineTimer()
+{
+ stopDownloadDeadlineTimer();
+ d->m_downloadDeadlineTimer.start(d->m_downloadDeadlineTimerInterval, this);
+}
+
+/*!
+ Stops the download deadline timer.
+*/
+void KDUpdater::FileDownloader::stopDownloadDeadlineTimer()
+{
+ d->m_downloadDeadlineTimer.stop();
+}
+
+/*!
+ Sets the download into a paused state.
+*/
+void KDUpdater::FileDownloader::setDownloadPaused(bool paused)
+{
+ d->m_downloadPaused = paused;
+}
+
+/*!
+ Gets the download paused state.
+*/
+bool KDUpdater::FileDownloader::isDownloadPaused()
+{
+ return d->m_downloadPaused;
+}
+
+/*!
+ Sets the download into a paused state.
+*/
+void KDUpdater::FileDownloader::setDownloadResumed(bool resumed)
+{
+ d->m_downloadResumed = resumed;
+}
+
+/*!
+ Gets the download resumed state.
+*/
+bool KDUpdater::FileDownloader::isDownloadResumed()
+{
+ return d->m_downloadResumed;
+}
+
+/*!
+ Gets the amount of bytes downloaded before download resume.
+*/
+qint64 KDUpdater::FileDownloader::bytesDownloadedBeforeResume()
+{
+ return d->m_bytesBeforeResume;
+}
+
+/*!
+ Gets the total amount of bytes downloaded before download resume.
+*/
+qint64 KDUpdater::FileDownloader::totalBytesDownloadedBeforeResume()
+{
+ return d->m_totalBytesBeforeResume;
+}
+
+/*!
+ Clears the amount of bytes downloaded before download resume.
+*/
+void KDUpdater::FileDownloader::clearBytesDownloadedBeforeResume()
+{
+ d->m_bytesBeforeResume = 0;
+ d->m_totalBytesBeforeResume = 0;
+}
+
+/*!
+ Updates the amount of bytes downloaded before download resume.
+*/
+void KDUpdater::FileDownloader::updateBytesDownloadedBeforeResume(qint64 bytes)
+{
+ d->m_bytesBeforeResume += bytes;
+}
+
+/*!
+ Updates the total amount of bytes downloaded before download resume.
+*/
+void KDUpdater::FileDownloader::updateTotalBytesDownloadedBeforeResume()
+{
+ d->m_totalBytesBeforeResume = d->m_bytesBeforeResume;
}
/*!
@@ -407,7 +509,15 @@ void KDUpdater::FileDownloader::addSample(qint64 sample)
*/
int KDUpdater::FileDownloader::downloadSpeedTimerId() const
{
- return d->m_timer.timerId();
+ return d->m_speedIntervalTimer.timerId();
+}
+
+/*!
+ Returns the download deadline timer ID.
+*/
+int KDUpdater::FileDownloader::downloadDeadlineTimerId() const
+{
+ return d->m_downloadDeadlineTimer.timerId();
}
/*!
@@ -792,7 +902,6 @@ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event)
}
addSample(numRead);
addCheckSumData(buffer.data(), numRead);
-
if (numRead > 0) {
setProgress(d->source->pos(), d->source->size());
emit downloadProgress(calcProgress(d->source->pos(), d->source->size()));
@@ -1050,21 +1159,31 @@ struct KDUpdater::HttpDownloader::Private
HttpDownloader *const q;
QNetworkAccessManager manager;
QNetworkReply *http;
+ QUrl sourceUrl;
QFile *destination;
QString destFileName;
bool downloaded;
bool aborted;
int m_authenticationCount;
- void shutDown()
+ void shutDown(bool closeDestination = true)
{
- disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished);
- http->deleteLater();
+ if (http) {
+ disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished);
+ disconnect(http, &QNetworkReply::downloadProgress,
+ q, &HttpDownloader::httpReadProgress);
+ void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
+
+ disconnect(http, errorSignal, q, &HttpDownloader::httpError);
+ http->deleteLater();
+ }
http = 0;
- destination->close();
- destination->deleteLater();
- destination = 0;
- q->resetCheckSumData();
+ if (closeDestination) {
+ destination->close();
+ destination->deleteLater();
+ destination = 0;
+ q->resetCheckSumData();
+ }
}
};
@@ -1081,6 +1200,9 @@ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent)
#endif
connect(&d->manager, &QNetworkAccessManager::authenticationRequired,
this, &HttpDownloader::onAuthenticationRequired);
+ connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged,
+ this, &HttpDownloader::onNetworkAccessibleChanged);
+
}
/*!
@@ -1123,6 +1245,7 @@ void KDUpdater::HttpDownloader::doDownload()
startDownload(url());
runDownloadSpeedTimer();
+ runDownloadDeadlineTimer();
}
/*!
@@ -1170,6 +1293,7 @@ void KDUpdater::HttpDownloader::httpReadyRead()
}
addSample(written);
addCheckSumData(buffer.data(), read);
+ updateBytesDownloadedBeforeResume(written);
}
}
@@ -1194,6 +1318,11 @@ void KDUpdater::HttpDownloader::cancelDownload()
void KDUpdater::HttpDownloader::httpDone(bool error)
{
if (error) {
+ if (isDownloadResumed()) {
+ d->shutDown(false);
+ resumeDownload();
+ return;
+ }
QString err;
if (d->http) {
err = d->http->errorString();
@@ -1206,10 +1335,11 @@ void KDUpdater::HttpDownloader::httpDone(bool error)
d->aborted = false;
setDownloadCanceled();
} else {
- setDownloadAborted(err);
+ d->shutDown(false);
+ resumeDownload();
}
}
- //PENDING: what about the non-error case??
+ setDownloadResumed(false);
}
/*!
@@ -1223,6 +1353,7 @@ void KDUpdater::HttpDownloader::onError()
delete d->destination;
d->destination = 0;
stopDownloadSpeedTimer();
+ stopDownloadDeadlineTimer();
}
/*!
@@ -1232,12 +1363,16 @@ void KDUpdater::HttpDownloader::onError()
void KDUpdater::HttpDownloader::onSuccess()
{
d->downloaded = true;
- d->destFileName = d->destination->fileName();
- if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination))
- file->setAutoRemove(false);
+ if (d->destination) {
+ d->destFileName = d->destination->fileName();
+ if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination))
+ file->setAutoRemove(false);
+ }
delete d->destination;
d->destination = 0;
stopDownloadSpeedTimer();
+ stopDownloadDeadlineTimer();
+ setDownloadResumed(false);
}
void KDUpdater::HttpDownloader::httpReqFinished()
@@ -1266,9 +1401,11 @@ void KDUpdater::HttpDownloader::httpReqFinished()
}
}
httpReadyRead();
- d->destination->flush();
+ if (d->destination)
+ d->destination->flush();
setDownloadCompleted();
- d->http->deleteLater();
+ if (d->http)
+ d->http->deleteLater();
d->http = 0;
}
}
@@ -1281,8 +1418,16 @@ void KDUpdater::HttpDownloader::httpReadProgress(qint64 done, qint64 total)
return; // if we are a redirection, do not emit the progress
}
- setProgress(done, total);
- emit downloadProgress(calcProgress(done, total));
+ if (isDownloadResumed())
+ setProgress(done + totalBytesDownloadedBeforeResume(),
+ total + totalBytesDownloadedBeforeResume());
+ else
+ setProgress(done, total);
+ runDownloadDeadlineTimer();
+ if (isDownloadResumed())
+ emit downloadProgress(calcProgress(done + totalBytesDownloadedBeforeResume(), total + totalBytesDownloadedBeforeResume()));
+ else
+ emit downloadProgress(calcProgress(done, total));
}
/*!
@@ -1295,15 +1440,19 @@ void KDUpdater::HttpDownloader::timerEvent(QTimerEvent *event)
emitDownloadStatus();
emitDownloadProgress();
emitEstimatedDownloadTime();
+ } else if (event->timerId() == downloadDeadlineTimerId()) {
+ d->shutDown(false);
+ resumeDownload();
}
}
void KDUpdater::HttpDownloader::startDownload(const QUrl &url)
{
+ d->sourceUrl = url;
d->m_authenticationCount = 0;
d->manager.setProxyFactory(proxyFactory());
+ clearBytesDownloadedBeforeResume();
d->http = d->manager.get(QNetworkRequest(url));
-
connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead);
connect(d->http, &QNetworkReply::downloadProgress,
this, &HttpDownloader::httpReadProgress);
@@ -1329,6 +1478,28 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url)
}
}
+void KDUpdater::HttpDownloader::resumeDownload()
+{
+ updateTotalBytesDownloadedBeforeResume();
+ d->m_authenticationCount = 0;
+ QNetworkRequest request(d->sourceUrl);
+
+ request.setRawHeader(QByteArray("Range"),
+ QString(QStringLiteral("bytes=%1-"))
+ .arg(bytesDownloadedBeforeResume())
+ .toLatin1());
+ setDownloadResumed(true);
+ d->http = d->manager.get(request);
+ connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead);
+ connect(d->http, &QNetworkReply::downloadProgress,
+ this, &HttpDownloader::httpReadProgress);
+ connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished);
+ void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
+ connect(d->http, errorSignal, this, &HttpDownloader::httpError);
+ runDownloadSpeedTimer();
+ runDownloadDeadlineTimer();
+}
+
void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
{
Q_UNUSED(reply)
@@ -1366,6 +1537,21 @@ void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, Q
}
}
+void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
+{
+ if (accessible == QNetworkAccessManager::NotAccessible) {
+ d->shutDown(false);
+ setDownloadPaused(true);
+ setDownloadResumed(false);
+ stopDownloadDeadlineTimer();
+ } else if (accessible == QNetworkAccessManager::Accessible) {
+ if (isDownloadPaused()) {
+ setDownloadPaused(false);
+ resumeDownload();
+ }
+ }
+}
+
#ifndef QT_NO_SSL
#include "messageboxhandler.h"
diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h
index 8c73cbeb0..ede20dcfa 100644
--- a/src/libs/kdtools/filedownloader.h
+++ b/src/libs/kdtools/filedownloader.h
@@ -115,8 +115,21 @@ protected:
void runDownloadSpeedTimer();
void stopDownloadSpeedTimer();
+ void runDownloadDeadlineTimer();
+ void stopDownloadDeadlineTimer();
+ void setDownloadPaused(bool paused);
+ bool isDownloadPaused();
+ void setDownloadResumed(bool resumed);
+ bool isDownloadResumed();
+ qint64 bytesDownloadedBeforeResume();
+ qint64 totalBytesDownloadedBeforeResume();
+ void clearBytesDownloadedBeforeResume();
+ void updateBytesDownloadedBeforeResume(qint64 bytes);
+ void updateTotalBytesDownloadedBeforeResume();
+
void addSample(qint64 sample);
int downloadSpeedTimerId() const;
+ int downloadDeadlineTimerId() const;
void setProgress(qint64 bytesReceived, qint64 bytesToReceive);
void emitDownloadSpeed();
diff --git a/src/libs/kdtools/filedownloader_p.h b/src/libs/kdtools/filedownloader_p.h
index f5a80c432..41a430554 100644
--- a/src/libs/kdtools/filedownloader_p.h
+++ b/src/libs/kdtools/filedownloader_p.h
@@ -32,6 +32,7 @@
#include "filedownloader.h"
#include <QtNetwork/QNetworkReply>
+#include <QNetworkAccessManager>
// these classes are not a part of the public API
@@ -128,11 +129,13 @@ private Q_SLOTS:
void httpDone(bool error);
void httpReqFinished();
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
+ void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
#ifndef QT_NO_SSL
void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors);
#endif
private:
void startDownload(const QUrl &url);
+ void resumeDownload();
private:
struct Private;