diff --git a/src/providers/wfs/qgswfsdatasourceuri.cpp b/src/providers/wfs/qgswfsdatasourceuri.cpp index 21c527c2d5d9..5718065b633c 100644 --- a/src/providers/wfs/qgswfsdatasourceuri.cpp +++ b/src/providers/wfs/qgswfsdatasourceuri.cpp @@ -202,11 +202,18 @@ QUrl QgsWFSDataSourceURI::requestUrl( const QString &request, const Method &meth // One could argue that the server should expose them in the DCP endpoint. url = QUrl( mGetEndpoints[ request ] ); urlQuery = QUrlQuery( url ); + // OGC case insensitive keys KVP: see https://github.com/qgis/QGIS/issues/34148 + QSet upperCaseQueryItemKeys; + const QList> constQueryItems { urlQuery.queryItems() }; + for ( const auto &qi : constQueryItems ) + { + upperCaseQueryItemKeys.insert( qi.first.toUpper() ); + } const QUrlQuery defaultUrlQuery( defaultUrl ); const auto itemsDefaultUrl( defaultUrlQuery.queryItems() ); for ( const auto &item : itemsDefaultUrl ) { - if ( !urlQuery.hasQueryItem( item.first ) ) + if ( !upperCaseQueryItemKeys.contains( item.first.toUpper() ) ) { urlQuery.addQueryItem( item.first, item.second ); } diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index fbaef1baf40c..3f348d5eb7db 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -4258,6 +4258,81 @@ def do_GET(self): errors = vl.dataProvider().errors() self.assertEqual(len(errors), 0, errors) + def testWFS20CaseInsensitiveKVP(self): + """Test an URL with non standard query string arguments where the server exposes + the same parameters with different case: see https://github.com/qgis/QGIS/issues/34148 + """ + endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_case_insensitive_kvp_2.0' + + get_cap = """ + + + + my:typename + Title + Abstract + EPSG:32631 + + + + + + + + + + + + + + + + + + + + + + """.format(endpoint).encode('UTF-8') + + with open(sanitize(endpoint, '?PARAMETER1=Value1&PARAMETER2=Value2&FOO=BAR&SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.0.0'), 'wb') as f: + f.write(get_cap) + + with open(sanitize(endpoint, '?Parameter1=Value1&Parameter2=Value2&FOO=BAR&SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.0.0'), 'wb') as f: + f.write(get_cap) + + with open(sanitize(endpoint, '?PARAMETER1=Value1&PARAMETER2=Value2&FOO=BAR&SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename'), 'wb') as f: + f.write(""" + + + + + + + + + + + + + + + + + + + +""".encode('UTF-8')) + + vl = QgsVectorLayer("url='http://" + endpoint + "?Parameter1=Value1&Parameter2=Value2&FOO=BAR&SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.1.0" + "' typename='my:typename' version='1.0.0'", 'test', 'WFS') + self.assertTrue(vl.isValid()) + self.assertEqual(vl.wkbType(), QgsWkbTypes.Point) + self.assertEqual(len(vl.fields()), 5) + self.assertEqual(vl.featureCount(), 0) + reference = QgsGeometry.fromRect(QgsRectangle(400000.0, 5400000.0, 450000.0, 5500000.0)) + vl_extent = QgsGeometry.fromRect(vl.extent()) + assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt()) + if __name__ == '__main__': unittest.main()