diff --git a/python/server/auto_generated/qgsserversettings.sip.in b/python/server/auto_generated/qgsserversettings.sip.in
index 27b8870cb449..bae041124ae9 100644
--- a/python/server/auto_generated/qgsserversettings.sip.in
+++ b/python/server/auto_generated/qgsserversettings.sip.in
@@ -134,6 +134,24 @@ Show group (thousand) separator
:return: if group separator must be shown, default to FALSE.
+.. versionadded:: 3.8
+%End
+
+ int wmsMaxHeight() const;
+%Docstring
+Returns the server-wide max height of a WMS GetMap request. The lower one of this and the project configuration is used.
+
+:return: the max height of a WMS GetMap request.
+
+.. versionadded:: 3.8
+%End
+
+ int wmsMaxWidth() const;
+%Docstring
+Returns the server-wide max width of a WMS GetMap request. The lower one of this and the project configuration is used.
+
+:return: the max width of a WMS GetMap request.
+
.. versionadded:: 3.8
%End
diff --git a/src/server/qgsserversettings.cpp b/src/server/qgsserversettings.cpp
index b0be9feb6a4e..78524cf01dbd 100644
--- a/src/server/qgsserversettings.cpp
+++ b/src/server/qgsserversettings.cpp
@@ -33,8 +33,8 @@ void QgsServerSettings::initSettings()
// options path
const Setting sOptPath = { QgsServerSettingsEnv::QGIS_OPTIONS_PATH,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Override the default path for user configuration",
- "",
+ QStringLiteral( "Override the default path for user configuration" ),
+ QString(),
QVariant::String,
QVariant( "" ),
QVariant()
@@ -44,8 +44,8 @@ void QgsServerSettings::initSettings()
// parallel rendering
const Setting sParRend = { QgsServerSettingsEnv::QGIS_SERVER_PARALLEL_RENDERING,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Activate/Deactivate parallel rendering for WMS getMap request",
- "/qgis/parallel_rendering",
+ QStringLiteral( "Activate/Deactivate parallel rendering for WMS getMap request" ),
+ QStringLiteral( "/qgis/parallel_rendering" ),
QVariant::Bool,
QVariant( false ),
QVariant()
@@ -55,8 +55,8 @@ void QgsServerSettings::initSettings()
// max threads
const Setting sMaxThreads = { QgsServerSettingsEnv::QGIS_SERVER_MAX_THREADS,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Number of threads to use when parallel rendering is activated",
- "/qgis/max_threads",
+ QStringLiteral( "Number of threads to use when parallel rendering is activated" ),
+ QStringLiteral( "/qgis/max_threads" ),
QVariant::Int,
QVariant( -1 ),
QVariant()
@@ -66,8 +66,8 @@ void QgsServerSettings::initSettings()
// log level
const Setting sLogLevel = { QgsServerSettingsEnv::QGIS_SERVER_LOG_LEVEL,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Log level",
- "",
+ QStringLiteral( "Log level" ),
+ QString(),
QVariant::Int,
QVariant( Qgis::None ),
QVariant()
@@ -77,8 +77,8 @@ void QgsServerSettings::initSettings()
// log file
const Setting sLogFile = { QgsServerSettingsEnv::QGIS_SERVER_LOG_FILE,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Log file",
- "",
+ QStringLiteral( "Log file" ),
+ QString(),
QVariant::String,
QVariant( "" ),
QVariant()
@@ -88,8 +88,8 @@ void QgsServerSettings::initSettings()
// log to stderr
const Setting sLogStderr = { QgsServerSettingsEnv::QGIS_SERVER_LOG_STDERR,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Activate/Deactivate logging to stderr",
- "",
+ QStringLiteral( "Activate/Deactivate logging to stderr" ),
+ QString(),
QVariant::Bool,
QVariant( false ),
QVariant()
@@ -99,8 +99,8 @@ void QgsServerSettings::initSettings()
// project file
const Setting sProject = { QgsServerSettingsEnv::QGIS_PROJECT_FILE,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "QGIS project file",
- "",
+ QStringLiteral( "QGIS project file" ),
+ QString(),
QVariant::String,
QVariant( "" ),
QVariant()
@@ -110,8 +110,8 @@ void QgsServerSettings::initSettings()
// max cache layers
const Setting sMaxCacheLayers = { QgsServerSettingsEnv::MAX_CACHE_LAYERS,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Specify the maximum number of cached layers",
- "",
+ QStringLiteral( "Specify the maximum number of cached layers" ),
+ QString(),
QVariant::Int,
QVariant( 100 ),
QVariant()
@@ -121,8 +121,8 @@ void QgsServerSettings::initSettings()
// cache directory
const Setting sCacheDir = { QgsServerSettingsEnv::QGIS_SERVER_CACHE_DIRECTORY,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Specify the cache directory",
- "/cache/directory",
+ QStringLiteral( "Specify the cache directory" ),
+ QStringLiteral( "/cache/directory" ),
QVariant::String,
QVariant( QgsApplication::qgisSettingsDirPath() + "cache" ),
QVariant()
@@ -132,8 +132,8 @@ void QgsServerSettings::initSettings()
// cache size
const Setting sCacheSize = { QgsServerSettingsEnv::QGIS_SERVER_CACHE_SIZE,
QgsServerSettingsEnv::DEFAULT_VALUE,
- "Specify the cache size",
- "/cache/size",
+ QStringLiteral( "Specify the cache size" ),
+ QStringLiteral( "/cache/size" ),
QVariant::LongLong,
QVariant( 50 * 1024 * 1024 ),
QVariant()
@@ -162,6 +162,27 @@ void QgsServerSettings::initSettings()
};
mSettings[ sShowGroupSeparator.envVar ] = sShowGroupSeparator;
+ // max height
+ const Setting sMaxHeight = { QgsServerSettingsEnv::QGIS_SERVER_WMS_MAX_HEIGHT,
+ QgsServerSettingsEnv::DEFAULT_VALUE,
+ QStringLiteral( "Maximum height for a WMS request. The lower one of this and the project configuration is used." ),
+ QStringLiteral( "/qgis/max_wms_height" ),
+ QVariant::LongLong,
+ QVariant( -1 ),
+ QVariant()
+ };
+ mSettings[ sMaxHeight.envVar ] = sMaxHeight;
+
+ // max width
+ const Setting sMaxWidth = { QgsServerSettingsEnv::QGIS_SERVER_WMS_MAX_WIDTH,
+ QgsServerSettingsEnv::DEFAULT_VALUE,
+ QStringLiteral( "Maximum width for a WMS request. The most conservative between this and the project one is used" ),
+ QStringLiteral( "/qgis/max_wms_width" ),
+ QVariant::LongLong,
+ QVariant( -1 ),
+ QVariant()
+ };
+ mSettings[ sMaxWidth.envVar ] = sMaxWidth;
}
void QgsServerSettings::load()
@@ -353,3 +374,12 @@ bool QgsServerSettings::showGroupSeparator() const
return value( QgsServerSettingsEnv::QGIS_SERVER_SHOW_GROUP_SEPARATOR ).toBool();
}
+int QgsServerSettings::wmsMaxHeight() const
+{
+ return value( QgsServerSettingsEnv::QGIS_SERVER_WMS_MAX_HEIGHT ).toInt();
+}
+
+int QgsServerSettings::wmsMaxWidth() const
+{
+ return value( QgsServerSettingsEnv::QGIS_SERVER_WMS_MAX_WIDTH ).toInt();
+}
diff --git a/src/server/qgsserversettings.h b/src/server/qgsserversettings.h
index fd9cfd9e2f77..fc8e67d53c0f 100644
--- a/src/server/qgsserversettings.h
+++ b/src/server/qgsserversettings.h
@@ -63,6 +63,8 @@ class SERVER_EXPORT QgsServerSettingsEnv : public QObject
QGIS_SERVER_CACHE_SIZE,
QGIS_SERVER_SHOW_GROUP_SEPARATOR, //! Show group (thousands) separator when formatting numeric values, defaults to FALSE (since QGIS 3.8)
QGIS_SERVER_OVERRIDE_SYSTEM_LOCALE, //! Override system locale (since QGIS 3.8)
+ QGIS_SERVER_WMS_MAX_HEIGHT, //! Maximum height for a WMS request. The most conservative between this and the project one is used (since QGIS 3.8)
+ QGIS_SERVER_WMS_MAX_WIDTH //! Maximum width for a WMS request. The most conservative between this and the project one is used (since QGIS 3.8)
};
Q_ENUM( EnvVar )
};
@@ -184,6 +186,20 @@ class SERVER_EXPORT QgsServerSettings
*/
bool showGroupSeparator() const;
+ /**
+ * Returns the server-wide max height of a WMS GetMap request. The lower one of this and the project configuration is used.
+ * \returns the max height of a WMS GetMap request.
+ * \since QGIS 3.8
+ */
+ int wmsMaxHeight() const;
+
+ /**
+ * Returns the server-wide max width of a WMS GetMap request. The lower one of this and the project configuration is used.
+ * \returns the max width of a WMS GetMap request.
+ * \since QGIS 3.8
+ */
+ int wmsMaxWidth() const;
+
private:
void initSettings();
QVariant value( QgsServerSettingsEnv::EnvVar envVar ) const;
diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp
index d3e1b94b0e69..a92b00512361 100644
--- a/src/server/services/wms/qgswmsrenderer.cpp
+++ b/src/server/services/wms/qgswmsrenderer.cpp
@@ -2053,21 +2053,51 @@ namespace QgsWms
bool QgsRenderer::checkMaximumWidthHeight() const
{
- //test if maxWidth / maxHeight set and WIDTH / HEIGHT parameter is in the range
- int wmsMaxWidth = QgsServerProjectUtils::wmsMaxWidth( *mProject );
- int width = mWmsParameters.widthAsInt();
+ //test if maxWidth / maxHeight are set in the project or as an env variable
+ //and WIDTH / HEIGHT parameter is in the range allowed range
+ //WIDTH
+ int wmsMaxWidthProj = QgsServerProjectUtils::wmsMaxWidth( *mProject );
+ int wmsMaxWidthEnv = mContext.settings().wmsMaxWidth();
+ int wmsMaxWidth;
+ if ( wmsMaxWidthEnv != -1 && wmsMaxWidthProj != -1 )
+ {
+ // both are set, so we take the more conservative one
+ wmsMaxWidth = std::min( wmsMaxWidthProj, wmsMaxWidthEnv );
+ }
+ else
+ {
+ // none or one are set, so we take the bigger one which is the one set or -1
+ wmsMaxWidth = std::max( wmsMaxWidthProj, wmsMaxWidthEnv );
+ }
+
+ int width = this->width();
if ( wmsMaxWidth != -1 && width > wmsMaxWidth )
{
return false;
}
- int wmsMaxHeight = QgsServerProjectUtils::wmsMaxHeight( *mProject );
- int height = mWmsParameters.heightAsInt();
+ //HEIGHT
+ int wmsMaxHeightProj = QgsServerProjectUtils::wmsMaxHeight( *mProject );
+ int wmsMaxHeightEnv = mContext.settings().wmsMaxHeight();
+ int wmsMaxHeight;
+ if ( wmsMaxWidthEnv != -1 && wmsMaxWidthProj != -1 )
+ {
+ // both are set, so we take the more conservative one
+ wmsMaxHeight = std::min( wmsMaxHeightProj, wmsMaxHeightEnv );
+ }
+ else
+ {
+ // none or one are set, so we take the bigger one which is the one set or -1
+ wmsMaxHeight = std::max( wmsMaxHeightProj, wmsMaxHeightEnv );
+ }
+
+ int height = this->height();
if ( wmsMaxHeight != -1 && height > wmsMaxHeight )
{
return false;
}
+
// Sanity check from internal QImage checks (see qimage.cpp)
// this is to report a meaningful error message in case of
// image creation failure and to differentiate it from out
diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt
index f00bb8a10863..c10a7942e72e 100644
--- a/tests/src/python/CMakeLists.txt
+++ b/tests/src/python/CMakeLists.txt
@@ -280,6 +280,8 @@ IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServerPlugins test_qgsserver_plugins.py)
ADD_PYTHON_TEST(PyQgsServerWMS test_qgsserver_wms.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetMap test_qgsserver_wms_getmap.py)
+ ADD_PYTHON_TEST(PyQgsServerWMSGetMapSizeProject test_qgsserver_wms_getmap_size_project.py)
+ ADD_PYTHON_TEST(PyQgsServerWMSGetMapSizeServer test_qgsserver_wms_getmap_size_server.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetFeatureInfo test_qgsserver_wms_getfeatureinfo.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetLegendGraphic test_qgsserver_wms_getlegendgraphic.py)
ADD_PYTHON_TEST(PyQgsServerWMSGetPrint test_qgsserver_wms_getprint.py)
diff --git a/tests/src/python/test_qgsserver_wms_getmap_size_project.py b/tests/src/python/test_qgsserver_wms_getmap_size_project.py
new file mode 100644
index 000000000000..d9acd7398c53
--- /dev/null
+++ b/tests/src/python/test_qgsserver_wms_getmap_size_project.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for QgsServer MaxHeight and MaxWidth Override Options.
+
+From build dir, run: ctest -R PyQgsServerGetMapSize -V
+
+.. note:: This test needs env vars to be set before the server is
+ configured for the first time, for this
+ reason it cannot run as a test case of another server
+ test.
+
+.. note:: This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+"""
+__author__ = 'Marco Bernasocchi'
+__date__ = '01/04/2019'
+__copyright__ = 'Copyright 2019, The QGIS Project'
+# This will get replaced with a git SHA1 when you do a git archive
+__revision__ = '$Format:%H$'
+
+import os
+
+# Needed on Qt 5 so that the serialization of XML is consistent among all
+# executions
+os.environ['QT_HASH_SEED'] = '1'
+
+
+import urllib.parse
+
+from qgis.testing import unittest
+
+from test_qgsserver import QgsServerTestBase
+
+
+class TestQgsServerWMSGetMapSizeProject(QgsServerTestBase):
+ """QGIS Server WMS Tests for GetFeatureInfo request"""
+
+ # Set to True to re-generate reference files for this class
+ regenerate_reference = False
+
+ def setUp(self):
+ os.environ['QGIS_SERVER_WMS_MAX_WIDTH'] = '6000'
+ os.environ['QGIS_SERVER_WMS_MAX_HEIGHT'] = '6000'
+ super(TestQgsServerWMSGetMapSizeProject, self).setUp()
+ self.project = os.path.join(self.testdata_path, "test_project_with_size.qgs")
+ self.expected_too_big = self.strip_version_xmlns(b'\n The requested map size is too large\n\n')
+
+ def test_wms_getmap_invalid_size_project(self):
+ # test the 6000 limit from server is overridden by the more conservative 5000 in the project
+ r = make_request(self, 5001, 5000)
+ self.assertEqual(self.strip_version_xmlns(r), self.expected_too_big)
+
+
+def make_request(instance, height, width):
+ qs = "?" + "&".join(["%s=%s" % i for i in list({
+ "MAP": urllib.parse.quote(instance.project),
+ "SERVICE": "WMS",
+ "VERSION": "1.3.0",
+ "REQUEST": "GetMap",
+ "LAYERS": "",
+ "STYLES": "",
+ "FORMAT": "image/png",
+ "HEIGHT": str(height),
+ "WIDTH": str(width)
+ }.items())])
+ r, h = instance._result(instance._execute_request(qs))
+ return instance.strip_version_xmlns(r)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/src/python/test_qgsserver_wms_getmap_size_server.py b/tests/src/python/test_qgsserver_wms_getmap_size_server.py
new file mode 100644
index 000000000000..67285d9780c7
--- /dev/null
+++ b/tests/src/python/test_qgsserver_wms_getmap_size_server.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for QgsServer MaxHeight and MaxWidth Override Options.
+
+From build dir, run: ctest -R PyQgsServerGetMapSize -V
+
+.. note:: This test needs env vars to be set before the server is
+ configured for the first time, for this
+ reason it cannot run as a test case of another server
+ test.
+
+.. note:: This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+"""
+__author__ = 'Marco Bernasocchi'
+__date__ = '01/04/2019'
+__copyright__ = 'Copyright 2019, The QGIS Project'
+# This will get replaced with a git SHA1 when you do a git archive
+__revision__ = '$Format:%H$'
+
+import os
+
+# Needed on Qt 5 so that the serialization of XML is consistent among all
+# executions
+os.environ['QT_HASH_SEED'] = '1'
+
+from qgis.testing import unittest
+
+from test_qgsserver import QgsServerTestBase
+from test_qgsserver_wms_getmap_size_project import make_request
+
+
+class TestQgsServerWMSGetMapSizeServer(QgsServerTestBase):
+ """QGIS Server WMS Tests for GetFeatureInfo request"""
+
+ # Set to True to re-generate reference files for this class
+ regenerate_reference = False
+
+ def setUp(self):
+ os.environ['QGIS_SERVER_WMS_MAX_WIDTH'] = '3000'
+ os.environ['QGIS_SERVER_WMS_MAX_HEIGHT'] = '3000'
+ super(TestQgsServerWMSGetMapSizeServer, self).setUp()
+ self.project = os.path.join(self.testdata_path, "test_project_with_size.qgs")
+ self.expected_too_big = self.strip_version_xmlns(b'\n The requested map size is too large\n\n')
+
+ def test_wms_getmap_invalid_size_server(self):
+ # test the 3000 limit from server is overriding the less conservative 5000 in the project
+ r = make_request(self, 3001, 3000)
+ self.assertEqual(self.strip_version_xmlns(r), self.expected_too_big)
+
+
+if __name__ == '__main__':
+ unittest.main()