Skip to content

Commit a50f76f

Browse files
committedNov 11, 2018
OGC parameter names are case-insensitive
(cherry picked from commit 669f6bb)
1 parent 20a0053 commit a50f76f

File tree

7 files changed

+94
-58
lines changed

7 files changed

+94
-58
lines changed
 

‎debian/control.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Build-Depends:
3030
pyqt5-dev-tools, pyqt5-dev, pyqt5.qsci-dev,
3131
python3-pyqt5, python3-pyqt5.qsci, python3-pyqt5.qtsql, python3-pyqt5.qtsvg,
3232
python3-gdal,
33-
python3-nose2, python3-yaml, python3-mock, python3-psycopg2, python3-future, python3-termcolor,
33+
python3-nose2, python3-yaml, python3-mock, python3-psycopg2, python3-future, python3-termcolor, python3-owslib,
3434
pkg-config,
3535
git,
3636
txt2tags,

‎src/providers/wfs/qgswfsdatasourceuri.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )
2828
// http://example.com/?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=x&SRSNAME=y&username=foo&password=
2929
if ( !mURI.hasParam( QgsWFSConstants::URI_PARAM_URL ) )
3030
{
31+
static QSet<QString> sFilter
32+
{
33+
QStringLiteral( "service" ),
34+
QgsWFSConstants::URI_PARAM_VERSION,
35+
QgsWFSConstants::URI_PARAM_TYPENAME,
36+
QStringLiteral( "request" ),
37+
QgsWFSConstants::URI_PARAM_BBOX,
38+
QgsWFSConstants::URI_PARAM_SRSNAME,
39+
QgsWFSConstants::URI_PARAM_FILTER,
40+
QgsWFSConstants::URI_PARAM_OUTPUTFORMAT,
41+
QgsWFSConstants::URI_PARAM_USERNAME,
42+
QgsWFSConstants::URI_PARAM_PASSWORD,
43+
QgsWFSConstants::URI_PARAM_AUTHCFG
44+
};
45+
3146
QUrl url( uri );
3247
// Transform all param keys to lowercase
3348
QList<queryItem> items( url.queryItems() );
@@ -58,17 +73,11 @@ QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )
5873
}
5974

6075
// Now remove all stuff that is not the core URL
61-
url.removeQueryItem( QStringLiteral( "service" ) );
62-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_VERSION );
63-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_TYPENAME );
64-
url.removeQueryItem( QStringLiteral( "request" ) );
65-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_BBOX );
66-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_SRSNAME );
67-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_FILTER );
68-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_OUTPUTFORMAT );
69-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_USERNAME );
70-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_PASSWORD );
71-
url.removeQueryItem( QgsWFSConstants::URI_PARAM_AUTHCFG );
76+
for ( auto param : url.queryItems() )
77+
{
78+
if ( sFilter.contains( param.first.toLower() ) )
79+
url.removeAllQueryItems( param.first );
80+
}
7281

7382
mURI = QgsDataSourceUri();
7483
mURI.setParam( QgsWFSConstants::URI_PARAM_URL, url.toEncoded() );

‎src/server/services/wcs/qgswcsutils.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,14 @@ namespace QgsWcs
233233

234234
QString serviceUrl( const QgsServerRequest &request, const QgsProject *project )
235235
{
236+
static QSet< QString > sFilter
237+
{
238+
QStringLiteral( "REQUEST" ),
239+
QStringLiteral( "VERSION" ),
240+
QStringLiteral( "SERVICE" ),
241+
QStringLiteral( "_DC" )
242+
};
243+
236244
QString href;
237245
if ( project )
238246
{
@@ -245,10 +253,11 @@ namespace QgsWcs
245253
QUrl url = request.url();
246254
QUrlQuery q( url );
247255

248-
q.removeAllQueryItems( QStringLiteral( "REQUEST" ) );
249-
q.removeAllQueryItems( QStringLiteral( "VERSION" ) );
250-
q.removeAllQueryItems( QStringLiteral( "SERVICE" ) );
251-
q.removeAllQueryItems( QStringLiteral( "_DC" ) );
256+
for ( auto param : q.queryItems() )
257+
{
258+
if ( sFilter.contains( param.first.toUpper() ) )
259+
q.removeAllQueryItems( param.first );
260+
}
252261

253262
url.setQuery( q );
254263
href = url.toString();

‎src/server/services/wfs/qgswfsgetfeature.cpp

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,22 @@ namespace QgsWfs
946946

947947
namespace
948948
{
949+
static QSet< QString > sParamFilter
950+
{
951+
QStringLiteral( "REQUEST" ),
952+
QStringLiteral( "FORMAT" ),
953+
QStringLiteral( "OUTPUTFORMAT" ),
954+
QStringLiteral( "BBOX" ),
955+
QStringLiteral( "FEATUREID" ),
956+
QStringLiteral( "TYPENAME" ),
957+
QStringLiteral( "FILTER" ),
958+
QStringLiteral( "EXP_FILTER" ),
959+
QStringLiteral( "MAXFEATURES" ),
960+
QStringLiteral( "STARTINDEX" ),
961+
QStringLiteral( "PROPERTYNAME" ),
962+
QStringLiteral( "_DC" )
963+
};
964+
949965

950966
void hitGetFeature( const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project, QgsWfsParameters::Format format,
951967
int numberOfFeatures, const QStringList &typeNames )
@@ -983,18 +999,11 @@ namespace QgsWfs
983999
else
9841000
query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.0.0" ) );
9851001

986-
query.removeAllQueryItems( QStringLiteral( "REQUEST" ) );
987-
query.removeAllQueryItems( QStringLiteral( "FORMAT" ) );
988-
query.removeAllQueryItems( QStringLiteral( "OUTPUTFORMAT" ) );
989-
query.removeAllQueryItems( QStringLiteral( "BBOX" ) );
990-
query.removeAllQueryItems( QStringLiteral( "FEATUREID" ) );
991-
query.removeAllQueryItems( QStringLiteral( "TYPENAME" ) );
992-
query.removeAllQueryItems( QStringLiteral( "FILTER" ) );
993-
query.removeAllQueryItems( QStringLiteral( "EXP_FILTER" ) );
994-
query.removeAllQueryItems( QStringLiteral( "MAXFEATURES" ) );
995-
query.removeAllQueryItems( QStringLiteral( "STARTINDEX" ) );
996-
query.removeAllQueryItems( QStringLiteral( "PROPERTYNAME" ) );
997-
query.removeAllQueryItems( QStringLiteral( "_DC" ) );
1002+
for ( auto param : query.queryItems() )
1003+
{
1004+
if ( sParamFilter.contains( param.first.toUpper() ) )
1005+
query.removeAllQueryItems( param.first );
1006+
}
9981007

9991008
query.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
10001009
query.addQueryItem( QStringLiteral( "TYPENAME" ), typeNames.join( ',' ) );
@@ -1092,18 +1101,11 @@ namespace QgsWfs
10921101
else
10931102
query.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.0.0" ) );
10941103

1095-
query.removeAllQueryItems( QStringLiteral( "REQUEST" ) );
1096-
query.removeAllQueryItems( QStringLiteral( "FORMAT" ) );
1097-
query.removeAllQueryItems( QStringLiteral( "OUTPUTFORMAT" ) );
1098-
query.removeAllQueryItems( QStringLiteral( "BBOX" ) );
1099-
query.removeAllQueryItems( QStringLiteral( "FEATUREID" ) );
1100-
query.removeAllQueryItems( QStringLiteral( "TYPENAME" ) );
1101-
query.removeAllQueryItems( QStringLiteral( "FILTER" ) );
1102-
query.removeAllQueryItems( QStringLiteral( "EXP_FILTER" ) );
1103-
query.removeAllQueryItems( QStringLiteral( "MAXFEATURES" ) );
1104-
query.removeAllQueryItems( QStringLiteral( "STARTINDEX" ) );
1105-
query.removeAllQueryItems( QStringLiteral( "PROPERTYNAME" ) );
1106-
query.removeAllQueryItems( QStringLiteral( "_DC" ) );
1104+
for ( auto param : query.queryItems() )
1105+
{
1106+
if ( sParamFilter.contains( param.first.toUpper() ) )
1107+
query.removeAllQueryItems( param.first );
1108+
}
11071109

11081110
query.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
11091111
query.addQueryItem( QStringLiteral( "TYPENAME" ), typeNames.join( ',' ) );

‎src/server/services/wms/qgswmsutils.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,25 @@ namespace QgsWms
4545
// Build default url
4646
if ( href.isEmpty() )
4747
{
48+
static QSet<QString> sFilter
49+
{
50+
QStringLiteral( "REQUEST" ),
51+
QStringLiteral( "VERSION" ),
52+
QStringLiteral( "SERVICE" ),
53+
QStringLiteral( "LAYERS" ),
54+
QStringLiteral( "STYLES" ),
55+
QStringLiteral( "SLD_VERSION" ),
56+
QStringLiteral( "_DC" )
57+
};
58+
4859
href = request.url();
4960
QUrlQuery q( href );
5061

51-
q.removeAllQueryItems( QStringLiteral( "REQUEST" ) );
52-
q.removeAllQueryItems( QStringLiteral( "VERSION" ) );
53-
q.removeAllQueryItems( QStringLiteral( "SERVICE" ) );
54-
q.removeAllQueryItems( QStringLiteral( "LAYERS" ) );
55-
q.removeAllQueryItems( QStringLiteral( "SLD_VERSION" ) );
56-
q.removeAllQueryItems( QStringLiteral( "_DC" ) );
62+
for ( auto param : q.queryItems() )
63+
{
64+
if ( sFilter.contains( param.first.toUpper() ) )
65+
q.removeAllQueryItems( param.first );
66+
}
5767

5868
href.setQuery( q );
5969
}

‎tests/src/providers/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ ADD_QGIS_TEST(gdalprovidertest testqgsgdalprovider.cpp)
7171

7272
ADD_QGIS_TEST(ogrprovidertest testqgsogrprovider.cpp)
7373

74-
ADD_QGIS_TEST(wmscapabilititestest
74+
ADD_QGIS_TEST(wmscapabilitiestest
7575
testqgswmscapabilities.cpp)
76-
TARGET_LINK_LIBRARIES(qgis_wmscapabilititestest wmsprovider_a)
76+
TARGET_LINK_LIBRARIES(qgis_wmscapabilitiestest wmsprovider_a)
7777

7878
ADD_QGIS_TEST(wmsprovidertest
7979
testqgswmsprovider.cpp)

‎tests/src/python/test_qgsserver_wms.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import urllib.error
2828

2929
from qgis.testing import unittest
30-
from qgis.PyQt.QtCore import QSize
3130

3231
import osgeo.gdal # NOQA
3332

@@ -37,8 +36,9 @@
3736
from qgis.core import QgsProject
3837

3938
# Strip path and content length because path may vary
40-
RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+'
41-
RE_ATTRIBUTES = b'[^>\s]+=[^>\s]+'
39+
RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \\d+'
40+
RE_STRIP_EXTENTS = b'<(north|east|south|west)Bound(Lat|Long)itude>.*</(north|east|south|west)Bound(Lat|Long)itude>|<BoundingBox .*/>'
41+
RE_ATTRIBUTES = b'[^>\\s]+=[^>\\s]+'
4242

4343

4444
class TestQgsServerWMSTestBase(QgsServerTestBase):
@@ -57,7 +57,7 @@ def wms_request(self, request, extra=None, project='test_project.qgs', version='
5757
header, body = self._execute_request(query_string)
5858
return (header, body, query_string)
5959

60-
def wms_request_compare(self, request, extra=None, reference_file=None, project='test_project.qgs', version='1.3.0'):
60+
def wms_request_compare(self, request, extra=None, reference_file=None, project='test_project.qgs', version='1.3.0', ignoreExtent=False):
6161
response_header, response_body, query_string = self.wms_request(request, extra, project, version)
6262
response = response_header + response_body
6363
reference_path = self.testdata_path + (request.lower() if not reference_file else reference_file) + '.txt'
@@ -67,6 +67,9 @@ def wms_request_compare(self, request, extra=None, reference_file=None, project=
6767
f.close()
6868
response = re.sub(RE_STRIP_UNCHECKABLE, b'*****', response)
6969
expected = re.sub(RE_STRIP_UNCHECKABLE, b'*****', expected)
70+
if ignoreExtent:
71+
response = re.sub(RE_STRIP_EXTENTS, b'*****', response)
72+
expected = re.sub(RE_STRIP_EXTENTS, b'*****', expected)
7073

7174
msg = "request %s failed.\nQuery: %s\nExpected file: %s\nResponse:\n%s" % (query_string, request, reference_path, response.decode('utf-8'))
7275
self.assertXMLEqual(response, expected, msg=msg)
@@ -158,12 +161,13 @@ def test_wms_getcapabilities_without_title(self):
158161
# according to OGC specifications tests.
159162
self.wms_request_compare('GetCapabilities', reference_file='wms_getcapabilities_without_title', project='test_project_without_title.qgs')
160163

161-
def test_wms_getcapabilitie_empty_spatial_layer(self):
164+
def test_wms_getcapabilities_empty_spatial_layer(self):
162165
# The project contains a spatial layer without feature and the WMS
163166
# extent is not configured in the project.
164167
self.wms_request_compare('GetCapabilities',
165168
reference_file='wms_getcapabilities_empty_spatial_layer',
166-
project='test_project_empty_spatial_layer.qgz')
169+
project='test_project_empty_spatial_layer.qgz',
170+
ignoreExtent=True)
167171

168172
def test_wms_getcapabilities_versions(self):
169173
# default version 1.3.0 when empty VERSION parameter
@@ -226,13 +230,14 @@ def test_wms_getcapabilities_url(self):
226230
item_found = True
227231
self.assertTrue(item_found)
228232

229-
# url passed in quesry string
233+
# url passed in query string
234+
# verify that GetCapabilities isn't put into the url for non-uppercase parameter names
230235
project = os.path.join(self.testdata_path, "test_project_without_urls.qgs")
231236
qs = "https://www.qgis-server.org?" + "&".join(["%s=%s" % i for i in list({
232237
"MAP": urllib.parse.quote(project),
233-
"SERVICE": "WMS",
234-
"VERSION": "1.3.0",
235-
"REQUEST": "GetCapabilities",
238+
"SeRvIcE": "WMS",
239+
"VeRsIoN": "1.3.0",
240+
"ReQuEsT": "GetCapabilities",
236241
"STYLES": ""
237242
}.items())])
238243

@@ -242,6 +247,7 @@ def test_wms_getcapabilities_url(self):
242247
for item in str(r).split("\\n"):
243248
if "OnlineResource" in item:
244249
self.assertEqual("xlink:href=\"https://www.qgis-server.org?" in item, True)
250+
self.assertEqual("GetCapabilities" in item, False)
245251
item_found = True
246252
self.assertTrue(item_found)
247253

0 commit comments

Comments
 (0)
Please sign in to comment.