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>
This commit is contained in:
parent
298d5a4bbd
commit
090991123d
@ -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)
|
||||
|
@ -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
|
||||
|
@ -131,7 +131,7 @@ public:
|
||||
|
||||
bool autoDeleteReplies = false;
|
||||
|
||||
int transferTimeout = 0;
|
||||
std::chrono::milliseconds transferTimeout{0};
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkAccessManager)
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
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");
|
||||
|
||||
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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user