1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/map/downloader.cpp

241 lines
6.1 KiB
C++
Raw Normal View History

2015-11-23 02:33:01 +01:00
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
2018-03-14 07:23:07 +01:00
#include <QBasicTimer>
#include <QDir>
#include <QTimerEvent>
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 ATTR_REDIRECT QNetworkRequest::RedirectionTargetAttribute
#define ATTR_FILE QNetworkRequest::User
2018-10-04 01:37:07 +02:00
#define ATTR_ORIGIN \
static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1)
#define ATTR_LEVEL \
static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2)
2017-01-16 21:45:27 +01:00
#define MAX_REDIRECT_LEVEL 5
#define RETRIES 3
2017-01-16 21:45:27 +01:00
Authorization::Authorization(const QString &username, const QString &password)
{
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
_header = "Basic " + data;
}
class Downloader::ReplyTimeout : public QObject
2018-03-14 07:23:07 +01:00
{
public:
static void setTimeout(QNetworkReply *reply, int timeout)
2018-03-14 07:23:07 +01:00
{
Q_ASSERT(reply);
new ReplyTimeout(reply, timeout);
}
private:
ReplyTimeout(QNetworkReply *reply, int timeout) : QObject(reply)
{
_timer.start(timeout * 1000, this);
}
void timerEvent(QTimerEvent *ev)
{
if (!_timer.isActive() || ev->timerId() != _timer.timerId())
return;
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning())
reply->close();
_timer.stop();
}
QBasicTimer _timer;
};
class Downloader::Redirect
{
public:
Redirect() : _level(0) {}
Redirect(const QUrl &origin, int level) :
_origin(origin), _level(level) {}
const QUrl &origin() const {return _origin;}
int level() const {return _level;}
private:
QUrl _origin;
int _level;
};
2018-10-08 22:07:36 +02:00
QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30;
2018-07-23 23:53:58 +02:00
#ifdef ENABLE_HTTP2
bool Downloader::_http2 = true;
#endif // ENABLE_HTTP2
2015-11-23 02:33:01 +01:00
bool Downloader::doDownload(const Download &dl,
const QByteArray &authorization, const Redirect *redirect)
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()));
if (redirect)
_errorDownloads.insert(redirect->origin(), RETRIES);
return false;
}
if (_errorDownloads.value(url) >= RETRIES)
return false;
if (_currentDownloads.contains(url) && !redirect)
return false;
2015-12-04 22:56:34 +01:00
2015-11-23 02:33:01 +01:00
QNetworkRequest request(url);
request.setAttribute(ATTR_FILE, QVariant(dl.file()));
if (redirect) {
request.setAttribute(ATTR_ORIGIN, QVariant(redirect->origin()));
request.setAttribute(ATTR_LEVEL, QVariant(redirect->level()));
2017-01-16 21:45:27 +01:00
}
request.setRawHeader("User-Agent", USER_AGENT);
if (!authorization.isNull())
request.setRawHeader("Authorization", authorization);
2018-07-23 23:53:58 +02:00
#ifdef ENABLE_HTTP2
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute,
QVariant(_http2));
#endif // ENABLE_HTTP2
2015-11-23 02:33:01 +01:00
2018-10-08 22:07:36 +02:00
Q_ASSERT(_manager);
QNetworkReply *reply = _manager->get(request);
if (reply && reply->isRunning()) {
_currentDownloads.insert(url);
ReplyTimeout::setTimeout(reply, _timeout);
connect(reply, SIGNAL(finished()), this, SLOT(emitFinished()));
} else if (reply)
downloadFinished(reply);
else
2018-03-14 07:23:07 +01:00
return false;
return true;
2015-11-23 02:33:01 +01:00
}
void Downloader::emitFinished()
{
downloadFinished(static_cast<QNetworkReply*>(sender()));
}
2015-11-23 02:33:01 +01:00
bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
2018-09-24 23:07:11 +02:00
qWarning("Error writing file: %s: %s",
2015-11-23 02:33:01 +01:00
qPrintable(filename), qPrintable(file.errorString()));
return false;
}
file.write(data->readAll());
file.close();
return true;
}
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);
}
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();
if (error) {
2018-10-04 23:02:43 +02:00
QUrl origin(reply->request().attribute(ATTR_ORIGIN).toUrl());
if (origin.isEmpty()) {
insertError(url, error);
2018-09-24 23:07:11 +02:00
qWarning("Error downloading file: %s: %s",
url.toEncoded().constData(), qPrintable(reply->errorString()));
} else {
insertError(origin, error);
2018-09-24 23:07:11 +02:00
qWarning("Error downloading file: %s -> %s: %s",
origin.toEncoded().constData(), url.toEncoded().constData(),
qPrintable(reply->errorString()));
}
2015-11-23 02:33:01 +01:00
} else {
2018-10-04 23:02:43 +02:00
QUrl location(reply->attribute(ATTR_REDIRECT).toUrl());
QString filename(reply->request().attribute(ATTR_FILE).toString());
2017-01-16 21:45:27 +01:00
if (!location.isEmpty()) {
2018-10-04 23:02:43 +02:00
QUrl origin(reply->request().attribute(ATTR_ORIGIN).toUrl());
2017-01-16 21:45:27 +01:00
int level = reply->request().attribute(ATTR_LEVEL).toInt();
if (level >= MAX_REDIRECT_LEVEL) {
_errorDownloads.insert(origin, RETRIES);
2018-02-20 23:37:19 +01:00
qWarning("Error downloading file: %s: "
2018-09-24 23:07:11 +02:00
"redirect level limit reached (redirect loop?)",
2017-01-16 21:45:27 +01:00
origin.toEncoded().constData());
2017-01-17 10:37:02 +01:00
} else {
QUrl redirectUrl;
if (location.isRelative()) {
QString path = QDir::isAbsolutePath(location.path())
? location.path() : "/" + location.path();
redirectUrl = QUrl(url.scheme() + "://" + url.host() + path);
} else
redirectUrl = location;
2017-01-17 10:37:02 +01:00
Redirect redirect(origin.isEmpty() ? url : origin, level + 1);
Download dl(redirectUrl, filename);
doDownload(dl, reply->request().rawHeader("Authorization"),
&redirect);
2017-01-16 21:45:27 +01:00
}
} else {
2016-08-02 23:05:52 +02:00
if (!saveToDisk(filename, reply))
_errorDownloads.insert(url, RETRIES);
}
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.header());
return finishEmitted;
2015-11-23 02:33:01 +01:00
}
2018-10-07 14:22:13 +02:00
#ifdef ENABLE_HTTP2
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
}
#endif // ENABLE_HTTP2