aboutsummaryrefslogtreecommitdiff
path: root/lib/jobs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jobs')
-rw-r--r--lib/jobs/basejob.cpp90
-rw-r--r--lib/jobs/basejob.h32
-rw-r--r--lib/jobs/downloadfilejob.h2
-rw-r--r--lib/jobs/requestdata.cpp5
4 files changed, 79 insertions, 50 deletions
diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp
index 13e65188..68adeaf6 100644
--- a/lib/jobs/basejob.cpp
+++ b/lib/jobs/basejob.cpp
@@ -138,8 +138,7 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
setObjectName(name);
connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout);
connect(&d->retryTimer, &QTimer::timeout, this, [this] {
- setStatus(Pending);
- sendRequest();
+ d->connection->submit(this);
});
}
@@ -226,8 +225,9 @@ void BaseJob::Private::sendRequest()
requestQuery) };
if (!requestHeaders.contains("Content-Type"))
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- req.setRawHeader("Authorization",
- QByteArray("Bearer ") + connection->accessToken());
+ if (needsToken)
+ req.setRawHeader("Authorization",
+ QByteArray("Bearer ") + connection->accessToken());
req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground);
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
req.setMaximumRedirectsAllowed(10);
@@ -258,14 +258,27 @@ void BaseJob::onSentRequest(QNetworkReply*) {}
void BaseJob::beforeAbandon(QNetworkReply*) {}
-void BaseJob::prepare(ConnectionData* connData, bool inBackground)
+void BaseJob::initiate(ConnectionData* connData, bool inBackground)
{
+ Q_ASSERT(connData != nullptr);
+
d->inBackground = inBackground;
d->connection = connData;
doPrepare();
- if (status().code != Unprepared && status().code != Pending)
+
+ if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put)
+ && d->requestData.source() && !d->requestData.source()->isReadable()) {
+ setStatus(FileError, "Request data not ready");
+ }
+ Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this
+ if (status().code == Unprepared) {
+ d->connection->submit(this);
+ } else {
+ qDebug(d->logCat).noquote()
+ << "Request failed preparation and won't be sent:"
+ << d->dumpRequest();
QTimer::singleShot(0, this, &BaseJob::finishJob);
- setStatus(Pending);
+ }
}
void BaseJob::sendRequest()
@@ -274,6 +287,7 @@ void BaseJob::sendRequest()
return;
Q_ASSERT(d->connection && status().code == Pending);
qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest();
+ d->needsToken |= d->connection->needsToken(objectName());
emit aboutToSendRequest();
d->sendRequest();
Q_ASSERT(d->reply);
@@ -290,7 +304,7 @@ void BaseJob::sendRequest()
onSentRequest(d->reply.data());
emit sentRequest();
} else
- qCWarning(d->logCat).noquote()
+ qCCritical(d->logCat).noquote()
<< "Request could not start:" << d->dumpRequest();
}
@@ -341,32 +355,32 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns)
return false;
}
-BaseJob::Status BaseJob::Status::fromHttpCode(int httpCode, QString msg)
+BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode)
{
+ if (httpCode / 10 == 41) // 41x errors
+ return httpCode == 410 ? IncorrectRequestError : NotFoundError;
+ switch (httpCode) {
+ case 401:
+ return Unauthorised;
+ // clang-format off
+ case 403: case 407: // clang-format on
+ return ContentAccessError;
+ case 404:
+ return NotFoundError;
// clang-format off
- return { [httpCode]() -> StatusCode {
- if (httpCode / 10 == 41) // 41x errors
- return httpCode == 410 ? IncorrectRequestError : NotFoundError;
- switch (httpCode) {
- case 401: case 403: case 407:
- return ContentAccessError;
- case 404:
- return NotFoundError;
- case 400: case 405: case 406: case 426: case 428: case 505:
- case 494: // Unofficial nginx "Request header too large"
- case 497: // Unofficial nginx "HTTP request sent to HTTPS port"
- return IncorrectRequestError;
- case 429:
- return TooManyRequestsError;
- case 501: case 510:
- return RequestNotImplementedError;
- case 511:
- return NetworkAuthRequiredError;
- default:
- return NetworkError;
- }
- }(), std::move(msg) };
- // clang-format on
+ case 400: case 405: case 406: case 426: case 428: case 505: // clang-format on
+ case 494: // Unofficial nginx "Request header too large"
+ case 497: // Unofficial nginx "HTTP request sent to HTTPS port"
+ return IncorrectRequestError;
+ case 429:
+ return TooManyRequestsError;
+ case 501: case 510:
+ return RequestNotImplementedError;
+ case 511:
+ return NetworkAuthRequiredError;
+ default:
+ return NetworkError;
+ }
}
QDebug BaseJob::Status::dumpToLog(QDebug dbg) const
@@ -492,10 +506,18 @@ void BaseJob::finishJob()
stop();
if (error() == TooManyRequests) {
emit rateLimited();
- setStatus(Pending);
d->connection->submit(this);
return;
}
+ if (error() == Unauthorised && !d->needsToken
+ && !d->connection->accessToken().isEmpty()) {
+ // Rerun with access token (extension of the spec while
+ // https://github.com/matrix-org/matrix-doc/issues/701 is pending)
+ d->connection->setNeedsToken(objectName());
+ qCWarning(d->logCat) << this << "re-running with authentication";
+ emit retryScheduled(d->retriesTaken, 0);
+ d->connection->submit(this);
+ }
if ((error() == NetworkError || error() == Timeout)
&& d->retriesTaken < d->maxRetries) {
// TODO: The whole retrying thing should be put to Connection(Manager)
@@ -596,6 +618,8 @@ QString BaseJob::statusCaption() const
return tr("Network problems");
case TimeoutError:
return tr("Request timed out");
+ case Unauthorised:
+ return tr("Unauthorised request");
case ContentAccessError:
return tr("Access error");
case NotFoundError:
diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h
index 6c1b802c..c8046e9e 100644
--- a/lib/jobs/basejob.h
+++ b/lib/jobs/basejob.h
@@ -57,9 +57,10 @@ public:
Unprepared = 25, //< Initial job state is incomplete, hence warning level
Abandoned = 50, //< A tiny period between abandoning and object deletion
ErrorLevel = 100, //< Errors have codes starting from this
- NetworkError = 100,
+ NetworkError = 101,
Timeout,
TimeoutError = Timeout,
+ Unauthorised,
ContentAccessError,
NotFoundError,
IncorrectRequest,
@@ -81,6 +82,7 @@ public:
UserConsentRequiredError = UserConsentRequired,
CannotLeaveRoom,
UserDeactivated,
+ FileError,
UserDefinedError = 256
};
Q_ENUM(StatusCode)
@@ -113,7 +115,12 @@ public:
struct Status {
Status(StatusCode c) : code(c) {}
Status(int c, QString m) : code(c), message(std::move(m)) {}
- static Status fromHttpCode(int httpCode, QString msg = {});
+
+ static StatusCode fromHttpCode(int httpCode);
+ static Status fromHttpCode(int httpCode, QString msg)
+ {
+ return { fromHttpCode(httpCode), std::move(msg) };
+ }
bool good() const { return code < ErrorLevel; }
QDebug dumpToLog(QDebug dbg) const;
@@ -184,11 +191,11 @@ public:
using duration_ms_t = std::chrono::milliseconds::rep; // normally int64_t
std::chrono::seconds getCurrentTimeout() const;
- Q_INVOKABLE duration_ms_t getCurrentTimeoutMs() const;
+ Q_INVOKABLE Quotient::BaseJob::duration_ms_t getCurrentTimeoutMs() const;
std::chrono::seconds getNextRetryInterval() const;
- Q_INVOKABLE duration_ms_t getNextRetryMs() const;
+ Q_INVOKABLE Quotient::BaseJob::duration_ms_t getNextRetryMs() const;
std::chrono::milliseconds timeToRetry() const;
- Q_INVOKABLE duration_ms_t millisToRetry() const;
+ Q_INVOKABLE Quotient::BaseJob::duration_ms_t millisToRetry() const;
friend QDebug operator<<(QDebug dbg, const BaseJob* j)
{
@@ -196,7 +203,7 @@ public:
}
public slots:
- void prepare(ConnectionData* connData, bool inBackground);
+ void initiate(ConnectionData* connData, bool inBackground);
/**
* Abandons the result of this job, arrived or unarrived.
@@ -215,7 +222,7 @@ signals:
void sentRequest();
/** The job has changed its status */
- void statusChanged(Status newStatus);
+ void statusChanged(Quotient::BaseJob::Status newStatus);
/**
* The previous network request has failed; the next attempt will
@@ -225,7 +232,8 @@ signals:
* @param inMilliseconds the interval after which the next attempt will be
* taken
*/
- void retryScheduled(int nextAttempt, duration_ms_t inMilliseconds);
+ void retryScheduled(int nextAttempt,
+ Quotient::BaseJob::duration_ms_t inMilliseconds);
/**
* The previous network request has been rate-limited; the next attempt
@@ -251,7 +259,7 @@ signals:
*
* @see result, success, failure
*/
- void finished(BaseJob* job);
+ void finished(Quotient::BaseJob* job);
/**
* Emitted when the job is finished (except when abandoned).
@@ -262,14 +270,14 @@ signals:
*
* @see success, failure
*/
- void result(BaseJob* job);
+ void result(Quotient::BaseJob* job);
/**
* Emitted together with result() in case there's no error.
*
* @see result, failure
*/
- void success(BaseJob*);
+ void success(Quotient::BaseJob*);
/**
* Emitted together with result() if there's an error.
@@ -277,7 +285,7 @@ signals:
*
* @see result, success
*/
- void failure(BaseJob*);
+ void failure(Quotient::BaseJob*);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h
index b7d2d75b..06dc145c 100644
--- a/lib/jobs/downloadfilejob.h
+++ b/lib/jobs/downloadfilejob.h
@@ -5,8 +5,6 @@
namespace Quotient {
class DownloadFileJob : public GetContentJob {
public:
- enum { FileError = BaseJob::UserDefinedError + 1 };
-
using GetContentJob::makeRequestUrl;
static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri);
diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp
index 0c70f085..cec15954 100644
--- a/lib/jobs/requestdata.cpp
+++ b/lib/jobs/requestdata.cpp
@@ -11,9 +11,8 @@ using namespace Quotient;
auto fromData(const QByteArray& data)
{
auto source = std::make_unique<QBuffer>();
- source->open(QIODevice::WriteOnly);
- source->write(data);
- source->close();
+ source->setData(data);
+ source->open(QIODevice::ReadOnly);
return source;
}