Skip to content

Commit

Permalink
Merge pull request #45463 from elpaso/bugfix-gh45439-server-api-path-…
Browse files Browse the repository at this point in the history
…match-backport

Bugfix gh45439 server api path match backport
  • Loading branch information
elpaso committed Oct 11, 2021
2 parents 5c73b29 + c1b88ce commit 57590fd
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 5 deletions.
8 changes: 8 additions & 0 deletions python/server/auto_generated/qgsserverapicontext.sip.in
Expand Up @@ -87,6 +87,14 @@ Returns the API root path
void setRequest( const QgsServerRequest *request );
%Docstring
Sets context request to ``request``
%End

QString handlerPath( ) const;
%Docstring
Returns the handler component of the URL path, i.e. the part of the path that comes
after the API path.

.. versionadded:: 3.22
%End

};
Expand Down
3 changes: 2 additions & 1 deletion python/server/auto_generated/qgsserverogcapi.sip.in
Expand Up @@ -105,7 +105,8 @@ Registers an OGC API ``handler``, ownership of the handler is transferred to the

static QUrl sanitizeUrl( const QUrl &url );
%Docstring
Returns a sanitized ``url`` with extra slashes removed
Returns a sanitized ``url`` with extra slashes removed and the path URL component that
always starts with a slash.
%End

static std::string relToString( const QgsServerOgcApi::Rel &rel );
Expand Down
14 changes: 14 additions & 0 deletions src/server/qgsserverapicontext.cpp
Expand Up @@ -81,3 +81,17 @@ void QgsServerApiContext::setRequest( const QgsServerRequest *request )
{
mRequest = request;
}

QString QgsServerApiContext::handlerPath() const
{
const QUrl url { request()->url() };
const QString urlBasePath { matchedPath() };
if ( ! urlBasePath.isEmpty() )
{
return url.path().mid( urlBasePath.length() );
}
else
{
return url.path();
}
}
8 changes: 8 additions & 0 deletions src/server/qgsserverapicontext.h
Expand Up @@ -101,6 +101,14 @@ class SERVER_EXPORT QgsServerApiContext
*/
void setRequest( const QgsServerRequest *request );

/**
* Returns the handler component of the URL path, i.e. the part of the path that comes
* after the API path.
*
* \since QGIS 3.22
*/
QString handlerPath( ) const;

private:

QString mApiRootPath;
Expand Down
7 changes: 6 additions & 1 deletion src/server/qgsserverogcapi.cpp
Expand Up @@ -75,13 +75,18 @@ QUrl QgsServerOgcApi::sanitizeUrl( const QUrl &url )
{
u.setPath( u.path().replace( QLatin1String( "//" ), QChar( '/' ) ) );
}
// Make sure the path starts with '/'
if ( !u.path().startsWith( '/' ) )
{
u.setPath( u.path().prepend( '/' ) );
}
return u;
}

void QgsServerOgcApi::executeRequest( const QgsServerApiContext &context ) const
{
// Get url
auto path { sanitizeUrl( context.request()->url() ).path() };
const auto path { sanitizeUrl( context.handlerPath( ) ).path() };
// Find matching handler
auto hasMatch { false };
for ( const auto &h : mHandlers )
Expand Down
3 changes: 2 additions & 1 deletion src/server/qgsserverogcapi.h
Expand Up @@ -145,7 +145,8 @@ class SERVER_EXPORT QgsServerOgcApi : public QgsServerApi
void registerHandler( QgsServerOgcApiHandler *handler SIP_TRANSFER );

/**
* Returns a sanitized \a url with extra slashes removed
* Returns a sanitized \a url with extra slashes removed and the path URL component that
* always starts with a slash.
*/
static QUrl sanitizeUrl( const QUrl &url );

Expand Down
5 changes: 3 additions & 2 deletions src/server/qgsserverogcapihandler.cpp
Expand Up @@ -46,7 +46,8 @@ QVariantMap QgsServerOgcApiHandler::values( const QgsServerApiContext &context )
// value() calls the validators and throws an exception if validation fails
result[p.name()] = p.value( context );
}
const auto match { path().match( context.request()->url().toString() ) };
const auto sanitizedPath { QgsServerOgcApi::sanitizeUrl( context.handlerPath( ) ).path() };
const auto match { path().match( sanitizedPath ) };
if ( match.hasMatch() )
{
const auto constNamed { path().namedCaptureGroups() };
Expand Down Expand Up @@ -138,7 +139,7 @@ std::string QgsServerOgcApiHandler::href( const QgsServerApiContext &context, co
{
QUrl url { context.request()->url() };
QString urlBasePath { context.matchedPath() };
const auto match { path().match( url.path() ) };
const auto match { path().match( QgsServerOgcApi::sanitizeUrl( context.handlerPath( ) ).path( ) ) };
if ( match.captured().count() > 0 )
{
url.setPath( urlBasePath + match.captured( 0 ) );
Expand Down
55 changes: 55 additions & 0 deletions tests/src/python/test_qgsserver_api.py
Expand Up @@ -24,6 +24,7 @@
from qgis.server import (
QgsBufferServerRequest,
QgsBufferServerResponse,
QgsServer,
QgsServerApi,
QgsServerApiBadRequestException,
QgsServerQueryStringParameter,
Expand Down Expand Up @@ -1747,6 +1748,36 @@ def templatePath(self, context):
return self.templatePathOverride


class Handler4(QgsServerOgcApiHandler):

def path(self):
return QtCore.QRegularExpression("/(?P<tilemapid>[^/]+)")

def operationId(self):
return "handler4"

def summary(self):
return "Fourth of its name"

def description(self):
return "The fourth handler ever"

def linkTitle(self):
return "Handler Four Link Title"

def linkType(self):
return QgsServerOgcApi.data

def handleRequest(self, context):
"""Simple mirror: returns the parameters"""

self.params = self.values(context)
self.write(self.params, context)

def parameters(self, context):
return []


class HandlerException(QgsServerOgcApiHandler):

def __init__(self):
Expand Down Expand Up @@ -1998,6 +2029,30 @@ def testOgcApiHandlerException(self):

del(project)

def test_path_capture(self):
"""Test issue GH #45439"""

api = QgsServerOgcApi(self.server.serverInterface(),
'/api4', 'apifour', 'a fourth api', '1.2')

h4 = Handler4()
api.registerHandler(h4)

request = QgsBufferServerRequest(
'http://localhost:19876/api4/france_parts.json?MAP=france_parts')
response = QgsBufferServerResponse()

server = QgsServer()
iface = server.serverInterface()
iface.serviceRegistry().registerApi(api)

server.handleRequest(request, response)

self.assertEqual(h4.params, {'tilemapid': 'france_parts.json'})

ctx = QgsServerApiContext(api.rootPath(), request, response, None, iface)
self.assertEqual(h4.href(ctx), 'http://localhost:19876/api4/france_parts?MAP=france_parts')


if __name__ == '__main__':
unittest.main()

0 comments on commit 57590fd

Please sign in to comment.