Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package qt6-httpserver for openSUSE:Factory
checked in at 2026-10-18 19:21:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qt6-httpserver (Old)
and /work/SRC/openSUSE:Factory/.qt6-httpserver.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qt6-httpserver"
Sun Oct 18 19:21:26 2026 rev:30 rq:1384393 version:6.12.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/qt6-httpserver/qt6-httpserver.changes
2026-08-21 16:51:41.607497879 +0200
+++
/work/SRC/openSUSE:Factory/.qt6-httpserver.new.3698736/qt6-httpserver.changes
2026-10-18 19:21:27.852133524 +0200
@@ -1,0 +2,6 @@
+Wed Sep 30 16:09:58 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 6.12.0
+ https://www.qt.io/blog/qt-6.12-released
+
+-------------------------------------------------------------------
Old:
----
qthttpserver-everywhere-src-6.11.2.tar.xz
New:
----
qthttpserver-everywhere-src-6.12.0.tar.xz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ qt6-httpserver.spec ++++++
--- /var/tmp/diff_new_pack.Hvdn46/_old 2026-10-18 19:21:28.710169145 +0200
+++ /var/tmp/diff_new_pack.Hvdn46/_new 2026-10-18 19:21:28.711169187 +0200
@@ -16,8 +16,8 @@
#
-%define real_version 6.11.2
-%define short_version 6.11
+%define real_version 6.12.0
+%define short_version 6.12
%define tar_name qthttpserver-everywhere-src
%define tar_suffix %{nil}
#
@@ -27,7 +27,7 @@
%endif
#
Name: qt6-httpserver%{?pkg_suffix}
-Version: 6.11.2
+Version: 6.12.0
Release: 0
Summary: Qt HTTP Server
License: GPL-3.0-only
++++++ qthttpserver-everywhere-src-6.11.2.tar.xz ->
qthttpserver-everywhere-src-6.12.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/.cmake.conf
new/qthttpserver-everywhere-src-6.12.0/.cmake.conf
--- old/qthttpserver-everywhere-src-6.11.2/.cmake.conf 2026-08-12
04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/.cmake.conf 2026-09-27
12:11:39.000000000 +0200
@@ -1,9 +1,10 @@
-set(QT_REPO_MODULE_VERSION "6.11.2")
+set(QT_REPO_MODULE_VERSION "6.12.0")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES
"QT_ENABLE_STRICT_MODE_UP_TO=QT_VERSION_CHECK(6,11,0)"
# not yet in strict mode:
"QT_LEAN_HEADERS=2"
+ "QT_NO_DISCONNECT_CONST_CONNECTION=1"
"QT_NO_QPAIR=1"
"QT_NO_SINGLE_ARGUMENT_QHASH_OVERLOAD=1"
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/.tag
new/qthttpserver-everywhere-src-6.12.0/.tag
--- old/qthttpserver-everywhere-src-6.11.2/.tag 2026-08-12 04:49:05.000000000
+0200
+++ new/qthttpserver-everywhere-src-6.12.0/.tag 2026-09-27 12:11:39.000000000
+0200
@@ -1 +1 @@
-86edbe897f1e7097cff1ae392e455f3055b84e68
+b9b95d5ef79f88948c6f5f6f91742cbeea8cea1e
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/CONTRIBUTING.md
new/qthttpserver-everywhere-src-6.12.0/CONTRIBUTING.md
--- old/qthttpserver-everywhere-src-6.11.2/CONTRIBUTING.md 1970-01-01
01:00:00.000000000 +0100
+++ new/qthttpserver-everywhere-src-6.12.0/CONTRIBUTING.md 2026-09-27
12:11:39.000000000 +0200
@@ -0,0 +1,11 @@
+## Qt contribution guidelines
+
+We welcome contributions to Qt!
+
+Note that we cannot accept pull requests on GitHub. All contributions
+to the Qt project are exclusively handled through the
+[Gerrit code review system](https://codereview.qt-project.org).
+
+See
+[contribute.qt-project.org](https://contribute.qt-project.org/) to learn
+more.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/REUSE.toml
new/qthttpserver-everywhere-src-6.12.0/REUSE.toml
--- old/qthttpserver-everywhere-src-6.11.2/REUSE.toml 2026-08-12
04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/REUSE.toml 2026-09-27
12:11:39.000000000 +0200
@@ -33,7 +33,8 @@
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
[[annotations]]
-path = ["doc/images/**", "**/README*", "**.qdocconf"]
+path = ["doc/images/**", "**.qdocconf",
+ "**/README*", "CONTRIBUTING.md"]
comment = "documentation"
precedence = "closest"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/dependencies.yaml
new/qthttpserver-everywhere-src-6.12.0/dependencies.yaml
--- old/qthttpserver-everywhere-src-6.11.2/dependencies.yaml 2026-08-12
04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/dependencies.yaml 2026-09-27
12:11:39.000000000 +0200
@@ -1,7 +1,7 @@
dependencies:
../qtbase:
- ref: ef55f427f2c8b410d34f8a7681020a3000cf6866
+ ref: 025bdad181de241e81bf853c8a2d7bf3d19261a9
required: true
../qtwebsockets:
- ref: 3eab06de7076a83514e98c2403baa433d137b013
+ ref: 6aedb199b5d8ee4afa4cfc956ded22bc0a2632e9
required: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/doc/qthttpserver-security.qdoc
new/qthttpserver-everywhere-src-6.12.0/doc/qthttpserver-security.qdoc
--- old/qthttpserver-everywhere-src-6.11.2/doc/qthttpserver-security.qdoc
2026-08-12 04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/doc/qthttpserver-security.qdoc
2026-09-27 12:11:39.000000000 +0200
@@ -27,7 +27,10 @@
Ideally there should be no access to the internet at all. This is the
safest setup, but you often need internet access. If Qt HTTP Server
is bound to localhost or to a \l{QLocalServer}, it does not accept
- connections from the internet.
+ connections from the internet. If multiple clients are connected to
+ 127.0.0.1, the connections are added to connections per host,
+ and \l{QHttpServerConfiguration::}{setMaximumConnectionsPerHost()}
+ is affected. By default, no per-host connection limit is set.
\section2 Binding to QSslServer on the internet
@@ -58,6 +61,19 @@
\l{QHttpServerConfiguration::}{setRateLimitPerSecond()}.
By default, there is no rate limit. When the limit is exceeded,
the server responds with status code 429 (Too Many Requests).
+
+ \li Configure the maximum number of simultaneous connections
+ using \l{QHttpServerConfiguration::}{setMaximumConnections()}.
+ By default, there is no connection limit. When the limit is
+ exceeded, the server responds with status code 429 (Too Many
+ Requests), and the connection is closed.
+
+ \li Configure the maximum number of simultaneous
+ connections per client IP using
+ \l{QHttpServerConfiguration::}{setMaximumConnectionsPerHost()}.
+ By default, there is no limit. When the limit is exceeded,
+ the server responds with status code 429 (Too Many Requests), and
+ the connection is closed.
\endlist
\section2 Connection limits
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qthttpserver-everywhere-src-6.11.2/licenseRule.json
new/qthttpserver-everywhere-src-6.12.0/licenseRule.json
--- old/qthttpserver-everywhere-src-6.11.2/licenseRule.json 2026-08-12
04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/licenseRule.json 2026-09-27
12:11:39.000000000 +0200
@@ -58,7 +58,9 @@
},
{
"comment": "Files with the following endings are Documentation
licensed.",
- "file_pattern_ending": [".qdoc", ".qdocinc" , ".qdocconf", "README",
"qt_attribution.json"],
+ "file_pattern_ending": [".qdoc", ".qdocinc" , ".qdocconf",
+ "README", "CONTRIBUTING.md",
+ "qt_attribution.json"],
"location":{
"": {
"comment": "",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qabstracthttpserver.cpp
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qabstracthttpserver.cpp
---
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qabstracthttpserver.cpp
2026-08-12 04:49:05.000000000 +0200
+++
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qabstracthttpserver.cpp
2026-09-27 12:11:39.000000000 +0200
@@ -14,6 +14,10 @@
#include <QtCore/qloggingcategory.h>
#include <QtNetwork/qtcpserver.h>
#include <QtNetwork/qtcpsocket.h>
+#if QT_CONFIG(ssl) && QT_CONFIG(http)
+#include <QtNetwork/private/qhttp2connection_p.h>
+#endif
+
#if QT_CONFIG(localserver)
#include <QtNetwork/qlocalserver.h>
#include <QtNetwork/qlocalsocket.h>
@@ -31,7 +35,29 @@
QT_BEGIN_NAMESPACE
-Q_STATIC_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver")
+Q_STATIC_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver", QtWarningMsg)
+
+bool LessHostAddress::operator()(const QHostAddress &lhs, const QHostAddress
&rhs) const
+{
+ bool lhsIsIPv4 = false;
+ bool rhsIsIPv4 = false;
+ quint32 lhsIpAddressV4 = lhs.toIPv4Address(&lhsIsIPv4);
+ quint32 rhsIpAddressV4 = rhs.toIPv4Address(&rhsIsIPv4);
+ if (lhsIsIPv4 != rhsIsIPv4)
+ return lhsIsIPv4;
+ if (lhsIsIPv4)
+ return lhsIpAddressV4 < rhsIpAddressV4;
+
+ Q_IPV6ADDR lhsIpAddressV6 = lhs.toIPv6Address();
+ Q_IPV6ADDR rhsIpAddressV6 = rhs.toIPv6Address();
+ for (int i = 0; i < 16; ++i) {
+ if (lhsIpAddressV6[i] != rhsIpAddressV6[i])
+ return lhsIpAddressV6[i] < rhsIpAddressV6[i];
+ }
+
+ return false;
+}
+
/*!
\internal
@@ -84,9 +110,23 @@
void QAbstractHttpServerPrivate::createHttp1Handler(QIODevice *socket)
{
+ using namespace std::chrono_literals;
Q_Q(QAbstractHttpServer);
+ if (hasTooManyConnections(socket)) {
+ if (auto tcpSocket = qobject_cast<QTcpSocket *>(socket)) {
+ QByteArray buffer;
+ buffer.append("HTTP/1.1 429 Too Many Requests");
+ buffer.append("\r\nConnection: close\r\n\r\n");
+ tcpSocket->write(buffer);
+ tcpSocket->flush();
+ QTimer::singleShot(1s, tcpSocket, &QObject::deleteLater);
+ }
+ return;
+ }
+
auto handler = new QHttpServerHttp1ProtocolHandler(q, socket,
&requestFilter, &configuration);
+ updateSocketCounter(socket);
QObject::connect(&heartbeatTimer, &QTimer::timeout,
handler, &QHttpServerHttp1ProtocolHandler::checkKeepAliveTimeout);
}
@@ -94,14 +134,89 @@
#if QT_CONFIG(ssl) && QT_CONFIG(http)
void QAbstractHttpServerPrivate::createHttp2Handler(QIODevice *socket)
{
+ using namespace std::chrono_literals;
Q_Q(QAbstractHttpServer);
+ if (hasTooManyConnections(socket)) {
+ if (auto tcpSocket = qobject_cast<QTcpSocket *>(socket)) {
+ auto *connection = QHttp2Connection::createDirectServerConnection(
+ socket, q->http2Configuration());
+ connection->close(Http2::Http2Error::ENHANCE_YOUR_CALM);
+ QTimer::singleShot(1s, tcpSocket, &QObject::deleteLater);
+ }
+ return;
+ }
+
auto handler = new QHttpServerHttp2ProtocolHandler(q, socket,
&requestFilter);
+ updateSocketCounter(socket);
QObject::connect(&heartbeatTimer, &QTimer::timeout,
handler, &QHttpServerHttp2ProtocolHandler::checkKeepAliveTimeout);
}
#endif
+bool QAbstractHttpServerPrivate::hasTooManyConnections(QIODevice *socket)
+{
+ if (configuration.maximumConnections() != 0
+ && totalConnections >= configuration.maximumConnections())
+ return true;
+
+ if (configuration.maximumConnectionsPerHost() == 0)
+ return false;
+
+ if (auto tcpSocket = qobject_cast<QTcpSocket *>(socket)) {
+ auto foundNumberOfConnections =
connectionsPerHost.find(tcpSocket->peerAddress());
+ if (foundNumberOfConnections != connectionsPerHost.end()) {
+ if (foundNumberOfConnections->second >=
configuration.maximumConnectionsPerHost()) {
+ qCInfo(lcHttpServer) << "QAbstractHttpServer:"
+ << "closing socket: too many connections
from"
+ << tcpSocket->peerAddress().toString();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void QAbstractHttpServerPrivate::updateSocketCounter(QIODevice *socket)
+{
+ // If there are no limits on connections per host or in total, there's no
point in tracking
+ // the amount of connections, so we skip doing so.
+ if (configuration.maximumConnectionsPerHost() == 0 &&
configuration.maximumConnections() == 0)
+ return;
+
+ if (auto tcpSocket = qobject_cast<QTcpSocket *>(socket)) {
+ auto [it, _] =
connectionsPerHost.try_emplace(tcpSocket->peerAddress(), 0);
+ ++(it->second);
+ ++totalConnections;
+ QObjectPrivate::connect(tcpSocket, &QAbstractSocket::disconnected,
this,
+
&QAbstractHttpServerPrivate::socketDisconnected);
+ }
+}
+
+void QAbstractHttpServerPrivate::socketDisconnected()
+{
+
+ Q_Q(QAbstractHttpServer);
+ if (auto tcpSocket = qobject_cast<QTcpSocket *>(q->sender())) {
+ --totalConnections;
+ auto foundNumberOfConnections =
connectionsPerHost.find(tcpSocket->peerAddress());
+ if (foundNumberOfConnections != connectionsPerHost.end()) {
+ if (foundNumberOfConnections->second == 1)
+ connectionsPerHost.erase(foundNumberOfConnections);
+ else
+ --(foundNumberOfConnections->second);
+ }
+ }
+}
+
+#if QT_CONFIG(ssl)
+void QAbstractHttpServerPrivate::handleStartedEncryptionHandshake(QSslSocket
*socket)
+{
+ if (hasTooManyConnections(socket))
+ socket->close();
+}
+#endif
+
void QAbstractHttpServerPrivate::restartHeartbeatTimer()
{
if (heartbeatTimer.isActive())
@@ -224,6 +339,13 @@
QObjectPrivate::connect(server, &QTcpServer::pendingConnectionAvailable, d,
&QAbstractHttpServerPrivate::handleNewConnections,
Qt::UniqueConnection);
+#if QT_CONFIG(ssl)
+ if (auto *sslServer = qobject_cast<QSslServer *>(server)) {
+
QObjectPrivate::connect(sslServer,&QSslServer::startedEncryptionHandshake, d,
+
&QAbstractHttpServerPrivate::handleStartedEncryptionHandshake,
+ Qt::UniqueConnection);
+ }
+#endif
return true;
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qabstracthttpserver_p.h
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qabstracthttpserver_p.h
---
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qabstracthttpserver_p.h
2026-08-12 04:49:05.000000000 +0200
+++
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qabstracthttpserver_p.h
2026-09-27 12:11:39.000000000 +0200
@@ -33,11 +33,17 @@
#if QT_CONFIG(ssl)
#include <QtNetwork/qhttp2configuration.h>
+#include <QtNetwork/qsslsocket.h>
#endif
QT_BEGIN_NAMESPACE
class QHttpServerRequest;
+class LessHostAddress
+{
+public:
+ [[nodiscard]] bool operator()(const QHostAddress &lhs, const QHostAddress
&rhs) const;
+};
class QAbstractHttpServerPrivate: public QObjectPrivate
{
@@ -65,7 +71,12 @@
void createHttp2Handler(QIODevice *socket);
#endif
void restartHeartbeatTimer();
-
+ [[nodiscard]] bool hasTooManyConnections(QIODevice *socket);
+ void updateSocketCounter(QIODevice *socket);
+ void socketDisconnected();
+#if QT_CONFIG(ssl)
+ void handleStartedEncryptionHandshake(QSslSocket *socket);
+#endif
#if defined(QT_WEBSOCKETS_LIB)
mutable bool handlingWebSocketUpgrade = false;
struct WebSocketUpgradeVerifier
@@ -81,6 +92,8 @@
QHttpServerConfiguration configuration;
QHttpServerRequestFilter requestFilter;
QTimer heartbeatTimer;
+ std::map<QHostAddress, quint32, LessHostAddress> connectionsPerHost;
+ quint32 totalConnections = 0;
};
QT_END_NAMESPACE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverconfiguration.cpp
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverconfiguration.cpp
---
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverconfiguration.cpp
2026-08-12 04:49:05.000000000 +0200
+++
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverconfiguration.cpp
2026-09-27 12:11:39.000000000 +0200
@@ -18,6 +18,8 @@
bool equals(const QHttpServerConfigurationPrivate &other) const noexcept
{
return rateLimit == other.rateLimit
+ && maxConnectionsPerHost == other.maxConnectionsPerHost
+ && maxConnections == other.maxConnections
&& keepAliveTimeout == other.keepAliveTimeout
&& whitelist == other.whitelist
&& blacklist == other.blacklist
@@ -29,6 +31,8 @@
}
quint32 rateLimit = 0;
+ quint32 maxConnectionsPerHost = 0;
+ quint32 maxConnections = 0;
std::chrono::seconds keepAliveTimeout = std::chrono::seconds(15);
QList<std::pair<QHostAddress, int>> whitelist;
QList<std::pair<QHostAddress, int>> blacklist;
@@ -47,6 +51,8 @@
Such a configuration has the following values:
\list
\li Rate limit is disabled.
+ \li Maximum connections is disabled.
+ \li Maximum connections per host is disabled.
\li Keep-alive timeout is set to 15 seconds.
\li Whitelist is empty.
\li Blacklist is empty.
@@ -124,6 +130,86 @@
}
/*!
+ \since 6.12
+
+ Sets limit on \a maxPerHost simultaneous connections
+ per host that will be accepted by QHttpServer.
+ If the limit is exceeded, QHttpServer will respond with
+ QHttpServerResponder::StatusCode::TooManyRequests.
+
+ If \a maxPerHost is set to 0, the per-host
+ connection limit is disabled. This should be done when
+ running behind a reverse proxy, as all incoming
+ sockets will appear to originate from the reverse proxy.
+
+ When the per-host connection limit is reached, HTTP/1
+ connections are closed with a 429 (Too Many Requests) response.
+ For SSL connections the connection is closed when the limit is exceeded.
+
+ By default this is disabled.
+
+ \sa maximumConnectionsPerHost(), QHttpServerResponder::StatusCode
+*/
+void QHttpServerConfiguration::setMaximumConnectionsPerHost(quint32 maxPerHost)
+{
+ d.detach();
+ d->maxConnectionsPerHost = maxPerHost;
+}
+
+/*!
+ \since 6.12
+
+ Returns maximum number of simultaneous connections
+ per host that will be accepted by the server.
+ The host is identified by the IP address of the incoming TCP
+ connection.
+
+ Returns 0 if the per-host connection limit is disabled.
+
+ \sa setMaximumConnectionsPerHost()
+*/
+quint32 QHttpServerConfiguration::maximumConnectionsPerHost() const
+{
+ return d->maxConnectionsPerHost;
+}
+
+/*!
+ \since 6.12
+
+ Sets limit of \a maxConnections on the total simultaneous
+ connections that will be accepted by the server.
+ If the limit is reached, the server rejects new connections. For
+ HTTP/1 connections, QHttpServer responds with
+ QHttpServerResponder::StatusCode::TooManyRequests and closes the
connection.
+
+ If \a maxConnections is set to 0, the total connection
+ limit is disabled.
+
+ By default this limit is disabled.
+
+ \sa maximumConnections(), QHttpServerResponder::StatusCode
+*/
+void QHttpServerConfiguration::setMaximumConnections(quint32 maxConnections)
+{
+ d.detach();
+ d->maxConnections = maxConnections;
+}
+
+/*!
+ \since 6.12
+
+ Returns the maximum number of simultaneous connections
+ accepted by the server in total.
+
+ Returns 0 if the total connection limit is disabled.
+
+ \sa setMaximumConnections()
+*/
+quint32 QHttpServerConfiguration::maximumConnections() const
+{
+ return d->maxConnections;
+}
+/*!
\since 6.10
Sets \a timeout as keep-alive timeout for QHttpServer.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverconfiguration.h
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverconfiguration.h
---
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverconfiguration.h
2026-08-12 04:49:05.000000000 +0200
+++
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverconfiguration.h
2026-09-27 12:11:39.000000000 +0200
@@ -34,6 +34,12 @@
Q_HTTPSERVER_EXPORT void setRateLimitPerSecond(quint32 maxRequests);
Q_HTTPSERVER_EXPORT quint32 rateLimitPerSecond() const;
+ Q_HTTPSERVER_EXPORT void setMaximumConnectionsPerHost(quint32 maxPerHost);
+ Q_HTTPSERVER_EXPORT quint32 maximumConnectionsPerHost() const;
+
+ Q_HTTPSERVER_EXPORT void setMaximumConnections(quint32 maxConnections);
+ Q_HTTPSERVER_EXPORT quint32 maximumConnections() const;
+
Q_HTTPSERVER_EXPORT void setKeepAliveTimeout(std::chrono::seconds timeout);
Q_HTTPSERVER_EXPORT std::chrono::seconds keepAliveTimeout() const;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverstream_p.h
new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverstream_p.h
--- old/qthttpserver-everywhere-src-6.11.2/src/httpserver/qhttpserverstream_p.h
2026-08-12 04:49:05.000000000 +0200
+++ new/qthttpserver-everywhere-src-6.12.0/src/httpserver/qhttpserverstream_p.h
2026-09-27 12:11:39.000000000 +0200
@@ -30,12 +30,14 @@
class QTcpSocket;
class QHttpServerResponderPrivate;
+class QAbstractHttpServerPrivate;
class QHttpServerStream : public QObject
{
Q_OBJECT
friend class QHttpServerResponderPrivate;
+ friend class QAbstractHttpServerPrivate;
protected:
QHttpServerStream(QIODevice *socket, QHttpServerRequestFilter *filter,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qthttpserver-everywhere-src-6.11.2/tests/auto/qhttpserver/tst_qhttpserver.cpp
new/qthttpserver-everywhere-src-6.12.0/tests/auto/qhttpserver/tst_qhttpserver.cpp
---
old/qthttpserver-everywhere-src-6.11.2/tests/auto/qhttpserver/tst_qhttpserver.cpp
2026-08-12 04:49:05.000000000 +0200
+++
new/qthttpserver-everywhere-src-6.12.0/tests/auto/qhttpserver/tst_qhttpserver.cpp
2026-09-27 12:11:39.000000000 +0200
@@ -21,6 +21,7 @@
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qsemaphore.h>
#include <QtCore/qset.h>
#include <QtCore/qtimer.h>
@@ -44,7 +45,6 @@
#endif
#include <QtTest/private/qtesthelpers_p.h>
-
#include <array>
QT_BEGIN_NAMESPACE
@@ -255,6 +255,10 @@
void concurrentRequestsToSequentialDevice();
void timeoutConnection();
void useCanceledResponders();
+ void maximumConnectionsPerHost_data();
+ void maximumConnectionsPerHost();
+ void maximumConnections_data();
+ void maximumConnections();
#if QT_CONFIG(localserver)
void localSocket();
@@ -267,7 +271,9 @@
QHttpServer httpserver;
QString clearUrlBase;
QString sslUrlBase;
- std::array<QNetworkAccessManager, 10> networkAccessManagers;
+ QString captureBase;
+ static constexpr qsizetype numberOfNetworkAccessManagers = 10;
+ std::array<QNetworkAccessManager, numberOfNetworkAccessManagers>
networkAccessManagers;
QNetworkAccessManager &networkAccessManager = networkAccessManagers[0];
ReplyObject replyObject;
std::vector<std::unique_ptr<QHttpServerResponder>> responders;
@@ -891,7 +897,8 @@
if (useSsl && QTestPrivate::isSecureTransportBlockingTest())
QSKIP("SslServer is blocking the test execution while trying to access
the login keychain");
#endif // QT_CONFIG(ssl)
- networkAccessManager.clearConnectionCache();
+ for (auto &qnam : networkAccessManagers)
+ qnam.clearConnectionCache();
}
void tst_QHttpServer::routeGet_data()
@@ -1464,25 +1471,15 @@
QString urlBase = useSsl ? sslUrlBase : clearUrlBase;
QNetworkRequest request;
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, useHttp2);
-
- httpserver.route("/capture-this/", this, [&urlBase] () {
- return urlBase;
- });
-
- QString msg = urlBase + "/pod";
- httpserver.route("/capture-non-pod-data/", this, [&msg] () {
- return msg;
+ captureBase = urlBase;
+ httpserver.route("/capture-this/", this, [this] () {
+ return captureBase;
});
request.setUrl(QUrl(urlBase.arg("/capture-this/")));
checkReply(networkAccessManager.get(request), urlBase);
if (QTest::currentTestFailed())
return;
-
- request.setUrl(QUrl(urlBase.arg("/capture-non-pod-data/")));
- checkReply(networkAccessManager.get(request), msg);
- if (QTest::currentTestFailed())
- return;
}
void tst_QHttpServer::afterRequest()
@@ -2341,6 +2338,144 @@
responder->writeEndChunked("end");
}
+void tst_QHttpServer::maximumConnectionsPerHost_data()
+{
+ QTest::addColumn<int>("limit");
+ QTest::addColumn<int>("expectedOkCount");
+
+ QTest::newRow("no-limit") << 0 << numberOfNetworkAccessManagers;
+ QTest::newRow("same-as-number-of-connections")
+ << numberOfNetworkAccessManagers <<
numberOfNetworkAccessManagers;
+ QTest::newRow("total-limit-5") << 5 << 5;
+}
+
+void tst_QHttpServer::maximumConnectionsPerHost()
+{
+#if QT_CONFIG(concurrent)
+ QFETCH_GLOBAL(bool, useSsl);
+ QFETCH_GLOBAL(bool, useHttp2);
+
+ QFETCH(int, limit);
+ QFETCH(int, expectedOkCount);
+ const QString urlBase = useSsl ? sslUrlBase : clearUrlBase;
+
+ auto cleanup = qScopeGuard([this] {
+ QHttpServerConfiguration config;
+ httpserver.setConfiguration(config);
+ for (auto &qnam : networkAccessManagers)
+ qnam.clearConnectionCache();
+ QCoreApplication::processEvents();
+ QLoggingCategory::setFilterRules(QStringLiteral(""));
+ });
+
+
QLoggingCategory::setFilterRules(QStringLiteral("qt.network.http2.warning=false"));
+ constexpr int waitInterval = 2000;
+ const QByteArray returnValue = QByteArray::number(waitInterval);
+ const QUrl requestUrl(urlBase.arg("/wait/") + returnValue);
+ constexpr qsizetype NumberOfConnections = numberOfNetworkAccessManagers;
+ std::array<std::unique_ptr<QNetworkReply>, NumberOfConnections> replies;
+ QThreadPool::globalInstance()->setMaxThreadCount(NumberOfConnections + 1);
+ QNetworkRequest req(requestUrl);
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+ req.setAttribute(QNetworkRequest::Http2AllowedAttribute, useHttp2);
+ QHttpServerConfiguration config;
+ config.setMaximumConnectionsPerHost(limit);
+ httpserver.setConfiguration(config);
+
+ for (qsizetype i = 0; i < NumberOfConnections; ++i)
+ replies[i].reset(networkAccessManagers[i].get(req));
+
+ int okCount = 0;
+ for (qsizetype i = 0; i < NumberOfConnections; ++i) {
+ QTRY_VERIFY(replies[i]->isFinished());
+ if (replies[i]->error() == QNetworkReply::NoError) {
+ ++okCount;
+ } else {
+ if (!useHttp2) {
+ bool isInt = false;
+ const int statusCode =
+
replies[i]->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&isInt);
+ QVERIFY(isInt);
+ QCOMPARE(statusCode, 429);
+ }
+ }
+ }
+ QCOMPARE(okCount, expectedOkCount);
+#else
+ QSKIP("QtConcurrent is not available, skipping test");
+#endif // QT_CONFIG(concurrent)
+}
+
+void tst_QHttpServer::maximumConnections_data()
+{
+ QTest::addColumn<int>("limit");
+ QTest::addColumn<int>("expectedOkCount");
+
+ QTest::newRow("no-limit") << 0 << numberOfNetworkAccessManagers;
+ QTest::newRow("same-as-number-of-connections")
+ << numberOfNetworkAccessManagers <<
numberOfNetworkAccessManagers;
+ QTest::newRow("total-limit-5") << 5 << 5;
+}
+
+void tst_QHttpServer::maximumConnections()
+{
+#if QT_CONFIG(concurrent)
+ QFETCH_GLOBAL(bool, useSsl);
+ QFETCH_GLOBAL(bool, useHttp2);
+
+ QFETCH(int, limit);
+ QFETCH(int, expectedOkCount);
+ const QString urlBase = useSsl ? sslUrlBase : clearUrlBase;
+
+ auto cleanup = qScopeGuard([this] {
+ QHttpServerConfiguration config;
+ httpserver.setConfiguration(config);
+ for (auto &qnam : networkAccessManagers)
+ qnam.clearConnectionCache();
+ QCoreApplication::processEvents();
+ QLoggingCategory::setFilterRules(QStringLiteral(""));
+ });
+
+
QLoggingCategory::setFilterRules(QStringLiteral("qt.network.http2.warning=false"));
+ constexpr int waitInterval = 2000;
+ const QByteArray returnValue = QByteArray::number(waitInterval);
+ const QUrl requestUrl(urlBase.arg("/wait/") + returnValue);
+ constexpr qsizetype NumberOfConnections = numberOfNetworkAccessManagers;
+ std::array<std::unique_ptr<QNetworkReply>, NumberOfConnections> replies;
+ QThreadPool::globalInstance()->setMaxThreadCount(NumberOfConnections + 1);
+ QNetworkRequest req(requestUrl);
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+ req.setAttribute(QNetworkRequest::Http2AllowedAttribute, useHttp2);
+ QHttpServerConfiguration config;
+ config.setMaximumConnections(limit);
+ httpserver.setConfiguration(config);
+
+ for (qsizetype i = 0; i < NumberOfConnections; ++i)
+ replies[i].reset(networkAccessManagers[i].get(req));
+
+ int okCount = 0;
+ for (qsizetype i = 0; i < NumberOfConnections; ++i) {
+ QTRY_VERIFY(replies[i]->isFinished());
+ if (replies[i]->error() == QNetworkReply::NoError) {
+ ++okCount;
+ } else {
+ if (!useHttp2) {
+ bool isInt = false;
+ const int statusCode =
+
replies[i]->attribute(QNetworkRequest::HttpStatusCodeAttribute)
+ .toInt(&isInt);
+ QVERIFY(isInt);
+ QCOMPARE(statusCode, 429);
+ }
+ }
+ }
+ QCOMPARE(okCount, expectedOkCount);
+#else
+ QSKIP("QtConcurrent is not available, skipping test");
+#endif // QT_CONFIG(concurrent)
+}
+
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(CustomArg);