QNetworkReply/http2: Add a contentEncoding test

Will be useful when DecompressHelper gets taken into use for both.

Task-number: QTBUG-83269
Change-Id: Iaf253219bed193025c2b82d6609f4dcc4de33df8
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2020-05-07 15:20:55 +02:00
parent b35551a75e
commit 80e0d0e08e
4 changed files with 173 additions and 0 deletions

View File

@ -120,6 +120,11 @@ void Http2Server::setResponseBody(const QByteArray &body)
responseBody = body;
}
void Http2Server::setContentEncoding(const QByteArray &encoding)
{
contentEncoding = encoding;
}
void Http2Server::emulateGOAWAY(int timeout)
{
Q_ASSERT(timeout >= 0);
@ -841,6 +846,9 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
QString("%1").arg(responseBody.size()).toLatin1()));
}
if (!contentEncoding.isEmpty())
header.push_back(HPack::HeaderField("content-encoding", contentEncoding));
HPack::BitOStream ostream(writer.outboundFrame().buffer);
const bool result = encoder.encodeResponse(ostream, header);
Q_ASSERT(result);

View File

@ -86,6 +86,8 @@ public:
// To be called before server started:
void enablePushPromise(bool enabled, const QByteArray &path = QByteArray());
void setResponseBody(const QByteArray &body);
// No content encoding is actually performed, call setResponseBody with already encoded data
void setContentEncoding(const QByteArray &contentEncoding);
void emulateGOAWAY(int timeout);
void redirectOpenStream(quint16 targetPort);
@ -211,6 +213,8 @@ private:
bool redirectSent = false;
quint16 targetPort = 0;
QAtomicInt interrupted;
QByteArray contentEncoding;
protected slots:
void ignoreErrorSlot();
};

View File

@ -111,6 +111,9 @@ private slots:
void connectToHost();
void maxFrameSize();
void contentEncoding_data();
void contentEncoding();
protected slots:
// Slots to listen to our in-process server:
void serverStarted(quint16 port);
@ -767,6 +770,109 @@ void tst_Http2::maxFrameSize()
QVERIFY(serverGotSettingsACK);
}
void tst_Http2::contentEncoding_data()
{
QTest::addColumn<QByteArray>("encoding");
QTest::addColumn<QByteArray>("body");
QTest::addColumn<QByteArray>("expected");
QTest::addColumn<QNetworkRequest::Attribute>("h2Attribute");
QTest::addColumn<H2Type>("connectionType");
struct ContentEncodingData
{
ContentEncodingData(QByteArray &&ce, QByteArray &&body, QByteArray &&ex)
: contentEncoding(ce), body(body), expected(ex)
{
}
QByteArray contentEncoding;
QByteArray body;
QByteArray expected;
};
QVector<ContentEncodingData> contentEncodingData;
contentEncodingData.emplace_back(
"gzip", QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA=="),
"hello world");
contentEncodingData.emplace_back(
"deflate", QByteArray::fromBase64("eJzLSM3JyVcozy/KSQEAGgsEXQ=="), "hello world");
// Loop through and add the data...
for (const auto &data : contentEncodingData) {
const char *name = data.contentEncoding.data();
QTest::addRow("%s-h2c-upgrade", name)
<< data.contentEncoding << data.body << data.expected
<< QNetworkRequest::Http2AllowedAttribute << H2Type::h2c;
QTest::addRow("%s-h2c-direct", name)
<< data.contentEncoding << data.body << data.expected
<< QNetworkRequest::Http2DirectAttribute << H2Type::h2cDirect;
if (!clearTextHTTP2)
QTest::addRow("%s-h2-ALPN", name)
<< data.contentEncoding << data.body << data.expected
<< QNetworkRequest::Http2AllowedAttribute << H2Type::h2Alpn;
#if QT_CONFIG(ssl)
QTest::addRow("%s-h2-direct", name)
<< data.contentEncoding << data.body << data.expected
<< QNetworkRequest::Http2DirectAttribute << H2Type::h2Direct;
#endif
}
}
void tst_Http2::contentEncoding()
{
clearHTTP2State();
#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
QFETCH(H2Type, connectionType);
ServerPtr targetServer(newServer(defaultServerSettings, connectionType));
QFETCH(QByteArray, body);
targetServer->setResponseBody(body);
QFETCH(QByteArray, encoding);
targetServer->setContentEncoding(encoding);
QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
runEventLoop();
QVERIFY(serverPort != 0);
nRequests = 1;
auto url = requestUrl(connectionType);
url.setPath("/index.html");
QNetworkRequest request(url);
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
request.setAttribute(h2Attribute, QVariant(true));
auto reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
// Since we're using self-signed certificates,
// ignore SSL errors:
reply->ignoreSslErrors();
runEventLoop();
STOP_ON_FAILURE
QVERIFY(nRequests == 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
QTEST(reply->readAll(), "expected");
}
void tst_Http2::serverStarted(quint16 port)
{
serverPort = port;

View File

@ -503,6 +503,10 @@ private Q_SLOTS:
void getWithTimeout();
void postWithTimeout();
void contentEncoding_data();
void contentEncoding();
// NOTE: This test must be last!
void parentingRepliesToTheApp();
private:
@ -9175,6 +9179,57 @@ void tst_QNetworkReply::postWithTimeout()
manager.setTransferTimeout(0);
}
void tst_QNetworkReply::contentEncoding_data()
{
QTest::addColumn<QByteArray>("encoding");
QTest::addColumn<QByteArray>("body");
QTest::addColumn<QByteArray>("expected");
QTest::newRow("gzip-hello-world")
<< QByteArray("gzip")
<< QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==")
<< QByteArray("hello world");
QTest::newRow("deflate-hello-world")
<< QByteArray("deflate") << QByteArray::fromBase64("eJzLSM3JyVcozy/KSQEAGgsEXQ==")
<< QByteArray("hello world");
}
void tst_QNetworkReply::contentEncoding()
{
QFETCH(QByteArray, encoding);
QFETCH(QByteArray, body);
QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
header = header.arg(encoding, QString::number(body.size()));
MiniHttpServer server(header.toLatin1() + body);
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply(manager.get(request));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
QCOMPARE(reply->error(), QNetworkReply::NoError);
{
// Check that we included the content encoding method in our Accept-Encoding header
const QByteArray &receivedData = server.receivedData;
int start = receivedData.indexOf("Accept-Encoding");
QVERIFY(start != -1);
int end = receivedData.indexOf("\r\n", start);
QVERIFY(end != -1);
QByteArray acceptedEncoding = receivedData.mid(start, end - start);
acceptedEncoding = acceptedEncoding.mid(acceptedEncoding.indexOf(':') + 1).trimmed();
QByteArrayList list = acceptedEncoding.split(',');
for (QByteArray &encoding : list)
encoding = encoding.trimmed();
QVERIFY2(list.contains(encoding), acceptedEncoding.data());
}
QFETCH(QByteArray, expected);
QCOMPARE(reply->bytesAvailable(), expected.size());
QCOMPARE(reply->readAll(), expected);
}
// NOTE: This test must be last testcase in tst_qnetworkreply!
void tst_QNetworkReply::parentingRepliesToTheApp()
{