diff --git a/python/server/qgsserver.sip b/python/server/qgsserver.sip index 9cf2f4e1b0f..adb3e2f2a20 100644 --- a/python/server/qgsserver.sip +++ b/python/server/qgsserver.sip @@ -35,7 +35,7 @@ class QgsServer .. versionadded:: 2.14 %End - void handleRequest( QgsServerRequest &request, QgsServerResponse &response ); + void handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project = 0 ); %Docstring Handles the request. The query string is normally read from environment @@ -44,6 +44,9 @@ class QgsServer \param request a QgsServerRequest holding request parameters \param response a QgsServerResponse for handling response I/O) + \param project a QgsProject or None, if it is None the project + is created from the MAP param specified in request or from + the QGIS_PROJECT_FILE setting %End diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index d2a7b4f7443..dd47115012d 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -300,8 +300,7 @@ void QgsServer::putenv( const QString &var, const QString &val ) * @param queryString * @return response headers and body */ - -void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &response ) +void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project ) { QgsMessageLog::MessageLevel logLevel = QgsServerLogger::instance()->logLevel(); QTime time; //used for measuring request time if loglevel < 1 @@ -346,16 +345,23 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res printRequestParameters( parameterMap, logLevel ); //Config file path - QString configFilePath = configPath( *sConfigFilePath, parameterMap ); - - // load the project if needed and not empty - const QgsProject *project = mConfigCache->project( configFilePath ); if ( ! project ) { - throw QgsServerException( QStringLiteral( "Project file error" ) ); - } + QString configFilePath = configPath( *sConfigFilePath, parameterMap ); - sServerInterface->setConfigFilePath( configFilePath ); + // load the project if needed and not empty + project = mConfigCache->project( configFilePath ); + if ( ! project ) + { + throw QgsServerException( QStringLiteral( "Project file error" ) ); + } + + sServerInterface->setConfigFilePath( configFilePath ); + } + else + { + sServerInterface->setConfigFilePath( project->fileName() ); + } //Service parameter QString serviceString = parameterMap.value( QStringLiteral( "SERVICE" ) ); diff --git a/src/server/qgsserver.h b/src/server/qgsserver.h index 6f4ffd908bf..1ea88ed2b1d 100644 --- a/src/server/qgsserver.h +++ b/src/server/qgsserver.h @@ -75,8 +75,11 @@ class SERVER_EXPORT QgsServer * * \param request a QgsServerRequest holding request parameters * \param response a QgsServerResponse for handling response I/O) + * \param project a QgsProject or nullptr, if it is nullptr the project + * is created from the MAP param specified in request or from + * the QGIS_PROJECT_FILE setting */ - void handleRequest( QgsServerRequest &request, QgsServerResponse &response ); + void handleRequest( QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project = nullptr ); //! Returns a pointer to the server interface diff --git a/tests/src/python/test_qgsserver.py b/tests/src/python/test_qgsserver.py index c2c6edbc531..70568fd12f8 100644 --- a/tests/src/python/test_qgsserver.py +++ b/tests/src/python/test_qgsserver.py @@ -214,6 +214,17 @@ class QgsServerTestBase(unittest.TestCase): headers.append(("%s: %s" % (k, rh[k])).encode('utf-8')) return b"\n".join(headers) + b"\n\n", bytes(response.body()) + def _execute_request_project(self, qs, project, requestMethod=QgsServerRequest.GetMethod, data=None): + request = QgsBufferServerRequest(qs, requestMethod, {}, data) + response = QgsBufferServerResponse() + self.server.handleRequest(request, response, project) + headers = [] + rh = response.headers() + rk = sorted(rh.keys()) + for k in rk: + headers.append(("%s: %s" % (k, rh[k])).encode('utf-8')) + return b"\n".join(headers) + b"\n\n", bytes(response.body()) + class TestQgsServerTestBase(unittest.TestCase): @@ -285,6 +296,16 @@ class TestQgsServer(QgsServerTestBase): self.assertEqual(response.headers(), {'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8'}) self.assertEqual(response.statusCode(), 500) + def test_requestHandlerProject(self): + """Test request handler with none project""" + headers = {'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2'} + request = QgsBufferServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers) + response = QgsBufferServerResponse() + self.server.handleRequest(request, response, None) + self.assertEqual(bytes(response.body()), b'Project file error\n') + self.assertEqual(response.headers(), {'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8'}) + self.assertEqual(response.statusCode(), 500) + def test_api(self): """Using an empty query string (returns an XML exception) we are going to test if headers and body are returned correctly""" diff --git a/tests/src/python/test_qgsserver_wms.py b/tests/src/python/test_qgsserver_wms.py index b4598c95f34..687184c1abb 100644 --- a/tests/src/python/test_qgsserver_wms.py +++ b/tests/src/python/test_qgsserver_wms.py @@ -32,6 +32,7 @@ from qgis.PyQt.QtCore import QSize import osgeo.gdal # NOQA from test_qgsserver import QgsServerTestBase +from qgis.core import QgsProject # Strip path and content length because path may vary RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+' @@ -196,6 +197,31 @@ class TestQgsServerWMS(QgsServerTestBase): '', 'getschemaextension') + def wms_request_compare_project(self, request, extra=None, reference_file=None): + projectPath = self.testdata_path + "test_project.qgs" + assert os.path.exists(projectPath), "Project file not found: " + projectPath + + project = QgsProject() + project.read(projectPath) + + query_string = 'https://www.qgis.org/?SERVICE=WMS&VERSION=1.3&REQUEST=%s' % (request) + if extra is not None: + query_string += extra + header, body = self._execute_request_project(query_string, project) + response = header + body + reference_path = self.testdata_path + (request.lower() if not reference_file else reference_file) + '.txt' + self.store_reference(reference_path, response) + f = open(reference_path, 'rb') + expected = f.read() + f.close() + response = re.sub(RE_STRIP_UNCHECKABLE, b'*****', response) + expected = re.sub(RE_STRIP_UNCHECKABLE, b'*****', expected) + + self.assertXMLEqual(response, expected, msg="request %s failed.\nQuery: %s\nExpected file: %s\nResponse:\n%s" % (query_string, request, reference_path, response.decode('utf-8'))) + + def test_wms_getcapabilities_project(self): + self.wms_request_compare_project('GetCapabilities') + def wms_inspire_request_compare(self, request): """WMS INSPIRE tests""" project = self.testdata_path + "test_project_inspire.qgs"