From e1465f4848d003cab3d3d1288c1a0f3b945083ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 29 Apr 2017 02:24:00 +0200 Subject: NOISSUE refactor NetAction to be based on Task Still missing some things, this is part 1. --- api/logic/net/ByteArraySink.h | 22 +++++------ api/logic/net/Download.cpp | 60 +++++++++++++++--------------- api/logic/net/Download.h | 4 +- api/logic/net/FileSink.cpp | 36 +++++++++--------- api/logic/net/FileSink.h | 12 +++--- api/logic/net/MetaCacheSink.cpp | 10 ++--- api/logic/net/MetaCacheSink.h | 4 +- api/logic/net/NetAction.h | 68 ++++------------------------------ api/logic/net/NetJob.cpp | 82 ++++++++++++++++++++++++++++++----------- api/logic/net/NetJob.h | 53 +++++++++----------------- api/logic/net/PasteUpload.cpp | 2 +- api/logic/net/Sink.h | 8 ++-- 12 files changed, 165 insertions(+), 196 deletions(-) (limited to 'api/logic/net') diff --git a/api/logic/net/ByteArraySink.h b/api/logic/net/ByteArraySink.h index 03b77fcc..c81b952b 100644 --- a/api/logic/net/ByteArraySink.h +++ b/api/logic/net/ByteArraySink.h @@ -21,34 +21,34 @@ public: } public: - JobStatus init(QNetworkRequest & request) override + Task::Status init(QNetworkRequest & request) override { m_output->clear(); if(initAllValidators(request)) - return Job_InProgress; - return Job_Failed; + return Task::Status::InProgress; + return Task::Status::Failed; }; - JobStatus write(QByteArray & data) override + Task::Status write(QByteArray & data) override { m_output->append(data); if(writeAllValidators(data)) - return Job_InProgress; - return Job_Failed; + return Task::Status::InProgress; + return Task::Status::Failed; } - JobStatus abort() override + Task::Status abort() override { m_output->clear(); failAllValidators(); - return Job_Failed; + return Task::Status::Failed; } - JobStatus finalize(QNetworkReply &reply) override + Task::Status finalize(QNetworkReply &reply) override { if(finalizeAllValidators(reply)) - return Job_Finished; - return Job_Failed; + return Task::Status::Finished; + return Task::Status::Failed; } bool hasLocalData() override diff --git a/api/logic/net/Download.cpp b/api/logic/net/Download.cpp index ee872e33..b346d5e0 100644 --- a/api/logic/net/Download.cpp +++ b/api/logic/net/Download.cpp @@ -28,7 +28,7 @@ namespace Net { Download::Download():NetAction() { - m_status = Job_NotStarted; + m_status = Status::NotStarted; } Download::Ptr Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) @@ -66,30 +66,30 @@ void Download::addValidator(Validator * v) m_sink->addValidator(v); } -void Download::start() +void Download::executeTask() { - if(m_status == Job_Aborted) + if(m_status == Status::Aborted) { qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); - emit aborted(m_index_within_job); + emit aborted(); return; } QNetworkRequest request(m_url); m_status = m_sink->init(request); switch(m_status) { - case Job_Finished: - emit succeeded(m_index_within_job); + case Status::Finished: + emit succeeded(); qDebug() << "Download cache hit " << m_url.toString(); return; - case Job_InProgress: + case Status::InProgress: qDebug() << "Downloading " << m_url.toString(); break; - case Job_NotStarted: - case Job_Failed: - emit failed(m_index_within_job); + case Status::NotStarted: + case Status::Failed: + emit failed(); return; - case Job_Aborted: + case Status::Aborted: return; } @@ -106,9 +106,9 @@ void Download::start() void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - m_total_progress = bytesTotal; + m_progressTotal = bytesTotal; m_progress = bytesReceived; - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); + emit progress(bytesReceived, bytesTotal); } void Download::downloadError(QNetworkReply::NetworkError error) @@ -116,7 +116,7 @@ void Download::downloadError(QNetworkReply::NetworkError error) if(error == QNetworkReply::OperationCanceledError) { qCritical() << "Aborted " << m_url.toString(); - m_status = Job_Aborted; + m_status = Status::Aborted; } else { @@ -124,13 +124,13 @@ void Download::downloadError(QNetworkReply::NetworkError error) { if(m_sink->hasLocalData()) { - m_status = Job_Failed_Proceed; + m_status = Status::Failed_Proceed; return; } } // error happened during download. qCritical() << "Failed " << m_url.toString() << " with reason " << error; - m_status = Job_Failed; + m_status = Status::Failed; } } @@ -172,28 +172,28 @@ void Download::downloadFinished() } // if the download failed before this point ... - if (m_status == Job_Failed_Proceed) + if (m_status == Status::Failed_Proceed) { qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit succeeded(m_index_within_job); + emit succeeded(); return; } - else if (m_status == Job_Failed) + else if (m_status == Status::Failed) { qDebug() << "Download failed in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit failed(m_index_within_job); + emit failed(); return; } - else if(m_status == Job_Aborted) + else if(m_status == Status::Aborted) { qDebug() << "Download aborted in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit aborted(m_index_within_job); + emit aborted(); return; } @@ -207,26 +207,26 @@ void Download::downloadFinished() // otherwise, finalize the whole graph m_status = m_sink->finalize(*m_reply.get()); - if (m_status != Job_Finished) + if (m_status != Status::Finished) { qDebug() << "Download failed to finalize:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit failed(m_index_within_job); + emit failed(); return; } m_reply.reset(); qDebug() << "Download succeeded:" << m_url.toString(); - emit succeeded(m_index_within_job); + emit succeeded(); } void Download::downloadReadyRead() { - if(m_status == Job_InProgress) + if(m_status == Status::InProgress) { auto data = m_reply->readAll(); m_status = m_sink->write(data); - if(m_status == Job_Failed) + if(m_status == Status::Failed) { qCritical() << "Failed to process response chunk for " << m_target_path; } @@ -234,7 +234,7 @@ void Download::downloadReadyRead() } else { - qCritical() << "Cannot write to " << m_target_path << ", illegal status" << m_status; + qCritical() << "Cannot write to " << m_target_path << ", illegal status" << int(m_status); } } @@ -248,12 +248,12 @@ bool Net::Download::abort() } else { - m_status = Job_Aborted; + m_status = Status::Aborted; } return true; } -bool Net::Download::canAbort() +bool Net::Download::canAbort() const { return true; } diff --git a/api/logic/net/Download.h b/api/logic/net/Download.h index 3347dc96..31e51792 100644 --- a/api/logic/net/Download.h +++ b/api/logic/net/Download.h @@ -50,7 +50,7 @@ public: /* methods */ } void addValidator(Validator * v); bool abort() override; - bool canAbort() override; + bool canAbort() const override; private: /* methods */ bool handleRedirect(); @@ -62,7 +62,7 @@ protected slots: void downloadReadyRead() override; public slots: - void start() override; + void executeTask() override; private: /* data */ // FIXME: remove this, it has no business being here. diff --git a/api/logic/net/FileSink.cpp b/api/logic/net/FileSink.cpp index 7dfc4336..83df6843 100644 --- a/api/logic/net/FileSink.cpp +++ b/api/logic/net/FileSink.cpp @@ -17,10 +17,10 @@ FileSink::~FileSink() // nil }; -JobStatus FileSink::init(QNetworkRequest& request) +Task::Status FileSink::init(QNetworkRequest& request) { auto result = initCache(request); - if(result != Job_InProgress) + if(result != Task::Status::InProgress) { return result; } @@ -28,27 +28,27 @@ JobStatus FileSink::init(QNetworkRequest& request) if (!FS::ensureFilePathExists(m_filename)) { qCritical() << "Could not create folder for " + m_filename; - return Job_Failed; + return Task::Status::Failed; } wroteAnyData = false; m_output_file.reset(new QSaveFile(m_filename)); if (!m_output_file->open(QIODevice::WriteOnly)) { qCritical() << "Could not open " + m_filename + " for writing"; - return Job_Failed; + return Task::Status::Failed; } if(initAllValidators(request)) - return Job_InProgress; - return Job_Failed; + return Task::Status::InProgress; + return Task::Status::Failed; } -JobStatus FileSink::initCache(QNetworkRequest &) +Task::Status FileSink::initCache(QNetworkRequest &) { - return Job_InProgress; + return Task::Status::InProgress; } -JobStatus FileSink::write(QByteArray& data) +Task::Status FileSink::write(QByteArray& data) { if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) { @@ -56,20 +56,20 @@ JobStatus FileSink::write(QByteArray& data) m_output_file->cancelWriting(); m_output_file.reset(); wroteAnyData = false; - return Job_Failed; + return Task::Status::Failed; } wroteAnyData = true; - return Job_InProgress; + return Task::Status::InProgress; } -JobStatus FileSink::abort() +Task::Status FileSink::abort() { m_output_file->cancelWriting(); failAllValidators(); - return Job_Failed; + return Task::Status::Failed; } -JobStatus FileSink::finalize(QNetworkReply& reply) +Task::Status FileSink::finalize(QNetworkReply& reply) { // if we wrote any data to the save file, we try to commit the data to the real file. if (wroteAnyData) @@ -77,13 +77,13 @@ JobStatus FileSink::finalize(QNetworkReply& reply) // ask validators for data consistency // we only do this for actual downloads, not 'your data is still the same' cache hits if(!finalizeAllValidators(reply)) - return Job_Failed; + return Task::Status::Failed; // nothing went wrong... if (!m_output_file->commit()) { qCritical() << "Failed to commit changes to " << m_filename; m_output_file->cancelWriting(); - return Job_Failed; + return Task::Status::Failed; } } // then get rid of the save file @@ -92,9 +92,9 @@ JobStatus FileSink::finalize(QNetworkReply& reply) return finalizeCache(reply); } -JobStatus FileSink::finalizeCache(QNetworkReply &) +Task::Status FileSink::finalizeCache(QNetworkReply &) { - return Job_Finished; + return Task::Status::Finished; } bool FileSink::hasLocalData() diff --git a/api/logic/net/FileSink.h b/api/logic/net/FileSink.h index 035d5bfd..a7528b5d 100644 --- a/api/logic/net/FileSink.h +++ b/api/logic/net/FileSink.h @@ -10,15 +10,15 @@ public: /* con/des */ virtual ~FileSink(); public: /* methods */ - JobStatus init(QNetworkRequest & request) override; - JobStatus write(QByteArray & data) override; - JobStatus abort() override; - JobStatus finalize(QNetworkReply & reply) override; + Task::Status init(QNetworkRequest & request) override; + Task::Status write(QByteArray & data) override; + Task::Status abort() override; + Task::Status finalize(QNetworkReply & reply) override; bool hasLocalData() override; protected: /* methods */ - virtual JobStatus initCache(QNetworkRequest &); - virtual JobStatus finalizeCache(QNetworkReply &reply); + virtual Task::Status initCache(QNetworkRequest &); + virtual Task::Status finalizeCache(QNetworkReply &reply); protected: /* data */ QString m_filename; diff --git a/api/logic/net/MetaCacheSink.cpp b/api/logic/net/MetaCacheSink.cpp index c9d75310..4fe4f3fb 100644 --- a/api/logic/net/MetaCacheSink.cpp +++ b/api/logic/net/MetaCacheSink.cpp @@ -17,11 +17,11 @@ MetaCacheSink::~MetaCacheSink() // nil }; -JobStatus MetaCacheSink::initCache(QNetworkRequest& request) +Task::Status MetaCacheSink::initCache(QNetworkRequest& request) { if (!m_entry->isStale()) { - return Job_Finished; + return Task::Status::Finished; } // check if file exists, if it does, use its information for the request QFile current(m_filename); @@ -36,10 +36,10 @@ JobStatus MetaCacheSink::initCache(QNetworkRequest& request) request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); } } - return Job_InProgress; + return Task::Status::InProgress; } -JobStatus MetaCacheSink::finalizeCache(QNetworkReply & reply) +Task::Status MetaCacheSink::finalizeCache(QNetworkReply & reply) { QFileInfo output_file_info(m_filename); if(wroteAnyData) @@ -54,7 +54,7 @@ JobStatus MetaCacheSink::finalizeCache(QNetworkReply & reply) m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); m_entry->setStale(false); ENV.metacache()->updateEntry(m_entry); - return Job_Finished; + return Task::Status::Finished; } bool MetaCacheSink::hasLocalData() diff --git a/api/logic/net/MetaCacheSink.h b/api/logic/net/MetaCacheSink.h index 0f3bbdf6..8df03d06 100644 --- a/api/logic/net/MetaCacheSink.h +++ b/api/logic/net/MetaCacheSink.h @@ -12,8 +12,8 @@ public: /* con/des */ bool hasLocalData() override; protected: /* methods */ - JobStatus initCache(QNetworkRequest & request) override; - JobStatus finalizeCache(QNetworkReply & reply) override; + Task::Status initCache(QNetworkRequest & request) override; + Task::Status finalizeCache(QNetworkReply & reply) override; private: /* data */ MetaEntryPtr m_entry; diff --git a/api/logic/net/NetAction.h b/api/logic/net/NetAction.h index a533c317..13966a5c 100644 --- a/api/logic/net/NetAction.h +++ b/api/logic/net/NetAction.h @@ -15,6 +15,8 @@ #pragma once +#include "tasks/Task.h" + #include #include #include @@ -23,82 +25,28 @@ #include "multimc_logic_export.h" -enum JobStatus -{ - Job_NotStarted, - Job_InProgress, - Job_Finished, - Job_Failed, - Job_Aborted, - Job_Failed_Proceed -}; - typedef std::shared_ptr NetActionPtr; -class MULTIMC_LOGIC_EXPORT NetAction : public QObject +class MULTIMC_LOGIC_EXPORT NetAction : public Task { Q_OBJECT protected: - explicit NetAction() : QObject(0) {}; + explicit NetAction(QObject *parent = 0) : Task(parent) {}; public: virtual ~NetAction() {}; public: - virtual qint64 totalProgress() const - { - return m_total_progress; - } - virtual qint64 currentProgress() const - { - return m_progress; - } - virtual qint64 numberOfFailures() const - { - return m_failures; - } - virtual bool abort() - { - return false; - } - virtual bool canAbort() - { - return false; - } - -public: - /// the network reply unique_qobject_ptr m_reply; - - /// source URL QUrl m_url; - - /// The file's status - JobStatus m_status = Job_NotStarted; - - /// index within the parent job - int m_index_within_job = 0; - - qint64 m_progress = 0; - qint64 m_total_progress = 1; - - /// number of failures up to this point - int m_failures = 0; + Status m_status = Status::NotStarted; signals: - void started(int index); - void netActionProgress(int index, qint64 current, qint64 total); - void succeeded(int index); - void failed(int index); - void aborted(int index); + void failed(); + void aborted(); -protected -slots: +protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - -public -slots: - virtual void start() = 0; }; diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp index 275da749..f4fd7245 100644 --- a/api/logic/net/NetJob.cpp +++ b/api/logic/net/NetJob.cpp @@ -18,22 +18,24 @@ #include -void NetJob::partSucceeded(int index) +void NetJob::partSucceeded() { + auto index = m_partsIndex[(NetAction *)QObject::sender()]; // do progress. all slots are 1 in size at least - auto &slot = parts_progress[index]; - partProgress(index, slot.total_progress, slot.total_progress); + auto &slot = m_parts[index]; + setPartProgress(index, slot.total_progress, slot.total_progress); m_doing.remove(index); m_done.insert(index); - downloads[index].get()->disconnect(this); + slot.download->disconnect(this); startMoreParts(); } -void NetJob::partFailed(int index) +void NetJob::partFailed() { + auto index = m_partsIndex[(NetAction *)QObject::sender()]; m_doing.remove(index); - auto &slot = parts_progress[index]; + auto &slot = m_parts[index]; if (slot.failures == 3) { m_failed.insert(index); @@ -43,22 +45,30 @@ void NetJob::partFailed(int index) slot.failures++; m_todo.enqueue(index); } - downloads[index].get()->disconnect(this); + slot.download->disconnect(this); startMoreParts(); } -void NetJob::partAborted(int index) +void NetJob::partAborted() { + auto index = m_partsIndex[(NetAction *)QObject::sender()]; m_aborted = true; m_doing.remove(index); + auto &slot = m_parts[index]; m_failed.insert(index); - downloads[index].get()->disconnect(this); + slot.download->disconnect(this); startMoreParts(); } -void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) +void NetJob::partProgress(qint64 bytesReceived, qint64 bytesTotal) { - auto &slot = parts_progress[index]; + auto index = m_partsIndex[(NetAction *)QObject::sender()]; + setPartProgress(index, bytesReceived, bytesTotal); +} + +void NetJob::setPartProgress(int index, qint64 bytesReceived, qint64 bytesTotal) +{ + auto &slot = m_parts[index]; current_progress -= slot.current_progress; slot.current_progress = bytesReceived; @@ -74,7 +84,7 @@ void NetJob::executeTask() { qDebug() << m_job_name.toLocal8Bit() << " started."; m_running = true; - for (int i = 0; i < downloads.size(); i++) + for (int i = 0; i < m_parts.size(); i++) { m_todo.enqueue(i); } @@ -114,24 +124,28 @@ void NetJob::startMoreParts() return; int doThis = m_todo.dequeue(); m_doing.insert(doThis); - auto part = downloads[doThis]; + auto part = m_parts[doThis].download; // connect signals :D - connect(part.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(part.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(part.get(), SIGNAL(aborted(int)), SLOT(partAborted(int))); - connect(part.get(), SIGNAL(netActionProgress(int, qint64, qint64)), - SLOT(partProgress(int, qint64, qint64))); + connectAction(part.get()); part->start(); } } +void NetJob::connectAction(NetAction* action) +{ + connect(action, &NetAction::succeeded, this, &NetJob::partSucceeded); + connect(action, &NetAction::failed, this, &NetJob::partFailed); + connect(action, &NetAction::aborted, this, &NetJob::partAborted); + connect(action, &NetAction::progress, this, &NetJob::partProgress); +} + QStringList NetJob::getFailedFiles() { QStringList failed; for (auto index: m_failed) { - failed.push_back(downloads[index]->m_url.toString()); + failed.push_back(m_parts[index].download->m_url.toString()); } failed.sort(); return failed; @@ -143,13 +157,13 @@ bool NetJob::canAbort() const // can abort the waiting? for(auto index: m_todo) { - auto part = downloads[index]; + auto part = m_parts[index].download; canFullyAbort &= part->canAbort(); } // can abort the active? for(auto index: m_doing) { - auto part = downloads[index]; + auto part = m_parts[index].download; canFullyAbort &= part->canAbort(); } return canFullyAbort; @@ -165,8 +179,32 @@ bool NetJob::abort() auto toKill = m_doing.toList(); for(auto index: toKill) { - auto part = downloads[index]; + auto part = m_parts[index].download; fullyAborted &= part->abort(); } return fullyAborted; } + +void NetJob::addNetAction(NetActionPtr action) +{ + m_partsIndex[action.get()] = m_parts.count(); + part_info pi; + { + pi.current_progress = action->getProgress(); + pi.total_progress = action->getTotalProgress(); + pi.failures = 0; + pi.download = action; + } + m_parts.append(pi); + + total_progress += pi.total_progress; + current_progress += pi.current_progress; + + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + setProgress(current_progress, total_progress); + connectAction(action.get()); + action->start(); + } +} diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h index ca4f5df1..d49c8bf7 100644 --- a/api/logic/net/NetJob.h +++ b/api/logic/net/NetJob.h @@ -32,48 +32,26 @@ class MULTIMC_LOGIC_EXPORT NetJob : public Task public: explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {} virtual ~NetJob() {} - bool addNetAction(NetActionPtr action) - { - action->m_index_within_job = downloads.size(); - downloads.append(action); - part_info pi; - { - pi.current_progress = action->currentProgress(); - pi.total_progress = action->totalProgress(); - pi.failures = action->numberOfFailures(); - } - parts_progress.append(pi); - total_progress += pi.total_progress; - // if this is already running, the action needs to be started right away! - if (isRunning()) - { - setProgress(current_progress, total_progress); - connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), - SLOT(partProgress(int, qint64, qint64))); - action->start(); - } - return true; - } + + void addNetAction(NetActionPtr action); NetActionPtr operator[](int index) { - return downloads[index]; + return m_parts[index].download; } const NetActionPtr at(const int index) { - return downloads.at(index); + return m_parts[index].download; } NetActionPtr first() { - if (downloads.size()) - return downloads[0]; + if (m_parts.size()) + return m_parts[0].download; return NetActionPtr(); } int size() const { - return downloads.size(); + return m_parts.size(); } virtual bool isRunning() const override { @@ -91,22 +69,27 @@ public slots: virtual bool abort() override; private slots: - void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal); - void partSucceeded(int index); - void partFailed(int index); - void partAborted(int index); + void partProgress(qint64 bytesReceived, qint64 bytesTotal); + void partSucceeded(); + void partFailed(); + void partAborted(); + +private: + void setPartProgress(int index, qint64 bytesReceived, qint64 bytesTotal); + void connectAction(NetAction * action); private: struct part_info { + NetActionPtr download; qint64 current_progress = 0; qint64 total_progress = 1; int failures = 0; bool connected = false; }; QString m_job_name; - QList downloads; - QList parts_progress; + QList m_parts; + QMap m_partsIndex; QQueue m_todo; QSet m_doing; QSet m_done; diff --git a/api/logic/net/PasteUpload.cpp b/api/logic/net/PasteUpload.cpp index 59779b2c..17b59e6b 100644 --- a/api/logic/net/PasteUpload.cpp +++ b/api/logic/net/PasteUpload.cpp @@ -39,7 +39,7 @@ void PasteUpload::executeTask() QNetworkReply *rep = ENV.qnam().post(request, buf); m_reply = std::shared_ptr(rep); - setStatus(tr("Uploading to paste.ee")); + setStatusText(tr("Uploading to paste.ee")); connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); diff --git a/api/logic/net/Sink.h b/api/logic/net/Sink.h index de9b1722..28a54ef7 100644 --- a/api/logic/net/Sink.h +++ b/api/logic/net/Sink.h @@ -13,10 +13,10 @@ public: /* con/des */ virtual ~Sink() {}; public: /* methods */ - virtual JobStatus init(QNetworkRequest & request) = 0; - virtual JobStatus write(QByteArray & data) = 0; - virtual JobStatus abort() = 0; - virtual JobStatus finalize(QNetworkReply & reply) = 0; + virtual Task::Status init(QNetworkRequest & request) = 0; + virtual Task::Status write(QByteArray & data) = 0; + virtual Task::Status abort() = 0; + virtual Task::Status finalize(QNetworkReply & reply) = 0; virtual bool hasLocalData() = 0; void addValidator(Validator * validator) -- cgit v1.2.3