From 8d302aea33d54d7930fc700ab44a55e058d7bfcc Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 20 Aug 2019 12:09:42 +0200 Subject: HTTP/2: use a non-default MAX_FRAME_SIZE And send it in our 'SETTINGS' frame. Add an auto-test for this and (as a bonus) - fix a bug accidentally introduced by the previous change. Task-number: QTBUG-77412 Change-Id: I4277ff47e8d8d3b6b8666fbcd7dc73c827f349c0 Reviewed-by: Volker Hilsheimer --- tests/auto/network/access/http2/http2srv.cpp | 6 ++- tests/auto/network/access/http2/http2srv.h | 1 + tests/auto/network/access/http2/tst_http2.cpp | 70 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) (limited to 'tests/auto/network/access/http2') diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index 1ddb6aa77d..a8eebf5a24 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -218,7 +218,7 @@ void Http2Server::sendDATA(quint32 streamID, quint32 windowSize) quint32 bytesToSend = std::min(windowSize, responseBody.size() - offset); quint32 bytesSent = 0; - const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxPayloadSize)); + const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::minPayloadLimit)); const uchar *src = reinterpret_cast(responseBody.constData() + offset); const bool last = offset + bytesToSend == quint32(responseBody.size()); @@ -236,6 +236,10 @@ void Http2Server::sendDATA(quint32 streamID, quint32 windowSize) src += chunkSize; bytesToSend -= chunkSize; bytesSent += chunkSize; + if (frameSizeLimit != Http2::minPayloadLimit) { + // Our test is probably interested in how many DATA frames were sent. + emit sendingData(); + } } if (interrupted.loadAcquire()) diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index 9cb846b0b3..3105684d59 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -128,6 +128,7 @@ Q_SIGNALS: void receivedRequest(quint32 streamID); void receivedData(quint32 streamID); void windowUpdate(quint32 streamID); + void sendingData(); private slots: void connectionEstablished(); diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index 6a0dc6db02..e24a06bc34 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -82,6 +82,8 @@ RawSettings qt_H2ConfigurationToSettings(const QHttp2Configuration &config = qt_ RawSettings settings; settings[Http2::Settings::ENABLE_PUSH_ID] = config.serverPushEnabled(); settings[Http2::Settings::INITIAL_WINDOW_SIZE_ID] = config.streamReceiveWindowSize(); + if (config.maxFrameSize() != Http2::minPayloadLimit) + settings[Http2::Settings::MAX_FRAME_SIZE_ID] = config.maxFrameSize(); return settings; } @@ -107,6 +109,7 @@ private slots: void earlyResponse(); void connectToHost_data(); void connectToHost(); + void maxFrameSize(); protected slots: // Slots to listen to our in-process server: @@ -696,6 +699,73 @@ void tst_Http2::connectToHost() QVERIFY(reply->isFinished()); } +void tst_Http2::maxFrameSize() +{ +#if !QT_CONFIG(ssl) + QSKIP("TLS support is needed for this test"); +#endif // QT_CONFIG(ssl) + + // Here we test we send 'MAX_FRAME_SIZE' setting in our + // 'SETTINGS'. If done properly, our server will not chunk + // the payload into several DATA frames. + +#if QT_CONFIG(securetransport) + // Normally on macOS we use plain text only for SecureTransport + // does not support ALPN on the server side. With 'direct encrytped' + // we have to use TLS sockets (== private key) and thus suppress a + // keychain UI asking for permission to use a private key. + // Our CI has this, but somebody testing locally - will have a problem. + qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1")); + auto envRollback = qScopeGuard([](){ + qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); + }); +#endif // QT_CONFIG(securetransport) + + auto connectionType = H2Type::h2Alpn; + auto attribute = QNetworkRequest::HTTP2AllowedAttribute; + if (clearTextHTTP2) { + connectionType = H2Type::h2Direct; + attribute = QNetworkRequest::Http2DirectAttribute; + } + + auto h2Config = qt_defaultH2Configuration(); + h2Config.setMaxFrameSize(Http2::minPayloadLimit * 3); + + serverPort = 0; + nRequests = 1; + + ServerPtr srv(newServer(defaultServerSettings, connectionType, + qt_H2ConfigurationToSettings(h2Config))); + srv->setResponseBody(QByteArray(Http2::minPayloadLimit * 2, 'q')); + QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); + runEventLoop(); + QVERIFY(serverPort != 0); + + const QSignalSpy frameCounter(srv.data(), &Http2Server::sendingData); + auto url = requestUrl(connectionType); + url.setPath(QString("/stream1.html")); + + QNetworkRequest request(url); + request.setAttribute(attribute, QVariant(true)); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + request.setHttp2Configuration(h2Config); + + QNetworkReply *reply = manager->get(request); + reply->ignoreSslErrors(); + connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); + + runEventLoop(); + STOP_ON_FAILURE + + // Normally, with a 16kb limit, our server would split such + // a response into 3 'DATA' frames (16kb + 16kb + 0|END_STREAM). + QCOMPARE(frameCounter.count(), 1); + + QVERIFY(nRequests == 0); + QVERIFY(prefaceOK); + QVERIFY(serverGotSettingsACK); +} + void tst_Http2::serverStarted(quint16 port) { serverPort = port; -- cgit v1.2.3