tst_Http2: Test SETTINGS_HEADER_TABLE_SIZE handling

This somewhat duplicates some other tests, namely the QHttp2Connection
one, but tst_http2 is our only coverage of the HTTP/2 code prior to 6.8.

This change adds a test where the server sets the
SETTINGS_HEADER_TABLE_SIZE to 0, effectively disabling the dynamic
table. We, in an earlier patch, makes the HPack table error out if
a new header entry is about to be added if the table has not been
resized below a changed 'max'. So, we take advantage of that here and
set the new 'max' capacity without resizing the table, or updating
dynamic capacity causing it to fail if the client doesn't send the
expected Dynamic Table Size Update.

Task-number: QTBUG-132277
Pick-to: 6.8 6.5
Change-Id: I1ca8ca7828d5b83606e7adbcfc13c154fa1e3cab
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
(cherry picked from commit f7c3acd27ff48b24f99086be206acbe7e00e3208)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Mårten Nordheim 2024-12-18 16:17:10 +01:00 committed by Qt Cherry-pick Bot
parent aae66d7518
commit 9a1edf52d4
3 changed files with 63 additions and 0 deletions

View File

@ -191,6 +191,9 @@ void Http2Server::sendServerSettings()
writer.append(it.value());
if (it.key() == Settings::INITIAL_WINDOW_SIZE_ID)
streamRecvWindowSize = it.value();
if (it.key() == Settings::HEADER_TABLE_SIZE_ID) {
pendingMaxTableSizeUpdate = it.value();
}
}
writer.write(*socket);
// Now, let's update our peer on a session recv window size:
@ -669,6 +672,13 @@ void Http2Server::handleSETTINGS()
return;
}
// The client ACKed our setting, including the new decoder table size,
// so we can update it now:
if (pendingMaxTableSizeUpdate) {
decoder.setMaxDynamicTableSize(*pendingMaxTableSizeUpdate);
pendingMaxTableSizeUpdate.reset();
}
waitingClientAck = false;
emit serverSettingsAcked();
return;

View File

@ -213,6 +213,8 @@ private:
bool sendTrailingHEADERS = false;
int informationalStatusCode = 0;
std::optional<quint32> pendingMaxTableSizeUpdate;
protected slots:
void ignoreErrorSlot();
};

View File

@ -113,6 +113,8 @@ private slots:
void limitedConcurrentStreamsAllowed();
void maxHeaderTableSize();
protected slots:
// Slots to listen to our in-process server:
void serverStarted(quint16 port);
@ -1637,6 +1639,55 @@ void tst_Http2::limitedConcurrentStreamsAllowed()
QCOMPARE(finishedCount, TotalRequests);
}
void tst_Http2::maxHeaderTableSize()
{
clearHTTP2State();
serverPort = 0;
H2Type connectionType = H2Type::h2Direct;
RawSettings maxHeaderTableSize{ { Http2::Settings::HEADER_TABLE_SIZE_ID, 0 } };
ServerPtr targetServer(newServer(maxHeaderTableSize, connectionType));
QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
runEventLoop();
QVERIFY(serverPort != 0);
nRequests = 1;
const auto url = requestUrl(connectionType);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::Http2DirectAttribute, true);
constexpr int extraRequests = 5;
std::array<std::unique_ptr<QNetworkReply>, extraRequests> replies;
for (qint32 i = 0; i < 1 + extraRequests; ++i) {
for (qint32 j = 0; j < 100; ++j) {
request.setRawHeader("x-test" + QByteArray::number(j),
"Hello World" + QByteArray::number(i));
}
std::unique_ptr<QNetworkReply> reply{ manager->get(request) };
reply->ignoreSslErrors();
connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
if (i == 0) {
runEventLoop();
STOP_ON_FAILURE
QCOMPARE(reply->error(), QNetworkReply::NoError);
nRequests = extraRequests;
} else {
replies[i - 1] = std::move(reply);
}
}
runEventLoop();
STOP_ON_FAILURE
QCOMPARE(nRequests, 0);
for (const auto &reply : replies)
QCOMPARE(reply->error(), QNetworkReply::NoError);
}
void tst_Http2::serverStarted(quint16 port)
{
serverPort = port;