From 6f187699dea7a2844c7a26631da632b47c649df6 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Fri, 27 Oct 2017 18:00:00 +0300 Subject: 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 --- src/libs/kdtools/filedownloader.cpp | 232 ++++++++++++++++++++++++++++++++---- src/libs/kdtools/filedownloader.h | 13 ++ src/libs/kdtools/filedownloader_p.h | 3 + 3 files changed, 225 insertions(+), 23 deletions(-) (limited to 'src') 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(d->destination)) - file->setAutoRemove(false); + if (d->destination) { + d->destFileName = d->destination->fileName(); + if (QTemporaryFile *file = dynamic_cast(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 +#include // 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 &errors); #endif private: void startDownload(const QUrl &url); + void resumeDownload(); private: struct Private; -- cgit v1.2.3