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.9 6.8 6.5
Change-Id: I1ca8ca7828d5b83606e7adbcfc13c154fa1e3cab
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2024-12-18 16:17:10 +01:00
parent be477819ac
commit f7c3acd27f
3 changed files with 63 additions and 0 deletions

View File

@ -191,6 +191,9 @@ void Http2Server::sendServerSettings()
writer.append(it.value()); writer.append(it.value());
if (it.key() == Settings::INITIAL_WINDOW_SIZE_ID) if (it.key() == Settings::INITIAL_WINDOW_SIZE_ID)
streamRecvWindowSize = it.value(); streamRecvWindowSize = it.value();
if (it.key() == Settings::HEADER_TABLE_SIZE_ID) {
pendingMaxTableSizeUpdate = it.value();
}
} }
writer.write(*socket); writer.write(*socket);
// Now, let's update our peer on a session recv window size: // Now, let's update our peer on a session recv window size:
@ -669,6 +672,13 @@ void Http2Server::handleSETTINGS()
return; 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; waitingClientAck = false;
emit serverSettingsAcked(); emit serverSettingsAcked();
return; return;

View File

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

View File

@ -113,6 +113,8 @@ private slots:
void limitedConcurrentStreamsAllowed(); void limitedConcurrentStreamsAllowed();
void maxHeaderTableSize();
protected slots: protected slots:
// Slots to listen to our in-process server: // Slots to listen to our in-process server:
void serverStarted(quint16 port); void serverStarted(quint16 port);
@ -1637,6 +1639,55 @@ void tst_Http2::limitedConcurrentStreamsAllowed()
QCOMPARE(finishedCount, TotalRequests); 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) void tst_Http2::serverStarted(quint16 port)
{ {
serverPort = port; serverPort = port;