mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-09-17 08:35:00 -04:00
refactor(net): use std::expected for the sink interface (#6071)
This commit is contained in:
13 files changed
+203
-255
No files matched your search
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
#include "BaseEntity.h"
|
||||
#include <expected>
|
||||
#include <utility>
|
||||
|
||||
#include "Exception.h"
|
||||
#include "FileSystem.h"
|
||||
@@ -26,55 +28,44 @@
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Meta {
|
||||
namespace {
|
||||
|
||||
class ParsingValidator : public Net::Validator {
|
||||
public: /* con/des */
|
||||
ParsingValidator(BaseEntity* entity) : m_entity(entity) {};
|
||||
virtual ~ParsingValidator() = default;
|
||||
explicit ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity) {};
|
||||
~ParsingValidator() override = default;
|
||||
|
||||
public: /* methods */
|
||||
bool init(QNetworkRequest&) override
|
||||
{
|
||||
m_data.clear();
|
||||
return true;
|
||||
}
|
||||
bool write(QByteArray& data) override
|
||||
{
|
||||
this->m_data.append(data);
|
||||
return true;
|
||||
}
|
||||
bool abort() override
|
||||
{
|
||||
m_data.clear();
|
||||
return true;
|
||||
}
|
||||
bool validate(QNetworkReply&) override
|
||||
void init() override { m_data.clear(); }
|
||||
void write(const QByteArray& data) override { this->m_data.append(data); }
|
||||
void abort() override { m_data.clear(); }
|
||||
Result validate() override
|
||||
{
|
||||
auto fname = m_entity->localFilename();
|
||||
try {
|
||||
auto doc = Json::requireDocument(m_data, fname);
|
||||
auto obj = Json::requireObject(doc, fname);
|
||||
m_entity->parse(obj);
|
||||
return true;
|
||||
return {};
|
||||
} catch (const Exception& e) {
|
||||
qWarning() << "Unable to parse response:" << e.cause();
|
||||
return false;
|
||||
return std::unexpected<Error>("Unable to parse response:" + e.cause());
|
||||
}
|
||||
}
|
||||
|
||||
private: /* data */
|
||||
QByteArray m_data;
|
||||
BaseEntity* m_entity;
|
||||
Meta::BaseEntity* m_entity;
|
||||
};
|
||||
} // namespace
|
||||
namespace Meta {
|
||||
|
||||
QUrl BaseEntity::url() const
|
||||
{
|
||||
auto s = APPLICATION->settings();
|
||||
auto* s = APPLICATION->settings();
|
||||
QString metaOverride = s->get("MetaURLOverride").toString();
|
||||
if (metaOverride.isEmpty()) {
|
||||
return QUrl(BuildConfig.META_URL).resolved(localFilename());
|
||||
@@ -99,7 +90,7 @@ bool BaseEntity::isLoaded() const
|
||||
|
||||
void BaseEntity::setSha256(QString sha256)
|
||||
{
|
||||
m_sha256 = sha256;
|
||||
m_sha256 = std::move(sha256);
|
||||
}
|
||||
|
||||
BaseEntity::LoadStatus BaseEntity::status() const
|
||||
@@ -171,8 +162,9 @@ void BaseEntityLoadTask::executeTask()
|
||||
* The validator parses the file and loads it into the object.
|
||||
* If that fails, the file is not written to storage.
|
||||
*/
|
||||
if (!m_entity->m_sha256.isEmpty())
|
||||
if (!m_entity->m_sha256.isEmpty()) {
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256));
|
||||
}
|
||||
dl->addValidator(new ParsingValidator(m_entity));
|
||||
m_task->addNetAction(dl);
|
||||
m_task->setAskRetry(false);
|
||||
|
||||
@@ -48,41 +48,23 @@ class ByteArraySink : public Sink {
|
||||
~ByteArraySink() override = default;
|
||||
|
||||
public:
|
||||
auto init(QNetworkRequest& request) -> Task::State override
|
||||
InitResult init(QNetworkRequest& /*request*/) override
|
||||
{
|
||||
m_output.clear();
|
||||
if (initAllValidators(request)) {
|
||||
return Task::State::Running;
|
||||
}
|
||||
m_fail_reason = "Failed to initialize validators";
|
||||
return Task::State::Failed;
|
||||
initAllValidators();
|
||||
return InitType::Ok;
|
||||
};
|
||||
|
||||
auto write(QByteArray& data) -> Task::State override
|
||||
Result write(const QByteArray& data) override
|
||||
{
|
||||
m_output.append(data);
|
||||
if (writeAllValidators(data)) {
|
||||
return Task::State::Running;
|
||||
}
|
||||
m_fail_reason = "Failed to write validators";
|
||||
return Task::State::Failed;
|
||||
writeAllValidators(data);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto abort() -> Task::State override
|
||||
{
|
||||
failAllValidators();
|
||||
m_fail_reason = "Aborted";
|
||||
return Task::State::Failed;
|
||||
}
|
||||
void abort() override { failAllValidators(); }
|
||||
|
||||
auto finalize(QNetworkReply& reply) -> Task::State override
|
||||
{
|
||||
if (finalizeAllValidators(reply)) {
|
||||
return Task::State::Succeeded;
|
||||
}
|
||||
m_fail_reason = "Failed to finalize validators";
|
||||
return Task::State::Failed;
|
||||
}
|
||||
Result finalize(QNetworkReply& /*reply*/) override { return finalizeAllValidators(); }
|
||||
|
||||
auto hasLocalData() -> bool override { return false; }
|
||||
|
||||
|
||||
@@ -38,48 +38,35 @@
|
||||
#include "Validator.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <expected>
|
||||
#include <utility>
|
||||
|
||||
namespace Net {
|
||||
class ChecksumValidator : public Validator {
|
||||
public:
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QString expectedHex)
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, const QString& expectedHex)
|
||||
: Net::ChecksumValidator(algorithm, QByteArray::fromHex(expectedHex.toLatin1()))
|
||||
{}
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
|
||||
: m_checksum(algorithm), m_expected(expected) {};
|
||||
virtual ~ChecksumValidator() = default;
|
||||
explicit ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
|
||||
: m_checksum(algorithm), m_expected(std::move(expected)) {};
|
||||
~ChecksumValidator() override = default;
|
||||
|
||||
public:
|
||||
auto init(QNetworkRequest&) -> bool override
|
||||
{
|
||||
m_checksum.reset();
|
||||
return true;
|
||||
}
|
||||
void init() override { m_checksum.reset(); }
|
||||
void write(const QByteArray& data) override { m_checksum.addData(data); }
|
||||
void abort() override { m_checksum.reset(); }
|
||||
|
||||
auto write(QByteArray& data) -> bool override
|
||||
{
|
||||
m_checksum.addData(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto abort() -> bool override
|
||||
{
|
||||
m_checksum.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto validate(QNetworkReply& reply) -> bool override
|
||||
Result validate() override
|
||||
{
|
||||
if (!m_expected.isEmpty() && m_expected != hash()) {
|
||||
qWarning() << "Checksum mismatch for URL:" << reply.url().toString() << "expected:" << m_expected << "got:" << hash();
|
||||
return false;
|
||||
return std::unexpected<Error>(QString("Checksum mismatch: expected %1, got %2").arg(m_expected.toHex(), hash().toHex()));
|
||||
}
|
||||
return true;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto hash() -> QByteArray { return m_checksum.result(); }
|
||||
|
||||
void setExpected(QByteArray expected) { m_expected = expected; }
|
||||
void setExpected(QByteArray expected) { m_expected = std::move(expected); }
|
||||
|
||||
private:
|
||||
QCryptographicHash m_checksum;
|
||||
|
||||
@@ -25,10 +25,10 @@ class DummySink : public Sink {
|
||||
public:
|
||||
explicit DummySink() = default;
|
||||
~DummySink() override = default;
|
||||
auto init(QNetworkRequest& /*request*/) -> Task::State override { return Task::State::Running; }
|
||||
auto write(QByteArray& /*data*/) -> Task::State override { return Task::State::Succeeded; }
|
||||
auto abort() -> Task::State override { return Task::State::AbortedByUser; }
|
||||
auto finalize(QNetworkReply& /*reply*/) -> Task::State override { return Task::State::Succeeded; }
|
||||
InitResult init(QNetworkRequest& /*request*/) override { return InitType::Ok; }
|
||||
Result write(const QByteArray& /*data*/) override { return {}; }
|
||||
Result finalize(QNetworkReply& /*reply*/) override { return {}; }
|
||||
void abort() override {}
|
||||
auto hasLocalData() -> bool override { return false; }
|
||||
};
|
||||
|
||||
|
||||
+31
-46
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include "FileSink.h"
|
||||
#include <expected>
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
@@ -41,66 +42,61 @@
|
||||
|
||||
namespace Net {
|
||||
|
||||
Task::State FileSink::init(QNetworkRequest& request)
|
||||
auto FileSink::init(QNetworkRequest& request) -> InitResult
|
||||
{
|
||||
auto result = initCache(request);
|
||||
if (result != Task::State::Running) {
|
||||
if (!result || *result != InitType::Ok) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// create a new save file and open it for writing
|
||||
if (!FS::ensureFilePathExists(m_filename)) {
|
||||
qCCritical(taskNetLogC) << "Could not create folder for " + m_filename;
|
||||
m_fail_reason = "Could not create folder";
|
||||
return Task::State::Failed;
|
||||
return std::unexpected<Error>("Could not create folder");
|
||||
}
|
||||
|
||||
m_wroteAnyData = false;
|
||||
m_output_file.reset(new PSaveFile(m_filename));
|
||||
if (!m_output_file->open(QIODevice::WriteOnly)) {
|
||||
const auto error = QString("Could not open %1 for writing: %2").arg(m_filename).arg(m_output_file->errorString());
|
||||
m_outputFile.reset(new PSaveFile(m_filename));
|
||||
if (!m_outputFile->open(QIODevice::WriteOnly)) {
|
||||
const auto error = QString("Could not open %1 for writing: %2").arg(m_filename).arg(m_outputFile->errorString());
|
||||
qCCritical(taskNetLogC) << error;
|
||||
m_fail_reason = error;
|
||||
return Task::State::Failed;
|
||||
return std::unexpected<Error>(error);
|
||||
}
|
||||
|
||||
if (initAllValidators(request))
|
||||
return Task::State::Running;
|
||||
m_fail_reason = "Failed to initialize validators";
|
||||
return Task::State::Failed;
|
||||
initAllValidators();
|
||||
return InitType::Ok;
|
||||
}
|
||||
|
||||
Task::State FileSink::write(QByteArray& data)
|
||||
auto FileSink::write(const QByteArray& data) -> Result
|
||||
{
|
||||
if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) {
|
||||
writeAllValidators(data);
|
||||
if (m_outputFile->write(data) != data.size()) {
|
||||
QString error = QString("Failed writing into %1: %2").arg(m_filename);
|
||||
if (m_output_file->error() == QFileDevice::NoError) {
|
||||
if (m_outputFile->error() == QFileDevice::NoError) {
|
||||
error = error.arg("Validators failed");
|
||||
} else {
|
||||
error = error.arg(m_output_file->errorString());
|
||||
error = error.arg(m_outputFile->errorString());
|
||||
}
|
||||
qCCritical(taskNetLogC) << error;
|
||||
m_fail_reason = error;
|
||||
m_output_file->cancelWriting();
|
||||
m_output_file.reset();
|
||||
m_outputFile->cancelWriting();
|
||||
m_outputFile.reset();
|
||||
m_wroteAnyData = false;
|
||||
return Task::State::Failed;
|
||||
return std::unexpected<Error>(error);
|
||||
}
|
||||
|
||||
m_wroteAnyData = true;
|
||||
return Task::State::Running;
|
||||
return {};
|
||||
}
|
||||
|
||||
Task::State FileSink::abort()
|
||||
void FileSink::abort()
|
||||
{
|
||||
if (m_output_file) {
|
||||
m_output_file->cancelWriting();
|
||||
if (m_outputFile) {
|
||||
m_outputFile->cancelWriting();
|
||||
}
|
||||
failAllValidators();
|
||||
return Task::State::Failed;
|
||||
}
|
||||
|
||||
Task::State FileSink::finalize(QNetworkReply& reply)
|
||||
auto FileSink::finalize(QNetworkReply& reply) -> Result
|
||||
{
|
||||
bool gotFile = false;
|
||||
QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
@@ -116,37 +112,26 @@ Task::State FileSink::finalize(QNetworkReply& reply)
|
||||
if (gotFile || m_wroteAnyData) {
|
||||
// ask validators for data consistency
|
||||
// we only do this for actual downloads, not 'your data is still the same' cache hits
|
||||
if (!finalizeAllValidators(reply)) {
|
||||
m_fail_reason = "Failed to finalize validators";
|
||||
return Task::State::Failed;
|
||||
auto result = finalizeAllValidators();
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// nothing went wrong...
|
||||
if (!m_output_file->commit()) {
|
||||
const auto error = QString("Failed to commit changes to %1: %2").arg(m_filename).arg(m_output_file->errorString());
|
||||
if (!m_outputFile->commit()) {
|
||||
const auto error = QString("Failed to commit changes to %1: %2").arg(m_filename).arg(m_outputFile->errorString());
|
||||
qCCritical(taskNetLogC) << error;
|
||||
m_fail_reason = error;
|
||||
m_output_file->cancelWriting();
|
||||
return Task::State::Failed;
|
||||
m_outputFile->cancelWriting();
|
||||
return std::unexpected<Error>(error);
|
||||
}
|
||||
}
|
||||
|
||||
// then get rid of the save file
|
||||
m_output_file.reset();
|
||||
m_outputFile.reset();
|
||||
|
||||
return finalizeCache(reply);
|
||||
}
|
||||
|
||||
Task::State FileSink::initCache(QNetworkRequest&)
|
||||
{
|
||||
return Task::State::Running;
|
||||
}
|
||||
|
||||
Task::State FileSink::finalizeCache(QNetworkReply&)
|
||||
{
|
||||
return Task::State::Succeeded;
|
||||
}
|
||||
|
||||
bool FileSink::hasLocalData()
|
||||
{
|
||||
QFileInfo info(m_filename);
|
||||
|
||||
+11
-9
@@ -38,27 +38,29 @@
|
||||
#include "PSaveFile.h"
|
||||
#include "Sink.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Net {
|
||||
class FileSink : public Sink {
|
||||
public:
|
||||
FileSink(QString filename) : m_filename(filename) {};
|
||||
virtual ~FileSink() = default;
|
||||
explicit FileSink(QString filename) : m_filename(std::move(filename)) {};
|
||||
~FileSink() override = default;
|
||||
|
||||
public:
|
||||
auto init(QNetworkRequest& request) -> Task::State override;
|
||||
auto write(QByteArray& data) -> Task::State override;
|
||||
auto abort() -> Task::State override;
|
||||
auto finalize(QNetworkReply& reply) -> Task::State override;
|
||||
InitResult init(QNetworkRequest& request) override;
|
||||
Result write(const QByteArray& data) override;
|
||||
Result finalize(QNetworkReply& reply) override;
|
||||
void abort() override;
|
||||
|
||||
auto hasLocalData() -> bool override;
|
||||
|
||||
protected:
|
||||
virtual auto initCache(QNetworkRequest&) -> Task::State;
|
||||
virtual auto finalizeCache(QNetworkReply& reply) -> Task::State;
|
||||
virtual InitResult initCache(QNetworkRequest&) { return InitType::Ok; }
|
||||
virtual Result finalizeCache(QNetworkReply& /*reply*/) { return {}; }
|
||||
|
||||
protected:
|
||||
QString m_filename;
|
||||
bool m_wroteAnyData = false;
|
||||
std::unique_ptr<PSaveFile> m_output_file;
|
||||
std::unique_ptr<PSaveFile> m_outputFile;
|
||||
};
|
||||
} // namespace Net
|
||||
@@ -46,37 +46,37 @@ namespace Net {
|
||||
/** Maximum time to hold a cache entry
|
||||
* = 1 week in seconds
|
||||
*/
|
||||
#define MAX_TIME_TO_EXPIRE 1 * 7 * 24 * 60 * 60
|
||||
#define MAX_TIME_TO_EXPIRE (1 * 7 * 24 * 60 * 60)
|
||||
|
||||
MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal)
|
||||
: Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_is_eternal(is_eternal)
|
||||
MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool isEternal)
|
||||
: Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_isEternal(isEternal)
|
||||
{
|
||||
addValidator(md5sum);
|
||||
}
|
||||
|
||||
Task::State MetaCacheSink::initCache(QNetworkRequest& request)
|
||||
auto MetaCacheSink::initCache(QNetworkRequest& request) -> InitResult
|
||||
{
|
||||
if (!m_entry->isStale()) {
|
||||
return Task::State::Succeeded;
|
||||
return InitType::CacheHit;
|
||||
}
|
||||
|
||||
// check if file exists, if it does, use its information for the request
|
||||
QFile current(m_filename);
|
||||
if (current.exists() && current.size() != 0) {
|
||||
if (m_entry->getRemoteChangedTimestamp().size()) {
|
||||
if (!m_entry->getRemoteChangedTimestamp().isEmpty()) {
|
||||
request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1());
|
||||
}
|
||||
if (m_entry->getETag().size()) {
|
||||
if (!m_entry->getETag().isEmpty()) {
|
||||
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
|
||||
}
|
||||
}
|
||||
|
||||
return Task::State::Running;
|
||||
return InitType::Ok;
|
||||
}
|
||||
|
||||
Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
|
||||
auto MetaCacheSink::finalizeCache(QNetworkReply& reply) -> Result
|
||||
{
|
||||
QFileInfo output_file_info(m_filename);
|
||||
QFileInfo outputFileInfo(m_filename);
|
||||
|
||||
if (m_wroteAnyData) {
|
||||
m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
|
||||
@@ -88,36 +88,36 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
|
||||
m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData());
|
||||
}
|
||||
|
||||
m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
|
||||
m_entry->setLocalChangedTimestamp(outputFileInfo.lastModified().toUTC().toMSecsSinceEpoch());
|
||||
|
||||
{ // Cache lifetime
|
||||
if (m_is_eternal) {
|
||||
if (m_isEternal) {
|
||||
qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath();
|
||||
m_entry->makeEternal(true);
|
||||
} else if (reply.hasRawHeader("Cache-Control")) {
|
||||
auto cache_control_header = reply.rawHeader("Cache-Control");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cache_control_header;
|
||||
auto cacheControlHeader = reply.rawHeader("Cache-Control");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cacheControlHeader;
|
||||
|
||||
static const QRegularExpression s_maxAgeExpr("max-age=([0-9]+)");
|
||||
qint64 max_age = s_maxAgeExpr.match(cache_control_header).captured(1).toLongLong();
|
||||
m_entry->setMaximumAge(max_age);
|
||||
qint64 maxAge = s_maxAgeExpr.match(cacheControlHeader).captured(1).toLongLong();
|
||||
m_entry->setMaximumAge(maxAge);
|
||||
|
||||
} else if (reply.hasRawHeader("Expires")) {
|
||||
auto expires_header = reply.rawHeader("Expires");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expires_header;
|
||||
auto expiresHeader = reply.rawHeader("Expires");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expiresHeader;
|
||||
|
||||
qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
|
||||
m_entry->setMaximumAge(max_age);
|
||||
qint64 maxAge = QDateTime::fromString(expiresHeader).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
|
||||
m_entry->setMaximumAge(maxAge);
|
||||
} else {
|
||||
m_entry->setMaximumAge(MAX_TIME_TO_EXPIRE);
|
||||
}
|
||||
|
||||
if (reply.hasRawHeader("Age")) {
|
||||
auto age_header = reply.rawHeader("Age");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << age_header;
|
||||
auto ageHeader = reply.rawHeader("Age");
|
||||
qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << ageHeader;
|
||||
|
||||
qint64 current_age = age_header.toLongLong();
|
||||
m_entry->setCurrentAge(current_age);
|
||||
qint64 currentAge = ageHeader.toLongLong();
|
||||
m_entry->setCurrentAge(currentAge);
|
||||
} else {
|
||||
m_entry->setCurrentAge(0);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
|
||||
m_entry->setStale(false);
|
||||
APPLICATION->metacache()->updateEntry(m_entry);
|
||||
|
||||
return Task::State::Succeeded;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool MetaCacheSink::hasLocalData()
|
||||
|
||||
@@ -42,18 +42,18 @@
|
||||
namespace Net {
|
||||
class MetaCacheSink : public FileSink {
|
||||
public:
|
||||
MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal = false);
|
||||
virtual ~MetaCacheSink() = default;
|
||||
MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool isEternal = false);
|
||||
~MetaCacheSink() override = default;
|
||||
|
||||
auto hasLocalData() -> bool override;
|
||||
|
||||
protected:
|
||||
auto initCache(QNetworkRequest& request) -> Task::State override;
|
||||
auto finalizeCache(QNetworkReply& reply) -> Task::State override;
|
||||
InitResult initCache(QNetworkRequest& request) override;
|
||||
Result finalizeCache(QNetworkReply& reply) override;
|
||||
|
||||
private:
|
||||
MetaEntryPtr m_entry;
|
||||
ChecksumValidator* m_md5Node;
|
||||
bool m_is_eternal;
|
||||
bool m_isEternal;
|
||||
};
|
||||
} // namespace Net
|
||||
+17
-20
@@ -35,28 +35,25 @@ class Sink : public ByteArraySink {
|
||||
~Sink() override = default;
|
||||
|
||||
public:
|
||||
auto finalize(QNetworkReply& reply) -> Task::State override
|
||||
Result finalize(QNetworkReply& /*reply*/) override
|
||||
{
|
||||
if (finalizeAllValidators(reply)) {
|
||||
try {
|
||||
auto result = m_parseFunc(m_output);
|
||||
if (!result.has_value()) {
|
||||
m_fail_reason = result.error();
|
||||
return Task::State::Failed;
|
||||
}
|
||||
m_result = *result;
|
||||
} catch (const std::exception& e) {
|
||||
m_fail_reason = QString::fromUtf8(e.what());
|
||||
return Task::State::Failed;
|
||||
// ToDo: make this suppport QJsonException
|
||||
} catch (...) {
|
||||
m_fail_reason = QObject::tr("Unknown error while parsing RPC response");
|
||||
return Task::State::Failed;
|
||||
}
|
||||
return Task::State::Succeeded;
|
||||
auto validatorResult = finalizeAllValidators();
|
||||
if (!validatorResult) {
|
||||
return validatorResult;
|
||||
}
|
||||
m_fail_reason = "Failed to finalize validators";
|
||||
return Task::State::Failed;
|
||||
try {
|
||||
auto result = m_parseFunc(m_output);
|
||||
if (!result.has_value()) {
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
m_result = *result;
|
||||
} catch (const std::exception& e) {
|
||||
return std::unexpected<Error>(QString::fromUtf8(e.what()));
|
||||
// ToDo: make this suppport QJsonException
|
||||
} catch (...) {
|
||||
return std::unexpected<Error>(QObject::tr("Unknown error while parsing RPC response"));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
T* result() { return &m_result; }
|
||||
|
||||
+30
-27
@@ -64,6 +64,7 @@
|
||||
#include "net/ByteArraySink.h"
|
||||
#include "net/FileSink.h"
|
||||
#include "net/Logging.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "MMCTime.h"
|
||||
#include "StringUtils.h"
|
||||
@@ -139,24 +140,22 @@ void Request::executeTask()
|
||||
}
|
||||
|
||||
QNetworkRequest request(m_url);
|
||||
m_state = m_sink->init(request);
|
||||
switch (m_state) {
|
||||
case State::Succeeded:
|
||||
qCDebug(m_logCat) << getUid().toString() << "Request cache hit" << m_url.toString();
|
||||
emit succeeded();
|
||||
emit finished();
|
||||
return;
|
||||
case State::Running:
|
||||
auto result = m_sink->init(request);
|
||||
if (!result) {
|
||||
m_state = Task::State::Failed;
|
||||
m_failReason = result.error();
|
||||
emit failed(m_failReason);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
switch (*result) {
|
||||
case Sink::InitType::Ok:
|
||||
qCDebug(m_logCat) << getUid().toString() << "Running" << m_url.toString();
|
||||
break;
|
||||
case State::Inactive:
|
||||
case State::Failed:
|
||||
m_failReason = m_sink->failReason();
|
||||
emit failed(m_sink->failReason());
|
||||
emit finished();
|
||||
return;
|
||||
case State::AbortedByUser:
|
||||
emit aborted();
|
||||
case Sink::InitType::CacheHit:
|
||||
m_state = Task::State::Succeeded;
|
||||
qCDebug(m_logCat) << getUid().toString() << "Request cache hit" << m_url.toString();
|
||||
emit succeeded();
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
@@ -404,24 +403,26 @@ void Request::downloadFinished()
|
||||
auto data = m_reply->readAll();
|
||||
if (!data.isEmpty()) {
|
||||
qCDebug(m_logCat) << getUid().toString() << "Writing extra" << data.size() << "bytes";
|
||||
m_state = m_sink->write(data);
|
||||
if (m_state != State::Succeeded) {
|
||||
auto result = m_sink->write(data);
|
||||
if (!result) {
|
||||
m_state = Task::State::Failed;
|
||||
qCDebug(m_logCat) << getUid().toString() << "Request failed to write:" << m_url.toString();
|
||||
m_sink->abort();
|
||||
m_failReason = m_sink->failReason();
|
||||
emit failed(m_sink->failReason());
|
||||
m_failReason = result.error();
|
||||
emit failed(m_failReason);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, finalize the whole graph
|
||||
m_state = m_sink->finalize(*m_reply);
|
||||
if (m_state != State::Succeeded) {
|
||||
auto result = m_sink->finalize(*m_reply);
|
||||
if (!result) {
|
||||
m_state = Task::State::Failed;
|
||||
qCDebug(m_logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString();
|
||||
m_sink->abort();
|
||||
m_failReason = m_sink->failReason();
|
||||
emit failed(m_sink->failReason());
|
||||
m_failReason = result.error();
|
||||
emit failed(m_failReason);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
@@ -435,12 +436,14 @@ void Request::downloadReadyRead()
|
||||
{
|
||||
if (m_state == State::Running) {
|
||||
auto data = m_reply->readAll();
|
||||
m_state = m_sink->write(data);
|
||||
auto result = m_sink->write(data);
|
||||
if (replyStatusCode() >= 400) {
|
||||
m_errorResponse.append(data);
|
||||
}
|
||||
if (m_state == State::Failed) {
|
||||
qCCritical(m_logCat) << getUid().toString() << "Failed to process response chunk:" << m_sink->failReason();
|
||||
if (!result) {
|
||||
m_state = Task::State::Failed;
|
||||
m_failReason = result.error();
|
||||
qCCritical(m_logCat) << getUid().toString() << "Failed to process response chunk:" << m_failReason;
|
||||
}
|
||||
// qDebug() << "Request" << m_url.toString() << "gained" << data.size() << "bytes";
|
||||
} else {
|
||||
|
||||
@@ -53,11 +53,9 @@
|
||||
#include "EnumWrapper.h"
|
||||
#include "HeaderProxy.h"
|
||||
#include "HttpMetaCache.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "Sink.h"
|
||||
#include "Validator.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class QIODevice;
|
||||
@@ -195,7 +193,7 @@ class Request : public Task {
|
||||
int m_redirectCount = 0;
|
||||
|
||||
HttpMethod m_httpMethod = HttpMethod::Get;
|
||||
PostData m_postData{};
|
||||
PostData m_postData;
|
||||
};
|
||||
|
||||
} // namespace Net
|
||||
|
||||
+29
-30
@@ -35,8 +35,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include "Validator.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Net {
|
||||
class Sink {
|
||||
@@ -44,59 +44,58 @@ class Sink {
|
||||
Sink() = default;
|
||||
virtual ~Sink() = default;
|
||||
|
||||
using Error = Validator::Error;
|
||||
using Result = Validator::Result;
|
||||
|
||||
enum class InitType : std::uint8_t { Ok, CacheHit };
|
||||
using InitResult = std::expected<InitType, QString>;
|
||||
|
||||
public:
|
||||
virtual auto init(QNetworkRequest& request) -> Task::State = 0;
|
||||
virtual auto write(QByteArray& data) -> Task::State = 0;
|
||||
virtual auto abort() -> Task::State = 0;
|
||||
virtual auto finalize(QNetworkReply& reply) -> Task::State = 0;
|
||||
virtual InitResult init(QNetworkRequest& request) = 0;
|
||||
virtual Result write(const QByteArray& data) = 0;
|
||||
virtual Result finalize(QNetworkReply& reply) = 0;
|
||||
virtual void abort() = 0;
|
||||
|
||||
virtual auto hasLocalData() -> bool = 0;
|
||||
|
||||
QString failReason() const { return m_fail_reason; }
|
||||
|
||||
void addValidator(Validator* validator)
|
||||
{
|
||||
if (validator) {
|
||||
validators.push_back(std::shared_ptr<Validator>(validator));
|
||||
m_validators.push_back(std::shared_ptr<Validator>(validator));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool initAllValidators(QNetworkRequest& request)
|
||||
void initAllValidators()
|
||||
{
|
||||
for (auto& validator : validators) {
|
||||
if (!validator->init(request))
|
||||
return false;
|
||||
for (auto& validator : m_validators) {
|
||||
validator->init();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool finalizeAllValidators(QNetworkReply& reply)
|
||||
void writeAllValidators(const QByteArray& data)
|
||||
{
|
||||
for (auto& validator : validators) {
|
||||
if (!validator->validate(reply))
|
||||
return false;
|
||||
for (auto& validator : m_validators) {
|
||||
validator->write(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool failAllValidators()
|
||||
void failAllValidators()
|
||||
{
|
||||
bool success = true;
|
||||
for (auto& validator : validators) {
|
||||
success &= validator->abort();
|
||||
for (auto& validator : m_validators) {
|
||||
validator->abort();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
bool writeAllValidators(QByteArray& data)
|
||||
Validator::Result finalizeAllValidators()
|
||||
{
|
||||
for (auto& validator : validators) {
|
||||
if (!validator->write(data))
|
||||
return false;
|
||||
for (auto& validator : m_validators) {
|
||||
auto result = validator->validate();
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return {};
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::shared_ptr<Validator>> validators;
|
||||
QString m_fail_reason;
|
||||
std::vector<std::shared_ptr<Validator>> m_validators;
|
||||
};
|
||||
} // namespace Net
|
||||
@@ -35,17 +35,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <expected>
|
||||
|
||||
namespace Net {
|
||||
class Validator {
|
||||
public: /* con/des */
|
||||
Validator() {}
|
||||
virtual ~Validator() {}
|
||||
Validator() = default;
|
||||
virtual ~Validator() = default;
|
||||
using Error = QString;
|
||||
using Result = std::expected<void, Error>;
|
||||
|
||||
public: /* methods */
|
||||
virtual bool init(QNetworkRequest& request) = 0;
|
||||
virtual bool write(QByteArray& data) = 0;
|
||||
virtual bool abort() = 0;
|
||||
virtual bool validate(QNetworkReply& reply) = 0;
|
||||
virtual void init() = 0;
|
||||
virtual void write(const QByteArray& data) = 0;
|
||||
virtual void abort() = 0;
|
||||
virtual Result validate() = 0;
|
||||
};
|
||||
} // namespace Net
|
||||
Reference in new issue
Block a user