From db5b8bbea3f3cf1675d2ddd449359b6fbedc523e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 9 Feb 2022 17:40:48 +0100 Subject: Http2: Fix redirect-handling The redirect handling for http2 was a little simple. E.g. not handling relative URLs. Fix this using the redirect response parsing function which the http1 protocol handler already uses. Fixes: QTBUG-100651 Pick-to: 6.3 6.2 5.15 Change-Id: Ic0cec4cacc92707e7a7fde1f4665f80995a6057e Reviewed-by: Qt CI Bot Reviewed-by: Timur Pocheptsov --- tests/auto/network/access/http2/http2srv.cpp | 11 ++++- tests/auto/network/access/http2/http2srv.h | 6 +++ tests/auto/network/access/http2/tst_http2.cpp | 70 +++++++++++++++++++++++++++ 3 files changed, 86 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 8a4917049e..27c7b47eda 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -130,6 +130,12 @@ void Http2Server::setAuthenticationHeader(const QByteArray &authentication) authenticationHeader = authentication; } +void Http2Server::setRedirect(const QByteArray &url, int count) +{ + redirectUrl = url; + redirectCount = count; +} + void Http2Server::emulateGOAWAY(int timeout) { Q_ASSERT(timeout >= 0); @@ -860,7 +866,10 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody) const QString url("%1://localhost:%2/"); header.push_back({"location", url.arg(isClearText() ? QStringLiteral("http") : QStringLiteral("https"), QString::number(targetPort)).toLatin1()}); - + } else if (redirectCount > 0) { // Not redirecting while reading, unlike above + --redirectCount; + header.push_back({":status", "308"}); + header.push_back({"location", redirectUrl}); } else if (!authenticationHeader.isEmpty() && !hasAuth) { header.push_back({ ":status", "401" }); header.push_back(HPack::HeaderField("www-authenticate", authenticationHeader)); diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index 671cacbd54..73d195bd06 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -90,6 +90,9 @@ public: void setContentEncoding(const QByteArray &contentEncoding); // No authentication data is generated for the method, the full header value must be set void setAuthenticationHeader(const QByteArray &authentication); + // Set the redirect URL and count. The server will return a redirect response with the url + // 'count' amount of times + void setRedirect(const QByteArray &redirectUrl, int count); void emulateGOAWAY(int timeout); void redirectOpenStream(quint16 targetPort); @@ -222,6 +225,9 @@ private: QByteArray contentEncoding; QByteArray authenticationHeader; + + QByteArray redirectUrl; + int redirectCount = 0; 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 515d026a61..27f0b045a5 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -118,6 +118,9 @@ private slots: void h2cAllowedAttribute_data(); void h2cAllowedAttribute(); + void redirect_data(); + void redirect(); + protected slots: // Slots to listen to our in-process server: void serverStarted(quint16 port); @@ -1226,6 +1229,73 @@ void tst_Http2::h2cAllowedAttribute() } } +void tst_Http2::redirect_data() +{ + QTest::addColumn("maxRedirects"); + QTest::addColumn("redirectCount"); + QTest::addColumn("success"); + + QTest::addRow("1-redirects-none-allowed-failure") << 0 << 1 << false; + QTest::addRow("1-redirects-success") << 1 << 1 << true; + QTest::addRow("2-redirects-1-allowed-failure") << 1 << 2 << false; +} + +void tst_Http2::redirect() +{ + QFETCH(const int, maxRedirects); + QFETCH(const int, redirectCount); + QFETCH(const bool, success); + const QByteArray redirectUrl = "/b.html"_qba; + + clearHTTP2State(); + serverPort = 0; + + ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType())); + targetServer->setRedirect(redirectUrl, redirectCount); + + QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection); + runEventLoop(); + + QVERIFY(serverPort != 0); + + nRequests = 1 + maxRedirects; + + auto originalUrl = requestUrl(defaultConnectionType()); + auto url = originalUrl; + url.setPath("/index.html"); + QNetworkRequest request(url); + request.setMaximumRedirectsAllowed(maxRedirects); + // H2C might be used on macOS where SecureTransport doesn't support server-side ALPN + qputenv("QT_NETWORK_H2C_ALLOWED", "1"); + auto envCleanup = qScopeGuard([]() { qunsetenv("QT_NETWORK_H2C_ALLOWED"); }); + + QScopedPointer reply; + reply.reset(manager->get(request)); + + if (success) { + connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished); + } else { + connect(reply.get(), &QNetworkReply::errorOccurred, this, + &tst_Http2::replyFinishedWithError); + } + + // Since we're using self-signed certificates, + // ignore SSL errors: + reply->ignoreSslErrors(); + + runEventLoop(); + STOP_ON_FAILURE + + if (success) { + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->url().toString(), + originalUrl.resolved(QString::fromLatin1(redirectUrl)).toString()); + } else if (maxRedirects < redirectCount) { + QCOMPARE(reply->error(), QNetworkReply::TooManyRedirectsError); + } + QTRY_VERIFY(serverGotSettingsACK); +} + void tst_Http2::serverStarted(quint16 port) { serverPort = port; -- cgit v1.2.3