mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-29 01:06:27 -05:00
Implement request builder.
This commit is contained in:
parent
1b745deed3
commit
8d7539fa0e
@ -6,6 +6,18 @@
|
|||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QList<QPair<QString, QString>> createDefaultHeaders() {
|
||||||
|
QList<QPair<QString, QString>> headers;
|
||||||
|
headers.append(QPair{"User-Agent", "KeePassXC"});
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int DEFAULT_MAX_REDIRECTS = 5;
|
||||||
|
static constexpr std::chrono::milliseconds DEFAULT_TIMEOUT = std::chrono::seconds(5);
|
||||||
|
static QList<QPair<QString, QString>> DEFAULT_HEADERS = createDefaultHeaders();
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
QUrl getRedirectTarget(QNetworkReply* reply)
|
QUrl getRedirectTarget(QNetworkReply* reply)
|
||||||
@ -51,8 +63,6 @@ void NetworkRequest::fetch(const QUrl& url)
|
|||||||
|
|
||||||
connect(m_reply, &QNetworkReply::finished, this, &NetworkRequest::fetchFinished);
|
connect(m_reply, &QNetworkReply::finished, this, &NetworkRequest::fetchFinished);
|
||||||
connect(m_reply, &QIODevice::readyRead, this, &NetworkRequest::fetchReadyRead);
|
connect(m_reply, &QIODevice::readyRead, this, &NetworkRequest::fetchReadyRead);
|
||||||
|
|
||||||
m_timeout.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkRequest::fetchFinished()
|
void NetworkRequest::fetchFinished()
|
||||||
@ -81,12 +91,10 @@ void NetworkRequest::fetchFinished()
|
|||||||
redirectTarget = url.resolved(redirectTarget);
|
redirectTarget = url.resolved(redirectTarget);
|
||||||
}
|
}
|
||||||
// Request the redirect target
|
// Request the redirect target
|
||||||
qDebug() << "Following redirect to" << redirectTarget;
|
|
||||||
m_redirects += 1;
|
m_redirects += 1;
|
||||||
fetch(redirectTarget);
|
fetch(redirectTarget);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Too many redirects";
|
|
||||||
emit failure();
|
emit failure();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -142,32 +150,25 @@ QUrl NetworkRequest::URL() const
|
|||||||
return m_requested_url;
|
return m_requested_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkRequest::setMaxRedirects(int maxRedirects)
|
|
||||||
{
|
|
||||||
m_maxRedirects = std::max(0, maxRedirects);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkRequest::NetworkRequest(
|
NetworkRequest::NetworkRequest(
|
||||||
QUrl targetURL,
|
QUrl targetURL, bool allowInsecure, unsigned int maxRedirects, std::chrono::milliseconds timeoutDuration, QList<QPair<QString, QString>> headers,
|
||||||
int maxRedirects,
|
QNetworkAccessManager* manager)
|
||||||
std::chrono::milliseconds timeoutDuration,
|
: m_manager(manager)
|
||||||
QList<QPair<QString, QString>> headers,
|
, m_reply(nullptr)
|
||||||
QNetworkAccessManager* manager)
|
|
||||||
: m_reply(nullptr)
|
|
||||||
, m_finished(false)
|
, m_finished(false)
|
||||||
, m_maxRedirects(maxRedirects)
|
, m_maxRedirects(maxRedirects)
|
||||||
, m_redirects(0)
|
, m_redirects(0)
|
||||||
, m_timeoutDuration(timeoutDuration)
|
|
||||||
, m_headers(headers)
|
, m_headers(headers)
|
||||||
, m_requested_url(targetURL)
|
, m_requested_url(targetURL)
|
||||||
{
|
{
|
||||||
m_manager = manager ? manager : getNetMgr();
|
|
||||||
connect(&m_timeout, &QTimer::timeout, this, &NetworkRequest::fetchTimeout);
|
connect(&m_timeout, &QTimer::timeout, this, &NetworkRequest::fetchTimeout);
|
||||||
|
|
||||||
m_timeout.setInterval(m_timeoutDuration);
|
m_timeout.setInterval(timeoutDuration);
|
||||||
m_timeout.setSingleShot(true);
|
m_timeout.setSingleShot(true);
|
||||||
|
|
||||||
fetch(m_requested_url);
|
if(!allowInsecure) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& NetworkRequest::ContentType() const
|
const QString& NetworkRequest::ContentType() const
|
||||||
@ -180,11 +181,6 @@ const QHash<QString, QString>& NetworkRequest::ContentTypeParameters() const
|
|||||||
return m_content_type_parameters;
|
return m_content_type_parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkRequest::setTimeout(std::chrono::milliseconds timeoutDuration)
|
|
||||||
{
|
|
||||||
m_timeoutDuration = timeoutDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkRequest::fetchTimeout()
|
void NetworkRequest::fetchTimeout()
|
||||||
{
|
{
|
||||||
if(m_finished)
|
if(m_finished)
|
||||||
@ -200,16 +196,71 @@ QNetworkReply* NetworkRequest::Reply() const
|
|||||||
return m_reply;
|
return m_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRequest createRequest(QUrl target,int maxRedirects,
|
void NetworkRequest::fetch()
|
||||||
std::chrono::milliseconds timeoutDuration,
|
|
||||||
QList<QPair<QString, QString>> additionalHeaders,
|
|
||||||
QNetworkAccessManager* manager)
|
|
||||||
{
|
{
|
||||||
// Append user agent unless given
|
m_timeout.start();
|
||||||
if (std::none_of(additionalHeaders.begin(), additionalHeaders.end(), [](const auto& pair) {
|
fetch(m_requested_url);
|
||||||
return pair.first == "User-Agent";
|
}
|
||||||
})) {
|
|
||||||
additionalHeaders.append(QPair{"User-Agent", "KeePassXC"});
|
NetworkRequestBuilder::NetworkRequestBuilder()
|
||||||
}
|
{
|
||||||
return NetworkRequest(std::move(target), maxRedirects, timeoutDuration, additionalHeaders, manager);
|
this->setAllowInsecure(false);
|
||||||
|
this->setMaxRedirects(DEFAULT_MAX_REDIRECTS);
|
||||||
|
this->setTimeout(DEFAULT_TIMEOUT);
|
||||||
|
this->setHeaders(DEFAULT_HEADERS);
|
||||||
|
this->setManager(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder::NetworkRequestBuilder(QUrl url) : NetworkRequestBuilder()
|
||||||
|
{
|
||||||
|
this->setUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setManager(QNetworkAccessManager* manager)
|
||||||
|
{
|
||||||
|
m_manager = manager ? manager : getNetMgr();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequest NetworkRequestBuilder::build()
|
||||||
|
{
|
||||||
|
return NetworkRequest(m_url, m_allowInsecure, m_maxRedirects, m_timeoutDuration, m_headers, m_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setHeaders(QList<QPair<QString, QString>> headers)
|
||||||
|
{
|
||||||
|
m_headers = headers;
|
||||||
|
|
||||||
|
// Append user agent unless given
|
||||||
|
if (std::none_of(m_headers.begin(), m_headers.end(), [](const auto& pair) {
|
||||||
|
return pair.first == "User-Agent";
|
||||||
|
})) {
|
||||||
|
m_headers.append(QPair{"User-Agent", "KeePassXC"});
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setTimeout(std::chrono::milliseconds timeoutDuration)
|
||||||
|
{
|
||||||
|
m_timeoutDuration = timeoutDuration;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setUrl(QUrl url)
|
||||||
|
{
|
||||||
|
m_url = url;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setAllowInsecure(bool allowInsecure)
|
||||||
|
{
|
||||||
|
m_allowInsecure = allowInsecure;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRequestBuilder& NetworkRequestBuilder::setMaxRedirects(unsigned int maxRedirects)
|
||||||
|
{
|
||||||
|
m_maxRedirects = maxRedirects;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -47,31 +47,19 @@ class NetworkRequest : public QObject
|
|||||||
|
|
||||||
// Request parameters
|
// Request parameters
|
||||||
QTimer m_timeout;
|
QTimer m_timeout;
|
||||||
int m_maxRedirects;
|
unsigned int m_maxRedirects;
|
||||||
int m_redirects;
|
unsigned int m_redirects;
|
||||||
std::chrono::milliseconds m_timeoutDuration;
|
|
||||||
QList<QPair<QString, QString>> m_headers;
|
QList<QPair<QString, QString>> m_headers;
|
||||||
QUrl m_requested_url;
|
QUrl m_requested_url;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// TODO Disallow insecure connections by default?
|
// TODO Disallow insecure connections by default?
|
||||||
explicit NetworkRequest(QUrl targetURL, int maxRedirects,
|
explicit NetworkRequest(QUrl targetURL, bool allowInsecure, unsigned int maxRedirects,
|
||||||
std::chrono::milliseconds timeoutDuration,
|
std::chrono::milliseconds timeoutDuration,
|
||||||
QList<QPair<QString, QString>> headers,
|
QList<QPair<QString, QString>> headers,
|
||||||
QNetworkAccessManager* manager = nullptr);
|
QNetworkAccessManager* manager = nullptr);
|
||||||
~NetworkRequest() override;
|
|
||||||
|
|
||||||
/**
|
void fetch();
|
||||||
* Sets the maximum number of redirects to follow.
|
|
||||||
* @param maxRedirects The maximum number of redirects to follow. Must be >= 0.
|
|
||||||
*/
|
|
||||||
void setMaxRedirects(int maxRedirects);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout duration for the request. This is the maximum time the request may take, including redirects.
|
|
||||||
* @param timeoutDuration The duration of the timeout in milliseconds.
|
|
||||||
*/
|
|
||||||
void setTimeout(std::chrono::milliseconds timeoutDuration);
|
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
@ -96,6 +84,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
const QHash<QString, QString>& ContentTypeParameters() const;
|
const QHash<QString, QString>& ContentTypeParameters() const;
|
||||||
|
|
||||||
|
~NetworkRequest() override;
|
||||||
private:
|
private:
|
||||||
void reset();
|
void reset();
|
||||||
void fetch(const QUrl& url);
|
void fetch(const QUrl& url);
|
||||||
@ -109,17 +98,58 @@ signals:
|
|||||||
void failure();
|
void failure();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class NetworkRequestBuilder {
|
||||||
* Creates a NetworkRequest with default parameters.
|
QUrl m_url;
|
||||||
* @param maxRedirects
|
bool m_allowInsecure;
|
||||||
* @param timeoutDuration
|
unsigned int m_maxRedirects;
|
||||||
* @param headers
|
std::chrono::milliseconds m_timeoutDuration;
|
||||||
* @param manager
|
QList<QPair<QString, QString>> m_headers;
|
||||||
* @return
|
QNetworkAccessManager* m_manager;
|
||||||
*/
|
|
||||||
NetworkRequest createRequest(QUrl target, int maxRedirects = 5,
|
NetworkRequestBuilder();
|
||||||
std::chrono::milliseconds timeoutDuration = std::chrono::milliseconds(5000),
|
public:
|
||||||
QList<QPair<QString, QString>> additionalHeaders = {},
|
explicit NetworkRequestBuilder(QUrl url);
|
||||||
QNetworkAccessManager* manager = nullptr);
|
|
||||||
|
/**
|
||||||
|
* Sets the URL to request.
|
||||||
|
* @param url The URL to request.
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setUrl(QUrl url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether insecure connections are allowed.
|
||||||
|
* @param allowInsecure
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setAllowInsecure(bool allowInsecure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of redirects to follow.
|
||||||
|
* @param maxRedirects The maximum number of redirects to follow. Must be >= 0.
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setMaxRedirects(unsigned int maxRedirects);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout duration for the request. This is the maximum time the request may take, including redirects.
|
||||||
|
* @param timeoutDuration The duration of the timeout in milliseconds.
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setTimeout(std::chrono::milliseconds timeoutDuration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the headers to send with the request.
|
||||||
|
* @param headers
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setHeaders(QList<QPair<QString, QString>> headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the QNetworkAccessManager to use for the request.
|
||||||
|
* @param manager
|
||||||
|
*/
|
||||||
|
NetworkRequestBuilder& setManager(QNetworkAccessManager* manager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new NetworkRequest based on the configured parameters.
|
||||||
|
*/
|
||||||
|
NetworkRequest build();
|
||||||
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_NETWORKREQUEST_H
|
#endif // KEEPASSXC_NETWORKREQUEST_H
|
||||||
|
@ -11,6 +11,12 @@ using ContentTypeParameters_t = QHash<QString, QString>;
|
|||||||
Q_DECLARE_METATYPE(ContentTypeParameters_t);
|
Q_DECLARE_METATYPE(ContentTypeParameters_t);
|
||||||
Q_DECLARE_METATYPE(std::chrono::milliseconds);
|
Q_DECLARE_METATYPE(std::chrono::milliseconds);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void processRequests() {
|
||||||
|
QTest::qWait(300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestNetworkRequest::testNetworkRequest()
|
void TestNetworkRequest::testNetworkRequest()
|
||||||
{
|
{
|
||||||
QFETCH(QUrl, requestedURL);
|
QFETCH(QUrl, requestedURL);
|
||||||
@ -39,7 +45,7 @@ void TestNetworkRequest::testNetworkRequest()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
NetworkRequest request = createRequest(requestedURL, 5, std::chrono::milliseconds{5000}, QList<QPair<QString, QString>>{}, &manager);
|
NetworkRequest request = NetworkRequestBuilder(requestedURL).setManager(&manager).build();
|
||||||
|
|
||||||
QString actualContent;
|
QString actualContent;
|
||||||
bool didError = false, didSucceed = false;
|
bool didError = false, didSucceed = false;
|
||||||
@ -54,8 +60,8 @@ void TestNetworkRequest::testNetworkRequest()
|
|||||||
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
||||||
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
||||||
|
|
||||||
|
request.fetch();
|
||||||
QTest::qWait(3*100);
|
processRequests();
|
||||||
|
|
||||||
// Ensures that predicates match - i.e., the header was set correctly
|
// Ensures that predicates match - i.e., the header was set correctly
|
||||||
QCOMPARE(manager.matchedRequests().length(), 1);
|
QCOMPARE(manager.matchedRequests().length(), 1);
|
||||||
@ -134,7 +140,7 @@ void TestNetworkRequest::testNetworkRequestTimeout()
|
|||||||
reply.withFinishDelayUntil(&timer, &QTimer::timeout);
|
reply.withFinishDelayUntil(&timer, &QTimer::timeout);
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
NetworkRequest request = createRequest(requestedURL, 5, timeout, QList<QPair<QString, QString>>{}, &manager);
|
NetworkRequest request = NetworkRequestBuilder(requestedURL).setManager(&manager).setTimeout(timeout).build();
|
||||||
|
|
||||||
// Start timer
|
// Start timer
|
||||||
timer.start();
|
timer.start();
|
||||||
@ -149,8 +155,8 @@ void TestNetworkRequest::testNetworkRequestTimeout()
|
|||||||
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
||||||
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
||||||
|
|
||||||
|
request.fetch();
|
||||||
QTest::qWait(3*100);
|
processRequests();
|
||||||
|
|
||||||
QTEST_ASSERT(didError || didSucceed);
|
QTEST_ASSERT(didError || didSucceed);
|
||||||
|
|
||||||
@ -168,8 +174,8 @@ void TestNetworkRequest::testNetworkRequestTimeout_data()
|
|||||||
QTest::addColumn<std::chrono::milliseconds>("delay");
|
QTest::addColumn<std::chrono::milliseconds>("delay");
|
||||||
QTest::addColumn<std::chrono::milliseconds>("timeout");
|
QTest::addColumn<std::chrono::milliseconds>("timeout");
|
||||||
|
|
||||||
QTest::newRow("timeout") << true << std::chrono::milliseconds{200} << std::chrono::milliseconds{100};
|
QTest::newRow("timeout") << true << std::chrono::milliseconds{100} << std::chrono::milliseconds{50};
|
||||||
QTest::newRow("no timeout") << false << std::chrono::milliseconds{100} << std::chrono::milliseconds{200};
|
QTest::newRow("no timeout") << false << std::chrono::milliseconds{50} << std::chrono::milliseconds{100};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNetworkRequest::testNetworkRequestRedirects()
|
void TestNetworkRequest::testNetworkRequestRedirects()
|
||||||
@ -209,7 +215,8 @@ void TestNetworkRequest::testNetworkRequestRedirects()
|
|||||||
reply->withBody(QString{"test-content"}.toUtf8());
|
reply->withBody(QString{"test-content"}.toUtf8());
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
NetworkRequest request = createRequest(requestedURL, maxRedirects, std::chrono::milliseconds{5000}, QList<QPair<QString, QString>>{}, &manager);
|
NetworkRequest request = NetworkRequestBuilder(requestedURL).setManager(&manager)
|
||||||
|
.setMaxRedirects(maxRedirects).build();
|
||||||
|
|
||||||
bool didSucceed = false, didError = false;
|
bool didSucceed = false, didError = false;
|
||||||
// Check request
|
// Check request
|
||||||
@ -221,8 +228,8 @@ void TestNetworkRequest::testNetworkRequestRedirects()
|
|||||||
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
QSignalSpy errorSpy(&request, &NetworkRequest::failure);
|
||||||
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
connect(&request, &NetworkRequest::failure, [&didError]() { didError = true; });
|
||||||
|
|
||||||
|
request.fetch();
|
||||||
QTest::qWait(3*100);
|
processRequests();
|
||||||
|
|
||||||
QTEST_ASSERT(didError || didSucceed);
|
QTEST_ASSERT(didError || didSucceed);
|
||||||
// Ensures that predicates match - i.e., the header was set correctly
|
// Ensures that predicates match - i.e., the header was set correctly
|
||||||
@ -239,10 +246,10 @@ void TestNetworkRequest::testNetworkRequestRedirects_data()
|
|||||||
QTest::addColumn<int>("numRedirects");
|
QTest::addColumn<int>("numRedirects");
|
||||||
QTest::addColumn<int>("maxRedirects");
|
QTest::addColumn<int>("maxRedirects");
|
||||||
|
|
||||||
QTest::newRow("all good (0)") << 0 << 5;
|
QTest::newRow("fewer redirects than allowed (0)") << 0 << 5;
|
||||||
QTest::newRow("all good (1)") << 1 << 5;
|
QTest::newRow("fewer redirects than allowed (1)") << 1 << 5;
|
||||||
QTest::newRow("all good (2)") << 2 << 5;
|
QTest::newRow("fewer redirects than allowed (2)") << 2 << 5;
|
||||||
QTest::newRow("no good (1, 0)") << 1 << 0;
|
QTest::newRow("more redirects than allowed (1, 0)") << 1 << 0;
|
||||||
QTest::newRow("no good (2, 1)") << 2 << 1;
|
QTest::newRow("more redirects than allowed (2, 1)") << 2 << 1;
|
||||||
QTest::newRow("no good (3, 2)") << 3 << 2;
|
QTest::newRow("more redirects than allowed (3, 2)") << 3 << 2;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user