mirror of https://github.com/qt/qtbase.git
QNAM: Reintroduce h2c with an attribute
[ChangeLog][QtNetwork][QNetworkRequest] Added QNetworkRequest::Http2CleartextAllowedAttribute which controls whether HTTP/2 cleartext (h2c) is allowed or not. The default is false. This replaces the QT_NETWORK_H2C_ALLOWED environment variable. Task-number: QTBUG-98642 Change-Id: I43ae1cc671788f6d2559cd316f6667b412c8e75e Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
5074344c9c
commit
9909ec0bc6
|
@ -61,6 +61,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
|
|||
pipeliningAllowed(other.pipeliningAllowed),
|
||||
http2Allowed(other.http2Allowed),
|
||||
http2Direct(other.http2Direct),
|
||||
h2cAllowed(other.h2cAllowed),
|
||||
withCredentials(other.withCredentials),
|
||||
ssl(other.ssl),
|
||||
preConnect(other.preConnect),
|
||||
|
@ -85,6 +86,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
|
|||
&& (pipeliningAllowed == other.pipeliningAllowed)
|
||||
&& (http2Allowed == other.http2Allowed)
|
||||
&& (http2Direct == other.http2Direct)
|
||||
&& (h2cAllowed == other.h2cAllowed)
|
||||
// we do not clear the customVerb in setOperation
|
||||
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
|
||||
&& (withCredentials == other.withCredentials)
|
||||
|
@ -367,12 +369,12 @@ void QHttpNetworkRequest::setHTTP2Direct(bool b)
|
|||
|
||||
bool QHttpNetworkRequest::isH2cAllowed() const
|
||||
{
|
||||
return qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED");
|
||||
return d->h2cAllowed;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setH2cAllowed(bool b)
|
||||
{
|
||||
Q_UNUSED(b);
|
||||
d->h2cAllowed = b;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::withCredentials() const
|
||||
|
|
|
@ -182,6 +182,7 @@ public:
|
|||
bool pipeliningAllowed;
|
||||
bool http2Allowed;
|
||||
bool http2Direct;
|
||||
bool h2cAllowed = false;
|
||||
bool withCredentials;
|
||||
bool ssl;
|
||||
bool preConnect;
|
||||
|
|
|
@ -790,6 +790,12 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
|
|||
allowed.isValid() && allowed.canConvert<bool>()) {
|
||||
httpRequest.setHTTP2Allowed(allowed.value<bool>());
|
||||
}
|
||||
auto h2cAttribute = request.attribute(QNetworkRequest::Http2CleartextAllowedAttribute);
|
||||
// ### Qt7: Stop checking the environment variable
|
||||
if (h2cAttribute.toBool()
|
||||
|| (!h2cAttribute.isValid() && qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED"))) {
|
||||
httpRequest.setH2cAllowed(true);
|
||||
}
|
||||
|
||||
if (request.attribute(QNetworkRequest::Http2DirectAttribute).toBool()) {
|
||||
// Intentionally mutually exclusive - cannot be both direct and 'allowed'
|
||||
|
|
|
@ -272,7 +272,8 @@ QT_BEGIN_NAMESPACE
|
|||
Requests only, type: QMetaType::Bool (default: true)
|
||||
Indicates whether the QNetworkAccessManager code is
|
||||
allowed to use HTTP/2 with this request. This applies
|
||||
to SSL requests or 'cleartext' HTTP/2.
|
||||
to SSL requests or 'cleartext' HTTP/2 if Http2CleartextAllowedAttribute
|
||||
is set.
|
||||
|
||||
\value Http2WasUsedAttribute
|
||||
Replies only, type: QMetaType::Bool (default: false)
|
||||
|
@ -304,8 +305,9 @@ QT_BEGIN_NAMESPACE
|
|||
If set, this attribute will force QNetworkAccessManager to use
|
||||
HTTP/2 protocol without initial HTTP/2 protocol negotiation.
|
||||
Use of this attribute implies prior knowledge that a particular
|
||||
server supports HTTP/2. The attribute works with SSL or 'cleartext'
|
||||
HTTP/2. If a server turns out to not support HTTP/2, when HTTP/2 direct
|
||||
server supports HTTP/2. The attribute works with SSL or with 'cleartext'
|
||||
HTTP/2 if Http2CleartextAllowedAttribute is set.
|
||||
If a server turns out to not support HTTP/2, when HTTP/2 direct
|
||||
was specified, QNetworkAccessManager gives up, without attempting to
|
||||
fall back to HTTP/1.1. If both Http2AllowedAttribute and
|
||||
Http2DirectAttribute are set, Http2DirectAttribute takes priority.
|
||||
|
@ -325,6 +327,15 @@ QT_BEGIN_NAMESPACE
|
|||
be closed after the last pending request had been processed.
|
||||
(This value was introduced in 6.3.)
|
||||
|
||||
\value Http2CleartextAllowedAttribute
|
||||
Requests only, type: QMetaType::Bool (default: false)
|
||||
If set, this attribute will tell QNetworkAccessManager to attempt
|
||||
an upgrade to HTTP/2 over cleartext (also known as h2c).
|
||||
Until Qt 7 the default value for this attribute can be overridden
|
||||
to true by setting the QT_NETWORK_H2C_ALLOWED environment variable.
|
||||
This attribute is ignored if the Http2AllowedAttribute is not set.
|
||||
(This value was introduced in 6.3.)
|
||||
|
||||
\value User
|
||||
Special type. Additional information can be passed in
|
||||
QVariants with types ranging from User to UserMax. The default
|
||||
|
|
|
@ -98,6 +98,7 @@ public:
|
|||
ResourceTypeAttribute, // internal
|
||||
AutoDeleteReplyOnFinishAttribute,
|
||||
ConnectionCacheExpiryTimeoutSecondsAttribute,
|
||||
Http2CleartextAllowedAttribute,
|
||||
|
||||
User = 1000,
|
||||
UserMax = 32767
|
||||
|
|
|
@ -115,6 +115,9 @@ private slots:
|
|||
void authenticationRequired_data();
|
||||
void authenticationRequired();
|
||||
|
||||
void h2cAllowedAttribute_data();
|
||||
void h2cAllowedAttribute();
|
||||
|
||||
protected slots:
|
||||
// Slots to listen to our in-process server:
|
||||
void serverStarted(quint16 port);
|
||||
|
@ -223,7 +226,6 @@ tst_Http2::~tst_Http2()
|
|||
void tst_Http2::init()
|
||||
{
|
||||
manager.reset(new QNetworkAccessManager);
|
||||
qputenv("QT_NETWORK_H2C_ALLOWED", "1");
|
||||
}
|
||||
|
||||
void tst_Http2::singleRequest_data()
|
||||
|
@ -277,6 +279,7 @@ void tst_Http2::singleRequest()
|
|||
url.setPath("/index.html");
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
|
||||
request.setAttribute(h2Attribute, QVariant(true));
|
||||
|
||||
|
@ -452,6 +455,7 @@ void tst_Http2::pushPromise()
|
|||
url.setPath("/index.html");
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
|
||||
request.setHttp2Configuration(params);
|
||||
|
||||
|
@ -478,6 +482,7 @@ void tst_Http2::pushPromise()
|
|||
|
||||
url.setPath("/script.js");
|
||||
QNetworkRequest promisedRequest(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
promisedRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
|
||||
reply = manager->get(promisedRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
|
@ -532,6 +537,7 @@ void tst_Http2::goaway()
|
|||
for (int i = 0; i < nRequests; ++i) {
|
||||
url.setPath(QString("/%1").arg(i));
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
|
||||
replies[i] = manager->get(request);
|
||||
QCOMPARE(replies[i]->error(), QNetworkReply::NoError);
|
||||
|
@ -673,6 +679,7 @@ void tst_Http2::connectToHost()
|
|||
auto copyUrl = url;
|
||||
copyUrl.setScheme(QLatin1String("preconnect-https"));
|
||||
QNetworkRequest request(copyUrl);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(requestAttribute, true);
|
||||
reply = manager->get(request);
|
||||
// Since we're using self-signed certificates, ignore SSL errors:
|
||||
|
@ -685,6 +692,7 @@ void tst_Http2::connectToHost()
|
|||
auto copyUrl = url;
|
||||
copyUrl.setScheme(QLatin1String("preconnect-http"));
|
||||
QNetworkRequest request(copyUrl);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(requestAttribute, true);
|
||||
reply = manager->get(request);
|
||||
}
|
||||
|
@ -705,6 +713,7 @@ void tst_Http2::connectToHost()
|
|||
QCOMPARE(nRequests, 1);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(requestAttribute, QVariant(true));
|
||||
reply = manager->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
|
@ -770,6 +779,7 @@ void tst_Http2::maxFrameSize()
|
|||
url.setPath(QString("/stream1.html"));
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(attribute, QVariant(true));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
|
||||
request.setHttp2Configuration(h2Config);
|
||||
|
@ -916,6 +926,7 @@ void tst_Http2::moreActivitySignals()
|
|||
auto url = requestUrl(connectionType);
|
||||
url.setPath(QString("/stream1.html"));
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
|
||||
request.setAttribute(h2Attribute, QVariant(true));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
|
||||
|
@ -1036,6 +1047,7 @@ void tst_Http2::contentEncoding()
|
|||
url.setPath("/index.html");
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
|
||||
request.setAttribute(h2Attribute, QVariant(true));
|
||||
|
||||
|
@ -1093,6 +1105,7 @@ void tst_Http2::authenticationRequired()
|
|||
auto url = requestUrl(defaultConnectionType());
|
||||
url.setPath("/index.html");
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
|
||||
QByteArray expectedBody = "Hello, World!";
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
@ -1147,6 +1160,72 @@ void tst_Http2::authenticationRequired()
|
|||
QTRY_VERIFY(serverGotSettingsACK);
|
||||
}
|
||||
|
||||
void tst_Http2::h2cAllowedAttribute_data()
|
||||
{
|
||||
QTest::addColumn<bool>("h2cAllowed");
|
||||
QTest::addColumn<bool>("useAttribute"); // true: use attribute, false: use environment variable
|
||||
QTest::addColumn<bool>("success");
|
||||
|
||||
QTest::addRow("h2c-not-allowed") << false << false << false;
|
||||
// Use the attribute to enable/disable the H2C:
|
||||
QTest::addRow("attribute") << true << true << true;
|
||||
// Use the QT_NETWORK_H2C_ALLOWED environment variable to enable/disable the H2C:
|
||||
QTest::addRow("environment-variable") << true << false << true;
|
||||
}
|
||||
|
||||
void tst_Http2::h2cAllowedAttribute()
|
||||
{
|
||||
QFETCH(const bool, h2cAllowed);
|
||||
QFETCH(const bool, useAttribute);
|
||||
QFETCH(const bool, success);
|
||||
|
||||
clearHTTP2State();
|
||||
serverPort = 0;
|
||||
|
||||
ServerPtr targetServer(newServer(defaultServerSettings, H2Type::h2c));
|
||||
targetServer->setResponseBody("Hello");
|
||||
|
||||
QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
|
||||
runEventLoop();
|
||||
|
||||
QVERIFY(serverPort != 0);
|
||||
|
||||
nRequests = 1;
|
||||
|
||||
auto url = requestUrl(H2Type::h2c);
|
||||
url.setPath("/index.html");
|
||||
QNetworkRequest request(url);
|
||||
if (h2cAllowed) {
|
||||
if (useAttribute)
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
else
|
||||
qputenv("QT_NETWORK_H2C_ALLOWED", "1");
|
||||
}
|
||||
auto envCleanup = qScopeGuard([]() { qunsetenv("QT_NETWORK_H2C_ALLOWED"); });
|
||||
|
||||
QScopedPointer<QNetworkReply> reply;
|
||||
reply.reset(manager->get(request));
|
||||
|
||||
if (success)
|
||||
connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
else
|
||||
connect(reply.get(), &QNetworkReply::errorOccurred, this, &tst_Http2::replyFinishedWithError);
|
||||
|
||||
// Since we're using self-signed certificates,
|
||||
// ignore SSL errors:
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
if (!success) {
|
||||
QCOMPARE(reply->error(), QNetworkReply::ConnectionRefusedError);
|
||||
} else {
|
||||
QCOMPARE(reply->readAll(), QByteArray("Hello"));
|
||||
QTRY_VERIFY(serverGotSettingsACK);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Http2::serverStarted(quint16 port)
|
||||
{
|
||||
serverPort = port;
|
||||
|
@ -1204,6 +1283,7 @@ void tst_Http2::sendRequest(int streamNumber,
|
|||
url.setPath(QString("/stream%1.html").arg(streamNumber));
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
|
||||
|
|
Loading…
Reference in New Issue