diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 4ca51fb98f9..668e7e20a0e 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +using namespace std::chrono_literals; Q_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager") @@ -1172,8 +1173,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera } #if QT_CONFIG(http) || defined (Q_OS_WASM) - if (!req.transferTimeout()) - req.setTransferTimeout(transferTimeout()); + if (req.transferTimeoutAsDuration() == 0ms) + req.setTransferTimeout(transferTimeoutAsDuration()); #endif if (autoDeleteReplies() @@ -1428,12 +1429,11 @@ void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete) Returns the timeout used for transfers, in milliseconds. - This timeout is zero if setTransferTimeout() hasn't been - called, which means that the timeout is not used. + \sa setTransferTimeout() */ int QNetworkAccessManager::transferTimeout() const { - return d_func()->transferTimeout; + return int(d_func()->transferTimeout.count()); } /*! @@ -1441,21 +1441,49 @@ int QNetworkAccessManager::transferTimeout() const Sets \a timeout as the transfer timeout in milliseconds. + \sa setTransferTimeout(std::chrono::milliseconds), + transferTimeout(), transferTimeoutAsDuration() +*/ +void QNetworkAccessManager::setTransferTimeout(int timeout) +{ + setTransferTimeout(std::chrono::milliseconds(timeout)); +} + +/*! + \since 6.7 + + Returns the timeout duration after which the transfer is aborted if no + data is exchanged. + + The default duration is zero, which means that the timeout is not used. + + \sa setTransferTimeout(std::chrono::milliseconds) + */ +std::chrono::milliseconds QNetworkAccessManager::transferTimeoutAsDuration() const +{ + return d_func()->transferTimeout; +} + +/*! + \since 6.7 + + Sets the timeout \a duration to abort the transfer if no data is exchanged. + Transfers are aborted if no bytes are transferred before the timeout expires. Zero means no timer is set. If no argument is provided, the timeout is - QNetworkRequest::DefaultTransferTimeoutConstant. If this function + QNetworkRequest::DefaultTransferTimeout. If this function is not called, the timeout is disabled and has the value zero. The request-specific non-zero timeouts set for the requests that are executed override this value. This means that if QNetworkAccessManager has an enabled timeout, it needs to be disabled to execute a request without a timeout. - \sa transferTimeout() -*/ -void QNetworkAccessManager::setTransferTimeout(int timeout) + \sa transferTimeoutAsDuration() + */ +void QNetworkAccessManager::setTransferTimeout(std::chrono::milliseconds duration) { - d_func()->transferTimeout = timeout; + d_func()->transferTimeout = duration; } void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply) diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 5704061f153..6aa05b5cc81 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -112,7 +112,11 @@ public: void setAutoDeleteReplies(bool autoDelete); int transferTimeout() const; - void setTransferTimeout(int timeout = QNetworkRequest::DefaultTransferTimeoutConstant); + void setTransferTimeout(int timeout); + + std::chrono::milliseconds transferTimeoutAsDuration() const; + void setTransferTimeout(std::chrono::milliseconds duration = + QNetworkRequest::DefaultTransferTimeout); Q_SIGNALS: #ifndef QT_NO_NETWORKPROXY diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index 050da2060f8..491a5acaa44 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -131,7 +131,7 @@ public: bool autoDeleteReplies = false; - int transferTimeout = 0; + std::chrono::milliseconds transferTimeout{0}; Q_DECLARE_PUBLIC(QNetworkAccessManager) }; diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 0fa4e24264c..53bc0f5283d 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -34,6 +34,7 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; using namespace QtMiscUtils; +using namespace std::chrono_literals; class QNetworkProxy; @@ -2047,9 +2048,9 @@ void QNetworkReplyHttpImplPrivate::setupTransferTimeout() Qt::QueuedConnection); } transferTimeout->stop(); - if (request.transferTimeout()) { + if (request.transferTimeoutAsDuration() > 0ms) { transferTimeout->setSingleShot(true); - transferTimeout->setInterval(request.transferTimeout()); + transferTimeout->setInterval(request.transferTimeoutAsDuration()); QMetaObject::invokeMethod(transferTimeout, "start", Qt::QueuedConnection); diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 9c13d5474d8..10ecf6dd7b4 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -28,6 +28,7 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +using namespace std::chrono_literals; QT_IMPL_METATYPE_EXTERN(QNetworkRequest) QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest__RedirectPolicy) @@ -407,6 +408,16 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_ \value DefaultTransferTimeoutConstant The transfer timeout in milliseconds. Used if setTimeout() is called without an argument. + + \sa QNetworkRequest::DefaultTransferTimeout + */ + +/*! + \variable QNetworkRequest::DefaultTransferTimeout + + The transfer timeout with \l {QNetworkRequest::TransferTimeoutConstant} + milliseconds. Used if setTransferTimeout() is called without an + argument. */ class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate @@ -419,7 +430,6 @@ public: , sslConfiguration(nullptr) #endif , maxRedirectsAllowed(maxRedirectCount) - , transferTimeout(0) { qRegisterMetaType(); } ~QNetworkRequestPrivate() { @@ -479,7 +489,7 @@ public: QHttp2Configuration h2Configuration; qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll; #endif - int transferTimeout; + std::chrono::milliseconds transferTimeout = 0ms; }; /*! @@ -977,14 +987,11 @@ void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold) Returns the timeout used for transfers, in milliseconds. - This timeout is zero if setTransferTimeout hasn't been - called, which means that the timeout is not used. - - \sa setTransferTimeout + \sa setTransferTimeout() */ int QNetworkRequest::transferTimeout() const { - return d->transferTimeout; + return int(d->transferTimeout.count()); } /*! @@ -992,18 +999,46 @@ int QNetworkRequest::transferTimeout() const Sets \a timeout as the transfer timeout in milliseconds. - Transfers are aborted if no bytes are transferred before - the timeout expires. Zero means no timer is set. If no - argument is provided, the timeout is - QNetworkRequest::DefaultTransferTimeoutConstant. If this function - is not called, the timeout is disabled and has the - value zero. - - \sa transferTimeout + \sa setTransferTimeout(std::chrono::milliseconds), + transferTimeout(), transferTimeoutAsDuration() */ void QNetworkRequest::setTransferTimeout(int timeout) { - d->transferTimeout = timeout; + d->transferTimeout = std::chrono::milliseconds(timeout); +} + +/*! + \since 6.7 + + Returns the timeout duration after which the transfer is aborted if no + data is exchanged. + + The default duration is zero, which means that the timeout is not used. + + \sa setTransferTimeout(std::chrono::milliseconds) +*/ +std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const +{ + return d->transferTimeout; +} + +/*! + \since 6.7 + + Sets the timeout \a duration to abort the transfer if no data is exchanged. + + Transfers are aborted if no bytes are transferred before + the timeout expires. Zero means no timer is set. If no + argument is provided, the timeout is + QNetworkRequest::DefaultTransferTimeout. If this function + is not called, the timeout is disabled and has the + value zero. + + \sa transferTimeoutAsDuration() +*/ +void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration) +{ + d->transferTimeout = duration; } #endif // QT_CONFIG(http) || defined (Q_OS_WASM) diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 9ac3065ed63..7a656ce092a 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -97,6 +97,9 @@ public: DefaultTransferTimeoutConstant = 30000 }; + static constexpr auto DefaultTransferTimeout = + std::chrono::milliseconds(DefaultTransferTimeoutConstant); + QNetworkRequest(); explicit QNetworkRequest(const QUrl &url); QNetworkRequest(const QNetworkRequest &other); @@ -163,7 +166,9 @@ public: #if QT_CONFIG(http) || defined (Q_OS_WASM) int transferTimeout() const; - void setTransferTimeout(int timeout = DefaultTransferTimeoutConstant); + void setTransferTimeout(int timeout); + std::chrono::milliseconds transferTimeoutAsDuration() const; + void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout); #endif // QT_CONFIG(http) || defined (Q_OS_WASM) private: QSharedDataPointer d; diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index c169e77cac7..099cf1b0470 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -91,6 +91,7 @@ Q_DECLARE_METATYPE(QNetworkProxyQuery) typedef QSharedPointer QNetworkReplyPtr; using namespace Qt::StringLiterals; +using namespace std::chrono_literals; #if QT_CONFIG(ssl) QT_BEGIN_NAMESPACE @@ -535,8 +536,8 @@ private Q_SLOTS: void autoDeleteReplies_data(); void autoDeleteReplies(); - void getWithTimeout(); - void postWithTimeout(); + void requestWithTimeout_data(); + void requestWithTimeout(); void moreActivitySignals_data(); void moreActivitySignals(); @@ -9884,82 +9885,63 @@ void tst_QNetworkReply::autoDeleteReplies() } } -void tst_QNetworkReply::getWithTimeout() +void tst_QNetworkReply::requestWithTimeout_data() { - MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + using Operation = QNetworkAccessManager::Operation; + QTest::addColumn("method"); + QTest::addColumn("reqInt"); + QTest::addColumn("reqChrono"); + QTest::addColumn("mgrInt"); + QTest::addColumn("mgrChrono"); - QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); - QNetworkReplyPtr reply(manager.get(request)); - QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); + QTest::addRow("get_req_int") << Operation::GetOperation << 500 << 0ms << 0 << 0ms; + QTest::addRow("get_req_chrono") << Operation::GetOperation << 0 << 500ms << 0 << 0ms; + QTest::addRow("get_mgr_int") << Operation::GetOperation << 0 << 0ms << 500 << 0ms; + QTest::addRow("get_mgr_chrono") << Operation::GetOperation << 0 << 0ms << 0 << 500ms; - QCOMPARE(waitForFinish(reply), int(Success)); - - QCOMPARE(spy.size(), 0); - QVERIFY(reply->error() == QNetworkReply::NoError); - - request.setTransferTimeout(1000); - server.stopTransfer = true; - - QNetworkReplyPtr reply2(manager.get(request)); - QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply2), int(Failure)); - - QCOMPARE(spy2.size(), 1); - QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); - - request.setTransferTimeout(0); - manager.setTransferTimeout(1000); - - QNetworkReplyPtr reply3(manager.get(request)); - QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply3), int(Failure)); - - QCOMPARE(spy3.size(), 1); - QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError); - - manager.setTransferTimeout(0); + QTest::addRow("post_req_int") << Operation::PostOperation << 500 << 0ms << 0 << 0ms; + QTest::addRow("post_req_chrono") << Operation::PostOperation << 0 << 500ms << 0 << 0ms; + QTest::addRow("post_mgr_int") << Operation::PostOperation << 0 << 0ms << 500 << 0ms; + QTest::addRow("post_mgr_chrono") << Operation::PostOperation << 0 << 0ms << 0 << 500ms; } -void tst_QNetworkReply::postWithTimeout() +void tst_QNetworkReply::requestWithTimeout() { + QFETCH(QNetworkAccessManager::Operation, method); + QFETCH(int, reqInt); + QFETCH(int, mgrInt); + QFETCH(std::chrono::milliseconds, reqChrono); + QFETCH(std::chrono::milliseconds, mgrChrono); + const auto data = "some data"_ba; + // Manager instance remains between case runs => always reset it's transferTimeout to + // ensure setting its transferTimeout in this case has effect + manager.setTransferTimeout(0ms); + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + server.stopTransfer = true; QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); request.setRawHeader("Content-Type", "application/octet-stream"); - QByteArray postData("Just some nonsense"); - QNetworkReplyPtr reply(manager.post(request, postData)); - QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); + if (reqInt > 0) + request.setTransferTimeout(reqInt); + if (reqChrono > 0ms) + request.setTransferTimeout(reqChrono); + if (mgrInt > 0) + manager.setTransferTimeout(mgrInt); + if (mgrChrono > 0ms) + manager.setTransferTimeout(mgrChrono); - QCOMPARE(waitForFinish(reply), int(Success)); + QNetworkReplyPtr reply; + if (method == QNetworkAccessManager::GetOperation) + reply.reset(manager.get(request)); + else if (method == QNetworkAccessManager::PostOperation) + reply.reset(manager.post(request, data)); + QVERIFY(reply); - QCOMPARE(spy.size(), 0); - QVERIFY(reply->error() == QNetworkReply::NoError); - - request.setTransferTimeout(1000); - server.stopTransfer = true; - - QNetworkReplyPtr reply2(manager.post(request, postData)); - QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply2), int(Failure)); - - QCOMPARE(spy2.size(), 1); - QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); - - request.setTransferTimeout(0); - manager.setTransferTimeout(1000); - - QNetworkReplyPtr reply3(manager.post(request, postData)); - QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply3), int(Failure)); - - QCOMPARE(spy3.size(), 1); - QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError); - - manager.setTransferTimeout(0); + QSignalSpy spy(reply.data(), &QNetworkReply::errorOccurred); + QCOMPARE(waitForFinish(reply), int(Failure)); + QCOMPARE(spy.size(), 1); + QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError); } void tst_QNetworkReply::moreActivitySignals_data()