aboutsummaryrefslogtreecommitdiff
path: root/lib/jobs/basejob.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jobs/basejob.h')
-rw-r--r--lib/jobs/basejob.h99
1 files changed, 65 insertions, 34 deletions
diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h
index fd7beca0..6c1b802c 100644
--- a/lib/jobs/basejob.h
+++ b/lib/jobs/basejob.h
@@ -24,6 +24,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QObject>
#include <QtCore/QUrlQuery>
+#include <QtCore/QMetaEnum>
class QNetworkReply;
class QSslError;
@@ -33,28 +34,29 @@ class ConnectionData;
enum class HttpVerb { Get, Put, Post, Delete };
-struct JobTimeoutConfig {
- int jobTimeout;
- int nextRetryInterval;
-};
-
class BaseJob : public QObject {
Q_OBJECT
Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT)
Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries)
public:
+ /*! The status code of a job
+ *
+ * Every job is created in Unprepared status; upon calling prepare()
+ * from Connection (if things are fine) it go to Pending status. After
+ * that, the next transition comes after the reply arrives and its contents
+ * are analysed. At any point in time the job can be abandon()ed, causing
+ * it to switch to status Abandoned for a brief period before deletion.
+ */
enum StatusCode {
- NoError = 0 // To be compatible with Qt conventions
- ,
Success = 0,
+ NoError = Success, // To be compatible with Qt conventions
Pending = 1,
- WarningLevel = 20,
+ WarningLevel = 20, //< Warnings have codes starting from this
UnexpectedResponseType = 21,
UnexpectedResponseTypeWarning = UnexpectedResponseType,
- Abandoned = 50 //< A tiny period between abandoning and object deletion
- ,
- ErrorLevel = 100 //< Errors have codes starting from this
- ,
+ 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,
Timeout,
TimeoutError = Timeout,
@@ -64,10 +66,11 @@ public:
IncorrectRequestError = IncorrectRequest,
IncorrectResponse,
IncorrectResponseError = IncorrectResponse,
- JsonParseError //< deprecated; Use IncorrectResponse instead
+ JsonParseError //< \deprecated Use IncorrectResponse instead
= IncorrectResponse,
TooManyRequests,
TooManyRequestsError = TooManyRequests,
+ RateLimited = TooManyRequests,
RequestNotImplemented,
RequestNotImplementedError = RequestNotImplemented,
UnsupportedRoomVersion,
@@ -80,6 +83,7 @@ public:
UserDeactivated,
UserDefinedError = 256
};
+ Q_ENUM(StatusCode)
/**
* A simple wrapper around QUrlQuery that allows its creation from
@@ -97,7 +101,7 @@ public:
using Data = RequestData;
- /**
+ /*!
* This structure stores the status of a server call job. The status
* consists of a code, that is described (but not delimited) by the
* respective enum, and a freeform message.
@@ -106,16 +110,16 @@ public:
* along the lines of StatusCode, with additional values
* starting at UserDefinedError
*/
- class Status {
- 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 = {});
bool good() const { return code < ErrorLevel; }
- friend QDebug operator<<(QDebug dbg, const Status& s)
+ QDebug dumpToLog(QDebug dbg) const;
+ friend QDebug operator<<(const QDebug& dbg, const Status& s)
{
- QDebugStateSaver _s(dbg);
- return dbg.noquote().nospace() << s.code << ": " << s.message;
+ return s.dumpToLog(dbg);
}
bool operator==(const Status& other) const
@@ -131,8 +135,6 @@ public:
QString message;
};
- using duration_t = int; // milliseconds
-
public:
BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
bool needsToken = true);
@@ -144,13 +146,16 @@ public:
/** Current status of the job */
Status status() const;
+
/** Short human-friendly message on the job status */
QString statusCaption() const;
+
/** Get raw response body as received from the server
* \param bytesAtMost return this number of leftmost bytes, or -1
* to return the entire response
*/
QByteArray rawData(int bytesAtMost = -1) const;
+
/** Get UI-friendly sample of raw data
*
* This is almost the same as rawData but appends the "truncated"
@@ -166,17 +171,24 @@ public:
* \sa status
*/
int error() const;
+
/** Error-specific message, as returned by the server */
virtual QString errorString() const;
+
/** A URL to help/clarify the error, if provided by the server */
QUrl errorUrl() const;
int maxRetries() const;
void setMaxRetries(int newMaxRetries);
- Q_INVOKABLE duration_t getCurrentTimeout() const;
- Q_INVOKABLE duration_t getNextRetryInterval() const;
- Q_INVOKABLE duration_t millisToRetry() const;
+ using duration_ms_t = std::chrono::milliseconds::rep; // normally int64_t
+
+ std::chrono::seconds getCurrentTimeout() const;
+ Q_INVOKABLE duration_ms_t getCurrentTimeoutMs() const;
+ std::chrono::seconds getNextRetryInterval() const;
+ Q_INVOKABLE duration_ms_t getNextRetryMs() const;
+ std::chrono::milliseconds timeToRetry() const;
+ Q_INVOKABLE duration_ms_t millisToRetry() const;
friend QDebug operator<<(QDebug dbg, const BaseJob* j)
{
@@ -184,7 +196,7 @@ public:
}
public slots:
- void start(const ConnectionData* connData, bool inBackground = false);
+ void prepare(ConnectionData* connData, bool inBackground);
/**
* Abandons the result of this job, arrived or unarrived.
@@ -197,10 +209,10 @@ public slots:
signals:
/** The job is about to send a network request */
- void aboutToStart();
+ void aboutToSendRequest();
/** The job has sent a network request */
- void started();
+ void sentRequest();
/** The job has changed its status */
void statusChanged(Status newStatus);
@@ -213,7 +225,14 @@ signals:
* @param inMilliseconds the interval after which the next attempt will be
* taken
*/
- void retryScheduled(int nextAttempt, int inMilliseconds);
+ void retryScheduled(int nextAttempt, duration_ms_t inMilliseconds);
+
+ /**
+ * The previous network request has been rate-limited; the next attempt
+ * will be queued and run sometime later. Since other jobs may already
+ * wait in the queue, it's not possible to predict the wait time.
+ */
+ void rateLimited();
/**
* Emitted when the job is finished, in any case. It is used to notify
@@ -288,9 +307,20 @@ protected:
static QUrl makeRequestUrl(QUrl baseUrl, const QString& path,
const QUrlQuery& query = {});
- virtual void beforeStart(const ConnectionData* connData);
- virtual void afterStart(const ConnectionData* connData,
- QNetworkReply* reply);
+ /*! Prepares the job for execution
+ *
+ * This method is called no more than once per job lifecycle,
+ * when it's first scheduled for execution; in particular, it is not called
+ * on retries.
+ */
+ virtual void doPrepare();
+ /*! Postprocessing after the network request has been sent
+ *
+ * This method is called every time the job receives a running
+ * QNetworkReply object from NetworkAccessManager - basically, after
+ * successfully sending a network request (including retries).
+ */
+ virtual void onSentRequest(QNetworkReply*);
virtual void beforeAbandon(QNetworkReply*);
/**
@@ -332,8 +362,7 @@ protected:
* @param reply the HTTP reply from the server
* @param errorJson the JSON payload describing the error
*/
- virtual Status parseError(QNetworkReply* reply,
- const QJsonObject& errorJson);
+ virtual Status parseError(QNetworkReply*, const QJsonObject& errorJson);
void setStatus(Status s);
void setStatus(int code, QString message);
@@ -350,10 +379,12 @@ protected slots:
void timeout();
private slots:
- void sendRequest(bool inBackground);
+ void sendRequest();
void checkReply();
void gotReply();
+ friend class ConnectionData; // to provide access to sendRequest()
+
private:
void stop();
void finishJob();