aboutsummaryrefslogtreecommitdiff
path: root/lib/connectiondata.cpp
diff options
context:
space:
mode:
authorn-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
committern-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
commitd911b207f49e936b3e992200796110f0749ed150 (patch)
tree96d20ebb4d074a4c1755e21cb316a52d584daee3 /lib/connectiondata.cpp
parent8ad8a74152c5701b6ca1f9a00487ba9257a439b4 (diff)
parent56c2f2e2b809b9077393eb617828f33d144f5634 (diff)
downloadlibquotient-d911b207f49e936b3e992200796110f0749ed150.tar.gz
libquotient-d911b207f49e936b3e992200796110f0749ed150.zip
New upstream version 0.7.0
Diffstat (limited to 'lib/connectiondata.cpp')
-rw-r--r--lib/connectiondata.cpp142
1 files changed, 94 insertions, 48 deletions
diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp
index eb516ef7..aca218be 100644
--- a/lib/connectiondata.cpp
+++ b/lib/connectiondata.cpp
@@ -1,57 +1,107 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "connectiondata.h"
-#include "networkaccessmanager.h"
#include "logging.h"
+#include "networkaccessmanager.h"
+#include "jobs/basejob.h"
-using namespace QMatrixClient;
+#include <QtCore/QTimer>
+#include <QtCore/QPointer>
-struct ConnectionData::Private
-{
- explicit Private(const QUrl& url) : baseUrl(url) { }
+#include <array>
+#include <queue>
+
+using namespace Quotient;
+
+class ConnectionData::Private {
+public:
+ explicit Private(QUrl url) : baseUrl(std::move(url))
+ {
+ rateLimiter.setSingleShot(true);
+ }
QUrl baseUrl;
QByteArray accessToken;
QString lastEvent;
+ QString userId;
QString deviceId;
+ std::vector<QString> needToken;
mutable unsigned int txnCounter = 0;
- const qint64 id = QDateTime::currentMSecsSinceEpoch();
+ const qint64 txnBase = QDateTime::currentMSecsSinceEpoch();
+
+ QString id() const { return userId + '/' + deviceId; }
+
+ using job_queue_t = std::queue<QPointer<BaseJob>>;
+ std::array<job_queue_t, 2> jobs; // 0 - foreground, 1 - background
+ QTimer rateLimiter;
};
ConnectionData::ConnectionData(QUrl baseUrl)
- : d(std::make_unique<Private>(baseUrl))
-{ }
+ : d(makeImpl<Private>(std::move(baseUrl)))
+{
+ // Each lambda invocation below takes no more than one job from the
+ // queues (first foreground, then background) and resumes it; then
+ // restarts the rate limiter timer with duration 0, effectively yielding
+ // to the event loop and then resuming until both queues are empty.
+ QObject::connect(&d->rateLimiter, &QTimer::timeout, [this] {
+ // TODO: Consider moving out all job->sendRequest() invocations to
+ // a dedicated thread
+ d->rateLimiter.setInterval(0);
+ for (auto& q : d->jobs)
+ while (!q.empty()) {
+ const auto job = q.front();
+ q.pop();
+ if (!job || job->error() == BaseJob::Abandoned)
+ continue;
+ if (job->error() != BaseJob::Pending) {
+ qCCritical(MAIN)
+ << "Job" << job
+ << "is in the wrong status:" << job->status();
+ Q_ASSERT(false);
+ job->setStatus(BaseJob::Pending);
+ }
+ job->sendRequest();
+ d->rateLimiter.start();
+ return;
+ }
+ qCDebug(MAIN) << d->id() << "job queues are empty";
+ });
+}
-ConnectionData::~ConnectionData() = default;
+ConnectionData::~ConnectionData()
+{
+ d->rateLimiter.disconnect();
+ d->rateLimiter.stop();
+}
-QByteArray ConnectionData::accessToken() const
+void ConnectionData::submit(BaseJob* job)
{
- return d->accessToken;
+ job->setStatus(BaseJob::Pending);
+ if (!d->rateLimiter.isActive()) {
+ QTimer::singleShot(0, job, &BaseJob::sendRequest);
+ return;
+ }
+ d->jobs[size_t(job->isBackground())].emplace(job);
+ qCDebug(MAIN) << job << "queued," << d->jobs.front().size() << "+"
+ << d->jobs.back().size() << "total jobs in" << d->id()
+ << "queues";
}
-QUrl ConnectionData::baseUrl() const
+void ConnectionData::limitRate(std::chrono::milliseconds nextCallAfter)
{
- return d->baseUrl;
+ qCDebug(MAIN) << "Jobs for" << (d->userId + "/" + d->deviceId)
+ << "suspended for" << nextCallAfter.count() << "ms";
+ d->rateLimiter.start(nextCallAfter);
}
+QByteArray ConnectionData::accessToken() const { return d->accessToken; }
+
+QUrl ConnectionData::baseUrl() const { return d->baseUrl; }
+
QNetworkAccessManager* ConnectionData::nam() const
{
return NetworkAccessManager::instance();
@@ -68,41 +118,37 @@ void ConnectionData::setToken(QByteArray token)
d->accessToken = std::move(token);
}
-void ConnectionData::setHost(QString host)
-{
- d->baseUrl.setHost(host);
- qCDebug(MAIN) << "updated baseUrl to" << d->baseUrl;
-}
+const QString& ConnectionData::deviceId() const { return d->deviceId; }
-void ConnectionData::setPort(int port)
-{
- d->baseUrl.setPort(port);
- qCDebug(MAIN) << "updated baseUrl to" << d->baseUrl;
-}
+const QString& ConnectionData::userId() const { return d->userId; }
-const QString& ConnectionData::deviceId() const
+bool ConnectionData::needsToken(const QString& requestName) const
{
- return d->deviceId;
+ return std::find(d->needToken.cbegin(), d->needToken.cend(), requestName)
+ != d->needToken.cend();
}
void ConnectionData::setDeviceId(const QString& deviceId)
{
d->deviceId = deviceId;
- qCDebug(MAIN) << "updated deviceId to" << d->deviceId;
}
-QString ConnectionData::lastEvent() const
+void ConnectionData::setUserId(const QString& userId) { d->userId = userId; }
+
+void ConnectionData::setNeedsToken(const QString& requestName)
{
- return d->lastEvent;
+ d->needToken.push_back(requestName);
}
+QString ConnectionData::lastEvent() const { return d->lastEvent; }
+
void ConnectionData::setLastEvent(QString identifier)
{
- d->lastEvent = identifier;
+ d->lastEvent = std::move(identifier);
}
QByteArray ConnectionData::generateTxnId() const
{
- return QByteArray::number(d->id) + 'q' +
- QByteArray::number(++d->txnCounter);
+ return d->deviceId.toLatin1() + QByteArray::number(d->txnBase)
+ + QByteArray::number(++d->txnCounter);
}