diff options
Diffstat (limited to 'src/network')
| -rw-r--r-- | src/network/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/network/access/qhttp2protocolhandler_p.h | 1 | ||||
| -rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 12 | ||||
| -rw-r--r-- | src/network/access/qhttpnetworkconnection_p.h | 6 | ||||
| -rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel.cpp | 16 | ||||
| -rw-r--r-- | src/network/access/qhttpthreaddelegate.cpp | 2 | ||||
| -rw-r--r-- | src/network/access/qhttpthreaddelegate_p.h | 2 | ||||
| -rw-r--r-- | src/network/access/qnetworkreplyhttpimpl.cpp | 3 | ||||
| -rw-r--r-- | src/network/access/qnetworkrequest.cpp | 99 | ||||
| -rw-r--r-- | src/network/access/qnetworkrequest.h | 21 | ||||
| -rw-r--r-- | src/network/access/qtcpkeepaliveconfiguration_p.h | 50 | ||||
| -rw-r--r-- | src/network/socket/qabstractsocket.cpp | 14 | ||||
| -rw-r--r-- | src/network/socket/qabstractsocket_p.h | 2 |
13 files changed, 224 insertions, 5 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index cb304fc865c..2a15dca0635 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -148,6 +148,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http access/qrestaccessmanager.cpp access/qrestaccessmanager.h access/qrestaccessmanager_p.h access/qrestreply.cpp access/qrestreply.h access/qrestreply_p.h access/qsocketabstraction_p.h + access/qtcpkeepaliveconfiguration_p.h socket/qhttpsocketengine.cpp socket/qhttpsocketengine_p.h ) diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index 2fde9e4c9d5..37e960b19dc 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -93,7 +93,6 @@ private: // Stream's lifecycle management: QHttp2Stream *createNewStream(const HttpMessagePair &message, bool uploadDone = false); void connectStream(const HttpMessagePair &message, QHttp2Stream *stream); - quint32 popStreamToResume(); QHttp2Connection *h2Connection; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index e0f5bfc2d64..1b8bfd5d72b 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -1485,6 +1485,18 @@ void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration ¶m d->http2Parameters = params; } +QTcpKeepAliveConfiguration QHttpNetworkConnection::tcpKeepAliveParameters() const +{ + Q_D(const QHttpNetworkConnection); + return d->tcpKeepAliveConfiguration; +} + +void QHttpNetworkConnection::setTcpKeepAliveParameters(QTcpKeepAliveConfiguration config) +{ + Q_D(QHttpNetworkConnection); + d->tcpKeepAliveConfiguration = config; +} + // SSL support below #ifndef QT_NO_SSL void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config) diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 67b568caea8..f35b89d1aec 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -36,6 +36,7 @@ #include <private/http2protocol_p.h> #include <private/qhttpnetworkconnectionchannel_p.h> +#include <private/qtcpkeepaliveconfiguration_p.h> #include <utility> @@ -98,6 +99,9 @@ public: QHttp2Configuration http2Parameters() const; void setHttp2Parameters(const QHttp2Configuration ¶ms); + QTcpKeepAliveConfiguration tcpKeepAliveParameters() const; + void setTcpKeepAliveParameters(QTcpKeepAliveConfiguration config); + #ifndef QT_NO_SSL void setSslConfiguration(const QSslConfiguration &config); void ignoreSslErrors(int channel = -1); @@ -255,6 +259,8 @@ public: QString peerVerifyName; + QTcpKeepAliveConfiguration tcpKeepAliveConfiguration; + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index ffd5d8ff333..e427175ce61 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -923,9 +923,19 @@ void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket // not sure yet if it helps, but it makes sense absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); - int kaIdleOption = qEnvironmentVariableIntegerValue(keepAliveIdleOption).value_or(TCP_KEEPIDLE_DEF); - int kaIntervalOption = qEnvironmentVariableIntegerValue(keepAliveIntervalOption).value_or(TCP_KEEPINTVL_DEF); - int kaCountOption = qEnvironmentVariableIntegerValue(keepAliveCountOption).value_or(TCP_KEEPCNT_DEF); + QTcpKeepAliveConfiguration keepAliveConfig = connection->tcpKeepAliveParameters(); + + auto getKeepAliveValue = [](int configValue, + const char* envName, + int defaultValue) { + if (configValue > 0) + return configValue; + return static_cast<int>(qEnvironmentVariableIntegerValue(envName).value_or(defaultValue)); + }; + + int kaIdleOption = getKeepAliveValue(keepAliveConfig.idleTimeBeforeProbes.count(), keepAliveIdleOption, TCP_KEEPIDLE_DEF); + int kaIntervalOption = getKeepAliveValue(keepAliveConfig.intervalBetweenProbes.count(), keepAliveIntervalOption, TCP_KEEPINTVL_DEF); + int kaCountOption = getKeepAliveValue(keepAliveConfig.probeCount, keepAliveCountOption, TCP_KEEPCNT_DEF); absSocket->setSocketOption(QAbstractSocket::KeepAliveIdleOption, kaIdleOption); absSocket->setSocketOption(QAbstractSocket::KeepAliveIntervalOption, kaIntervalOption); absSocket->setSocketOption(QAbstractSocket::KeepAliveCountOption, kaCountOption); diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index fbbc55dc4a4..82455e96a81 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -327,6 +327,8 @@ void QHttpThreadDelegate::startRequest() || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { httpConnection->setHttp2Parameters(http2Parameters); } + + httpConnection->setTcpKeepAliveParameters(tcpKeepAliveParameters); #ifndef QT_NO_SSL // Set the QSslConfiguration from this QNetworkRequest. if (ssl) diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 2ce64dc9a17..f179d95ac17 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -34,6 +34,7 @@ #include "qnetworkaccessauthenticationmanager_p.h" #include <QtNetwork/private/http2protocol_p.h> #include <QtNetwork/qhttpheaders.h> +#include "qtcpkeepaliveconfiguration_p.h" #ifndef QT_NO_SSL #include <memory> @@ -91,6 +92,7 @@ public: QString incomingErrorDetail; QHttp1Configuration http1Parameters; QHttp2Configuration http2Parameters; + QTcpKeepAliveConfiguration tcpKeepAliveParameters; protected: // The zerocopy download buffer, if used: diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index f76d79571c3..9a27da00960 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -896,6 +896,9 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq // Propagate Http/2 settings: delegate->http2Parameters = request.http2Configuration(); delegate->http1Parameters = request.http1Configuration(); + delegate->tcpKeepAliveParameters.idleTimeBeforeProbes = request.tcpKeepAliveIdleTimeBeforeProbes(); + delegate->tcpKeepAliveParameters.intervalBetweenProbes = request.tcpKeepAliveIntervalBetweenProbes(); + delegate->tcpKeepAliveParameters.probeCount = request.tcpKeepAliveProbeCount(); if (request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).isValid()) delegate->connectionCacheExpiryTimeoutSeconds = request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).toInt(); diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 5047fc77bd5..d41124f7b14 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -475,6 +475,9 @@ public: decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold; #endif transferTimeout = other.transferTimeout; + idleTimeBeforeProbes = other.idleTimeBeforeProbes; + intervalBetweenProbes = other.intervalBetweenProbes; + probeCount = other.probeCount; } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -491,6 +494,9 @@ public: #endif && transferTimeout == other.transferTimeout && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders) + && idleTimeBeforeProbes == other.idleTimeBeforeProbes + && intervalBetweenProbes == other.intervalBetweenProbes + && probeCount == other.probeCount; ; // don't compare cookedHeaders } @@ -508,6 +514,9 @@ public: qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll; #endif std::chrono::milliseconds transferTimeout = 0ms; + std::chrono::duration<int> idleTimeBeforeProbes{0}; + std::chrono::duration<int> intervalBetweenProbes{0}; + int probeCount = 0; }; /*! @@ -1035,6 +1044,96 @@ void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold) } #endif // QT_CONFIG(http) +/*! + \since 6.11 + + Returns the time the connection needs to remain idle before TCP + starts sending keepalive probes, if the TCP Keepalive functionality has + been turned on. + + \sa setIdleTimeBeforeProbes +*/ + +std::chrono::seconds QNetworkRequest::tcpKeepAliveIdleTimeBeforeProbes() const +{ + return d->idleTimeBeforeProbes; +} + +/*! + \fn void QNetworkRequest::setTcpKeepAliveIdleTimeBeforeProbes(std::chrono::seconds idle) + \since 6.11 + + Sets the time the connection needs to remain idle before TCP starts + sending keepalive probes to be \a idle, if the TCP Keepalive + functionality has been turned on. + + \sa idleTimeBeforeProbes +*/ + +void QNetworkRequest::doSetIdleTimeBeforeProbes(std::chrono::duration<int> seconds) noexcept +{ + d->idleTimeBeforeProbes = seconds; +} + +/*! + \since 6.11 + + Returns the time between individual keepalive probes, if the TCP + Keepalive functionality has been turned on. + + \sa setIntervalBetweenProbes +*/ + +std::chrono::seconds QNetworkRequest::tcpKeepAliveIntervalBetweenProbes() const +{ + return d->intervalBetweenProbes; +} + +/*! + \fn void QNetworkRequest::setTcpKeepAliveIntervalBetweenProbes(std::chrono::seconds interval) + \since 6.11 + + Sets the time between individual keepalive probes to be \a interval, + if the TCP Keepalive functionality has been turned on. + + \sa intervalBetweenProbes +*/ + +void QNetworkRequest::doSetIntervalBetweenProbes(std::chrono::duration<int> seconds) noexcept +{ + d->intervalBetweenProbes = seconds; +} + +/*! + \since 6.11 + + Returns the maximum number of keepalive probes TCP should send before + dropping the connection, if the TCP Keepalive functionality has been + turned on. + + \sa setIntervalBetweenProbes +*/ + +int QNetworkRequest::tcpKeepAliveProbeCount() const +{ + return d->probeCount; +} + +/*! + \since 6.11 + + Sets the maximum number of keepalive \a probes TCP should send + before dropping the connection, if the TCP Keepalive functionality has + been turned on. + + \sa probeCount +*/ + +void QNetworkRequest::setTcpKeepAliveProbeCount(int probes) noexcept +{ + d->probeCount = probes; +} + #if QT_CONFIG(http) || defined (Q_OS_WASM) /*! \fn int QNetworkRequest::transferTimeout() const diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index ea70255a718..49aa45233af 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -7,12 +7,15 @@ #include <QtNetwork/qtnetworkglobal.h> #include <QtNetwork/qhttpheaders.h> + +#include <QtCore/qassert.h> #include <QtCore/QSharedDataPointer> #include <QtCore/QString> #include <QtCore/QUrl> #include <QtCore/QVariant> #include <QtCore/q26numeric.h> +#include <QtCore/q20utility.h> #include <chrono> @@ -178,6 +181,22 @@ public: qint64 decompressedSafetyCheckThreshold() const; void setDecompressedSafetyCheckThreshold(qint64 threshold); #endif // QT_CONFIG(http) + std::chrono::seconds tcpKeepAliveIdleTimeBeforeProbes() const; + void setTcpKeepAliveIdleTimeBeforeProbes(std::chrono::seconds idle) + { + const auto r = q26::saturate_cast<int>(idle.count()); + Q_PRE(q20::cmp_equal(r, idle.count())); + doSetIdleTimeBeforeProbes(std::chrono::duration<int>(r)); + } + std::chrono::seconds tcpKeepAliveIntervalBetweenProbes() const; + void setTcpKeepAliveIntervalBetweenProbes(std::chrono::seconds interval) + { + const auto r = q26::saturate_cast<int>(interval.count()); + Q_PRE(q20::cmp_equal(r, interval.count())); + doSetIntervalBetweenProbes(std::chrono::duration<int>(r)); + } + int tcpKeepAliveProbeCount() const; + void setTcpKeepAliveProbeCount(int probes) noexcept; #if QT_CONFIG(http) || defined (Q_OS_WASM) QT_NETWORK_INLINE_SINCE(6, 8) @@ -189,6 +208,8 @@ public: void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout); #endif // QT_CONFIG(http) || defined (Q_OS_WASM) private: + void doSetIdleTimeBeforeProbes(std::chrono::duration<int> idle) noexcept; + void doSetIntervalBetweenProbes(std::chrono::duration<int> interval) noexcept; QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; }; diff --git a/src/network/access/qtcpkeepaliveconfiguration_p.h b/src/network/access/qtcpkeepaliveconfiguration_p.h new file mode 100644 index 00000000000..b8bd96666ef --- /dev/null +++ b/src/network/access/qtcpkeepaliveconfiguration_p.h @@ -0,0 +1,50 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default + +#ifndef QTCPKEEPALIVECONFIGURATION_P_H +#define QTCPKEEPALIVECONFIGURATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <QtNetwork/qtnetworkglobal.h> + +#include <chrono> + +QT_BEGIN_NAMESPACE + +struct QTcpKeepAliveConfiguration +{ + std::chrono::duration<int> idleTimeBeforeProbes; + std::chrono::duration<int> intervalBetweenProbes; + int probeCount; + + bool isEqual(const QTcpKeepAliveConfiguration &other) const noexcept + { + return idleTimeBeforeProbes == other.idleTimeBeforeProbes + && intervalBetweenProbes == other.intervalBetweenProbes + && probeCount == other.probeCount; + } + + friend bool operator==(const QTcpKeepAliveConfiguration &lhs, const QTcpKeepAliveConfiguration &rhs) noexcept + { return lhs.isEqual(rhs); } + friend bool operator!=(const QTcpKeepAliveConfiguration &lhs, const QTcpKeepAliveConfiguration &rhs) noexcept + { return !lhs.isEqual(rhs); } + +}; + +Q_DECLARE_TYPEINFO(QTcpKeepAliveConfiguration, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif // QTCPKEEPALIVECONFIGURATION_P_H diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 975332a14ab..eb95d891e42 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -1045,7 +1045,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress() host = addresses.takeFirst(); #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connecting to %s:%i, %d left to try", - host.toString().toLatin1().constData(), port, addresses.count()); + host.toString().toLatin1().constData(), port, int(addresses.count())); #endif if (cachedSocketDescriptor == -1 && !initSocketLayer(host.protocol())) { @@ -1247,6 +1247,9 @@ void QAbstractSocketPrivate::emitReadyRead(int channel) void QAbstractSocketPrivate::emitBytesWritten(qint64 bytes, int channel) { Q_Q(QAbstractSocket); + + bytesWrittenEmissionCount++; + // Only emit bytesWritten() when not recursing. if (!emittedBytesWritten && channel == currentWriteChannel) { QScopedValueRollback<bool> r(emittedBytesWritten); @@ -2265,6 +2268,8 @@ bool QAbstractSocket::waitForBytesWritten(int msecs) if (d->writeBuffer.isEmpty()) return false; + const quint32 bwEmissionCountAtEntry = d->bytesWrittenEmissionCount; + QDeadlineTimer deadline{msecs}; // handle a socket in connecting state @@ -2304,6 +2309,13 @@ bool QAbstractSocket::waitForBytesWritten(int msecs) qDebug("QAbstractSocket::waitForBytesWritten returns true"); #endif return true; + } else if (d->bytesWrittenEmissionCount != bwEmissionCountAtEntry) { + // A slot connected to any signal emitted by this method has written data, which + // fulfills the condition to return true that at least one byte has been written. +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForBytesWritten returns true (write in signal handler)"); +#endif + return true; } } diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index 5f33eddbc7b..5a0a6489e2e 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -117,6 +117,8 @@ public: bool hasPendingData = false; bool hasPendingDatagram = false; + quint32 bytesWrittenEmissionCount = 0; + QTimer *connectTimer = nullptr; int hostLookupId = -1; |
