porymap/include/core/network.h
2026-04-19 15:07:21 -04:00

105 lines
3.2 KiB
C++

#ifndef NETWORK_H
#define NETWORK_H
/*
The two classes defined here provide a simplified interface for Qt's network classes QNetworkAccessManager and QNetworkReply.
With the Qt classes, the workflow for a GET is roughly: create a QNetworkAccessManager, generate a QNetworkRequest, give this request to the manager,
connect the returned object to QNetworkReply::finished, and in the slot of that connection handle the various HTTP headers and attributes,
then manage errors or process the webpage's body.
These classes handle generating the QNetworkRequest with a given URL and manage the HTTP headers in the reply. They will automatically
respect rate limits and return cached data if the webpage hasn't changed since previous requests. Instead of interacting with a QNetworkReply,
callers interact with a simplified NetworkReplyData. Per Qt's manual, a single QNetworkAccessManager instance is sufficient for a whole application,
and this manager will be created internally when the first network request is made.
Example that logs Porymap's description on GitHub:
NetworkReplyData * reply = Network::get("https://api.github.com/repos/huderlem/porymap");
connect(reply, &NetworkReplyData::received, [reply] () {
if (!reply->errorString().isEmpty()) {
logError(QString("Failed to read description: %1").arg(reply->errorString()));
} else {
auto webpage = QJsonDocument::fromJson(reply->body());
logInfo(QString("Porymap: %1").arg(webpage["description"].toString()));
}
reply->deleteLater();
});
*/
#if __has_include(<QNetworkAccessManager>)
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDateTime>
#include <QPointer>
#endif
#ifdef QT_NETWORK_LIB
class NetworkReplyData : public QObject
{
Q_OBJECT
public:
QUrl url() const { return m_url; }
QUrl nextUrl() const { return m_nextUrl; }
QByteArray body() const { return m_body; }
QString errorString() const { return m_error; }
QDateTime retryAfter() const { return m_retryAfter; }
bool isReceived() const { return m_received; }
friend class Network;
friend class NetworkAccessManager;
private:
QUrl m_url;
QUrl m_nextUrl;
QByteArray m_body;
QString m_error;
QDateTime m_retryAfter;
bool m_received = false;
void finish() {
m_received = true;
emit received();
};
signals:
void received();
};
class NetworkAccessManager : public QNetworkAccessManager
{
Q_OBJECT
public:
NetworkAccessManager(QObject * parent = nullptr);
~NetworkAccessManager();
static QPointer<NetworkAccessManager> instance();
friend class Network;
private:
// For a more complex cache we could implement a QAbstractCache for the manager
struct CacheEntry {
QString eTag;
QByteArray data;
};
QMap<QUrl, CacheEntry*> cache;
QMap<QUrl, QDateTime> rateLimitTimes;
void processReply(QNetworkReply * reply, NetworkReplyData * data);
const QNetworkRequest getRequest(const QUrl &url);
};
class Network
{
public:
static NetworkReplyData * get(const QString &url);
static NetworkReplyData * get(const QUrl &url);
};
#endif // QT_NETWORK_LIB
#endif // NETWORK_H