summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@qt.io>2023-11-02 14:41:39 +0200
committerJuha Vuolle <juha.vuolle@qt.io>2023-12-08 15:53:34 +0200
commit090991123dd82796fe956e4153bc26ace22280ca (patch)
tree662642bf647c898e8f9f0975071da6decd0ffae7
parent298d5a4bbd3d5f68034c5419f1b80e4535a5c6f5 (diff)
Support for std::chrono as transferTimeout type
Provide users with means to use more modern time/duration type. Please note that since QTimer does not currently support timeouts larger than 'int' milliseconds, the limit on how long durations can be expressed, remains. This should not be an issue in practice with network requests, as a typical int32 system can express timeouts of ~24 days. [ChangeLog][QtNetwork][QNetworkAccessManager] Add std::chrono support for transfer timeout. [ChangeLog][QtNetwork][QNetworkRequest] Add std::chrono support for transfer timeout. Fixes: QTBUG-118714 Change-Id: If85678a5994c59bac5926e47f98c9cfeb2a07c30 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp48
-rw-r--r--src/network/access/qnetworkaccessmanager.h6
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h2
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp5
-rw-r--r--src/network/access/qnetworkrequest.cpp57
-rw-r--r--src/network/access/qnetworkrequest.h7
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp114
7 files changed, 147 insertions, 92 deletions
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 4ca51fb98f..668e7e20a0 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 5704061f15..6aa05b5cc8 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 050da2060f..491a5acaa4 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 0fa4e24264..53bc0f5283 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 9c13d5474d..10ecf6dd7b 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<QNetworkRequest>(); }
~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.
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
+*/
+void QNetworkRequest::setTransferTimeout(int 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::DefaultTransferTimeoutConstant. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero.
- \sa transferTimeout
+ \sa transferTimeoutAsDuration()
*/
-void QNetworkRequest::setTransferTimeout(int timeout)
+void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
{
- d->transferTimeout = timeout;
+ 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 9ac3065ed6..7a656ce092 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<QNetworkRequestPrivate> d;
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index c169e77cac..099cf1b047 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<QNetworkReply> 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);
-
- QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
- QNetworkReplyPtr reply(manager.get(request));
- QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
-
- QCOMPARE(waitForFinish(reply), int(Success));
-
- QCOMPARE(spy.size(), 0);
- QVERIFY(reply->error() == QNetworkReply::NoError);
+ using Operation = QNetworkAccessManager::Operation;
+ QTest::addColumn<Operation>("method");
+ QTest::addColumn<int>("reqInt");
+ QTest::addColumn<std::chrono::milliseconds>("reqChrono");
+ QTest::addColumn<int>("mgrInt");
+ QTest::addColumn<std::chrono::milliseconds>("mgrChrono");
- request.setTransferTimeout(1000);
- server.stopTransfer = true;
-
- QNetworkReplyPtr reply2(manager.get(request));
- QSignalSpy spy2(reply2.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(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)));
-
- QCOMPARE(waitForFinish(reply), int(Success));
-
- 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);
+ if (reqInt > 0)
+ request.setTransferTimeout(reqInt);
+ if (reqChrono > 0ms)
+ request.setTransferTimeout(reqChrono);
+ if (mgrInt > 0)
+ manager.setTransferTimeout(mgrInt);
+ if (mgrChrono > 0ms)
+ manager.setTransferTimeout(mgrChrono);
- 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);
+ QNetworkReplyPtr reply;
+ if (method == QNetworkAccessManager::GetOperation)
+ reply.reset(manager.get(request));
+ else if (method == QNetworkAccessManager::PostOperation)
+ reply.reset(manager.post(request, data));
+ QVERIFY(reply);
- 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()