Browse Source

Merge pull request #10291 from Chocobo1/redirect

Fix crash due to network redirections
adaptive-webui-19844
Mike Tzou 6 years ago committed by GitHub
parent
commit
e621a98a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 117
      src/base/net/downloadhandler.cpp
  2. 3
      src/base/net/downloadhandler.h
  3. 13
      src/base/net/downloadmanager.cpp
  4. 10
      src/base/net/downloadmanager.h

117
src/base/net/downloadhandler.cpp

@ -41,10 +41,11 @@
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/gzip.h" #include "base/utils/gzip.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "downloadmanager.h"
namespace namespace
{ {
const int MAX_REDIRECTIONS = 20; // the common value for web browsers
bool saveToFile(const QByteArray &replyData, QString &filePath) bool saveToFile(const QByteArray &replyData, QString &filePath)
{ {
QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"}; QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"};
@ -95,44 +96,43 @@ QString Net::DownloadHandler::url() const
void Net::DownloadHandler::processFinishedDownload() void Net::DownloadHandler::processFinishedDownload()
{ {
QString url = m_reply->url().toString(); const QString url = m_reply->url().toString();
qDebug("Download finished: %s", qUtf8Printable(url)); qDebug("Download finished: %s", qUtf8Printable(url));
// Check if the request was successful // Check if the request was successful
if (m_reply->error() != QNetworkReply::NoError) { if (m_reply->error() != QNetworkReply::NoError) {
// Failure // Failure
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error()))); qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error())); emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error()));
this->deleteLater(); this->deleteLater();
return;
}
// Check if the server ask us to redirect somewhere else
const QVariant redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirection.isValid()) {
// We should redirect
handleRedirection(redirection.toUrl());
return;
}
// Success
const QByteArray replyData = (m_reply->rawHeader("Content-Encoding") == "gzip")
? Utils::Gzip::decompress(m_reply->readAll())
: m_reply->readAll();
if (m_downloadRequest.saveToFile()) {
QString filePath;
if (saveToFile(replyData, filePath))
emit downloadFinished(m_downloadRequest.url(), filePath);
else
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error"));
} }
else { else {
// Check if the server ask us to redirect somewhere else emit downloadFinished(m_downloadRequest.url(), replyData);
const QVariant redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirection.isValid()) {
// We should redirect
handleRedirection(redirection.toUrl());
}
else {
// Success
QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// decompress gzip reply
replyData = Utils::Gzip::decompress(replyData);
}
if (m_downloadRequest.saveToFile()) {
QString filePath;
if (saveToFile(replyData, filePath))
emit downloadFinished(m_downloadRequest.url(), filePath);
else
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error"));
}
else {
emit downloadFinished(m_downloadRequest.url(), replyData);
}
this->deleteLater();
}
} }
this->deleteLater();
} }
void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
@ -155,13 +155,17 @@ void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesT
} }
} }
void Net::DownloadHandler::handleRedirection(QUrl newUrl) void Net::DownloadHandler::handleRedirection(const QUrl &newUrl)
{ {
// Resolve relative urls if (m_redirectionCounter >= MAX_REDIRECTIONS) {
if (newUrl.isRelative()) emit downloadFailed(url(), tr("Exceeded max redirections (%1)").arg(MAX_REDIRECTIONS));
newUrl = m_reply->url().resolved(newUrl); this->deleteLater();
return;
}
const QString newUrlString = newUrl.toString(); // Resolve relative urls
const QUrl resolvedUrl = (newUrl.isRelative()) ? m_reply->url().resolved(newUrl) : newUrl;
const QString newUrlString = resolvedUrl.toString();
qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString)); qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
// Redirect to magnet workaround // Redirect to magnet workaround
@ -174,29 +178,30 @@ void Net::DownloadHandler::handleRedirection(QUrl newUrl)
emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI.")); emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI."));
this->deleteLater(); this->deleteLater();
return;
} }
else {
DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString)); DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString));
connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater); redirected->m_redirectionCounter = (m_redirectionCounter + 1);
connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason) connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater);
{ connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason)
emit downloadFailed(url(), reason); {
}); emit downloadFailed(url(), reason);
connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri) });
{ connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri)
emit redirectedToMagnet(url(), magnetUri); {
}); emit redirectedToMagnet(url(), magnetUri);
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished) });
, this, [this](const QString &, const QString &fileName) connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished)
{ , this, [this](const QString &, const QString &fileName)
emit downloadFinished(url(), fileName); {
}); emit downloadFinished(url(), fileName);
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished) });
, this, [this](const QString &, const QByteArray &data) connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished)
{ , this, [this](const QString &, const QByteArray &data)
emit downloadFinished(url(), data); {
}); emit downloadFinished(url(), data);
} });
} }
QString Net::DownloadHandler::errorCodeToString(const QNetworkReply::NetworkError status) QString Net::DownloadHandler::errorCodeToString(const QNetworkReply::NetworkError status)

3
src/base/net/downloadhandler.h

@ -67,13 +67,14 @@ namespace Net
private: private:
void assignNetworkReply(QNetworkReply *reply); void assignNetworkReply(QNetworkReply *reply);
void handleRedirection(QUrl newUrl); void handleRedirection(const QUrl &newUrl);
static QString errorCodeToString(QNetworkReply::NetworkError status); static QString errorCodeToString(QNetworkReply::NetworkError status);
QNetworkReply *m_reply; QNetworkReply *m_reply;
DownloadManager *m_manager; DownloadManager *m_manager;
const DownloadRequest m_downloadRequest; const DownloadRequest m_downloadRequest;
short m_redirectionCounter = 0;
}; };
} }

13
src/base/net/downloadmanager.cpp

@ -42,6 +42,7 @@
#include <QUrl> #include <QUrl>
#include "base/global.h" #include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "downloadhandler.h" #include "downloadhandler.h"
#include "proxyconfigurationmanager.h" #include "proxyconfigurationmanager.h"
@ -130,9 +131,7 @@ Net::DownloadManager *Net::DownloadManager::m_instance = nullptr;
Net::DownloadManager::DownloadManager(QObject *parent) Net::DownloadManager::DownloadManager(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
#ifndef QT_NO_OPENSSL
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &Net::DownloadManager::ignoreSslErrors); connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &Net::DownloadManager::ignoreSslErrors);
#endif
connect(&m_networkManager, &QNetworkAccessManager::finished, this, &DownloadManager::handleReplyFinished); connect(&m_networkManager, &QNetworkAccessManager::finished, this, &DownloadManager::handleReplyFinished);
connect(ProxyConfigurationManager::instance(), &ProxyConfigurationManager::proxyConfigurationChanged connect(ProxyConfigurationManager::instance(), &ProxyConfigurationManager::proxyConfigurationChanged
, this, &DownloadManager::applyProxySettings); , this, &DownloadManager::applyProxySettings);
@ -269,14 +268,16 @@ void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
handler->disconnect(this); handler->disconnect(this);
} }
#ifndef QT_NO_OPENSSL
void Net::DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors) void Net::DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{ {
Q_UNUSED(errors) QStringList errorList;
for (const QSslError &error : errors)
errorList += error.errorString();
LogMsg(tr("Ignoring SSL error, URL: \"%1\", errors: \"%2\"").arg(reply->url().toString(), errorList.join(". ")), Log::WARNING);
// Ignore all SSL errors // Ignore all SSL errors
reply->ignoreSslErrors(); reply->ignoreSslErrors();
} }
#endif
Net::DownloadRequest::DownloadRequest(const QString &url) Net::DownloadRequest::DownloadRequest(const QString &url)
: m_url {url} : m_url {url}
@ -343,7 +344,7 @@ Net::ServiceID Net::ServiceID::fromURL(const QUrl &url)
return {url.host(), url.port(80)}; return {url.host(), url.port(80)};
} }
uint Net::qHash(const ServiceID &serviceID, uint seed) uint Net::qHash(const ServiceID &serviceID, const uint seed)
{ {
return ::qHash(serviceID.hostName, seed) ^ serviceID.port; return ::qHash(serviceID.hostName, seed) ^ serviceID.port;
} }

10
src/base/net/downloadmanager.h

@ -37,8 +37,8 @@
#include <QQueue> #include <QQueue>
#include <QSet> #include <QSet>
class QNetworkReply;
class QNetworkCookie; class QNetworkCookie;
class QNetworkReply;
class QSslError; class QSslError;
class QUrl; class QUrl;
@ -83,6 +83,9 @@ namespace Net
static ServiceID fromURL(const QUrl &url); static ServiceID fromURL(const QUrl &url);
}; };
uint qHash(const ServiceID &serviceID, uint seed);
bool operator==(const ServiceID &lhs, const ServiceID &rhs);
class DownloadManager : public QObject class DownloadManager : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -106,9 +109,7 @@ namespace Net
static bool hasSupportedScheme(const QString &url); static bool hasSupportedScheme(const QString &url);
private slots: private slots:
#ifndef QT_NO_OPENSSL
void ignoreSslErrors(QNetworkReply *, const QList<QSslError> &); void ignoreSslErrors(QNetworkReply *, const QList<QSslError> &);
#endif
private: private:
explicit DownloadManager(QObject *parent = nullptr); explicit DownloadManager(QObject *parent = nullptr);
@ -123,9 +124,6 @@ namespace Net
QSet<ServiceID> m_busyServices; QSet<ServiceID> m_busyServices;
QHash<ServiceID, QQueue<DownloadHandler *>> m_waitingJobs; QHash<ServiceID, QQueue<DownloadHandler *>> m_waitingJobs;
}; };
uint qHash(const ServiceID &serviceID, uint seed);
bool operator==(const ServiceID &lhs, const ServiceID &rhs);
} }
#endif // NET_DOWNLOADMANAGER_H #endif // NET_DOWNLOADMANAGER_H

Loading…
Cancel
Save