From 9a1edf52d4b5af35d29b4b2563d1cebafe889385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 18 Dec 2024 16:17:10 +0100 Subject: [PATCH] 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 (cherry picked from commit f7c3acd27ff48b24f99086be206acbe7e00e3208) Reviewed-by: Qt Cherry-pick Bot --- tests/auto/network/access/http2/http2srv.cpp | 10 ++++ tests/auto/network/access/http2/http2srv.h | 2 + tests/auto/network/access/http2/tst_http2.cpp | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index b52ea5527bd..00c7669b38f 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -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; diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index dc94318527b..c74a51bdb9d 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -213,6 +213,8 @@ private: bool sendTrailingHEADERS = false; int informationalStatusCode = 0; + + std::optional pendingMaxTableSizeUpdate; protected slots: void ignoreErrorSlot(); }; diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index d80a7fbc656..d7a36f61274 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -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, 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 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;