mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-07-04 04:16:57 +03:00
feat: Auto handle Http 429 Too Many Requests with retry
- Must be explicitly enabled for a request - Uses Retry-After Header if present, falls back to exponential back off starting with 10 seconds - if retry delay is greater than 1 minute or it retries more than 3 times then fail with a "Rate Limited" reason - Sets task status to inform user of retry. Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
parent
9171e2b2e1
commit
e8da9ee4fb
9 changed files with 75 additions and 3 deletions
|
|
@ -41,8 +41,10 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QLocale>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
|
|
@ -55,6 +57,11 @@
|
|||
|
||||
namespace Net {
|
||||
|
||||
NetRequest::NetRequest() : Task()
|
||||
{
|
||||
connect(&m_retryTimer, &QTimer::timeout, this, &NetRequest::executeTask);
|
||||
}
|
||||
|
||||
void NetRequest::addValidator(Validator* v)
|
||||
{
|
||||
m_sink->addValidator(v);
|
||||
|
|
@ -161,6 +168,20 @@ void NetRequest::downloadError(QNetworkReply::NetworkError error)
|
|||
if (error == QNetworkReply::OperationCanceledError) {
|
||||
qCCritical(logCat) << getUid().toString() << "Aborted" << m_url.toString();
|
||||
m_state = State::Failed;
|
||||
} else if (replyStatusCode() == 429 /* HTTP Too Many Requests*/ && m_options & Option::AutoRetry) {
|
||||
qCDebug(logCat) << getUid().toString() << "Rate Limited!";
|
||||
int64_t delay = 10 * std::pow(2, m_retryCount);
|
||||
if (m_reply->hasRawHeader("Retry-After")) {
|
||||
auto retryAfter = m_reply->rawHeader("Retry-After");
|
||||
if (retryAfter.trimmed().endsWith("GMT")) /* HTTP Date format */ {
|
||||
auto afterTimestamp = QDateTime::fromString(QString::fromUtf8(retryAfter.trimmed()), "ddd, dd MMM yyyy HH:mm:ss 'GMT'");
|
||||
auto now = QDateTime::currentDateTime();
|
||||
delay = now.secsTo(afterTimestamp);
|
||||
} else {
|
||||
delay = retryAfter.toLong();
|
||||
}
|
||||
}
|
||||
handleAutoRetry(delay);
|
||||
} else {
|
||||
if (m_options & Option::AcceptLocalFiles) {
|
||||
if (m_sink->hasLocalData()) {
|
||||
|
|
@ -182,7 +203,8 @@ void NetRequest::sslErrors(const QList<QSslError>& errors)
|
|||
{
|
||||
int i = 1;
|
||||
for (auto error : errors) {
|
||||
qCCritical(logCat).nospace() << getUid().toString() << " Request " << m_url.toString() << " SSL Error #" << i << ": " << error.errorString();
|
||||
qCCritical(logCat).nospace() << getUid().toString() << " Request " << m_url.toString() << " SSL Error #" << i << ": "
|
||||
<< error.errorString();
|
||||
auto cert = error.certificate();
|
||||
qCCritical(logCat) << getUid().toString() << "Certificate in question:\n" << cert.toText();
|
||||
i++;
|
||||
|
|
@ -243,8 +265,33 @@ auto NetRequest::handleRedirect() -> bool
|
|||
return true;
|
||||
}
|
||||
|
||||
void NetRequest::handleAutoRetry(int64_t delay)
|
||||
{
|
||||
m_retryCount++;
|
||||
if (delay > 60 || m_retryCount > 4) {
|
||||
/* 1 minute is too long to wait for retry, fail for now */
|
||||
m_state = State::Failed;
|
||||
auto retryAfter = QDateTime::currentDateTime().addSecs(delay);
|
||||
emitFailed(tr("Request Rate Limited for %n second(s): Retry After %1", "seconds", delay)
|
||||
.arg(retryAfter.toLocalTime().toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat))));
|
||||
return;
|
||||
} else {
|
||||
qCDebug(logCat) << getUid().toString() << "Retyring Request in" << delay << "seconds";
|
||||
setStatus(tr("Rate Limited: Waiting %n second(s)", "seconds", delay));
|
||||
m_retryTimer.setTimerType(Qt::VeryCoarseTimer);
|
||||
m_retryTimer.setSingleShot(true);
|
||||
m_retryTimer.setInterval(delay * 1000);
|
||||
m_retryTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void NetRequest::downloadFinished()
|
||||
{
|
||||
// currently waiting for retry
|
||||
if (m_retryTimer.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle HTTP redirection first
|
||||
if (handleRedirect()) {
|
||||
qCDebug(logCat) << getUid().toString() << "Request redirected:" << m_url.toString();
|
||||
|
|
@ -351,4 +398,14 @@ QString NetRequest::errorString() const
|
|||
{
|
||||
return m_reply ? m_reply->errorString() : "";
|
||||
}
|
||||
|
||||
void NetRequest::enableAutoRetry(bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
m_options |= Option::AutoRetry;
|
||||
} else {
|
||||
m_options &= ~static_cast<int>(Option::AutoRetry);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Net
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue