summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/network/access/qhttp1configuration.cpp125
-rw-r--r--src/network/access/qhttp1configuration.h51
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp11
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp6
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h2
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp1
-rw-r--r--src/network/access/qnetworkrequest.cpp28
-rw-r--r--src/network/access/qnetworkrequest.h4
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp18
10 files changed, 242 insertions, 5 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 48f0e737f0c..20a8280c6dd 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -121,6 +121,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http
access/http2/huffman.cpp access/http2/huffman_p.h
access/qabstractprotocolhandler.cpp access/qabstractprotocolhandler_p.h
access/qdecompresshelper.cpp access/qdecompresshelper_p.h
+ access/qhttp1configuration.cpp access/qhttp1configuration.h
access/qhttp2configuration.cpp access/qhttp2configuration.h
access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h
access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
diff --git a/src/network/access/qhttp1configuration.cpp b/src/network/access/qhttp1configuration.cpp
new file mode 100644
index 00000000000..1116467dbab
--- /dev/null
+++ b/src/network/access/qhttp1configuration.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2022 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
+
+#include "qhttp1configuration.h"
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHttp1Configuration
+ \brief The QHttp1Configuration class controls HTTP/1 parameters and settings.
+ \since 6.5
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ QHttp1Configuration controls HTTP/1 parameters and settings that
+ QNetworkAccessManager will use to send requests and process responses.
+
+ \note The configuration must be set before the first request
+ was sent to a given host (and thus an HTTP/1 session established).
+
+ \sa QNetworkRequest::setHttp1Configuration(), QNetworkRequest::http1Configuration(), QNetworkAccessManager
+*/
+
+class QHttp1ConfigurationPrivate : public QSharedData
+{
+public:
+ unsigned numberOfConnectionsPerHost = 6; // QHttpNetworkConnectionPrivate::defaultHttpChannelCount
+};
+
+/*!
+ Default constructs a QHttp1Configuration object.
+*/
+QHttp1Configuration::QHttp1Configuration()
+ : d(new QHttp1ConfigurationPrivate)
+{
+}
+
+/*!
+ Copy-constructs this QHttp1Configuration.
+*/
+QHttp1Configuration::QHttp1Configuration(const QHttp1Configuration &) = default;
+
+/*!
+ Move-constructs this QHttp1Configuration from \a other
+*/
+QHttp1Configuration::QHttp1Configuration(QHttp1Configuration &&other) noexcept
+{
+ swap(other);
+}
+
+/*!
+ Copy-assigns \a other to this QHttp1Configuration.
+*/
+QHttp1Configuration &QHttp1Configuration::operator=(const QHttp1Configuration &) = default;
+
+/*!
+ Move-assigns \a other to this QHttp1Configuration.
+*/
+QHttp1Configuration &QHttp1Configuration::operator=(QHttp1Configuration &&) noexcept = default;
+
+/*!
+ Destructor.
+*/
+QHttp1Configuration::~QHttp1Configuration()
+ = default;
+
+/*!
+ Sets number of connections (default 6) to a http(s)://host:port
+ \sa numberOfConnectionsPerHost
+*/
+void QHttp1Configuration::setNumberOfConnectionsPerHost(unsigned number)
+{
+ if (number == 0) {
+ return;
+ }
+ d->numberOfConnectionsPerHost = number;
+}
+/*!
+ Returns the number of connections (default 6) to a http(s)://host:port
+ \sa setNumberOfConnectionsPerHost
+*/
+unsigned QHttp1Configuration::numberOfConnectionsPerHost() const
+{
+ return d->numberOfConnectionsPerHost;
+}
+
+/*!
+ Swaps this configuration with the \a other configuration.
+*/
+void QHttp1Configuration::swap(QHttp1Configuration &other) noexcept
+{
+ d.swap(other.d);
+}
+
+/*!
+ \fn bool QHttp1Configuration::operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+
+ Returns \c true if \a lhs and \a rhs represent the same set of HTTP/1
+ parameters.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+
+ Returns \c true if \a lhs and \a rhs do not represent the same set of
+ HTTP/1 parameters.
+*/
+
+/*!
+ \internal
+*/
+bool QHttp1Configuration::isEqual(const QHttp1Configuration &other) const noexcept
+{
+ if (d == other.d)
+ return true;
+
+ return d->numberOfConnectionsPerHost == other.d->numberOfConnectionsPerHost;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttp1configuration.h b/src/network/access/qhttp1configuration.h
new file mode 100644
index 00000000000..bc67fc7232d
--- /dev/null
+++ b/src/network/access/qhttp1configuration.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 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
+
+#ifndef QHTTP1CONFIGURATION_H
+#define QHTTP1CONFIGURATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtCore/qshareddata.h>
+
+#ifndef Q_CLANG_QDOC
+QT_REQUIRE_CONFIG(http);
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QHttp1ConfigurationPrivate;
+class Q_NETWORK_EXPORT QHttp1Configuration
+{
+public:
+ QHttp1Configuration();
+ QHttp1Configuration(const QHttp1Configuration &other);
+ QHttp1Configuration(QHttp1Configuration &&other) noexcept;
+
+ QHttp1Configuration &operator=(const QHttp1Configuration &other);
+ QHttp1Configuration &operator=(QHttp1Configuration &&other) noexcept;
+
+ ~QHttp1Configuration();
+
+ void setNumberOfConnectionsPerHost(unsigned amount);
+ unsigned numberOfConnectionsPerHost() const;
+
+ void swap(QHttp1Configuration &other) noexcept;
+
+private:
+ QSharedDataPointer<QHttp1ConfigurationPrivate> d;
+
+ bool isEqual(const QHttp1Configuration &other) const noexcept;
+
+ friend bool operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return !lhs.isEqual(rhs); }
+
+};
+
+Q_DECLARE_SHARED(QHttp1Configuration)
+
+QT_END_NAMESPACE
+
+#endif // QHTTP1CONFIGURATION_H
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 73bf8bebc38..dc9c9f610f4 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -32,6 +32,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+// Note: Only used from auto tests, normal usage is via QHttp1Configuration
const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
// The pipeline length. So there will be 4 requests in flight.
@@ -69,7 +70,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionC
QHttpNetworkConnection::ConnectionType type)
: state(RunningState), networkLayerState(Unknown),
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- activeChannelCount(connectionCount), channelCount(connectionCount)
+ channelCount(connectionCount)
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
#endif
@@ -77,6 +78,14 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionC
, connectionType(type)
{
channels = new QHttpNetworkConnectionChannel[channelCount];
+
+ activeChannelCount = (type == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
+ type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct)
+ ? 1 : connectionCount;
+ // We allocate all 6 channels even if it's an HTTP/2-enabled
+ // connection: in case the protocol negotiation via NPN/ALPN fails,
+ // we will have normally working HTTP/1.1.
+ Q_ASSERT(channelCount >= activeChannelCount);
}
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 4a11b828a8a..c5c35d522a3 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -145,9 +145,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
{
// Q_OBJECT
public:
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
QHttpNetworkConnection::ConnectionType connectionType)
- : QHttpNetworkConnection(hostName, port, encrypt, connectionType)
+ : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType)
{
setExpires(true);
setShareable(true);
@@ -297,7 +297,7 @@ void QHttpThreadDelegate::startRequest()
if (!httpConnection) {
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
+ httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index d16edf3b0f4..c9202b61c4a 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -26,6 +26,7 @@
#include <QNetworkReply>
#include "qhttpnetworkrequest_p.h"
#include "qhttpnetworkconnection_p.h"
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include <QSharedPointer>
#include <QScopedPointer>
@@ -82,6 +83,7 @@ public:
qint64 removedContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
+ QHttp1Configuration http1Parameters;
QHttp2Configuration http2Parameters;
bool isCompressed;
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 58e768583db..9d1fad5c696 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -784,6 +784,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
// Propagate Http/2 settings:
delegate->http2Parameters = request.http2Configuration();
+ delegate->http1Parameters = request.http1Configuration();
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 238ed2eddaf..6e771f6bc77 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -7,6 +7,7 @@
#include "qnetworkcookie.h"
#include "qsslconfiguration.h"
#if QT_CONFIG(http) || defined(Q_QDOC)
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include "private/http2protocol_p.h"
#endif
@@ -439,6 +440,7 @@ public:
#endif
peerVerifyName = other.peerVerifyName;
#if QT_CONFIG(http)
+ h1Configuration = other.h1Configuration;
h2Configuration = other.h2Configuration;
decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
#endif
@@ -454,6 +456,7 @@ public:
maxRedirectsAllowed == other.maxRedirectsAllowed &&
peerVerifyName == other.peerVerifyName
#if QT_CONFIG(http)
+ && h1Configuration == other.h1Configuration
&& h2Configuration == other.h2Configuration
&& decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
#endif
@@ -470,6 +473,7 @@ public:
int maxRedirectsAllowed;
QString peerVerifyName;
#if QT_CONFIG(http)
+ QHttp1Configuration h1Configuration;
QHttp2Configuration h2Configuration;
qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
#endif
@@ -855,6 +859,30 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
#if QT_CONFIG(http) || defined(Q_QDOC)
/*!
+ \since 6.5
+
+ Returns the current parameters that QNetworkAccessManager is
+ using for the underlying HTTP/1 connection of this request.
+
+ \sa setHttp1Configuration
+*/
+QHttp1Configuration QNetworkRequest::http1Configuration() const
+{
+ return d->h1Configuration;
+}
+/*!
+ \since 6.5
+
+ Sets request's HTTP/1 parameters from \a configuration.
+
+ \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
+*/
+void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
+{
+ d->h1Configuration = configuration;
+}
+
+/*!
\since 5.14
Returns the current parameters that QNetworkAccessManager is
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 8dd16e16d79..3476aa02736 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -14,6 +14,7 @@ QT_BEGIN_NAMESPACE
class QSslConfiguration;
class QHttp2Configuration;
+class QHttp1Configuration;
class QNetworkRequestPrivate;
class Q_NETWORK_EXPORT QNetworkRequest
@@ -144,6 +145,9 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
#if QT_CONFIG(http) || defined(Q_QDOC)
+ QHttp1Configuration http1Configuration() const;
+ void setHttp1Configuration(const QHttp1Configuration &configuration);
+
QHttp2Configuration http2Configuration() const;
void setHttp2Configuration(const QHttp2Configuration &configuration);
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index 4c67317e9c9..1123b18c09a 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -44,6 +44,7 @@
#include <QtNetwork/qnetworkdiskcache.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/QHttp1Configuration>
#include <QtNetwork/qnetworkcookie.h>
#include <QtNetwork/QNetworkCookieJar>
#include <QtNetwork/QHttpPart>
@@ -65,6 +66,7 @@ Q_DECLARE_METATYPE(QSharedPointer<char>)
#endif
#include <memory>
+#include <optional>
#ifdef Q_OS_UNIX
# include <sys/types.h>
@@ -445,6 +447,7 @@ private Q_SLOTS:
void varyingCacheExpiry_data();
void varyingCacheExpiry();
+ void amountOfHttp1ConnectionsQtbug25280_data();
void amountOfHttp1ConnectionsQtbug25280();
void dontInsertPartialContentIntoTheCache();
@@ -8120,10 +8123,18 @@ public:
}
};
+void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280_data()
+{
+ QTest::addColumn<int>("amount");
+ QTest::addRow("default") << 6;
+ QTest::addRow("minimize") << 1;
+ QTest::addRow("increase") << 12;
+}
+
// Also kind of QTBUG-8468
void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280()
{
- const int amount = 6;
+ QFETCH(const int, amount);
QNetworkAccessManager manager; // function local instance
Qtbug25280Server server(tst_QNetworkReply::httpEmpty200Response);
server.doClose = false;
@@ -8131,11 +8142,16 @@ void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280()
QUrl url(QLatin1String("http://127.0.0.1")); // not "localhost" to prevent "Happy Eyeballs"
// from skewing the counting
url.setPort(server.serverPort());
+ std::optional<QHttp1Configuration> http1Configuration;
+ if (amount != 6) // don't set if it's the default
+ http1Configuration.emplace().setNumberOfConnectionsPerHost(amount);
constexpr int NumRequests = 200; // send a lot more than we have sockets
int finished = 0;
std::array<std::unique_ptr<QNetworkReply>, NumRequests> replies;
for (auto &reply : replies) {
QNetworkRequest request(url);
+ if (http1Configuration)
+ request.setHttp1Configuration(*http1Configuration);
reply.reset(manager.get(request));
QObject::connect(reply.get(), &QNetworkReply::finished,
[&finished] { ++finished; });