diff options
Diffstat (limited to 'jobs/basejob.cpp')
-rw-r--r-- | jobs/basejob.cpp | 170 |
1 files changed, 119 insertions, 51 deletions
diff --git a/jobs/basejob.cpp b/jobs/basejob.cpp index 50c85048..e0dff287 100644 --- a/jobs/basejob.cpp +++ b/jobs/basejob.cpp @@ -19,48 +19,56 @@ #include "basejob.h" #include <QtNetwork/QNetworkAccessManager> -#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkRequest> +#include <QtNetwork/QNetworkReply> +#include <QtNetwork/QSslError> #include <QtCore/QTimer> #include "../connectiondata.h" using namespace QMatrixClient; +struct NetworkReplyDeleter : public QScopedPointerDeleteLater +{ + static inline void cleanup(QNetworkReply* reply) + { + if (reply && reply->isRunning()) + reply->abort(); + QScopedPointerDeleteLater::cleanup(reply); + } +}; + class BaseJob::Private { public: Private(ConnectionData* c, JobHttpType t, bool nt) - : connection(c), reply(nullptr), type(t), needsToken(nt) {} + : connection(c), type(t), needsToken(nt) + , reply(nullptr), status(NoError) + {} ConnectionData* connection; - QNetworkReply* reply; JobHttpType type; bool needsToken; + + QScopedPointer<QNetworkReply, NetworkReplyDeleter> reply; + Status status; }; +inline QDebug operator<<(QDebug dbg, BaseJob* j) +{ + return dbg << "Job" << j->objectName(); +} + BaseJob::BaseJob(ConnectionData* connection, JobHttpType type, QString name, bool needsToken) : d(new Private(connection, type, needsToken)) { - // Work around KJob inability to separate success and failure signals - connect(this, &BaseJob::result, [this]() { - if (error() == NoError) - emit success(this); - else - emit failure(this); - }); setObjectName(name); + qDebug() << this << "created"; } BaseJob::~BaseJob() { - if( d->reply ) - { - if( d->reply->isRunning() ) - d->reply->abort(); - d->reply->deleteLater(); - } - delete d; + qDebug() << this << "destroyed"; } ConnectionData* BaseJob::connection() const @@ -78,10 +86,6 @@ QUrlQuery BaseJob::query() const return QUrlQuery(); } -void BaseJob::parseJson(const QJsonDocument& data) -{ -} - void BaseJob::start() { QUrl url = d->connection->baseUrl(); @@ -100,63 +104,127 @@ void BaseJob::start() switch( d->type ) { case JobHttpType::GetJob: - d->reply = d->connection->nam()->get(req); + d->reply.reset( d->connection->nam()->get(req) ); break; case JobHttpType::PostJob: - d->reply = d->connection->nam()->post(req, data.toJson()); + d->reply.reset( d->connection->nam()->post(req, data.toJson()) ); break; case JobHttpType::PutJob: - d->reply = d->connection->nam()->put(req, data.toJson()); + d->reply.reset( d->connection->nam()->put(req, data.toJson()) ); break; } - connect( d->reply, &QNetworkReply::sslErrors, this, &BaseJob::sslErrors ); - connect( d->reply, &QNetworkReply::finished, this, &BaseJob::gotReply ); + connect( d->reply.data(), &QNetworkReply::sslErrors, this, &BaseJob::sslErrors ); + connect( d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply ); QTimer::singleShot( 120*1000, this, SLOT(timeout()) ); // connect( d->reply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), // this, &BaseJob::networkError ); // http://doc.qt.io/qt-5/qnetworkreply.html#error-1 } -void BaseJob::fail(int errorCode, QString errorString) +void BaseJob::gotReply() { - setError( errorCode ); - setErrorText( errorString ); - if( d->reply->isRunning() ) - d->reply->abort(); - qWarning() << "Job" << objectName() << "failed:" << errorString; - emitResult(); + setStatus(checkReply(d->reply.data())); + if (status().good()) + setStatus(parseReply(d->reply->readAll())); + + finishJob(true); } -QNetworkReply* BaseJob::networkReply() const +BaseJob::Status BaseJob::checkReply(QNetworkReply* reply) const { - return d->reply; + switch( reply->error() ) + { + case QNetworkReply::NoError: + return NoError; + + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + return { ContentAccessError, reply->errorString() }; + + default: + return { NetworkError, reply->errorString() }; + } } -// void BaseJob::networkError(QNetworkReply::NetworkError code) -// { -// fail( KJob::UserDefinedError+1, d->reply->errorString() ); -// } +BaseJob::Status BaseJob::parseReply(QByteArray data) +{ + QJsonParseError error; + QJsonDocument json = QJsonDocument::fromJson(data, &error); + if( error.error == QJsonParseError::NoError ) + return parseJson(json); + else + return { JsonParseError, error.errorString() }; +} -void BaseJob::gotReply() +BaseJob::Status BaseJob::parseJson(const QJsonDocument&) +{ + return Success; +} + +void BaseJob::finishJob(bool emitResult) { - if( d->reply->error() != QNetworkReply::NoError ) + if (!d->reply) { - qDebug() << "NetworkError:" << d->reply->error(); - fail( NetworkError, d->reply->errorString() ); - return; + qWarning() << this << "finishes with empty network reply"; } - QJsonParseError error; - QJsonDocument data = QJsonDocument::fromJson(d->reply->readAll(), &error); - if( error.error != QJsonParseError::NoError ) + else if (d->reply->isRunning()) + { + qWarning() << this << "finishes without ready network reply"; + d->reply->disconnect(this); // Ignore whatever comes from the reply + } + + // Notify those that are interested in any completion of the job (including killing) + emit finished(this); + + if (emitResult) { + emit result(this); + if (error()) + emit failure(this); + else + emit success(this); + } + + deleteLater(); +} + +BaseJob::Status BaseJob::status() const +{ + return d->status; +} + +int BaseJob::error() const +{ + return d->status.code; +} + +QString BaseJob::errorString() const +{ + return d->status.message; +} + +void BaseJob::setStatus(Status s) +{ + d->status = s; + if (!s.good()) { - fail( JsonParseError, error.errorString() ); - return; + qWarning() << this << "status" << s.code << ":" << s.message; } - parseJson(data); +} + +void BaseJob::setStatus(int code, QString message) +{ + setStatus({ code, message }); +} + +void BaseJob::abandon() +{ + finishJob(false); } void BaseJob::timeout() { - fail( TimeoutError, "The job has timed out" ); + setStatus( TimeoutError, "The job has timed out" ); + finishJob(true); } void BaseJob::sslErrors(const QList<QSslError>& errors) |