Skip to content

Commit

Permalink
Improved REST API network driver (WLED, Philips Hue etc) (awawa-dev#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev authored and chbartsch committed Nov 29, 2022
1 parent 9a6dd08 commit 4188992
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 200 deletions.
5 changes: 2 additions & 3 deletions sources/leddevice/dev_net/LedDeviceWled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,8 @@ bool LedDeviceWled::powerOn()
{
this->setInError(response.getErrorReason());

// power on simultaneously with Rpi causes timeout
if (_maxRetry > 0 && response.error() &&
(response.getNetworkReplyError() == 99 || response.getNetworkReplyError() == 1 || response.getNetworkReplyError() == 2 || response.getNetworkReplyError() == 5))
// power on simultaneously with Rpi causes timeout
if (_maxRetry > 0 && response.error())
{
if (_currentRetry <= 0)
_currentRetry = _maxRetry + 1;
Expand Down
232 changes: 74 additions & 158 deletions sources/leddevice/dev_net/ProviderRestApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include <QNetworkReply>
#include <QByteArray>
#include <QThread>
#include <QTime>
#include <QTimer>
#include <QCoreApplication>
#include <QElapsedTimer>

Expand All @@ -43,13 +43,6 @@ const int TIMEOUT = (500);

std::unique_ptr<networkHelper> ProviderRestApi::_networkWorker(nullptr);

// Constants
namespace {

const QChar ONE_SLASH = '/';

} //End of constants

ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
:_log(Logger::getInstance("LEDDEVICE"))
, _scheme("http")
Expand Down Expand Up @@ -104,32 +97,8 @@ void ProviderRestApi::appendPath(const QString& path)
}

void ProviderRestApi::appendPath(QString& path, const QString& appendPath) const
{
if (!appendPath.isEmpty() && appendPath != ONE_SLASH)
{
if (path.isEmpty() || path == ONE_SLASH)
{
path.clear();
if (appendPath[0] != ONE_SLASH)
{
path.push_back(ONE_SLASH);
}
}
else if (path[path.size() - 1] == ONE_SLASH && appendPath[0] == ONE_SLASH)
{
path.chop(1);
}
else if (path[path.size() - 1] != ONE_SLASH && appendPath[0] != ONE_SLASH)
{
path.push_back(ONE_SLASH);
}
else
{
// Only one slash.
}

path.append(appendPath);
}
{
path = QUrl(path).resolved(appendPath).toString();
}

void ProviderRestApi::setFragment(const QString& fragment)
Expand All @@ -155,151 +124,99 @@ QUrl ProviderRestApi::getUrl() const
return url;
}

httpResponse ProviderRestApi::put(const QString& body)
httpResponse ProviderRestApi::put(const QUrl& url, const QString& body)
{
return put(getUrl(), body);
return executeOperation(QNetworkAccessManager::PutOperation, url, body);
}

httpResponse ProviderRestApi::put(const QUrl& url, const QString& body)
httpResponse ProviderRestApi::post(const QUrl& url, const QString& body)
{
Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));

// Perform request
QNetworkRequest request(url);

QElapsedTimer networkTimer;

bool networkTimeout = false;
return executeOperation(QNetworkAccessManager::PostOperation, url, body);
}

QNetworkReply* networkReply;
httpResponse ProviderRestApi::get(const QUrl& url)
{
return executeOperation(QNetworkAccessManager::GetOperation, url);
}

QMetaObject::invokeMethod(_networkWorker.get(), "put", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QNetworkReply*, networkReply), Q_ARG(QNetworkRequest, request), Q_ARG(QByteArray, body.toUtf8()));
httpResponse ProviderRestApi::put(const QString& body)
{
return put(getUrl(), body);
}

networkTimer.start();
httpResponse ProviderRestApi::post(const QString& body)
{
return post(getUrl(), body);
}

while (!networkReply->isFinished()) {
QThread::msleep(5);
if (networkTimer.elapsed() >= TIMEOUT) {
networkTimeout = true;
break;
}
}

if (networkTimeout)
{
networkReply->abort();
}
httpResponse ProviderRestApi::get()
{
return get(getUrl());
}

Debug(_log, "PUT exit: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));
httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QString& body)
{
qint64 begin = InternalClock::nowPrecise();

httpResponse response;
QString opCode = (op == QNetworkAccessManager::PutOperation) ? "PUT" :
(op == QNetworkAccessManager::PostOperation) ? "POST" :
(op == QNetworkAccessManager::GetOperation) ? "GET" : "";

if (networkReply->operation() == QNetworkAccessManager::PutOperation)
if (opCode.length() == 0)
{
response = getResponse(networkReply, networkTimeout);
Error(_log, "Unsupported opertion code");
return httpResponse();
}

// Free space.
networkReply->deleteLater();

return response;
}

httpResponse ProviderRestApi::post(const QUrl& url, const QString& body)
{
Debug(_log, "POST: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));

Debug(_log, "%s begin: [%s] [%s]", QSTRING_CSTR(opCode), QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));


// Perform request
QNetworkRequest request(url);
QNetworkReply* networkReply = nullptr;

QElapsedTimer networkTimer;

bool networkTimeout = false;

QNetworkReply* networkReply;

QMetaObject::invokeMethod(_networkWorker.get(), "post", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QNetworkReply*, networkReply), Q_ARG(QNetworkRequest, request), Q_ARG(QByteArray, body.toUtf8()));

networkTimer.start();
request.setOriginatingObject(this);

while (!networkReply->isFinished()) {
QThread::msleep(5);
if (networkTimer.elapsed() >= TIMEOUT) {
networkTimeout = true;
break;
}
}
QMetaObject::invokeMethod(_networkWorker.get(), "executeOperation", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QNetworkReply*, networkReply), Q_ARG(QNetworkAccessManager::Operation, op), Q_ARG(QNetworkRequest, request), Q_ARG(QByteArray, body.toUtf8()));

if (networkTimeout)
{
networkReply->abort();
}

Debug(_log, "POST exit: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));
bool networkTimeout = waitForResult(networkReply);
long long timeTotal = InternalClock::nowPrecise() - begin;

httpResponse response;
Debug(_log, "%s end (%lld ms): [%s] [%s]", QSTRING_CSTR(opCode), timeTotal, QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));

if (networkReply->operation() == QNetworkAccessManager::PostOperation)
{
response = getResponse(networkReply, networkTimeout);
}
httpResponse response = (networkReply->operation() == op) ? getResponse(networkReply, networkTimeout) : httpResponse();

// Free space.
networkReply->deleteLater();

return response;
}

httpResponse ProviderRestApi::get()
bool ProviderRestApi::waitForResult(QNetworkReply* networkReply)
{
return get(getUrl());
}

httpResponse ProviderRestApi::get(const QUrl& url)
{
Debug(_log, "GET: [%s]", QSTRING_CSTR(url.toString()));

// Perform request
QNetworkRequest request(url);

QElapsedTimer networkTimer;

bool networkTimeout = false;

QNetworkReply* networkReply;

QMetaObject::invokeMethod(_networkWorker.get(), "get", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QNetworkReply*, networkReply), Q_ARG(QNetworkRequest, request));

networkTimer.start();

while (!networkReply->isFinished()) {
QThread::msleep(5);
if (networkTimer.elapsed() >= TIMEOUT) {
networkTimeout = true;
break;
}
}

if (networkTimeout)
{
networkReply->abort();
}

Debug(_log, "GET exit: [%s]", QSTRING_CSTR(url.toString()));
bool networkTimeout = false;

httpResponse response;
if (networkReply->operation() == QNetworkAccessManager::GetOperation)
if (!networkReply->isFinished() && networkReply->error() == QNetworkReply::NoError &&
!_resultLocker.try_lock_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(TIMEOUT)) && !networkReply->isFinished())
{
response = getResponse(networkReply, networkTimeout);
networkTimeout = true;
disconnect(networkReply, &QNetworkReply::finished, nullptr, nullptr);
QTimer::singleShot(0, networkReply, &QNetworkReply::abort);
}

return networkTimeout;
}

// Free space.
networkReply->deleteLater();
void ProviderRestApi::aquireResultLock()
{
_resultLocker.tryLock();
}

return response;
void ProviderRestApi::releaseResultLock()
{
_resultLocker.unlock();
}

httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply, bool timeout)
Expand All @@ -309,8 +226,10 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply, bool time
int httpStatusCode = (timeout) ? 408 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
response.setHttpStatusCode(httpStatusCode);


response.setNetworkReplyError(reply->error());
if (timeout)
response.setNetworkReplyError(QNetworkReply::TimeoutError);
else
response.setNetworkReplyError(reply->error());

if (reply->error() == QNetworkReply::NoError)
{
Expand Down Expand Up @@ -411,20 +330,16 @@ networkHelper::~networkHelper()
_networkManager = nullptr;
}


QNetworkReply* networkHelper::get(QNetworkRequest request)
QNetworkReply* networkHelper::executeOperation(QNetworkAccessManager::Operation op, QNetworkRequest request, QByteArray body)
{
return _networkManager->get(request);
}

QNetworkReply* networkHelper::put(QNetworkRequest request, QByteArray body)
{
return _networkManager->put(request, body);
}
ProviderRestApi* parent = static_cast<ProviderRestApi*>(request.originatingObject());
parent->aquireResultLock();

auto ret = (op == QNetworkAccessManager::PutOperation) ? _networkManager->put(request, body):
(op == QNetworkAccessManager::PostOperation) ? _networkManager->post(request, body) : _networkManager->get(request);

QNetworkReply* networkHelper::post(QNetworkRequest request, QByteArray body)
{
return _networkManager->post(request, body);
connect(ret, &QNetworkReply::finished, this, [=](){parent->releaseResultLock();});
return ret;
}

bool httpResponse::error() const
Expand Down Expand Up @@ -474,5 +389,6 @@ QNetworkReply::NetworkError httpResponse::getNetworkReplyError() const

void httpResponse::setNetworkReplyError(const QNetworkReply::NetworkError networkReplyError)
{
_hasError = (networkReplyError != QNetworkReply::NetworkError::NoError);
_networkReplyError = networkReplyError;
}
Loading

0 comments on commit 4188992

Please sign in to comment.