c++qtproxyqmlqtlocation

How to let QML map traffic pass through a proxy, and disable this proxy for the rest of the program?


I've got an app that uses local network to talk to some devices, and an http proxy to fetch anything external. I'd like to use the map plugin in it. Now the map would require an http proxy to download the tiles. If I set global Qt proxy settings using http_proxy variable, or do it manually using QNetworkProxy::setApplicationProxy then the map is loading fine, but the rest of the program breaks. If I disable the proxy then I can talk to my devices, but I can't see the map. I've tried using QQmlNetworkAccessManagerFactory, as described in the example here, and it works fine for an Image type object, as defined in the view.qml, but it doesn't work if I replace the Image with an Item that contains my map.

Item{
    width: 500
    height: 500
    Map {
        Plugin {
            id: mapPlugin
            name: "osm"
        }
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(59.91, 10.75)
        zoomLevel: 14
        visible: true
    }
}

The MyNetworkAccessManagerFactory::create method never gets called, and I get the following error: QGeoTileRequestManager: Failed to fetch tile (17366,9541,15) 5 times, giving up. Last error message was: 'Error transferring http://c.tile.openstreetmap.org/15/17366/9541.png - server replied: Service Unavailable'. What could be the cause of that? And how can I set a QNetworkProxy for a map plugin?


Solution

  • You have to modify the source code of the plugin:

    src/plugins/geoservices/osm/qgeocodingmanagerengineosm.cpp
    src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.cpp
    src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp
    src/plugins/geoservices/osm/qplacemanagerengineosm.cpp
    

    In each of these files is a QNetworkAccessManager so you must add the following code:

    #include <QtNetwork/QNetworkProxy>
    
    // ...
    // constructor
    
        QString proxy = parameters.value(QStringLiteral("proxy")).toString();
        if (!proxy.isEmpty()) {
    #ifndef QT_NO_NETWORKPROXY
            if (proxy.toLower() != QStringLiteral("system")) {
                QUrl proxyUrl(proxy);
                if (proxyUrl.isValid()) {
                    qDebug() << "Setting proxy to " << proxyUrl.toString();
                    m_networkManager->setProxy(
                                QNetworkProxy(QNetworkProxy::HttpProxy,
                                              proxyUrl.host(),
                                              proxyUrl.port(8080),
                                              proxyUrl.userName(),
                                              proxyUrl.password()));
                }
            } else if (QNetworkProxy::applicationProxy().type() == QNetworkProxy::NoProxy) {
                QNetworkProxyFactory::setUseSystemConfiguration(true);
                qDebug() << "Setting system proxy.";
            }
    #else
            qDebug() << "No proxy support";
    #endif
        } else {
            qDebug() << "No proxy parameter specified.";
        }
    

    I have created a .patch to make the update simple, for it download it from here.

    Then you place the ends in the folder src/plugins/geoservices/osm, apply the patch, compile and install with the following:

    git clone -b 5.12.1 git@github.com:qt/qtlocation.git
    cd qtlocation/src/plugins/geoservices/osm
    wget https://raw.githubusercontent.com/eyllanesc/stackoverflow/master/questions/55105933/proxy-osm.patch
    git apply proxy-osm.patch
    qmake
    make
    sudo make install
    

    Then after that you can use the proxy using a plugin parameter:

    Map {
        Plugin {
            id: mapPlugin
            name: "osm"
            PluginParameter { name: "proxy"; value: "http://179.179.253.147:8080" } // <---
        }
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(59.91, 10.75)
        zoomLevel: 14
        visible: true
    }