summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h1
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp12
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h6
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp16
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp2
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h2
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp3
-rw-r--r--src/network/access/qnetworkrequest.cpp99
-rw-r--r--src/network/access/qnetworkrequest.h21
-rw-r--r--src/network/access/qtcpkeepaliveconfiguration_p.h50
-rw-r--r--src/network/socket/qabstractsocket.cpp14
-rw-r--r--src/network/socket/qabstractsocket_p.h2
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 &param
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 &params);
+ 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;