Skip to content

Commit 569593b

Browse files
committedMay 3, 2017
[server] Python plugins API cleanup part 3
This removes the handleRequest method that returns the headers and body as byte array. This superceeded by the implementation that takes a request and response instances.
1 parent 2afcad2 commit 569593b

10 files changed

+106
-324
lines changed
 

‎python/server/qgsbufferserverrequest.sip

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/************************************************************************
22
* This file has been generated automatically from *
33
* *
4-
* ../src/server/qgsbufferserverrequest.h *
4+
* src/server/qgsbufferserverrequest.h *
55
* *
66
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
77
************************************************************************/
@@ -46,7 +46,7 @@ class QgsBufferServerRequest : QgsServerRequest
4646
/************************************************************************
4747
* This file has been generated automatically from *
4848
* *
49-
* ../src/server/qgsbufferserverrequest.h *
49+
* src/server/qgsbufferserverrequest.h *
5050
* *
5151
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
5252
************************************************************************/

‎python/server/qgsbufferserverresponse.sip

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/************************************************************************
22
* This file has been generated automatically from *
33
* *
4-
* ../src/server/qgsbufferserverresponse.h *
4+
* src/server/qgsbufferserverresponse.h *
55
* *
66
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
77
************************************************************************/
@@ -141,13 +141,13 @@ class QgsBufferServerResponse: QgsServerResponse
141141

142142

143143
private:
144-
QgsBufferServerResponse(const QgsBufferServerResponse &) ;
144+
QgsBufferServerResponse( const QgsBufferServerResponse & ) ;
145145
};
146146

147147
/************************************************************************
148148
* This file has been generated automatically from *
149149
* *
150-
* ../src/server/qgsbufferserverresponse.h *
150+
* src/server/qgsbufferserverresponse.h *
151151
* *
152152
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
153153
************************************************************************/

‎python/server/qgsserver.sip

Lines changed: 0 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -17,141 +17,6 @@
1717
***************************************************************************/
1818

1919

20-
%MappedType QPair<QByteArray, QByteArray>
21-
{
22-
%TypeHeaderCode
23-
#include <QPair>
24-
#include <QByteArray>
25-
%End
26-
27-
28-
%TypeCode
29-
// Convenience function for converting a QByteArray to a Python str object. (from QtCore/qbytearray.sip)
30-
static PyObject *QByteArrayToPyStr(QByteArray *ba)
31-
{
32-
char *data = ba->data();
33-
34-
if (data)
35-
// QByteArrays may have embedded '\0's so set the size explicitly.
36-
return SIPBytes_FromStringAndSize(data, ba->size());
37-
return SIPBytes_FromString("");
38-
}
39-
40-
%End
41-
42-
43-
%ConvertFromTypeCode
44-
// Create the tuple.
45-
return Py_BuildValue((char *)"OO", QByteArrayToPyStr( &sipCpp->first ), QByteArrayToPyStr( &sipCpp->second ) );
46-
%End
47-
48-
%ConvertToTypeCode
49-
50-
// See if we are just being asked to check the type of the Python
51-
// object.
52-
if (!sipIsErr)
53-
{
54-
// Checking whether or not None has been passed instead of a list
55-
// has already been done.
56-
if (!PyTuple_Check(sipPy) || PyTuple_Size(sipPy) != 2)
57-
return 0;
58-
59-
// Check the type of each element. We specify SIP_NOT_NONE to
60-
// disallow None because it is a list of QPoint, not of a pointer
61-
// to a QPoint, so None isn't appropriate.
62-
for (int i = 0; i < PyTuple_Size(sipPy); ++i)
63-
if (!sipCanConvertToType(PyTuple_GET_ITEM(sipPy, i),
64-
sipType_QByteArray, SIP_NOT_NONE))
65-
return 0;
66-
67-
// The type is valid.
68-
return 1;
69-
}
70-
71-
// Create the instance on the heap.
72-
QPair<QByteArray, QByteArray> *qp = new QPair<QByteArray, QByteArray>;
73-
74-
QByteArray *qba1;
75-
int state;
76-
77-
// Get the address of the element's C++ instance. Note that, in
78-
// this case, we don't apply any ownership changes to the list
79-
// elements, only to the list itself.
80-
qba1 = reinterpret_cast<QByteArray *>(sipConvertToType(
81-
PyTuple_GET_ITEM(sipPy, 0),
82-
sipType_QByteArray, 0,
83-
SIP_NOT_NONE,
84-
&state, sipIsErr));
85-
86-
// Deal with any errors.
87-
if (*sipIsErr)
88-
{
89-
sipReleaseType(qba1, sipType_QByteArray, state);
90-
91-
// Tidy up.
92-
delete qp;
93-
94-
// There is no temporary instance.
95-
return 0;
96-
}
97-
98-
qp->first = *qba1;
99-
100-
// A copy of the QByteArray was assigned to the pair so we no longer
101-
// need it. It may be a temporary instance that should be
102-
// destroyed, or a wrapped instance that should not be destroyed.
103-
// sipReleaseType() will do the right thing.
104-
sipReleaseType(qba1, sipType_QByteArray, state);
105-
106-
/////////////////////////////////////////////
107-
// Second item
108-
109-
QByteArray *qba2;
110-
111-
// Get the address of the element's C++ instance. Note that, in
112-
// this case, we don't apply any ownership changes to the list
113-
// elements, only to the list itself.
114-
qba2 = reinterpret_cast<QByteArray *>(sipConvertToType(
115-
PyTuple_GET_ITEM(sipPy, 1),
116-
sipType_QByteArray, 0,
117-
SIP_NOT_NONE,
118-
&state, sipIsErr));
119-
120-
// Deal with any errors.
121-
if (*sipIsErr)
122-
{
123-
sipReleaseType(qba1, sipType_QByteArray, state);
124-
sipReleaseType(qba2, sipType_QByteArray, state);
125-
126-
// Tidy up.
127-
delete qp;
128-
129-
// There is no temporary instance.
130-
return 0;
131-
}
132-
133-
qp->second = *qba2;
134-
135-
136-
// A copy of the QByteArray was assigned to the pair so we no longer
137-
// need it. It may be a temporary instance that should be
138-
// destroyed, or a wrapped instance that should not be destroyed.
139-
// sipReleaseType() will do the right thing.
140-
sipReleaseType(qba2, sipType_QByteArray, state);
141-
142-
143-
// Return the instance.
144-
*sipCppPtr = qp;
145-
146-
// The instance should be regarded as temporary (and be destroyed as
147-
// soon as it has been used) unless it has been transferred from
148-
// Python. sipGetState() is a convenience function that implements
149-
// this common transfer behavior.
150-
return sipGetState(sipTransferObj);
151-
152-
%End
153-
};
154-
15520
/** \ingroup server
15621
* The QgsServer class provides OGC web services.
15722
*/
@@ -185,18 +50,6 @@ class QgsServer
18550
*/
18651
void handleRequest( QgsServerRequest &request, QgsServerResponse &response );
18752

188-
/** Handles the request from query string
189-
* The query string is normally read from environment
190-
* but can be also passed in args and in this case overrides the environment
191-
* variable.
192-
*
193-
* \param urlstr QString containing the request url (simple quely string must be preceded by '?')
194-
* \param requestMethod QgsServerRequest::Method that indicates the method. Only "GET" or "POST" are supported.
195-
* \param data array of bytes containing post data
196-
* \param map of request headers
197-
* \returns the response headers and body QPair of QByteArray
198-
*/
199-
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers(), const char *data = nullptr );
20053

20154
/** Returns a pointer to the server interface */
20255
QgsServerInterface *serverInterface();

‎src/server/qgsserver.cpp

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -427,49 +427,6 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res
427427
}
428428
}
429429

430-
QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod, const QgsServerRequest::Headers &headers, const char *data )
431-
{
432-
433-
QUrl url( urlstr );
434-
435-
QByteArray ba;
436-
437-
if ( requestMethod == QgsServerRequest::PostMethod )
438-
{
439-
if ( data )
440-
{
441-
ba.append( data );
442-
}
443-
}
444-
else if ( requestMethod != QgsServerRequest::GetMethod )
445-
{
446-
throw QgsServerException( QStringLiteral( "Invalid method in handleRequest(): only GET or POST is supported" ) );
447-
}
448-
449-
QgsBufferServerRequest request( url, requestMethod, headers, &ba );
450-
QgsBufferServerResponse response;
451-
452-
handleRequest( request, response );
453-
454-
/*
455-
* XXX For compatibility only:
456-
* We should return a (moved) QgsBufferServerResponse instead
457-
*/
458-
QByteArray headerBuffer;
459-
QMap<QString, QString>::const_iterator it;
460-
for ( it = response.headers().constBegin(); it != response.headers().constEnd(); ++it )
461-
{
462-
headerBuffer.append( it.key().toUtf8() );
463-
headerBuffer.append( ": " );
464-
headerBuffer.append( it.value().toUtf8() );
465-
headerBuffer.append( "\n" );
466-
}
467-
headerBuffer.append( "\n" );
468-
469-
// TODO: check that this is not an evil bug!
470-
return QPair<QByteArray, QByteArray>( headerBuffer, response.body() );
471-
472-
}
473430

474431
#ifdef HAVE_SERVER_PYTHON_PLUGINS
475432
void QgsServer::initPython()

‎src/server/qgsserver.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,6 @@ class SERVER_EXPORT QgsServer
7373
*/
7474
void handleRequest( QgsServerRequest &request, QgsServerResponse &response );
7575

76-
/** Handles the request from query string
77-
* The query string is normally read from environment
78-
* but can be also passed in args and in this case overrides the environment
79-
* variable.
80-
*
81-
* \param urlstr QString containing the request url (simple quely string must be preceded by '?')
82-
* \param requestMethod QgsServerRequest::Method that indicates the method. Only "GET" or "POST" are supported.
83-
* \param data array of bytes containing post data
84-
* \param map of request headers
85-
* \returns the response headers and body QPair of QByteArray
86-
*/
87-
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers(), const char *data = nullptr );
8876

8977
//! Returns a pointer to the server interface
9078
QgsServerInterfaceImpl *serverInterface() { return sServerInterface; }

‎tests/src/python/test_qgsserver.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ def _img_diff_error(self, response, headers, image, max_diff=10, max_size_diff=Q
180180

181181
self.assertTrue(test, message)
182182

183+
def _execute_request(self, qs, requestMethod=QgsServerRequest.GetMethod, data=None):
184+
request = QgsBufferServerRequest(qs, requestMethod, {}, data)
185+
response = QgsBufferServerResponse()
186+
self.server.handleRequest(request, response)
187+
headers = []
188+
rh = response.headers()
189+
rk = list(rh.keys())
190+
rk.sort()
191+
for k in rk:
192+
headers.append(("%s: %s" % (k, rh[k])).encode('utf-8'))
193+
return b"\n".join(headers) + b"\n\n", bytes(response.body())
194+
183195

184196
class TestQgsServer(QgsServerTestBase):
185197
"""Tests container"""
@@ -196,7 +208,9 @@ def test_multiple_servers(self):
196208
"""Segfaults?"""
197209
for i in range(10):
198210
locals()["s%s" % i] = QgsServer()
199-
locals()["s%s" % i].handleRequest("")
211+
locals()["rq%s" % i] = QgsBufferServerRequest("")
212+
locals()["re%s" % i] = QgsBufferServerResponse()
213+
locals()["s%s" % i].handleRequest(locals()["rq%s" % i], locals()["re%s" % i])
200214

201215
def test_requestHandler(self):
202216
"""Test request handler"""
@@ -212,7 +226,7 @@ def test_api(self):
212226
"""Using an empty query string (returns an XML exception)
213227
we are going to test if headers and body are returned correctly"""
214228
# Test as a whole
215-
header, body = [_v for _v in self.server.handleRequest("")]
229+
header, body = self._execute_request("")
216230
response = self.strip_version_xmlns(header + body)
217231
expected = self.strip_version_xmlns(b'Content-Length: 54\nContent-Type: text/xml; charset=utf-8\n\n<ServerException>Project file error</ServerException>\n')
218232
self.assertEqual(response, expected)
@@ -222,7 +236,7 @@ def test_api(self):
222236
# Test response when project is specified but without service
223237
project = self.testdata_path + "test_project_wfs.qgs"
224238
qs = '?MAP=%s' % (urllib.parse.quote(project))
225-
header, body = [_v for _v in self.server.handleRequest(qs)]
239+
header, body = self._execute_request(qs)
226240
response = self.strip_version_xmlns(header + body)
227241
expected = self.strip_version_xmlns(b'Content-Length: 206\nContent-Type: text/xml; charset=utf-8\n\n<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc">\n <ServiceException code="Service configuration error">Service unknown or unsupported</ServiceException>\n</ServiceExceptionReport>\n')
228242
self.assertEqual(response, expected)
@@ -239,7 +253,7 @@ def wfs_request_compare(self, request):
239253
assert os.path.exists(project), "Project file not found: " + project
240254

241255
query_string = '?MAP=%s&SERVICE=WFS&VERSION=1.0.0&REQUEST=%s' % (urllib.parse.quote(project), request)
242-
header, body = self.server.handleRequest(query_string)
256+
header, body = self._execute_request(query_string)
243257
self.assert_headers(header, body)
244258
response = header + body
245259
reference_path = self.testdata_path + 'wfs_' + request.lower() + '.txt'
@@ -262,7 +276,7 @@ def wfs_getfeature_compare(self, requestid, request):
262276
assert os.path.exists(project), "Project file not found: " + project
263277

264278
query_string = '?MAP=%s&SERVICE=WFS&VERSION=1.0.0&REQUEST=%s' % (urllib.parse.quote(project), request)
265-
header, body = self.server.handleRequest(query_string)
279+
header, body = self._execute_request(query_string)
266280
self.result_compare(
267281
'wfs_getfeature_' + requestid + '.txt',
268282
"request %s failed.\n Query: %s" % (
@@ -294,7 +308,7 @@ def test_wfs_getcapabilities_url(self):
294308
"STYLES": ""
295309
}.items())])
296310

297-
r, h = self._result(self.server.handleRequest(qs))
311+
r, h = self._result(self._execute_request(qs))
298312

299313
for item in str(r).split("\\n"):
300314
if "onlineResource" in item:
@@ -310,7 +324,7 @@ def test_wfs_getcapabilities_url(self):
310324
"STYLES": ""
311325
}.items())])
312326

313-
r, h = self._result(self.server.handleRequest(qs))
327+
r, h = self._result(self._execute_request(qs))
314328

315329
for item in str(r).split("\\n"):
316330
if "onlineResource" in item:
@@ -326,7 +340,7 @@ def test_wfs_getcapabilities_url(self):
326340
"STYLES": ""
327341
}.items())])
328342

329-
r, h = self._result(self.server.handleRequest(qs))
343+
r, h = self._result(self._execute_request(qs))
330344

331345
for item in str(r).split("\\n"):
332346
if "onlineResource" in item:
@@ -349,7 +363,7 @@ def wfs_getfeature_post_compare(self, requestid, request):
349363
assert os.path.exists(project), "Project file not found: " + project
350364

351365
query_string = '?MAP={}'.format(urllib.parse.quote(project))
352-
header, body = self.server.handleRequest(query_string, requestMethod=QgsServerRequest.PostMethod, data=request)
366+
header, body = self._execute_request(query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8'))
353367

354368
self.result_compare(
355369
'wfs_getfeature_{}.txt'.format(requestid),
@@ -389,7 +403,7 @@ def wcs_request_compare(self, request):
389403
assert os.path.exists(project), "Project file not found: " + project
390404

391405
query_string = '?MAP=%s&SERVICE=WCS&VERSION=1.0.0&REQUEST=%s' % (urllib.parse.quote(project), request)
392-
header, body = self.server.handleRequest(query_string)
406+
header, body = self._execute_request(query_string)
393407
self.assert_headers(header, body)
394408
response = header + body
395409
reference_path = self.testdata_path + 'wcs_' + request.lower() + '.txt'
@@ -418,7 +432,7 @@ def test_wcs_getcapabilities_url(self):
418432
"STYLES": ""
419433
}.items())])
420434

421-
r, h = self._result(self.server.handleRequest(qs))
435+
r, h = self._result(self._execute_request(qs))
422436

423437
item_found = False
424438
for item in str(r).split("\\n"):
@@ -437,7 +451,7 @@ def test_wcs_getcapabilities_url(self):
437451
"STYLES": ""
438452
}.items())])
439453

440-
r, h = self._result(self.server.handleRequest(qs))
454+
r, h = self._result(self._execute_request(qs))
441455

442456
item_found = False
443457
for item in str(r).split("\\n"):

‎tests/src/python/test_qgsserver_accesscontrol.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from utilities import unitTestDataPath
2424
from osgeo import gdal
2525
from osgeo.gdalconst import GA_ReadOnly
26-
from qgis.server import QgsServer, QgsAccessControlFilter, QgsServerRequest
26+
from qgis.server import QgsServer, QgsAccessControlFilter, QgsServerRequest, QgsBufferServerRequest, QgsBufferServerResponse
2727
from qgis.core import QgsRenderChecker, QgsApplication
2828
from qgis.PyQt.QtCore import QSize
2929
import tempfile
@@ -160,12 +160,27 @@ def cacheKey(self):
160160

161161
class TestQgsServerAccessControl(unittest.TestCase):
162162

163+
@classmethod
164+
def _execute_request(cls, qs, requestMethod=QgsServerRequest.GetMethod, data=None):
165+
if data is not None:
166+
data = data.encode('utf-8')
167+
request = QgsBufferServerRequest(qs, requestMethod, {}, data)
168+
response = QgsBufferServerResponse()
169+
cls._server.handleRequest(request, response)
170+
headers = []
171+
rh = response.headers()
172+
rk = list(rh.keys())
173+
rk.sort()
174+
for k in rk:
175+
headers.append(("%s: %s" % (k, rh[k])).encode('utf-8'))
176+
return b"\n".join(headers) + b"\n\n", bytes(response.body())
177+
163178
@classmethod
164179
def setUpClass(cls):
165180
"""Run before all tests"""
166181
cls._app = QgsApplication([], False)
167182
cls._server = QgsServer()
168-
cls._server.handleRequest("")
183+
cls._execute_request("")
169184
cls._server_iface = cls._server.serverInterface()
170185
cls._accesscontrol = RestrictedAccessControl(cls._server_iface)
171186
cls._server_iface.registerAccessControl(cls._accesscontrol, 100)
@@ -1375,7 +1390,7 @@ def test_wfs_getfeature_project_subsetstring3(self):
13751390
def _handle_request(self, restricted, query_string, **kwargs):
13761391
self._accesscontrol._active = restricted
13771392
qs = "?" + query_string if query_string is not None else ''
1378-
result = self._result(self._server.handleRequest(qs, **kwargs))
1393+
result = self._result(self._execute_request(qs, **kwargs))
13791394
return result
13801395

13811396
def _result(self, data):
@@ -1432,14 +1447,16 @@ def _img_diff_error(self, response, headers, image, max_diff=10, max_size_diff=Q
14321447
with open(os.path.join(tempfile.gettempdir(), image + "_result.png"), "rb") as rendered_file:
14331448
encoded_rendered_file = base64.b64encode(rendered_file.read())
14341449
message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.png" % (
1435-
report, encoded_rendered_file.strip(), tempfile.gettempdir(), image
1450+
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image
14361451
)
14371452

1438-
with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file:
1439-
encoded_diff_file = base64.b64encode(diff_file.read())
1440-
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % (
1441-
encoded_diff_file.strip(), tempfile.gettempdir(), image
1442-
)
1453+
# If the failure is in image sizes the diff file will not exists.
1454+
if os.path.exists(os.path.join(tempfile.gettempdir(), image + "_result_diff.png")):
1455+
with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file:
1456+
encoded_diff_file = base64.b64encode(diff_file.read())
1457+
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % (
1458+
encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image
1459+
)
14431460

14441461
self.assertTrue(test, message)
14451462

‎tests/src/python/test_qgsserver_plugins.py

Lines changed: 7 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@
1717
__revision__ = '$Format:%H$'
1818

1919
import os
20-
import re
21-
import urllib.request
22-
import urllib.parse
23-
import urllib.error
24-
import email
2520

26-
from io import StringIO
2721
from qgis.server import QgsServer
28-
from qgis.core import QgsMessageLog, QgsApplication
22+
from qgis.core import QgsMessageLog
2923
from qgis.testing import unittest
3024
from utilities import unitTestDataPath
25+
from test_qgsserver import QgsServerTestBase
26+
3127

3228
import osgeo.gdal # NOQA
3329

@@ -37,37 +33,7 @@
3733
RE_ATTRIBUTES = b'[^>\s]+=[^>\s]+'
3834

3935

40-
class TestQgsServerPlugins(unittest.TestCase):
41-
42-
def assertXMLEqual(self, response, expected, msg=''):
43-
"""Compare XML line by line and sorted attributes"""
44-
response_lines = response.splitlines()
45-
expected_lines = expected.splitlines()
46-
line_no = 1
47-
for expected_line in expected_lines:
48-
expected_line = expected_line.strip()
49-
response_line = response_lines[line_no - 1].strip()
50-
# Compare tag
51-
try:
52-
self.assertEqual(re.findall(b'<([^>\s]+)[ >]', expected_line)[0],
53-
re.findall(b'<([^>\s]+)[ >]', response_line)[0], msg=msg + "\nTag mismatch on line %s: %s != %s" % (line_no, expected_line, response_line))
54-
except IndexError:
55-
self.assertEqual(expected_line, response_line, msg=msg + "\nTag line mismatch %s: %s != %s" % (line_no, expected_line, response_line))
56-
# print("---->%s\t%s == %s" % (line_no, expected_line, response_line))
57-
# Compare attributes
58-
if re.match(RE_ATTRIBUTES, expected_line): # has attrs
59-
expected_attrs = sorted(re.findall(RE_ATTRIBUTES, expected_line))
60-
response_attrs = sorted(re.findall(RE_ATTRIBUTES, response_line))
61-
self.assertEqual(expected_attrs, response_attrs, msg=msg + "\nXML attributes differ at line {0}: {1} != {2}".format(line_no, expected_attrs, response_attrs))
62-
line_no += 1
63-
64-
@classmethod
65-
def setUpClass(cls):
66-
cls.app = QgsApplication([], False)
67-
68-
@classmethod
69-
def tearDownClass(cls):
70-
cls.app.exitQgis()
36+
class TestQgsServerPlugins(QgsServerTestBase):
7137

7238
def setUp(self):
7339
"""Create the server instance"""
@@ -85,20 +51,6 @@ def setUp(self):
8551
pass
8652
self.server = QgsServer()
8753

88-
def strip_version_xmlns(self, text):
89-
"""Order of attributes is random, strip version and xmlns"""
90-
return text.replace(b'version="1.3.0"', b'').replace(b'xmlns="http://www.opengis.net/ogc"', b'')
91-
92-
def assert_headers(self, header, body):
93-
stream = StringIO()
94-
header_string = header.decode('utf-8')
95-
stream.write(header_string)
96-
headers = email.message_from_string(header_string)
97-
if 'content-length' in headers:
98-
content_length = int(headers['content-length'])
99-
body_length = len(body)
100-
self.assertEqual(content_length, body_length, msg="Header reported content-length: %d Actual body length was: %d" % (content_length, body_length))
101-
10254
def test_pluginfilters(self):
10355
"""Test python plugins filters"""
10456
try:
@@ -193,7 +145,7 @@ def responseComplete(self):
193145
self.assertTrue(filter2 in serverIface.filters()[100])
194146
self.assertEqual(filter1, serverIface.filters()[101][0])
195147
self.assertEqual(filter2, serverIface.filters()[200][0])
196-
header, body = [_v for _v in self.server.handleRequest('?service=simple')]
148+
header, body = [_v for _v in self._execute_request('?service=simple')]
197149
response = header + body
198150
expected = b'Content-Length: 62\nContent-type: text/plain\n\nHello from SimpleServer!Hello from Filter1!Hello from Filter2!'
199151
self.assertEqual(response, expected)
@@ -211,15 +163,15 @@ def responseComplete(self):
211163
self.assertTrue(filter2 in serverIface.filters()[100])
212164
self.assertEqual(filter1, serverIface.filters()[101][0])
213165
self.assertEqual(filter2, serverIface.filters()[200][0])
214-
header, body = [_v for _v in self.server.handleRequest('?service=simple')]
166+
header, body = [_v for _v in self._execute_request('?service=simple')]
215167
response = header + body
216168
expected = b'Content-Length: 62\nContent-type: text/plain\n\nHello from SimpleServer!Hello from Filter1!Hello from Filter2!'
217169
self.assertEqual(response, expected)
218170

219171
# Now, re-run with body setter
220172
filter5 = Filter5(serverIface)
221173
serverIface.registerFilter(filter5, 500)
222-
header, body = [_v for _v in self.server.handleRequest('?service=simple')]
174+
header, body = [_v for _v in self._execute_request('?service=simple')]
223175
response = header + body
224176
expected = b'Content-Length: 19\nContent-type: text/plain\n\nnew body, new life!'
225177
self.assertEqual(response, expected)

‎tests/src/python/test_qgsserver_security.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
from qgis.server import QgsServer
2727
from qgis.testing import unittest
2828
from utilities import unitTestDataPath
29+
from test_qgsserver import QgsServerTestBase
2930

3031

31-
class TestQgsServerSecurity(unittest.TestCase):
32+
class TestQgsServerSecurity(QgsServerTestBase):
3233

3334
@classmethod
3435
def setUpClass(cls):
@@ -320,7 +321,7 @@ def handle_request_wfs_getfeature_filter(self, filter_xml):
320321
"CRS": "EPSG:32613",
321322
"FILTER": filter_xml}.items())])
322323

323-
return self.server.handleRequest(qs)
324+
return self._execute_request(qs)
324325

325326
def handle_request_wms_getfeatureinfo(self, filter_sql):
326327
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -338,7 +339,7 @@ def handle_request_wms_getfeatureinfo(self, filter_sql):
338339
"CRS": "EPSG:32613",
339340
"FILTER": filter_sql}.items())])
340341

341-
return self._result(self.server.handleRequest(qs))
342+
return self._result(self._execute_request(qs))
342343

343344
def handle_request_wms_getmap(self, sld):
344345
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -356,7 +357,7 @@ def handle_request_wms_getmap(self, sld):
356357
"CRS": "EPSG:32613",
357358
"SLD": sld}.items())])
358359

359-
return self._result(self.server.handleRequest(qs))
360+
return self._result(self._execute_request(qs))
360361

361362
def is_point_table_still_exist(self):
362363
conn = spatialite_connect(self.db_clone)

‎tests/src/python/test_qgsserver_wms.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def wms_request_compare(self, request, extra=None, reference_file=None):
5151
query_string = 'https://www.qgis.org/?MAP=%s&SERVICE=WMS&VERSION=1.3&REQUEST=%s' % (urllib.parse.quote(project), request)
5252
if extra is not None:
5353
query_string += extra
54-
header, body = self.server.handleRequest(query_string)
54+
header, body = self._execute_request(query_string)
5555
response = header + body
5656
reference_path = self.testdata_path + (request.lower() if not reference_file else reference_file) + '.txt'
5757
self.store_reference(reference_path, response)
@@ -102,7 +102,7 @@ def wms_inspire_request_compare(self, request):
102102
assert os.path.exists(project), "Project file not found: " + project
103103

104104
query_string = '?MAP=%s&SERVICE=WMS&VERSION=1.3.0&REQUEST=%s' % (urllib.parse.quote(project), request)
105-
header, body = self.server.handleRequest(query_string)
105+
header, body = self._execute_request(query_string)
106106
response = header + body
107107
reference_path = self.testdata_path + request.lower() + '_inspire.txt'
108108
self.store_reference(reference_path, response)
@@ -133,7 +133,7 @@ def test_wms_getmap_basic(self):
133133
"CRS": "EPSG:3857"
134134
}.items())])
135135

136-
r, h = self._result(self.server.handleRequest(qs))
136+
r, h = self._result(self._execute_request(qs))
137137
self._img_diff_error(r, h, "WMS_GetMap_Basic")
138138

139139
def test_wms_getmap_transparent(self):
@@ -152,7 +152,7 @@ def test_wms_getmap_transparent(self):
152152
"TRANSPARENT": "TRUE"
153153
}.items())])
154154

155-
r, h = self._result(self.server.handleRequest(qs))
155+
r, h = self._result(self._execute_request(qs))
156156
self._img_diff_error(r, h, "WMS_GetMap_Transparent")
157157

158158
def test_wms_getmap_background(self):
@@ -171,7 +171,7 @@ def test_wms_getmap_background(self):
171171
"BGCOLOR": "green"
172172
}.items())])
173173

174-
r, h = self._result(self.server.handleRequest(qs))
174+
r, h = self._result(self._execute_request(qs))
175175
self._img_diff_error(r, h, "WMS_GetMap_Background")
176176

177177
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -189,7 +189,7 @@ def test_wms_getmap_background(self):
189189
"BGCOLOR": "0x008000"
190190
}.items())])
191191

192-
r, h = self._result(self.server.handleRequest(qs))
192+
r, h = self._result(self._execute_request(qs))
193193
self._img_diff_error(r, h, "WMS_GetMap_Background_Hex")
194194

195195
def test_wms_getcapabilities_url(self):
@@ -203,7 +203,7 @@ def test_wms_getcapabilities_url(self):
203203
"STYLES": ""
204204
}.items())])
205205

206-
r, h = self._result(self.server.handleRequest(qs))
206+
r, h = self._result(self._execute_request(qs))
207207

208208
item_found = False
209209
for item in str(r).split("\\n"):
@@ -222,7 +222,7 @@ def test_wms_getcapabilities_url(self):
222222
"STYLES": ""
223223
}.items())])
224224

225-
r, h = self._result(self.server.handleRequest(qs))
225+
r, h = self._result(self._execute_request(qs))
226226

227227
item_found = False
228228
for item in str(r).split("\\n"):
@@ -241,7 +241,7 @@ def test_wms_getcapabilities_url(self):
241241
"STYLES": ""
242242
}.items())])
243243

244-
r, h = self._result(self.server.handleRequest(qs))
244+
r, h = self._result(self._execute_request(qs))
245245

246246
item_found = False
247247
for item in str(r).split("\\n"):
@@ -265,7 +265,7 @@ def test_wms_getmap_invalid_size(self):
265265
}.items())])
266266

267267
expected = self.strip_version_xmlns(b'<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc">\n <ServiceException code="Size error">The requested map size is too large</ServiceException>\n</ServiceExceptionReport>\n')
268-
r, h = self._result(self.server.handleRequest(qs))
268+
r, h = self._result(self._execute_request(qs))
269269

270270
self.assertEqual(self.strip_version_xmlns(r), expected)
271271

@@ -284,7 +284,7 @@ def test_wms_getmap_order(self):
284284
"CRS": "EPSG:3857"
285285
}.items())])
286286

287-
r, h = self._result(self.server.handleRequest(qs))
287+
r, h = self._result(self._execute_request(qs))
288288
self._img_diff_error(r, h, "WMS_GetMap_LayerOrder")
289289

290290
def test_wms_getmap_srs(self):
@@ -302,7 +302,7 @@ def test_wms_getmap_srs(self):
302302
"CRS": "EPSG:4326"
303303
}.items())])
304304

305-
r, h = self._result(self.server.handleRequest(qs))
305+
r, h = self._result(self._execute_request(qs))
306306
self._img_diff_error(r, h, "WMS_GetMap_SRS")
307307

308308
def test_wms_getmap_style(self):
@@ -321,7 +321,7 @@ def test_wms_getmap_style(self):
321321
"CRS": "EPSG:3857"
322322
}.items())])
323323

324-
r, h = self._result(self.server.handleRequest(qs))
324+
r, h = self._result(self._execute_request(qs))
325325
self._img_diff_error(r, h, "WMS_GetMap_StyleDefault")
326326

327327
# custom style
@@ -339,7 +339,7 @@ def test_wms_getmap_style(self):
339339
"CRS": "EPSG:3857"
340340
}.items())])
341341

342-
r, h = self._result(self.server.handleRequest(qs))
342+
r, h = self._result(self._execute_request(qs))
343343
self._img_diff_error(r, h, "WMS_GetMap_StyleCustom")
344344

345345
def test_wms_getmap_filter(self):
@@ -358,7 +358,7 @@ def test_wms_getmap_filter(self):
358358
"FILTER": "Country:\"name\" = 'eurasia'"
359359
}.items())])
360360

361-
r, h = self._result(self.server.handleRequest(qs))
361+
r, h = self._result(self._execute_request(qs))
362362
self._img_diff_error(r, h, "WMS_GetMap_Filter")
363363

364364
def test_wms_getmap_selection(self):
@@ -377,7 +377,7 @@ def test_wms_getmap_selection(self):
377377
"SELECTION": "Country: 4"
378378
}.items())])
379379

380-
r, h = self._result(self.server.handleRequest(qs))
380+
r, h = self._result(self._execute_request(qs))
381381
self._img_diff_error(r, h, "WMS_GetMap_Selection")
382382

383383
def test_wms_getmap_opacities(self):
@@ -396,7 +396,7 @@ def test_wms_getmap_opacities(self):
396396
"OPACITIES": "125, 50"
397397
}.items())])
398398

399-
r, h = self._result(self.server.handleRequest(qs))
399+
r, h = self._result(self._execute_request(qs))
400400
self._img_diff_error(r, h, "WMS_GetMap_Opacities")
401401

402402
def test_wms_getprint_basic(self):
@@ -414,7 +414,7 @@ def test_wms_getprint_basic(self):
414414
"CRS": "EPSG:3857"
415415
}.items())])
416416

417-
r, h = self._result(self.server.handleRequest(qs))
417+
r, h = self._result(self._execute_request(qs))
418418
self._img_diff_error(r, h, "WMS_GetPrint_Basic")
419419

420420
@unittest.skip('Randomly failing to draw the map layer')
@@ -433,7 +433,7 @@ def test_wms_getprint_srs(self):
433433
"CRS": "EPSG:4326"
434434
}.items())])
435435

436-
r, h = self._result(self.server.handleRequest(qs))
436+
r, h = self._result(self._execute_request(qs))
437437
self._img_diff_error(r, h, "WMS_GetPrint_SRS")
438438

439439
def test_wms_getprint_scale(self):
@@ -452,7 +452,7 @@ def test_wms_getprint_scale(self):
452452
"CRS": "EPSG:3857"
453453
}.items())])
454454

455-
r, h = self._result(self.server.handleRequest(qs))
455+
r, h = self._result(self._execute_request(qs))
456456
self._img_diff_error(r, h, "WMS_GetPrint_Scale")
457457

458458
def test_wms_getprint_grid(self):
@@ -472,7 +472,7 @@ def test_wms_getprint_grid(self):
472472
"CRS": "EPSG:3857"
473473
}.items())])
474474

475-
r, h = self._result(self.server.handleRequest(qs))
475+
r, h = self._result(self._execute_request(qs))
476476
self._img_diff_error(r, h, "WMS_GetPrint_Grid")
477477

478478
def test_wms_getprint_rotation(self):
@@ -491,7 +491,7 @@ def test_wms_getprint_rotation(self):
491491
"CRS": "EPSG:3857"
492492
}.items())])
493493

494-
r, h = self._result(self.server.handleRequest(qs))
494+
r, h = self._result(self._execute_request(qs))
495495
self._img_diff_error(r, h, "WMS_GetPrint_Rotation")
496496

497497
def test_wms_getprint_selection(self):
@@ -510,7 +510,7 @@ def test_wms_getprint_selection(self):
510510
"SELECTION": "Country: 4"
511511
}.items())])
512512

513-
r, h = self._result(self.server.handleRequest(qs))
513+
r, h = self._result(self._execute_request(qs))
514514
self._img_diff_error(r, h, "WMS_GetPrint_Selection")
515515

516516
def test_getLegendGraphics(self):
@@ -526,7 +526,7 @@ def test_getLegendGraphics(self):
526526
'LAYER': 'testlayer%20èé',
527527
}
528528
qs = '?' + '&'.join(["%s=%s" % (k, v) for k, v in parms.items()])
529-
h, r = self.server.handleRequest(qs)
529+
h, r = self._execute_request(qs)
530530
self.assertEqual(-1, h.find(b'Content-Type: text/xml; charset=utf-8'), "Header: %s\nResponse:\n%s" % (h, r))
531531
self.assertNotEqual(-1, h.find(b'Content-Type: image/png'), "Header: %s\nResponse:\n%s" % (h, r))
532532

@@ -544,7 +544,7 @@ def test_getLegendGraphics_layertitle(self):
544544
'LAYERTITLE': 'TRUE',
545545
}
546546
qs = '?' + '&'.join([u"%s=%s" % (k, v) for k, v in parms.items()])
547-
r, h = self._result(self.server.handleRequest(qs))
547+
r, h = self._result(self._execute_request(qs))
548548
self._img_diff_error(r, h, "WMS_GetLegendGraphic_test", 250, QSize(15, 15))
549549

550550
parms = {
@@ -559,7 +559,7 @@ def test_getLegendGraphics_layertitle(self):
559559
'LAYERTITLE': 'FALSE',
560560
}
561561
qs = '?' + '&'.join([u"%s=%s" % (k, v) for k, v in parms.items()])
562-
r, h = self._result(self.server.handleRequest(qs))
562+
r, h = self._result(self._execute_request(qs))
563563
self._img_diff_error(r, h, "WMS_GetLegendGraphic_test_layertitle_false", 250, QSize(15, 15))
564564

565565
def test_wms_GetLegendGraphic_Basic(self):
@@ -576,7 +576,7 @@ def test_wms_GetLegendGraphic_Basic(self):
576576
"CRS": "EPSG:3857"
577577
}.items())])
578578

579-
r, h = self._result(self.server.handleRequest(qs))
579+
r, h = self._result(self._execute_request(qs))
580580
self._img_diff_error(r, h, "WMS_GetLegendGraphic_Basic")
581581

582582
def test_wms_GetLegendGraphic_Transparent(self):
@@ -594,7 +594,7 @@ def test_wms_GetLegendGraphic_Transparent(self):
594594
"TRANSPARENT": "TRUE"
595595
}.items())])
596596

597-
r, h = self._result(self.server.handleRequest(qs))
597+
r, h = self._result(self._execute_request(qs))
598598
self._img_diff_error(r, h, "WMS_GetLegendGraphic_Transparent")
599599

600600
def test_wms_GetLegendGraphic_Background(self):
@@ -612,7 +612,7 @@ def test_wms_GetLegendGraphic_Background(self):
612612
"BGCOLOR": "green"
613613
}.items())])
614614

615-
r, h = self._result(self.server.handleRequest(qs))
615+
r, h = self._result(self._execute_request(qs))
616616
self._img_diff_error(r, h, "WMS_GetLegendGraphic_Background")
617617

618618
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -629,7 +629,7 @@ def test_wms_GetLegendGraphic_Background(self):
629629
"BGCOLOR": "0x008000"
630630
}.items())])
631631

632-
r, h = self._result(self.server.handleRequest(qs))
632+
r, h = self._result(self._execute_request(qs))
633633
self._img_diff_error(r, h, "WMS_GetLegendGraphic_Background_Hex")
634634

635635
def test_wms_GetLegendGraphic_BoxSpace(self):
@@ -647,7 +647,7 @@ def test_wms_GetLegendGraphic_BoxSpace(self):
647647
"CRS": "EPSG:3857"
648648
}.items())])
649649

650-
r, h = self._result(self.server.handleRequest(qs))
650+
r, h = self._result(self._execute_request(qs))
651651
self._img_diff_error(r, h, "WMS_GetLegendGraphic_BoxSpace")
652652

653653
def test_wms_GetLegendGraphic_SymbolSpace(self):
@@ -665,7 +665,7 @@ def test_wms_GetLegendGraphic_SymbolSpace(self):
665665
"CRS": "EPSG:3857"
666666
}.items())])
667667

668-
r, h = self._result(self.server.handleRequest(qs))
668+
r, h = self._result(self._execute_request(qs))
669669
self._img_diff_error(r, h, "WMS_GetLegendGraphic_SymbolSpace")
670670

671671
def test_wms_GetLegendGraphic_IconLabelSpace(self):
@@ -683,7 +683,7 @@ def test_wms_GetLegendGraphic_IconLabelSpace(self):
683683
"CRS": "EPSG:3857"
684684
}.items())])
685685

686-
r, h = self._result(self.server.handleRequest(qs))
686+
r, h = self._result(self._execute_request(qs))
687687
self._img_diff_error(r, h, "WMS_GetLegendGraphic_IconLabelSpace")
688688

689689
def test_wms_GetLegendGraphic_SymbolSize(self):
@@ -702,7 +702,7 @@ def test_wms_GetLegendGraphic_SymbolSize(self):
702702
"CRS": "EPSG:3857"
703703
}.items())])
704704

705-
r, h = self._result(self.server.handleRequest(qs))
705+
r, h = self._result(self._execute_request(qs))
706706
self._img_diff_error(r, h, "WMS_GetLegendGraphic_SymbolSize")
707707

708708
def test_wms_GetLegendGraphic_BBox(self):
@@ -720,7 +720,7 @@ def test_wms_GetLegendGraphic_BBox(self):
720720
"CRS": "EPSG:4326"
721721
}.items())])
722722

723-
r, h = self._result(self.server.handleRequest(qs))
723+
r, h = self._result(self._execute_request(qs))
724724
self._img_diff_error(r, h, "WMS_GetLegendGraphic_BBox")
725725

726726
def test_wms_GetLegendGraphic_BBox2(self):
@@ -738,7 +738,7 @@ def test_wms_GetLegendGraphic_BBox2(self):
738738
"SRS": "EPSG:4326"
739739
}.items())])
740740

741-
r, h = self._result(self.server.handleRequest(qs))
741+
r, h = self._result(self._execute_request(qs))
742742
self._img_diff_error(r, h, "WMS_GetLegendGraphic_BBox2")
743743

744744
# WCS tests
@@ -747,7 +747,7 @@ def wcs_request_compare(self, request):
747747
assert os.path.exists(project), "Project file not found: " + project
748748

749749
query_string = '?MAP=%s&SERVICE=WCS&VERSION=1.0.0&REQUEST=%s' % (urllib.parse.quote(project), request)
750-
header, body = self.server.handleRequest(query_string)
750+
header, body = self._execute_request(query_string)
751751
self.assert_headers(header, body)
752752
response = header + body
753753
reference_path = self.testdata_path + 'wcs_' + request.lower() + '.txt'

0 commit comments

Comments
 (0)
Please sign in to comment.