1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-12-01 07:01:16 +01:00
GPXSee/src/common/downloader.cpp

205 lines
5.0 KiB
C++
Raw Normal View History

2015-11-23 02:33:01 +01:00
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QDir>
#include <QTimerEvent>
#include "common/config.h"
2015-11-23 02:33:01 +01:00
#include "downloader.h"
2015-11-24 10:05:28 +01:00
#if defined(Q_OS_LINUX)
#define PLATFORM_STR "Linux"
2015-11-24 10:05:28 +01:00
#elif defined(Q_OS_WIN32)
#define PLATFORM_STR "Windows"
2015-11-24 10:05:28 +01:00
#elif defined(Q_OS_MAC)
#define PLATFORM_STR "OS X"
#else
#define PLATFORM_STR "Unknown"
#endif
2016-04-01 23:14:57 +02:00
#define USER_AGENT \
APP_NAME "/" APP_VERSION " (" PLATFORM_STR "; Qt " QT_VERSION_STR ")"
2017-01-16 21:45:27 +01:00
#define MAX_REDIRECT_LEVEL 5
#define RETRIES 3
2017-01-16 21:45:27 +01:00
#define ATTR_REDIRECT_POLICY QNetworkRequest::RedirectPolicyAttribute
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
#define ATTR_HTTP2_ALLOWED QNetworkRequest::HTTP2AllowedAttribute
#else // QT 5.15
#define ATTR_HTTP2_ALLOWED QNetworkRequest::Http2AllowedAttribute
#endif // QT 5.15
#define TMP_SUFFIX ".download"
static QString tmpName(const QString &origName)
{
return origName + TMP_SUFFIX;
}
static QString origName(const QString &tmpName)
{
return tmpName.left(tmpName.size() - (sizeof(TMP_SUFFIX) - 1));
}
Authorization::Authorization(const QString &username, const QString &password)
{
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
_header = "Basic " + data;
}
NetworkTimeout::NetworkTimeout(int timeout, QNetworkReply *reply)
: QObject(reply), _timeout(timeout)
2018-03-14 07:23:07 +01:00
{
connect(reply, &QIODevice::readyRead, this, &NetworkTimeout::reset);
_timer.start(timeout * 1000, this);
}
2018-03-14 07:23:07 +01:00
void NetworkTimeout::reset()
{
_timer.start(_timeout * 1000, this);
}
2018-03-14 07:23:07 +01:00
void NetworkTimeout::timerEvent(QTimerEvent *ev)
{
if (!_timer.isActive() || ev->timerId() != _timer.timerId())
return;
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning())
reply->close();
_timer.stop();
}
2018-03-14 07:23:07 +01:00
2018-10-08 22:07:36 +02:00
QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30;
2018-07-23 23:53:58 +02:00
bool Downloader::_http2 = true;
2015-11-23 02:33:01 +01:00
bool Downloader::doDownload(const Download &dl, const Authorization &auth)
2015-11-23 02:33:01 +01:00
{
2018-10-04 23:02:43 +02:00
const QUrl &url = dl.url();
2015-12-04 22:56:34 +01:00
2018-10-04 23:02:43 +02:00
if (!url.isValid() || !(url.scheme() == QLatin1String("http")
|| url.scheme() == QLatin1String("https"))) {
2018-09-24 23:07:11 +02:00
qWarning("%s: Invalid URL", qPrintable(url.toString()));
return false;
}
if (_errorDownloads.value(url) >= RETRIES)
return false;
if (_currentDownloads.contains(url))
return false;
2015-12-04 22:56:34 +01:00
2015-11-23 02:33:01 +01:00
QNetworkRequest request(url);
request.setMaximumRedirectsAllowed(MAX_REDIRECT_LEVEL);
request.setAttribute(ATTR_REDIRECT_POLICY,
QNetworkRequest::NoLessSafeRedirectPolicy);
request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2));
request.setRawHeader("User-Agent", USER_AGENT);
if (!auth.isNull())
request.setRawHeader("Authorization", auth.header());
QFile *file = new QFile(tmpName(dl.file()));
if (!file->open(QIODevice::WriteOnly)) {
qWarning("%s: %s", qPrintable(file->fileName()),
qPrintable(file->errorString()));
_errorDownloads.insert(url, RETRIES);
return false;
}
2015-11-23 02:33:01 +01:00
2018-10-08 22:07:36 +02:00
Q_ASSERT(_manager);
QNetworkReply *reply = _manager->get(request);
file->setParent(reply);
_currentDownloads.insert(url, file);
if (reply->isRunning()) {
/* Starting with Qt 5.15 this can be replaced by
QNetworkRequest::setTransferTimeout() */
new NetworkTimeout(_timeout, reply);
connect(reply, &QIODevice::readyRead, this, &Downloader::emitReadReady);
2021-04-28 00:01:07 +02:00
connect(reply, &QNetworkReply::finished, this, &Downloader::emitFinished);
} else {
readData(reply);
downloadFinished(reply);
}
return true;
2015-11-23 02:33:01 +01:00
}
void Downloader::emitFinished()
{
downloadFinished(static_cast<QNetworkReply*>(sender()));
}
void Downloader::emitReadReady()
2015-11-23 02:33:01 +01:00
{
readData(static_cast<QNetworkReply*>(sender()));
2015-11-23 02:33:01 +01:00
}
void Downloader::insertError(const QUrl &url, QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError)
_errorDownloads.insert(url, _errorDownloads.value(url) + 1);
else
_errorDownloads.insert(url, RETRIES);
}
void Downloader::readData(QNetworkReply *reply)
{
QFile *file = _currentDownloads.value(reply->request().url());
Q_ASSERT(file);
file->write(reply->readAll());
}
2015-11-23 02:33:01 +01:00
void Downloader::downloadFinished(QNetworkReply *reply)
{
2018-10-04 23:02:43 +02:00
QUrl url(reply->request().url());
QNetworkReply::NetworkError error = reply->error();
QFile *file = _currentDownloads.value(reply->request().url());
if (error) {
insertError(url, error);
qWarning("%s: %s", url.toEncoded().constData(),
qPrintable(reply->errorString()));
file->remove();
2015-11-23 02:33:01 +01:00
} else {
file->close();
file->rename(origName(file->fileName()));
2015-11-23 02:33:01 +01:00
}
2018-03-19 22:40:49 +01:00
_currentDownloads.remove(url);
2015-11-23 02:33:01 +01:00
reply->deleteLater();
2015-12-04 22:56:34 +01:00
if (_currentDownloads.isEmpty())
2015-11-23 02:33:01 +01:00
emit finished();
}
bool Downloader::get(const QList<Download> &list,
const Authorization &authorization)
2015-11-23 02:33:01 +01:00
{
bool finishEmitted = false;
2015-11-23 02:33:01 +01:00
for (int i = 0; i < list.count(); i++)
finishEmitted |= doDownload(list.at(i), authorization);
return finishEmitted;
2015-11-23 02:33:01 +01:00
}
2018-10-07 14:22:13 +02:00
void Downloader::enableHTTP2(bool enable)
{
2018-10-08 22:07:36 +02:00
Q_ASSERT(_manager);
2018-10-07 14:22:13 +02:00
_http2 = enable;
2018-10-08 22:07:36 +02:00
_manager->clearConnectionCache();
2018-10-07 14:22:13 +02:00
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Download &download)
{
dbg.nospace() << "Download(" << download.url() << "," << download.file()
<< ")";
return dbg.space();
}
#endif // QT_NO_DEBUG