Add NetworkRequest.

This commit is contained in:
Patrick Sean Klein 2023-05-07 21:30:46 +01:00
parent 825657b217
commit 9d7d3dd5fd
No known key found for this signature in database
GPG Key ID: B6D50F39A56F6906
3 changed files with 200 additions and 0 deletions

View File

@ -51,6 +51,7 @@ set(keepassx_SOURCES
core/Merger.cpp
core/Metadata.cpp
core/ModifiableObject.cpp
core/NetworkRequest.cpp
core/PasswordGenerator.cpp
core/PasswordHealth.cpp
core/PassphraseGenerator.cpp

143
src/core/NetworkRequest.cpp Normal file
View File

@ -0,0 +1,143 @@
#include "NetworkRequest.h"
#include "NetworkManager.h"
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
namespace
{
QUrl getRedirectTarget(QNetworkReply* reply)
{
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
QUrl url;
if (var.canConvert<QUrl>()) {
url = var.toUrl();
}
return url;
}
}
void NetworkRequest::fetch(const QUrl& url)
{
reset();
QNetworkRequest request(url);
m_reply = m_manager->get(request);
connect(m_reply, &QNetworkReply::finished, this, &NetworkRequest::fetchFinished);
connect(m_reply, &QIODevice::readyRead, this, &NetworkRequest::fetchReadyRead);
}
void NetworkRequest::fetchFinished()
{
auto error = m_reply->error();
QUrl redirectTarget = getRedirectTarget(m_reply);
QUrl url = m_reply->url();
// Returns an empty string if the header was not set
QString contentTypeHeader = m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
m_reply->deleteLater();
m_reply = nullptr;
if(error != QNetworkReply::NoError) {
// Do not emit on abort.
if(error != QNetworkReply::OperationCanceledError) {
emit failure();
}
return;
}
if (redirectTarget.isValid()) {
// Redirected, we need to follow it, or fall through if we have
// done too many redirects already.
if (m_redirects < m_maxRedirects) {
if (redirectTarget.isRelative()) {
redirectTarget = url.resolved(redirectTarget);
}
// Request the redirect target
fetch(redirectTarget);
return;
} else {
emit failure();
return;
}
}
// Parse content type
auto tokens = contentTypeHeader.split(";", Qt::SkipEmptyParts);
m_content_type = tokens[0].trimmed();
for(int i = 1; i < tokens.size(); ++i) {
auto parameterTokens = tokens[i].split("=");
m_content_type_parameters[parameterTokens[0]] = parameterTokens[1];
}
emit success(std::move(m_bytes));
}
void NetworkRequest::fetchReadyRead()
{
m_bytes += m_reply->readAll();
}
void NetworkRequest::reset()
{
m_redirects = 0;
m_bytes.clear();
m_content_type = "";
m_content_type_parameters.clear();
m_timeout.setInterval(m_timeoutDuration);
m_timeout.setSingleShot(true);
}
void NetworkRequest::cancel()
{
if(m_reply) {
m_reply->abort();
}
}
NetworkRequest::~NetworkRequest()
{
cancel();
}
QUrl NetworkRequest::url() const
{
if(m_reply) {
return m_reply->url();
}
return {};
}
void NetworkRequest::setMaxRedirects(int maxRedirects)
{
m_maxRedirects = std::max(0, maxRedirects);
}
NetworkRequest::NetworkRequest(int maxRedirects, std::chrono::milliseconds timeoutDuration, QNetworkAccessManager* manager) : m_reply(nullptr), m_maxRedirects(maxRedirects), m_redirects(0), m_timeoutDuration(timeoutDuration)
{
m_manager = manager ? manager : getNetMgr();
connect(&m_timeout, &QTimer::timeout, this, &NetworkRequest::fetchTimeout);
}
const QString& NetworkRequest::ContentType() const
{
return m_content_type;
}
const QHash<QString, QString>& NetworkRequest::ContentTypeParameters() const
{
return m_content_type_parameters;
}
void NetworkRequest::setTimeout(std::chrono::milliseconds timeoutDuration)
{
m_timeoutDuration = timeoutDuration;
}
void NetworkRequest::fetchTimeout()
{
// Cancel request on timeout
cancel();
}

56
src/core/NetworkRequest.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef KEEPASSXC_NETWORKREQUEST_H
#define KEEPASSXC_NETWORKREQUEST_H
#include <QObject>
#include <QHash>
#include <QTimer>
class QNetworkReply;
class QNetworkAccessManager;
class NetworkRequest : public QObject {
Q_OBJECT
QNetworkAccessManager* m_manager;
QNetworkReply* m_reply;
QByteArray m_bytes;
QString m_content_type;
QHash<QString, QString> m_content_type_parameters;
QTimer m_timeout;
int m_maxRedirects;
int m_redirects;
std::chrono::milliseconds m_timeoutDuration;
public:
explicit NetworkRequest(int maxRedirects, std::chrono::milliseconds timeoutDuration, QNetworkAccessManager* manager = nullptr);
~NetworkRequest() override;
void setMaxRedirects(int maxRedirects);
void setTimeout(std::chrono::milliseconds timeoutDuration);
void fetch(const QUrl& url);
void cancel();
QUrl url() const;
/***
* @return The MIME Type set in the Content-Type header. Empty string if Content-Type was not set.
*/
const QString& ContentType() const;
/***
* @return Any parameters set in the Content-Type header.
*/
const QHash<QString, QString>& ContentTypeParameters() const;
private:
void reset();
private slots:
void fetchFinished();
void fetchReadyRead();
void fetchTimeout();
signals:
void success(QByteArray);
void failure();
};
#endif // KEEPASSXC_NETWORKREQUEST_H