refactor: move Network files from src/common/ to src/common/network/ (#5089)
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
#include "common/network/NetworkCommon.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
std::vector<std::pair<QByteArray, QByteArray>> parseHeaderList(
|
||||
const QString &headerListString)
|
||||
{
|
||||
std::vector<std::pair<QByteArray, QByteArray>> res;
|
||||
|
||||
// Split the string into a list of header pairs
|
||||
// e.g. "Authorization:secretkey;NextHeader:boo" turning into ["Authorization:secretkey","NextHeader:boo"]
|
||||
auto headerPairs = headerListString.split(";");
|
||||
|
||||
for (const auto &headerPair : headerPairs)
|
||||
{
|
||||
const auto headerName =
|
||||
headerPair.section(":", 0, 0).trimmed().toUtf8();
|
||||
const auto headerValue = headerPair.section(":", 1).trimmed().toUtf8();
|
||||
|
||||
if (headerName.isEmpty() || headerValue.isEmpty())
|
||||
{
|
||||
// The header part either didn't contain a : or the name/value was empty
|
||||
// Skip the value
|
||||
continue;
|
||||
}
|
||||
|
||||
res.emplace_back(headerName, headerValue);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkResult;
|
||||
|
||||
using NetworkSuccessCallback = std::function<void(NetworkResult)>;
|
||||
using NetworkErrorCallback = std::function<void(NetworkResult)>;
|
||||
using NetworkFinallyCallback = std::function<void()>;
|
||||
|
||||
enum class NetworkRequestType {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Patch,
|
||||
};
|
||||
|
||||
// parseHeaderList takes a list of headers in string form,
|
||||
// where each header pair is separated by semicolons (;) and the header name and value is divided by a colon (:)
|
||||
//
|
||||
// We return a vector of pairs, where the first value is the header name and the second value is the header value
|
||||
//
|
||||
// e.g. "Authorization:secretkey;NextHeader:boo" will return [{"Authorization", "secretkey"}, {"NextHeader", "boo"}]
|
||||
std::vector<std::pair<QByteArray, QByteArray>> parseHeaderList(
|
||||
const QString &headerListString);
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "common/network/NetworkManager.hpp"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
QThread NetworkManager::workerThread;
|
||||
QNetworkAccessManager NetworkManager::accessManager;
|
||||
|
||||
void NetworkManager::init()
|
||||
{
|
||||
NetworkManager::accessManager.moveToThread(&NetworkManager::workerThread);
|
||||
NetworkManager::workerThread.start();
|
||||
}
|
||||
|
||||
void NetworkManager::deinit()
|
||||
{
|
||||
NetworkManager::workerThread.quit();
|
||||
NetworkManager::workerThread.wait();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QThread>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QThread workerThread;
|
||||
static QNetworkAccessManager accessManager;
|
||||
|
||||
static void init();
|
||||
static void deinit();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "common/network/NetworkPrivate.hpp"
|
||||
|
||||
#include "common/network/NetworkManager.hpp"
|
||||
#include "common/network/NetworkResult.hpp"
|
||||
#include "common/network/NetworkTask.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "util/AbandonObject.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
#include "util/PostToThread.hpp"
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <QCryptographicHash>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr qsizetype SLOW_HTTP_THRESHOLD = 30;
|
||||
#else
|
||||
constexpr qsizetype SLOW_HTTP_THRESHOLD = 90;
|
||||
#endif
|
||||
|
||||
using namespace chatterino::network::detail;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
void runCallback(bool concurrent, auto &&fn)
|
||||
{
|
||||
if (concurrent)
|
||||
{
|
||||
std::ignore = QtConcurrent::run(std::forward<decltype(fn)>(fn));
|
||||
}
|
||||
else
|
||||
{
|
||||
runInGuiThread(std::forward<decltype(fn)>(fn));
|
||||
}
|
||||
}
|
||||
|
||||
void loadUncached(std::shared_ptr<NetworkData> &&data)
|
||||
{
|
||||
DebugCount::increase("http request started");
|
||||
|
||||
NetworkRequester requester;
|
||||
auto *worker = new NetworkTask(std::move(data));
|
||||
|
||||
worker->moveToThread(&NetworkManager::workerThread);
|
||||
|
||||
QObject::connect(&requester, &NetworkRequester::requestUrl, worker,
|
||||
&NetworkTask::run);
|
||||
|
||||
emit requester.requestUrl();
|
||||
}
|
||||
|
||||
void loadCached(std::shared_ptr<NetworkData> &&data)
|
||||
{
|
||||
QFile cachedFile(getPaths()->cacheDirectory() + "/" + data->getHash());
|
||||
|
||||
if (!cachedFile.exists() || !cachedFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
loadUncached(std::move(data));
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX: check if bytes is empty?
|
||||
QByteArray bytes = cachedFile.readAll();
|
||||
|
||||
qCDebug(chatterinoHTTP).noquote() << data->typeString() << "[CACHED] 200"
|
||||
<< data->request.url().toString();
|
||||
|
||||
data->emitSuccess(
|
||||
{NetworkResult::NetworkError::NoError, QVariant(200), bytes});
|
||||
data->emitFinally();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
NetworkData::NetworkData()
|
||||
{
|
||||
DebugCount::increase("NetworkData");
|
||||
}
|
||||
|
||||
NetworkData::~NetworkData()
|
||||
{
|
||||
DebugCount::decrease("NetworkData");
|
||||
}
|
||||
|
||||
QString NetworkData::getHash()
|
||||
{
|
||||
if (this->hash_.isEmpty())
|
||||
{
|
||||
QByteArray bytes;
|
||||
|
||||
bytes.append(this->request.url().toString().toUtf8());
|
||||
|
||||
for (const auto &header : this->request.rawHeaderList())
|
||||
{
|
||||
bytes.append(header);
|
||||
}
|
||||
|
||||
QByteArray hashBytes(
|
||||
QCryptographicHash::hash(bytes, QCryptographicHash::Sha256));
|
||||
|
||||
this->hash_ = hashBytes.toHex();
|
||||
}
|
||||
|
||||
return this->hash_;
|
||||
}
|
||||
|
||||
void NetworkData::emitSuccess(NetworkResult &&result)
|
||||
{
|
||||
if (!this->onSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
runCallback(this->executeConcurrently,
|
||||
[cb = std::move(this->onSuccess), result = std::move(result),
|
||||
url = this->request.url(), hasCaller = this->hasCaller,
|
||||
caller = this->caller]() {
|
||||
if (hasCaller && caller.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
cb(result);
|
||||
if (timer.elapsed() > SLOW_HTTP_THRESHOLD)
|
||||
{
|
||||
qCWarning(chatterinoHTTP)
|
||||
<< "Slow HTTP success handler for" << url.toString()
|
||||
<< timer.elapsed()
|
||||
<< "ms (threshold:" << SLOW_HTTP_THRESHOLD << "ms)";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkData::emitError(NetworkResult &&result)
|
||||
{
|
||||
if (!this->onError)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
runCallback(this->executeConcurrently,
|
||||
[cb = std::move(this->onError), result = std::move(result),
|
||||
hasCaller = this->hasCaller, caller = this->caller]() {
|
||||
if (hasCaller && caller.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cb(result);
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkData::emitFinally()
|
||||
{
|
||||
if (!this->finally)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
runCallback(this->executeConcurrently,
|
||||
[cb = std::move(this->finally), hasCaller = this->hasCaller,
|
||||
caller = this->caller]() {
|
||||
if (hasCaller && caller.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
QLatin1String NetworkData::typeString() const
|
||||
{
|
||||
auto view = magic_enum::enum_name<NetworkRequestType>(this->requestType);
|
||||
return QLatin1String{view.data(),
|
||||
static_cast<QLatin1String::size_type>(view.size())};
|
||||
}
|
||||
|
||||
void load(std::shared_ptr<NetworkData> &&data)
|
||||
{
|
||||
if (data->cache)
|
||||
{
|
||||
std::ignore = QtConcurrent::run([data = std::move(data)]() mutable {
|
||||
loadCached(std::move(data));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
loadUncached(std::move(data));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Common.hpp"
|
||||
#include "common/network/NetworkCommon.hpp"
|
||||
|
||||
#include <QHttpMultiPart>
|
||||
#include <QNetworkRequest>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkResult;
|
||||
|
||||
class NetworkRequester : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void requestUrl();
|
||||
};
|
||||
|
||||
class NetworkData
|
||||
{
|
||||
public:
|
||||
NetworkData();
|
||||
~NetworkData();
|
||||
NetworkData(const NetworkData &) = delete;
|
||||
NetworkData(NetworkData &&) = delete;
|
||||
NetworkData &operator=(const NetworkData &) = delete;
|
||||
NetworkData &operator=(NetworkData &&) = delete;
|
||||
|
||||
QNetworkRequest request;
|
||||
bool hasCaller{};
|
||||
QPointer<QObject> caller;
|
||||
bool cache{};
|
||||
bool executeConcurrently{};
|
||||
|
||||
NetworkSuccessCallback onSuccess;
|
||||
NetworkErrorCallback onError;
|
||||
NetworkFinallyCallback finally;
|
||||
|
||||
NetworkRequestType requestType = NetworkRequestType::Get;
|
||||
|
||||
QByteArray payload;
|
||||
std::unique_ptr<QHttpMultiPart, DeleteLater> multiPartPayload;
|
||||
|
||||
/// By default, there's no explicit timeout for the request.
|
||||
/// To set a timeout, use NetworkRequest's timeout method
|
||||
std::optional<std::chrono::milliseconds> timeout{};
|
||||
|
||||
QString getHash();
|
||||
|
||||
void emitSuccess(NetworkResult &&result);
|
||||
void emitError(NetworkResult &&result);
|
||||
void emitFinally();
|
||||
|
||||
QLatin1String typeString() const;
|
||||
|
||||
private:
|
||||
QString hash_;
|
||||
};
|
||||
|
||||
void load(std::shared_ptr<NetworkData> &&data);
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "common/network/NetworkRequest.hpp"
|
||||
|
||||
#include "common/network/NetworkPrivate.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "common/Version.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
NetworkRequest::NetworkRequest(const std::string &url,
|
||||
NetworkRequestType requestType)
|
||||
: data(new NetworkData)
|
||||
{
|
||||
this->data->request.setUrl(QUrl(QString::fromStdString(url)));
|
||||
this->data->requestType = requestType;
|
||||
|
||||
this->initializeDefaultValues();
|
||||
}
|
||||
|
||||
NetworkRequest::NetworkRequest(const QUrl &url, NetworkRequestType requestType)
|
||||
: data(new NetworkData)
|
||||
{
|
||||
this->data->request.setUrl(url);
|
||||
this->data->requestType = requestType;
|
||||
|
||||
this->initializeDefaultValues();
|
||||
}
|
||||
|
||||
NetworkRequest::~NetworkRequest() = default;
|
||||
|
||||
NetworkRequest NetworkRequest::type(NetworkRequestType newRequestType) &&
|
||||
{
|
||||
this->data->requestType = newRequestType;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::caller(const QObject *caller) &&
|
||||
{
|
||||
if (caller)
|
||||
{
|
||||
// Caller must be in gui thread
|
||||
assert(caller->thread() == qApp->thread());
|
||||
|
||||
this->data->caller = const_cast<QObject *>(caller);
|
||||
this->data->hasCaller = true;
|
||||
}
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::onError(NetworkErrorCallback cb) &&
|
||||
{
|
||||
this->data->onError = std::move(cb);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::onSuccess(NetworkSuccessCallback cb) &&
|
||||
{
|
||||
this->data->onSuccess = std::move(cb);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::finally(NetworkFinallyCallback cb) &&
|
||||
{
|
||||
this->data->finally = std::move(cb);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::header(const char *headerName,
|
||||
const char *value) &&
|
||||
{
|
||||
this->data->request.setRawHeader(headerName, value);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::header(const char *headerName,
|
||||
const QByteArray &value) &&
|
||||
{
|
||||
this->data->request.setRawHeader(headerName, value);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::header(const char *headerName,
|
||||
const QString &value) &&
|
||||
{
|
||||
this->data->request.setRawHeader(headerName, value.toUtf8());
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::header(QNetworkRequest::KnownHeaders header,
|
||||
const QVariant &value) &&
|
||||
{
|
||||
this->data->request.setHeader(header, value);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::headerList(
|
||||
const std::vector<std::pair<QByteArray, QByteArray>> &headers) &&
|
||||
{
|
||||
for (const auto &[headerName, headerValue] : headers)
|
||||
{
|
||||
this->data->request.setRawHeader(headerName, headerValue);
|
||||
}
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::timeout(int ms) &&
|
||||
{
|
||||
this->data->timeout = std::chrono::milliseconds(ms);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::concurrent() &&
|
||||
{
|
||||
this->data->executeConcurrently = true;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::multiPart(QHttpMultiPart *payload) &&
|
||||
{
|
||||
this->data->multiPartPayload = {payload, {}};
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::followRedirects(bool on) &&
|
||||
{
|
||||
if (on)
|
||||
{
|
||||
this->data->request.setAttribute(
|
||||
QNetworkRequest::RedirectPolicyAttribute,
|
||||
QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->data->request.setAttribute(
|
||||
QNetworkRequest::RedirectPolicyAttribute,
|
||||
QNetworkRequest::ManualRedirectPolicy);
|
||||
}
|
||||
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::payload(const QByteArray &payload) &&
|
||||
{
|
||||
this->data->payload = payload;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::cache() &&
|
||||
{
|
||||
this->data->cache = true;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
void NetworkRequest::execute()
|
||||
{
|
||||
this->executed_ = true;
|
||||
|
||||
// Only allow caching for GET request
|
||||
if (this->data->cache && this->data->requestType != NetworkRequestType::Get)
|
||||
{
|
||||
qCDebug(chatterinoCommon) << "Can only cache GET requests!";
|
||||
this->data->cache = false;
|
||||
}
|
||||
|
||||
// Can not have a caller and be concurrent at the same time.
|
||||
assert(!(this->data->caller && this->data->executeConcurrently));
|
||||
|
||||
load(std::move(this->data));
|
||||
}
|
||||
|
||||
void NetworkRequest::initializeDefaultValues()
|
||||
{
|
||||
const auto userAgent = QStringLiteral("chatterino/%1 (%2)")
|
||||
.arg(Version::instance().version(),
|
||||
Version::instance().commitHash())
|
||||
.toUtf8();
|
||||
|
||||
this->data->request.setRawHeader("User-Agent", userAgent);
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::json(const QJsonArray &root) &&
|
||||
{
|
||||
return std::move(*this).json(QJsonDocument(root));
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::json(const QJsonObject &root) &&
|
||||
{
|
||||
return std::move(*this).json(QJsonDocument(root));
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::json(const QJsonDocument &document) &&
|
||||
{
|
||||
return std::move(*this).json(document.toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
NetworkRequest NetworkRequest::json(const QByteArray &payload) &&
|
||||
{
|
||||
return std::move(*this)
|
||||
.payload(payload)
|
||||
.header(QNetworkRequest::ContentTypeHeader, "application/json")
|
||||
.header(QNetworkRequest::ContentLengthHeader, payload.length())
|
||||
.header("Accept", "application/json");
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/network/NetworkCommon.hpp"
|
||||
|
||||
#include <QHttpMultiPart>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QJsonArray;
|
||||
class QJsonObject;
|
||||
class QJsonDocument;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkData;
|
||||
|
||||
class NetworkRequest final
|
||||
{
|
||||
// Stores all data about the request that needs to be passed around to each
|
||||
// part of the request
|
||||
std::shared_ptr<NetworkData> data;
|
||||
|
||||
// The NetworkRequest destructor will assert if executed_ hasn't been set to
|
||||
// true before dying
|
||||
bool executed_ = false;
|
||||
|
||||
public:
|
||||
explicit NetworkRequest(
|
||||
const std::string &url,
|
||||
NetworkRequestType requestType = NetworkRequestType::Get);
|
||||
explicit NetworkRequest(const QUrl &url, NetworkRequestType requestType =
|
||||
NetworkRequestType::Get);
|
||||
|
||||
// Enable move
|
||||
NetworkRequest(NetworkRequest &&other) = default;
|
||||
NetworkRequest &operator=(NetworkRequest &&other) = default;
|
||||
|
||||
// Disable copy
|
||||
NetworkRequest(const NetworkRequest &other) = delete;
|
||||
NetworkRequest &operator=(const NetworkRequest &other) = delete;
|
||||
|
||||
~NetworkRequest();
|
||||
|
||||
NetworkRequest type(NetworkRequestType newRequestType) &&;
|
||||
|
||||
NetworkRequest onError(NetworkErrorCallback cb) &&;
|
||||
NetworkRequest onSuccess(NetworkSuccessCallback cb) &&;
|
||||
NetworkRequest finally(NetworkFinallyCallback cb) &&;
|
||||
|
||||
NetworkRequest payload(const QByteArray &payload) &&;
|
||||
NetworkRequest cache() &&;
|
||||
/// NetworkRequest makes sure that the `caller` object still exists when the
|
||||
/// callbacks are executed. Cannot be used with concurrent() since we can't
|
||||
/// make sure that the object doesn't get deleted while the callback is
|
||||
/// running.
|
||||
NetworkRequest caller(const QObject *caller) &&;
|
||||
NetworkRequest header(const char *headerName, const char *value) &&;
|
||||
NetworkRequest header(const char *headerName, const QByteArray &value) &&;
|
||||
NetworkRequest header(const char *headerName, const QString &value) &&;
|
||||
NetworkRequest header(QNetworkRequest::KnownHeaders header,
|
||||
const QVariant &value) &&;
|
||||
NetworkRequest headerList(
|
||||
const std::vector<std::pair<QByteArray, QByteArray>> &headers) &&;
|
||||
NetworkRequest timeout(int ms) &&;
|
||||
NetworkRequest concurrent() &&;
|
||||
NetworkRequest multiPart(QHttpMultiPart *payload) &&;
|
||||
/**
|
||||
* This will change `RedirectPolicyAttribute`.
|
||||
* `QNetworkRequest`'s defaults are used by default (Qt 5: no-follow, Qt 6: follow).
|
||||
*/
|
||||
NetworkRequest followRedirects(bool on) &&;
|
||||
NetworkRequest json(const QJsonObject &root) &&;
|
||||
NetworkRequest json(const QJsonArray &root) &&;
|
||||
NetworkRequest json(const QJsonDocument &document) &&;
|
||||
NetworkRequest json(const QByteArray &payload) &&;
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
void initializeDefaultValues();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "common/network/NetworkResult.hpp"
|
||||
|
||||
#include "common/QLogging.hpp"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QMetaEnum>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/error/en.h>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
NetworkResult::NetworkResult(NetworkError error, const QVariant &httpStatusCode,
|
||||
QByteArray data)
|
||||
: data_(std::move(data))
|
||||
, error_(error)
|
||||
{
|
||||
if (httpStatusCode.isValid())
|
||||
{
|
||||
this->status_ = httpStatusCode.toInt();
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject NetworkResult::parseJson() const
|
||||
{
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(this->data_));
|
||||
if (jsonDoc.isNull())
|
||||
{
|
||||
return QJsonObject{};
|
||||
}
|
||||
|
||||
return jsonDoc.object();
|
||||
}
|
||||
|
||||
QJsonArray NetworkResult::parseJsonArray() const
|
||||
{
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(this->data_));
|
||||
if (jsonDoc.isNull())
|
||||
{
|
||||
return QJsonArray{};
|
||||
}
|
||||
|
||||
return jsonDoc.array();
|
||||
}
|
||||
|
||||
rapidjson::Document NetworkResult::parseRapidJson() const
|
||||
{
|
||||
rapidjson::Document ret(rapidjson::kObjectType);
|
||||
|
||||
rapidjson::ParseResult result =
|
||||
ret.Parse(this->data_.data(), this->data_.length());
|
||||
|
||||
if (result.Code() != rapidjson::kParseErrorNone)
|
||||
{
|
||||
qCWarning(chatterinoCommon)
|
||||
<< "JSON parse error:" << rapidjson::GetParseError_En(result.Code())
|
||||
<< "(" << result.Offset() << ")";
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const QByteArray &NetworkResult::getData() const
|
||||
{
|
||||
return this->data_;
|
||||
}
|
||||
|
||||
QString NetworkResult::formatError() const
|
||||
{
|
||||
if (this->status_)
|
||||
{
|
||||
return QString::number(*this->status_);
|
||||
}
|
||||
|
||||
const auto *name =
|
||||
QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(
|
||||
this->error_);
|
||||
if (name == nullptr)
|
||||
{
|
||||
return QStringLiteral("unknown error (%1)").arg(this->error_);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkResult
|
||||
{
|
||||
public:
|
||||
using NetworkError = QNetworkReply::NetworkError;
|
||||
|
||||
NetworkResult(NetworkError error, const QVariant &httpStatusCode,
|
||||
QByteArray data);
|
||||
|
||||
/// Parses the result as json and returns the root as an object.
|
||||
/// Returns empty object if parsing failed.
|
||||
QJsonObject parseJson() const;
|
||||
/// Parses the result as json and returns the root as an array.
|
||||
/// Returns empty object if parsing failed.
|
||||
QJsonArray parseJsonArray() const;
|
||||
/// Parses the result as json and returns the document.
|
||||
rapidjson::Document parseRapidJson() const;
|
||||
const QByteArray &getData() const;
|
||||
|
||||
/// The error code of the reply.
|
||||
/// In case of a successful reply, this will be NoError (0)
|
||||
NetworkError error() const
|
||||
{
|
||||
return this->error_;
|
||||
}
|
||||
|
||||
/// The HTTP status code if a response was received.
|
||||
std::optional<int> status() const
|
||||
{
|
||||
return this->status_;
|
||||
}
|
||||
|
||||
/// Formats the error.
|
||||
/// If a reply is received, returns the HTTP status otherwise, the network error.
|
||||
QString formatError() const;
|
||||
|
||||
private:
|
||||
QByteArray data_;
|
||||
|
||||
NetworkError error_;
|
||||
std::optional<int> status_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "common/network/NetworkTask.hpp"
|
||||
|
||||
#include "common/NetworkManager.hpp"
|
||||
#include "common/NetworkPrivate.hpp"
|
||||
#include "common/NetworkResult.hpp"
|
||||
#include "common/network/NetworkManager.hpp"
|
||||
#include "common/network/NetworkPrivate.hpp"
|
||||
#include "common/network/NetworkResult.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "util/AbandonObject.hpp"
|
||||
|
||||
Reference in New Issue
Block a user