diff --git a/app/backend/nvhttp.cpp b/app/backend/nvhttp.cpp index f447b4ff..fca6088b 100644 --- a/app/backend/nvhttp.cpp +++ b/app/backend/nvhttp.cpp @@ -516,14 +516,35 @@ NvHTTP::openConnection(QUrl baseUrl, auto sslErrorsConnection = connect(m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors); QNetworkReply* reply = m_Nam->get(request); - - // Run the request with a timeout if requested QEventLoop loop; + + // Connect the quit signals connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit); + + // Enforce a transfer timeout if requested if (timeoutMs) { QTimer::singleShot(timeoutMs, &loop, &QEventLoop::quit); } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) + // Time the request to distinguish a slow response from an unreachable host + auto* connectTimer = new QTimer(reply); + connectTimer->setSingleShot(true); + connect(connectTimer, &QTimer::timeout, reply, [reply, logLevel, url]() { + if (!reply->isFinished()) { + if (logLevel >= NvLogLevel::NVLL_ERROR) { + qWarning() << "Aborting timed out connection for" << url.toString(); + } + + reply->setProperty("timeout_reason", "connection_timeout"); + reply->abort(); + } + }); + connect(reply, &QNetworkReply::requestSent, connectTimer, &QTimer::stop); + connectTimer->start(REQUEST_TIMEOUT_MS); +#endif + if (logLevel >= NvLogLevel::NVLL_VERBOSE) { qInfo() << "Executing request:" << url.toString(); } @@ -559,7 +580,11 @@ NvHTTP::openConnection(QUrl baseUrl, throw exception; } else if (reply->error() == QNetworkReply::OperationCanceledError) { - QtNetworkReplyException exception(QNetworkReply::TimeoutError, "Request timed out"); + QString reason = reply->property("timeout_reason").toString(); + QtNetworkReplyException exception(QNetworkReply::TimeoutError, + reason == "connection_timeout" ? + "Connection timed out" : + "Request timed out"); delete reply; throw exception; } @@ -570,5 +595,11 @@ NvHTTP::openConnection(QUrl baseUrl, } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) + // The requestSent signal must have stopped the timer for any successful request, + // otherwise we could falsely time out a slow response when we connected fine. + Q_ASSERT(!connectTimer->isActive()); +#endif + return reply; }