Skip to content

Qt6 causes HTTP/2 errors #3644

@skinkie

Description

@skinkie

Describe the bug
I have opened the bug downstream. maplibre/maplibre-native-qt#221

And with a bit of help I found some workarounds. It is obviously that the true solution must be found here (or even more upstream in Qt6).

To Reproduce

Steps to reproduce the behavior:

  1. Run the widget test with bit longer timeout
  2. Scroll into map
  3. Notice that no new tiles are requested
  4. See error in the console

Expected behavior

It is obvious that the user wants an interface that works.

Screenshots

./test_mln_widgets
********* Start testing of TestWidgets *********
Config: Using QtTest library 6.9.1, Qt 6.9.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.3.1 20210422 (Red Hat 10.3.1-1)), arch unknown
PASS : TestWidgets::initTestCase()
QWARN : TestWidgets::testGLWidgetStyle() [ INFO ] "{unknown}[General]: GPU Identifier: llvmpipe (LLVM 20.1.8, 256 bits)"
QCRITICAL: TestWidgets::testGLWidgetStyle() qt.network.http2.connection: [0x7798ac05f8d0] Connection error: HEADERS on invalid stream (1)
QCRITICAL: TestWidgets::testGLWidgetStyle() qt.network.http2.connection: [0x7798ac0786c0] Connection error: HEADERS on invalid stream (1)
QCRITICAL: TestWidgets::testGLWidgetStyle() qt.network.http2.connection: [0x7798ac0736d0] Connection error: DATA on invalid stream (11)
QWARN : TestWidgets::testGLWidgetStyle() qt.network.http2: stream 13 error: "DATA on invalid stream"
QWARN : TestWidgets::testGLWidgetStyle() qt.network.http2: stream 13 finished with error: "Server dislikes our behavior, excessive load detected."
QWARN : TestWidgets::testGLWidgetStyle() qt.network.http2: stream 15 error: "DATA on invalid stream"
QWARN : TestWidgets::testGLWidgetStyle() qt.network.http2: stream 15 finished with error: "Server dislikes our behavior, excessive load detected."
QWARN : TestWidgets::testGLWidgetStyle() [ ERROR ] "{unknown}[Style]: Failed to load tile 2/2/1=>2 for source openmaptiles: HTTP status code 0"
QWARN : TestWidgets::testGLWidgetStyle() [ ERROR ] "{unknown}[Style]: Failed to load tile 2/1/1=>2 for source openmaptiles: HTTP status code 0"

** Workarounds **

  1. Disable HTTP/2, with HTTP/1 there are no errors.
diff --git a/platform/qt/src/mbgl/http_file_source.cpp b/platform/qt/src/mbgl/http_file_source.cpp
index 2cdb50f734b..fdada5298fe 100644
--- a/platform/qt/src/mbgl/http_file_source.cpp
+++ b/platform/qt/src/mbgl/http_file_source.cpp
@@ -37,7 +37,7 @@ void HTTPFileSource::Impl::request(HTTPRequest* req) {
     networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
 #endif
 #endif
-
+    networkRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
     data.first = m_manager->get(networkRequest);
     connect(data.first, &QNetworkReply::finished, this, &HTTPFileSource::Impl::onReplyFinished);
 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
  1. Explicitly handle the errors and clear the connection Cache. The latter is the most important part.
@@ -74,6 +74,7 @@ void HTTPFileSource::Impl::cancel(HTTPRequest* req) {
 
 void HTTPFileSource::Impl::onReplyFinished() {
     QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
+    if (!reply) return;
 
 #if defined(Q_OS_WASM)
     const QUrl& url = reply->url();
@@ -91,14 +92,27 @@ void HTTPFileSource::Impl::onReplyFinished() {
         return;
     }
 
-    QByteArray data = reply->readAll();
-    QVector<HTTPRequest*>& requestsVector = it.value().second;
+    // Error handling
+    if (reply->error() != QNetworkReply::NoError) {
+        qWarning() << "Network error for URL" << url << ":" << reply->errorString();
+
+        QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+        if (!statusCode.isValid()) {
+            qWarning() << "No HTTP status code received; possible connection/protocol failure.";
+        }
 
-    // Cannot use the iterator to walk the requestsVector
-    // because calling handleNetworkReply() might get
-    // requests added to the requestsVector.
-    while (!requestsVector.isEmpty()) {
-        requestsVector.takeFirst()->handleNetworkReply(reply, data);
+       m_manager->clearConnectionCache();
+    } else 
+    {
+           QByteArray data = reply->readAll();
+           QVector<HTTPRequest*>& requestsVector = it.value().second;
+
+           // Cannot use the iterator to walk the requestsVector
+           // because calling handleNetworkReply() might get
+           // requests added to the requestsVector.
+           while (!requestsVector.isEmpty()) {
+               requestsVector.takeFirst()->handleNetworkReply(reply, data);
+           }
     }
 
     m_pending.erase(it);

Platform information (please complete the following information):

  • Operating System: Linux
  • Platform (e.g. Node.js, Qt): Qt6
  • Version: 6.9.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions