feat(net): ApiUpload ^& fix unfired finished signals

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2023-06-25 12:02:46 -07:00
parent 3105f314cb
commit c8ff812ab8
No known key found for this signature in database
GPG Key ID: E10E321EB160949B
8 changed files with 330 additions and 227 deletions

View File

@ -1,6 +1,7 @@
#include "FileResolvingTask.h" #include "FileResolvingTask.h"
#include "Json.h" #include "Json.h"
#include "net/ApiUpload.h"
#include "net/Upload.h" #include "net/Upload.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
@ -34,7 +35,7 @@ void Flame::FileResolvingTask::executeTask()
return l; return l;
})); }));
QByteArray data = Json::toText(object); QByteArray data = Json::toText(object);
auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result.get(), data); auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result.get(), data);
m_dljob->addNetAction(dl); m_dljob->addNetAction(dl);
auto step_progress = std::make_shared<TaskStepProgress>(); auto step_progress = std::make_shared<TaskStepProgress>();

View File

@ -8,6 +8,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "Json.h" #include "Json.h"
#include "net/ApiUpload.h"
#include "net/NetJob.h" #include "net/NetJob.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include "net/Upload.h" #include "net/Upload.h"
@ -27,7 +28,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArra
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
@ -176,7 +177,7 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) cons
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
@ -199,7 +200,7 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) c
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });

View File

@ -6,9 +6,10 @@
#include "Application.h" #include "Application.h"
#include "Json.h" #include "Json.h"
#include "net/ApiDownload.h"
#include "net/ApiUpload.h"
#include "net/NetJob.h" #include "net/NetJob.h"
#include "net/Upload.h" #include "net/Upload.h"
#include "net/ApiDownload.h"
Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response)
{ {
@ -34,7 +35,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); netJob->addNetAction(Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
@ -65,7 +66,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray( netJob->addNetAction(Net::ApiUpload::makeByteArray(
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw)); QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
@ -100,7 +101,8 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
QJsonDocument body(body_obj); QJsonDocument body(body_obj);
auto body_raw = body.toJson(); auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); netJob->addNetAction(
Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
@ -114,9 +116,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) c
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] { QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] { delete response; });
delete response;
});
return netJob; return netJob;
} }

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "net/ApiUpload.h"
#include "ByteArraySink.h"
#include "ChecksumValidator.h"
#include "MetaCacheSink.h"
#include "net/NetAction.h"
namespace Net {
Upload::Ptr ApiUpload::makeByteArray(QUrl url, QByteArray* output, QByteArray m_post_data)
{
auto up = makeShared<ApiUpload>();
up->m_url = std::move(url);
up->m_sink.reset(new ByteArraySink(output));
up->m_post_data = std::move(m_post_data);
return up;
}
void ApiUpload::init()
{
qDebug() << "Setting up api upload";
auto api_headers = new ApiHeaderProxy();
addHeaderProxy(api_headers);
}
} // namespace Net

36
launcher/net/ApiUpload.h Normal file
View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "ApiHeaderProxy.h"
#include "Upload.h"
namespace Net {
class ApiUpload : public Upload {
public:
virtual ~ApiUpload() = default;
static Upload::Ptr makeByteArray(QUrl url, QByteArray* output, QByteArray m_post_data);
void init() override;
};
} // namespace Net

View File

@ -46,7 +46,10 @@
#include "ChecksumValidator.h" #include "ChecksumValidator.h"
#include "MetaCacheSink.h" #include "MetaCacheSink.h"
#if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif
#include "BuildConfig.h" #include "BuildConfig.h"
#include "net/Logging.h" #include "net/Logging.h"
@ -57,6 +60,7 @@
namespace Net { namespace Net {
#if defined(LAUNCHER_APPLICATION)
auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
{ {
auto dl = makeShared<Download>(); auto dl = makeShared<Download>();
@ -68,6 +72,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down
dl->m_sink.reset(cachedNode); dl->m_sink.reset(cachedNode);
return dl; return dl;
} }
#endif
auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr
{ {
@ -110,8 +115,8 @@ void Download::executeTask()
m_state = m_sink->init(request); m_state = m_sink->init(request);
switch (m_state) { switch (m_state) {
case State::Succeeded: case State::Succeeded:
emit succeeded();
qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
emitSucceeded();
return; return;
case State::Running: case State::Running:
qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
@ -125,7 +130,13 @@ void Download::executeTask()
return; return;
} }
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); #if defined (LAUNCHER_APPLICATION)
auto user_agent = APPLICATION->getUserAgent();
#else
auto user_agent = BuildConfig.USER_AGENT;
#endif
request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8());
for ( auto& header_proxy : m_headerProxies ) { for ( auto& header_proxy : m_headerProxies ) {
header_proxy->writeHeaders(request); header_proxy->writeHeaders(request);
@ -162,7 +173,7 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
auto bytes_received_since = bytesReceived - m_last_progress_bytes; auto bytes_received_since = bytesReceived - m_last_progress_bytes;
auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000;
auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; auto remaining_time_s = (bytesTotal - bytesReceived) / dl_speed_bps;
//: Current amount of bytes downloaded, out of the total amount of bytes in the download //: Current amount of bytes downloaded, out of the total amount of bytes in the download
QString dl_progress = QString dl_progress =
@ -170,7 +181,7 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
QString dl_speed_str; QString dl_speed_str;
if (elapsed_ms.count() > 0) { if (elapsed_ms.count() > 0) {
auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown"); auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaining_time_s) : tr("unknown");
//: Download speed, in bytes per second (remaining download time in parenthesis) //: Download speed, in bytes per second (remaining download time in parenthesis)
dl_speed_str = dl_speed_str =
tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta);
@ -282,19 +293,19 @@ void Download::downloadFinished()
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
m_sink->abort(); m_sink->abort();
m_reply.reset(); m_reply.reset();
emit succeeded(); emitSucceeded();
return; return;
} else if (m_state == State::Failed) { } else if (m_state == State::Failed) {
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
m_sink->abort(); m_sink->abort();
m_reply.reset(); m_reply.reset();
emit failed(""); emitFailed("");
return; return;
} else if (m_state == State::AbortedByUser) { } else if (m_state == State::AbortedByUser) {
qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
m_sink->abort(); m_sink->abort();
m_reply.reset(); m_reply.reset();
emit aborted(); emitAborted();
return; return;
} }
@ -311,13 +322,13 @@ void Download::downloadFinished()
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
m_sink->abort(); m_sink->abort();
m_reply.reset(); m_reply.reset();
emit failed(""); emitFailed("");
return; return;
} }
m_reply.reset(); m_reply.reset();
qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString(); qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
emit succeeded(); emitSucceeded();
} }
void Download::downloadReadyRead() void Download::downloadReadyRead()

View File

@ -39,218 +39,229 @@
#include "Upload.h" #include "Upload.h"
#include <utility> #include <utility>
#include "ByteArraySink.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "ByteArraySink.h"
#if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif
#include "net/Logging.h" #include "net/Logging.h"
namespace Net { namespace Net {
bool Upload::abort() bool Upload::abort()
{ {
if (m_reply) { if (m_reply) {
m_reply->abort(); m_reply->abort();
} else { } else {
m_state = State::AbortedByUser; m_state = State::AbortedByUser;
}
return true;
}
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
setProgress(bytesReceived, bytesTotal);
}
void Upload::downloadError(QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError) {
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
// error happened during download.
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
void Upload::sslErrors(const QList<QSslError>& errors)
{
int i = 1;
for (const auto& error : errors) {
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : "
<< error.errorString();
auto cert = error.certificate();
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
bool Upload::handleRedirect()
{
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
if (!redirect.isValid()) {
if (!m_reply->hasRawHeader("Location")) {
// no redirect -> it's fine to continue
return false;
} }
return true; // there is a Location header, but it's not correct. we need to apply some workarounds...
} QByteArray redirectBA = m_reply->rawHeader("Location");
if (redirectBA.size() == 0) {
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { // empty, yet present redirect header? WTF?
setProgress(bytesReceived, bytesTotal); return false;
}
void Upload::downloadError(QNetworkReply::NetworkError error) {
if (error == QNetworkReply::OperationCanceledError) {
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
// error happened during download.
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
} }
} QString redirectStr = QString::fromUtf8(redirectBA);
void Upload::sslErrors(const QList<QSslError> &errors) {
int i = 1;
for (const auto& error : errors) {
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
bool Upload::handleRedirect()
{
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
if (!redirect.isValid()) {
if (!m_reply->hasRawHeader("Location")) {
// no redirect -> it's fine to continue
return false;
}
// there is a Location header, but it's not correct. we need to apply some workarounds...
QByteArray redirectBA = m_reply->rawHeader("Location");
if (redirectBA.size() == 0) {
// empty, yet present redirect header? WTF?
return false;
}
QString redirectStr = QString::fromUtf8(redirectBA);
if (redirectStr.startsWith("//")) {
/*
* IF the URL begins with //, we need to insert the URL scheme.
* See: https://bugreports.qt.io/browse/QTBUG-41061
* See: http://tools.ietf.org/html/rfc3986#section-4.2
*/
redirectStr = m_reply->url().scheme() + ":" + redirectStr;
} else if (redirectStr.startsWith("/")) {
/*
* IF the URL begins with /, we need to process it as a relative URL
*/
auto url = m_reply->url();
url.setPath(redirectStr, QUrl::TolerantMode);
redirectStr = url.toString();
}
if (redirectStr.startsWith("//")) {
/* /*
* Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. * IF the URL begins with //, we need to insert the URL scheme.
* FIXME: report Qt bug for this * See: https://bugreports.qt.io/browse/QTBUG-41061
* See: http://tools.ietf.org/html/rfc3986#section-4.2
*/ */
redirect = QUrl(redirectStr, QUrl::TolerantMode); redirectStr = m_reply->url().scheme() + ":" + redirectStr;
if (!redirect.isValid()) { } else if (redirectStr.startsWith("/")) {
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; /*
downloadError(QNetworkReply::ProtocolFailure); * IF the URL begins with /, we need to process it as a relative URL
return false; */
} auto url = m_reply->url();
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect; url.setPath(redirectStr, QUrl::TolerantMode);
} else { redirectStr = url.toString();
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
} }
m_url = QUrl(redirect.toString()); /*
qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
startAction(m_network); * FIXME: report Qt bug for this
return true; */
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
} }
void Upload::downloadFinished() { m_url = QUrl(redirect.toString());
// handle HTTP redirection first qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
// very unlikely for post requests, still can happen startAction(m_network);
if (handleRedirect()) { return true;
qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString(); }
return;
}
// if the download failed before this point ... void Upload::downloadFinished()
if (m_state == State::Succeeded) { {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString(); // handle HTTP redirection first
m_sink->abort(); // very unlikely for post requests, still can happen
m_reply.reset(); if (handleRedirect()) {
emit succeeded(); qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();
return; return;
} else if (m_state == State::Failed) { }
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
return;
}
// make sure we got all the remaining data, if any // if the download failed before this point ...
auto data = m_reply->readAll(); if (m_state == State::Succeeded) {
if (data.size()) { qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();
qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; m_sink->abort();
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
}
m_reply.reset(); m_reply.reset();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString(); emitSucceeded();
emit succeeded(); return;
} else if (m_state == State::Failed) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emitFailed("");
return;
} else if (m_state == State::AbortedByUser) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emitAborted();
return;
} }
void Upload::downloadReadyRead() { // make sure we got all the remaining data, if any
if (m_state == State::Running) { auto data = m_reply->readAll();
auto data = m_reply->readAll(); if (data.size()) {
m_state = m_sink->write(data); qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
} m_state = m_sink->write(data);
} }
void Upload::executeTask() { // otherwise, finalize the whole graph
setStatus(tr("Uploading %1").arg(m_url.toString())); m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emitFailed("");
return;
}
m_reply.reset();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
emitSucceeded();
}
if (m_state == State::AbortedByUser) { void Upload::downloadReadyRead()
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString(); {
emit aborted(); if (m_state == State::Running) {
auto data = m_reply->readAll();
m_state = m_sink->write(data);
}
}
void Upload::executeTask()
{
setStatus(tr("Uploading %1").arg(m_url.toString()));
if (m_state == State::AbortedByUser) {
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();
emitAborted();
return;
}
QNetworkRequest request(m_url);
m_state = m_sink->init(request);
switch (m_state) {
case State::Succeeded:
emitSucceeded();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();
return; return;
} case State::Running:
QNetworkRequest request(m_url); qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();
m_state = m_sink->init(request); break;
switch (m_state) { case State::Inactive:
case State::Succeeded: case State::Failed:
emitSucceeded(); emitFailed("");
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString(); return;
return; case State::AbortedByUser:
case State::Running: emitAborted();
qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString(); return;
break; }
case State::Inactive:
case State::Failed:
emitFailed("");
return;
case State::AbortedByUser:
emitAborted();
return;
}
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); #if defined(LAUNCHER_APPLICATION)
// TODO remove duplication auto user_agent = APPLICATION->getUserAgent();
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8());
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
QString token = APPLICATION->getModrinthAPIToken();
if (!token.isNull())
request.setRawHeader("Authorization", token.toUtf8());
}
//TODO other types of post requests ?
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* rep = m_network->post(request, m_post_data);
m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError);
#else #else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError); auto user_agent = BuildConfig.USER_AGENT;
#endif #endif
connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors); request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8());
connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);
for (auto& header_proxy : m_headerProxies) {
header_proxy->writeHeaders(request);
} }
Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data) { // TODO other types of post requests ?
auto up = makeShared<Upload>(); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
up->m_url = std::move(url); QNetworkReply* rep = m_network->post(request, m_post_data);
up->m_sink.reset(new ByteArraySink(output));
up->m_post_data = std::move(m_post_data); m_reply.reset(rep);
return up; connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress);
} connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished);
} // Net #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError);
#else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError);
#endif
connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors);
connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);
}
Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray* output, QByteArray m_post_data)
{
auto up = makeShared<Upload>();
up->m_url = std::move(url);
up->m_sink.reset(new ByteArraySink(output));
up->m_post_data = std::move(m_post_data);
return up;
}
} // namespace Net

View File

@ -42,32 +42,32 @@
namespace Net { namespace Net {
class Upload : public NetAction { class Upload : public NetAction {
Q_OBJECT Q_OBJECT
public: public:
using Ptr = shared_qobject_ptr<Upload>; using Ptr = shared_qobject_ptr<Upload>;
static Upload::Ptr makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data); static Upload::Ptr makeByteArray(QUrl url, QByteArray* output, QByteArray m_post_data);
auto abort() -> bool override; auto abort() -> bool override;
auto canAbort() const -> bool override { return true; }; auto canAbort() const -> bool override { return true; };
virtual void init() override {}; virtual void init() override{};
protected slots: protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override; void downloadError(QNetworkReply::NetworkError error) override;
void sslErrors(const QList<QSslError> & errors) override; void sslErrors(const QList<QSslError>& errors) override;
void downloadFinished() override; void downloadFinished() override;
void downloadReadyRead() override; void downloadReadyRead() override;
public slots: public slots:
void executeTask() override; void executeTask() override;
private:
std::unique_ptr<Sink> m_sink;
QByteArray m_post_data;
bool handleRedirect(); protected:
}; std::unique_ptr<Sink> m_sink;
QByteArray m_post_data;
} // Net bool handleRedirect();
};
} // namespace Net