mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Fix POST requests for QGIS server
Followup https://github.com/qgis/QGIS/pull/8830 that fixed a regression with rewritten urls in the server, unfortunately my original solution introduced a side-effect on the POST request, with the new approach I'm introducing a new method to retrieve the URL as seen by the web server: by default this is the same URL seen by QGIS server, but in case a rewrite module made some changes, the original URL will be used as a base URL if not overridden by a config setting. This PR comes with an extended set of tests that should cover both (rewritten and unrewritten) cases for GET and POST and for WFS/WFS/WCS and WMTS.
This commit is contained in:
parent
1b11ba43a2
commit
691176b39b
@ -32,20 +32,6 @@ Class defining fcgi request
|
||||
Returns true if an error occurred during initialization
|
||||
%End
|
||||
|
||||
virtual QUrl url() const;
|
||||
|
||||
%Docstring
|
||||
|
||||
:return: the request url
|
||||
|
||||
Overrides base implementation because FCGI is typically behind
|
||||
a proxy server and QGIS Server will see a rewritten QUERY_STRING.
|
||||
FCGI implementation stores the REQUEST_URI (which is the URL seen
|
||||
by the proxy before it gets rewritten) and returns it instead of
|
||||
the rewritten one.
|
||||
%End
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -56,14 +56,13 @@ Constructor
|
||||
|
||||
virtual ~QgsServerRequest();
|
||||
|
||||
virtual QUrl url() const;
|
||||
QUrl url() const;
|
||||
%Docstring
|
||||
|
||||
:return: the request url
|
||||
:return: the request url as seen by QGIS server
|
||||
|
||||
Subclasses may override in case the original URL (not rewritten, e.g.from
|
||||
a web server rewrite module) needs to be returned instead of the URL
|
||||
seen by QGIS server.
|
||||
.. seealso:: :py:func:`originalUrl`
|
||||
server, by default the two are equal
|
||||
%End
|
||||
|
||||
QgsServerRequest::Method method() const;
|
||||
@ -139,6 +138,16 @@ is available.
|
||||
void setUrl( const QUrl &url );
|
||||
%Docstring
|
||||
Set the request url
|
||||
%End
|
||||
|
||||
QUrl originalUrl() const;
|
||||
%Docstring
|
||||
Returns the request url as seen by the web server,
|
||||
by default this is equal to the url seen by QGIS server
|
||||
|
||||
.. seealso:: :py:func:`url`
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
void setMethod( QgsServerRequest::Method method );
|
||||
@ -146,6 +155,18 @@ Set the request url
|
||||
Set the request method
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
void setOriginalUrl( const QUrl &url );
|
||||
%Docstring
|
||||
Set the request original \url (the request url as seen by the web server)
|
||||
|
||||
.. seealso:: :py:func:`setUrl`
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -68,7 +68,7 @@ QgsFcgiServerRequest::QgsFcgiServerRequest()
|
||||
}
|
||||
|
||||
// Store the URL before the server rewrite that could have been set in QUERY_STRING
|
||||
mOriginalUrl = url;
|
||||
setOriginalUrl( url );
|
||||
|
||||
// OGC parameters are passed with the query string, which is normally part of
|
||||
// the REQUEST_URI, we override the query string url in case it is defined
|
||||
@ -130,11 +130,6 @@ QByteArray QgsFcgiServerRequest::data() const
|
||||
return mData;
|
||||
}
|
||||
|
||||
QUrl QgsFcgiServerRequest::url() const
|
||||
{
|
||||
return mOriginalUrl.isEmpty() ? QgsServerRequest::url() : mOriginalUrl;
|
||||
}
|
||||
|
||||
// Read post put data
|
||||
void QgsFcgiServerRequest::readData()
|
||||
{
|
||||
@ -142,14 +137,22 @@ void QgsFcgiServerRequest::readData()
|
||||
const char *lengthstr = getenv( "CONTENT_LENGTH" );
|
||||
if ( lengthstr )
|
||||
{
|
||||
#ifdef QGISDEBUG
|
||||
qDebug() << "fcgi: reading " << lengthstr << " bytes from stdin";
|
||||
#endif
|
||||
bool success = false;
|
||||
int length = QString( lengthstr ).toInt( &success );
|
||||
#ifdef QGISDEBUG
|
||||
const char *request_body = getenv( "REQUEST_BODY" );
|
||||
if ( success && request_body )
|
||||
{
|
||||
QString body( request_body );
|
||||
body.truncate( length );
|
||||
mData.append( body.toUtf8() );
|
||||
length = 0;
|
||||
}
|
||||
qDebug() << "fcgi: reading " << lengthstr << " bytes from " << ( request_body ? "REQUEST_BODY" : "stdin" );
|
||||
#endif
|
||||
if ( success )
|
||||
{
|
||||
// XXX This not efficiont at all !!
|
||||
// XXX This not efficient at all !!
|
||||
for ( int i = 0; i < length; ++i )
|
||||
{
|
||||
mData.append( getchar() );
|
||||
|
@ -41,18 +41,6 @@ class SERVER_EXPORT QgsFcgiServerRequest: public QgsServerRequest
|
||||
*/
|
||||
bool hasError() const { return mHasError; }
|
||||
|
||||
/**
|
||||
* \returns the request url
|
||||
*
|
||||
* Overrides base implementation because FCGI is typically behind
|
||||
* a proxy server and QGIS Server will see a rewritten QUERY_STRING.
|
||||
* FCGI implementation stores the REQUEST_URI (which is the URL seen
|
||||
* by the proxy before it gets rewritten) and returns it instead of
|
||||
* the rewritten one.
|
||||
*/
|
||||
QUrl url() const override;
|
||||
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
|
@ -27,6 +27,7 @@ QgsServerRequest::QgsServerRequest( const QString &url, Method method, const Hea
|
||||
|
||||
QgsServerRequest::QgsServerRequest( const QUrl &url, Method method, const Headers &headers )
|
||||
: mUrl( url )
|
||||
, mOriginalUrl( url )
|
||||
, mMethod( method )
|
||||
, mHeaders( headers )
|
||||
{
|
||||
@ -60,6 +61,16 @@ QUrl QgsServerRequest::url() const
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
QUrl QgsServerRequest::originalUrl() const
|
||||
{
|
||||
return mOriginalUrl;
|
||||
}
|
||||
|
||||
void QgsServerRequest::setOriginalUrl( const QUrl &url )
|
||||
{
|
||||
mOriginalUrl = url;
|
||||
}
|
||||
|
||||
QgsServerRequest::Method QgsServerRequest::method() const
|
||||
{
|
||||
return mMethod;
|
||||
|
@ -82,13 +82,12 @@ class SERVER_EXPORT QgsServerRequest
|
||||
virtual ~QgsServerRequest() = default;
|
||||
|
||||
/**
|
||||
* \returns the request url
|
||||
* \returns the request url as seen by QGIS server
|
||||
*
|
||||
* Subclasses may override in case the original URL (not rewritten, e.g.from
|
||||
* a web server rewrite module) needs to be returned instead of the URL
|
||||
* seen by QGIS server.
|
||||
* \see originalUrl for the unrewritten url as seen by the web
|
||||
* server, by default the two are equal
|
||||
*/
|
||||
virtual QUrl url() const;
|
||||
QUrl url() const;
|
||||
|
||||
/**
|
||||
* \returns the request method
|
||||
@ -159,13 +158,36 @@ class SERVER_EXPORT QgsServerRequest
|
||||
*/
|
||||
void setUrl( const QUrl &url );
|
||||
|
||||
/**
|
||||
* Returns the request url as seen by the web server,
|
||||
* by default this is equal to the url seen by QGIS server
|
||||
*
|
||||
* \see url() for the rewritten url
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
QUrl originalUrl() const;
|
||||
|
||||
/**
|
||||
* Set the request method
|
||||
*/
|
||||
void setMethod( QgsServerRequest::Method method );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Set the request original \url (the request url as seen by the web server)
|
||||
*
|
||||
* \see setUrl() for the rewritten url
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
void setOriginalUrl( const QUrl &url );
|
||||
|
||||
|
||||
private:
|
||||
// Url as seen by QGIS server after web server rewrite
|
||||
QUrl mUrl;
|
||||
// Unrewritten url as seen by the web server
|
||||
QUrl mOriginalUrl;
|
||||
Method mMethod = GetMethod;
|
||||
// We mark as mutable in order
|
||||
// to support lazy initialization
|
||||
|
@ -250,7 +250,7 @@ namespace QgsWcs
|
||||
// Build default url
|
||||
if ( href.isEmpty() )
|
||||
{
|
||||
QUrl url = request.url();
|
||||
QUrl url = request.originalUrl();
|
||||
QUrlQuery q( url );
|
||||
|
||||
for ( auto param : q.queryItems() )
|
||||
|
@ -36,28 +36,36 @@ namespace QgsWfs
|
||||
|
||||
QString serviceUrl( const QgsServerRequest &request, const QgsProject *project )
|
||||
{
|
||||
QString href;
|
||||
QUrl href;
|
||||
if ( project )
|
||||
{
|
||||
href = QgsServerProjectUtils::wfsServiceUrl( *project );
|
||||
href.setUrl( QgsServerProjectUtils::wfsServiceUrl( *project ) );
|
||||
}
|
||||
|
||||
// Build default url
|
||||
if ( href.isEmpty() )
|
||||
{
|
||||
QUrl url = request.url();
|
||||
|
||||
QgsWfsParameters params;
|
||||
params.load( QUrlQuery( url ) );
|
||||
params.remove( QgsServerParameter::REQUEST );
|
||||
params.remove( QgsServerParameter::VERSION_SERVICE );
|
||||
params.remove( QgsServerParameter::SERVICE );
|
||||
static QSet<QString> sFilter
|
||||
{
|
||||
QStringLiteral( "REQUEST" ),
|
||||
QStringLiteral( "VERSION" ),
|
||||
QStringLiteral( "SERVICE" ),
|
||||
};
|
||||
|
||||
url.setQuery( params.urlQuery() );
|
||||
href = url.toString();
|
||||
href = request.originalUrl();
|
||||
QUrlQuery q( href );
|
||||
|
||||
for ( auto param : q.queryItems() )
|
||||
{
|
||||
if ( sFilter.contains( param.first.toUpper() ) )
|
||||
q.removeAllQueryItems( param.first );
|
||||
}
|
||||
|
||||
href.setQuery( q );
|
||||
}
|
||||
|
||||
return href;
|
||||
return href.toString();
|
||||
}
|
||||
|
||||
QString layerTypeName( const QgsMapLayer *layer )
|
||||
|
@ -56,7 +56,7 @@ namespace QgsWms
|
||||
QStringLiteral( "_DC" )
|
||||
};
|
||||
|
||||
href = request.url();
|
||||
href = request.originalUrl();
|
||||
QUrlQuery q( href );
|
||||
|
||||
for ( auto param : q.queryItems() )
|
||||
|
@ -59,7 +59,7 @@ namespace QgsWmts
|
||||
// Build default url
|
||||
if ( href.isEmpty() )
|
||||
{
|
||||
QUrl url = request.url();
|
||||
QUrl url = request.originalUrl();
|
||||
|
||||
QgsWmtsParameters params;
|
||||
params.load( QUrlQuery( url ) );
|
||||
|
@ -9,9 +9,6 @@ the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
import unittest
|
||||
import os
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '29/04/2017'
|
||||
@ -20,16 +17,38 @@ __copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from urllib.parse import parse_qs, urlencode, urlparse
|
||||
|
||||
from lxml import etree as ET
|
||||
|
||||
from qgis.PyQt.QtCore import QUrl
|
||||
from qgis.server import QgsServerRequest, QgsFcgiServerRequest
|
||||
from qgis.server import (QgsBufferServerResponse, QgsFcgiServerRequest,
|
||||
QgsServerRequest)
|
||||
from test_qgsserver import QgsServerTestBase
|
||||
|
||||
|
||||
class QgsServerRequestTest(unittest.TestCase):
|
||||
class QgsServerRequestTest(QgsServerTestBase):
|
||||
|
||||
@staticmethod
|
||||
def _set_env(env={}):
|
||||
for k in ('QUERY_STRING', 'REQUEST_URI', 'SERVER_NAME', 'CONTENT_LENGTH', 'SERVER_PORT', 'SCRIPT_NAME', 'REQUEST_BODY', 'REQUEST_METHOD'):
|
||||
try:
|
||||
del os.environ[k]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
os.environ[k] = env[k]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def test_requestHeaders(self):
|
||||
"""Test request headers"""
|
||||
headers = {'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2'}
|
||||
request = QgsServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers)
|
||||
headers = {'header-key-1': 'header-value-1',
|
||||
'header-key-2': 'header-value-2'}
|
||||
request = QgsServerRequest(
|
||||
'http://somesite.com/somepath', QgsServerRequest.GetMethod, headers)
|
||||
for k, v in request.headers().items():
|
||||
self.assertEqual(headers[k], v)
|
||||
request.removeHeader('header-key-1')
|
||||
@ -40,7 +59,8 @@ class QgsServerRequestTest(unittest.TestCase):
|
||||
|
||||
def test_requestParameters(self):
|
||||
"""Test request parameters"""
|
||||
request = QgsServerRequest('http://somesite.com/somepath?parm1=val1&parm2=val2', QgsServerRequest.GetMethod)
|
||||
request = QgsServerRequest(
|
||||
'http://somesite.com/somepath?parm1=val1&parm2=val2', QgsServerRequest.GetMethod)
|
||||
parameters = {'PARM1': 'val1', 'PARM2': 'val2'}
|
||||
for k, v in request.parameters().items():
|
||||
self.assertEqual(parameters[k], v)
|
||||
@ -52,18 +72,23 @@ class QgsServerRequestTest(unittest.TestCase):
|
||||
|
||||
def test_requestParametersDecoding(self):
|
||||
"""Test request parameters decoding"""
|
||||
request = QgsServerRequest('http://somesite.com/somepath?parm1=val1%20%2B+val2', QgsServerRequest.GetMethod)
|
||||
request = QgsServerRequest(
|
||||
'http://somesite.com/somepath?parm1=val1%20%2B+val2', QgsServerRequest.GetMethod)
|
||||
self.assertEqual(request.parameters()['PARM1'], 'val1 + val2')
|
||||
|
||||
def test_requestUrl(self):
|
||||
"""Test url"""
|
||||
request = QgsServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod)
|
||||
self.assertEqual(request.url().toString(), 'http://somesite.com/somepath')
|
||||
request = QgsServerRequest(
|
||||
'http://somesite.com/somepath', QgsServerRequest.GetMethod)
|
||||
self.assertEqual(request.url().toString(),
|
||||
'http://somesite.com/somepath')
|
||||
request.setUrl(QUrl('http://someother.com/someotherpath'))
|
||||
self.assertEqual(request.url().toString(), 'http://someother.com/someotherpath')
|
||||
self.assertEqual(request.url().toString(),
|
||||
'http://someother.com/someotherpath')
|
||||
|
||||
def test_requestMethod(self):
|
||||
request = QgsServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod)
|
||||
request = QgsServerRequest(
|
||||
'http://somesite.com/somepath', QgsServerRequest.GetMethod)
|
||||
self.assertEqual(request.method(), QgsServerRequest.GetMethod)
|
||||
request.setMethod(QgsServerRequest.PostMethod)
|
||||
self.assertEqual(request.method(), QgsServerRequest.PostMethod)
|
||||
@ -71,43 +96,114 @@ class QgsServerRequestTest(unittest.TestCase):
|
||||
def test_fcgiRequest(self):
|
||||
"""Test various combinations of FCGI env parameters with rewritten urls"""
|
||||
|
||||
def _test_url(url, env={}):
|
||||
for k in ('QUERY_STRING', 'REQUEST_URI', 'SERVER_NAME', 'SERVER_PORT', 'SCRIPT_NAME'):
|
||||
try:
|
||||
del os.environ[k]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
os.environ[k] = env[k]
|
||||
except KeyError:
|
||||
pass
|
||||
def _test_url(original_url, rewritten_url, env={}):
|
||||
self._set_env(env)
|
||||
request = QgsFcgiServerRequest()
|
||||
self.assertEqual(request.url().toString(), url)
|
||||
self.assertEqual(request.originalUrl().toString(), original_url)
|
||||
self.assertEqual(request.url().toString(), rewritten_url)
|
||||
# Check MAP
|
||||
if 'QUERY_STRING' in env:
|
||||
map = {k.upper(): v[0] for k, v in parse_qs(env['QUERY_STRING']).items()}['MAP']
|
||||
map = {k.upper(): v[0] for k, v in parse_qs(
|
||||
env['QUERY_STRING']).items()}['MAP']
|
||||
else:
|
||||
map = {k.upper(): v[0] for k, v in parse_qs(urlparse(env['REQUEST_URI']).query).items()}['MAP']
|
||||
map = {k.upper(): v[0] for k, v in parse_qs(
|
||||
urlparse(env['REQUEST_URI']).query).items()}['MAP']
|
||||
self.assertEqual(request.parameter('MAP'), map)
|
||||
|
||||
_test_url('http://somesite.com/somepath/index.html?map=/my/path.qgs', {
|
||||
'REQUEST_URI': '/somepath/index.html?map=/my/path.qgs',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
})
|
||||
_test_url('http://somesite.com/somepath?map=/my/path.qgs', {
|
||||
'REQUEST_URI': '/somepath?map=/my/path.qgs',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
})
|
||||
_test_url('http://somesite.com/somepath/path', {
|
||||
'REQUEST_URI': '/somepath/path',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
'QUERY_STRING': 'map=/my/path.qgs'
|
||||
})
|
||||
_test_url('http://somesite.com/somepath/path/?token=QGIS2019', {
|
||||
'REQUEST_URI': '/somepath/path/?token=QGIS2019',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
'QUERY_STRING': 'map=/my/path.qgs',
|
||||
})
|
||||
_test_url('http://somesite.com/somepath/project1/',
|
||||
'http://somesite.com/somepath/project1/?map=/my/project1.qgs', {
|
||||
'REQUEST_URI': '/somepath/project1/',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
'QUERY_STRING': 'map=/my/project1.qgs'
|
||||
})
|
||||
|
||||
_test_url('http://somesite.com/somepath/path/?token=QGIS2019',
|
||||
'http://somesite.com/somepath/path/?map=/my/path.qgs', {
|
||||
'REQUEST_URI': '/somepath/path/?token=QGIS2019',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
'QUERY_STRING': 'map=/my/path.qgs',
|
||||
})
|
||||
|
||||
_test_url('http://somesite.com/somepath/index.html?map=/my/path.qgs',
|
||||
'http://somesite.com/somepath/index.html?map=/my/path.qgs',
|
||||
{
|
||||
'REQUEST_URI': '/somepath/index.html?map=/my/path.qgs',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
})
|
||||
|
||||
_test_url('http://somesite.com/somepath?map=/my/path.qgs',
|
||||
'http://somesite.com/somepath?map=/my/path.qgs',
|
||||
{
|
||||
'REQUEST_URI': '/somepath?map=/my/path.qgs',
|
||||
'SERVER_NAME': 'somesite.com',
|
||||
})
|
||||
|
||||
def test_fcgiRequestPOST(self):
|
||||
"""Test various combinations of FCGI POST parameters with rewritten urls"""
|
||||
|
||||
def _check_links(params, method='GET'):
|
||||
data = urlencode(params)
|
||||
if method == 'GET':
|
||||
env = {
|
||||
'SERVER_NAME': 'www.myserver.com',
|
||||
'REQUEST_URI': '/aproject/',
|
||||
'QUERY_STRING': data,
|
||||
'REQUEST_METHOD': 'GET',
|
||||
}
|
||||
else:
|
||||
env = {
|
||||
'SERVER_NAME': 'www.myserver.com',
|
||||
'REQUEST_URI': '/aproject/',
|
||||
'REQUEST_BODY': data,
|
||||
'CONTENT_LENGTH': str(len(data)),
|
||||
'REQUEST_METHOD': 'POST',
|
||||
}
|
||||
|
||||
self._set_env(env)
|
||||
request = QgsFcgiServerRequest()
|
||||
response = QgsBufferServerResponse()
|
||||
self.server.handleRequest(request, response)
|
||||
self.assertFalse(b'ServiceExceptionReport' in response.body())
|
||||
|
||||
if method == 'POST':
|
||||
self.assertEqual(request.data(), data.encode('utf8'))
|
||||
else:
|
||||
original_url = request.originalUrl().toString()
|
||||
self.assertTrue(original_url.startswith('http://www.myserver.com/aproject/'))
|
||||
self.assertEqual(original_url.find(urlencode({'MAP': params['map']})), -1)
|
||||
|
||||
e = ET.fromstring(bytes(response.body()))
|
||||
elems = []
|
||||
for ns in ('wms', 'wfs', 'wcs'):
|
||||
elems += e.xpath('//xxx:OnlineResource', namespaces={'xxx': 'http://www.opengis.net/%s' % ns})
|
||||
elems += e.xpath('//ows:Get/ows:OnlineResource', namespaces={'ows': 'http://www.opengis.net/ows'})
|
||||
elems += e.xpath('//ows:Post', namespaces={'ows': 'http://www.opengis.net/ows'})
|
||||
elems += e.xpath('//ows:Get', namespaces={'ows': 'http://www.opengis.net/ows'})
|
||||
elems += e.xpath('//ows:Get', namespaces={'ows': 'http://www.opengis.net/ows/1.1'})
|
||||
self.assertTrue(len(elems) > 0)
|
||||
for _e in elems:
|
||||
attrs = _e.attrib
|
||||
href = attrs['{http://www.w3.org/1999/xlink}href']
|
||||
self.assertTrue(href.startswith('http://www.myserver.com/aproject/'))
|
||||
self.assertEqual(href.find(urlencode({'MAP': params['map']})), -1)
|
||||
|
||||
# Test post request handler
|
||||
params = {
|
||||
'map': os.path.join(self.testdata_path, 'test_project_wfs.qgs'),
|
||||
'REQUEST': 'GetCapabilities',
|
||||
'SERVICE': 'WFS',
|
||||
}
|
||||
_check_links(params)
|
||||
_check_links(params, 'POST')
|
||||
params['SERVICE'] = 'WMS'
|
||||
_check_links(params)
|
||||
_check_links(params, 'POST')
|
||||
params['SERVICE'] = 'WCS'
|
||||
_check_links(params)
|
||||
_check_links(params, 'POST')
|
||||
params['SERVICE'] = 'WMTS'
|
||||
_check_links(params)
|
||||
_check_links(params, 'POST')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,10 +4,10 @@
|
||||
Tests for WFS-T provider using QGIS Server through qgis_wrapped_server.py.
|
||||
|
||||
This is an integration test for QGIS Desktop WFS-T provider and QGIS Server
|
||||
WFS-T that check if QGIS can talk to and uderstand itself.
|
||||
WFS-T that check if QGIS can talk to and understand itself.
|
||||
|
||||
The test uses testdata/wfs_transactional/wfs_transactional.qgs and three
|
||||
initially empty shapefiles layrs with points, lines and polygons.
|
||||
initially empty shapefiles layers with points, lines and polygons.
|
||||
|
||||
All WFS-T calls are executed through the QGIS WFS data provider.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user