[Server][Feature][needs-docs] Server Cache can be manage by plugins

First commit to add a way to manage the QGIS Server cache with plugins.

In this commit only GetCapabilities document can be cached by plugins.
This commit is contained in:
rldhont 2018-07-20 11:00:52 +02:00
parent b39ee5a40c
commit 9bd23b5ac3
16 changed files with 685 additions and 13 deletions

View File

@ -83,6 +83,19 @@ Register an access control filter
virtual QgsAccessControl *accessControls() const = 0;
%Docstring
Gets the registered access control filters
%End
virtual void registerServerCache( QgsServerCacheFilter *serverCache /Transfer/, int priority = 0 ) = 0;
%Docstring
Register a server cache filter
:param serverCache: the server cache to register
:param priority: the priority used to order them
%End
virtual QgsServerCacheManager *cacheManager() const = 0;
%Docstring
Gets the registered server cache filters
%End
virtual QString getEnv( const QString &name ) const = 0;

View File

@ -29,3 +29,9 @@
%If ( HAVE_SERVER_PYTHON_PLUGINS )
%Include auto_generated/qgsaccesscontrol.sip
%End
%If ( HAVE_SERVER_PYTHON_PLUGINS )
%Include auto_generated/qgsservercachefilter.sip
%End
%If ( HAVE_SERVER_PYTHON_PLUGINS )
%Include auto_generated/qgsservercachemanager.sip
%End

View File

@ -73,6 +73,8 @@ IF (WITH_SERVER_PLUGINS)
qgsserverfilter.cpp
qgsaccesscontrolfilter.cpp
qgsaccesscontrol.cpp
qgsservercachefilter.cpp
qgsservercachemanager.cpp
)
ENDIF (WITH_SERVER_PLUGINS)

View File

@ -0,0 +1,56 @@
/***************************************************************************
qgsservercachefilter.cpp
------------------------
Cache interface for Qgis Server plugins
begin : 2018-07-05
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgsservercachefilter.h"
#include <QDomDocument>
//! Constructor
QgsServerCacheFilter::QgsServerCacheFilter( const QgsServerInterface *serverInterface ):
mServerInterface( serverInterface )
{
}
//! Returns cached document
QByteArray QgsServerCacheFilter::getCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
Q_UNUSED( project );
Q_UNUSED( request );
Q_UNUSED( key );
return QByteArray();
}
//! Updates or inserts the document in cache
bool QgsServerCacheFilter::setCachedDocument( const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
Q_UNUSED( doc );
Q_UNUSED( project );
Q_UNUSED( request );
Q_UNUSED( key );
return false;
}
//! Deletes the cached document
bool QgsServerCacheFilter::deleteCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
Q_UNUSED( project );
Q_UNUSED( request );
Q_UNUSED( key );
return false;
}

View File

@ -0,0 +1,93 @@
/***************************************************************************
qgsservercachefilter.h
------------------------
Cache interface for Qgis Server plugins
begin : 2018-07-05
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSSERVERCACHEPLUGIN_H
#define QGSSERVERCACHEPLUGIN_H
#include <QMultiMap>
#include <QDomDocument>
#include <QObject>
#include "qgsproject.h"
#include "qgsserverrequest.h"
#include "qgis_server.h"
#include "qgis_sip.h"
SIP_IF_MODULE( HAVE_SERVER_PYTHON_PLUGINS )
class QgsServerInterface;
/**
* \ingroup server
* \class QgsServerCacheFilter
* \brief Class defining cache interface for QGIS Server plugins.
*/
class SERVER_EXPORT QgsServerCacheFilter
{
public:
/**
* Constructor
* QgsServerInterface passed to plugins constructors
* and must be passed to QgsServerCacheFilter instances.
*/
QgsServerCacheFilter( const QgsServerInterface *serverInterface );
virtual ~QgsServerCacheFilter() = default;
/**
* Returns cached document (or 0 if document not in cache) like capabilities
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns QByteArray of the cached document or an empty one if no corresponding document found
*/
virtual QByteArray getCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
/**
* Updates or inserts the document in cache like capabilities
* \param doc the document to cache
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns true if the document has been cached
*/
virtual bool setCachedDocument( const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
/**
* Deletes the cached document
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns true if the document has been deleted
*/
virtual bool deleteCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
private:
//! The server interface
const QgsServerInterface *mServerInterface = nullptr;
};
//! The registry definition
typedef QMultiMap<int, QgsServerCacheFilter *> QgsServerCacheFilterMap;
#endif // QGSSERVERSECURITY_H

View File

@ -0,0 +1,72 @@
/***************************************************************************
qgsservercachemanager.cpp
-------------------------
begin : 2018-07-05
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgsservercachemanager.h"
//! Returns cached document (or 0 if document not in cache) like capabilities
const QDomDocument *QgsServerCacheManager::getCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
QgsServerCacheFilterMap::const_iterator scIterator;
for ( scIterator = mPluginsServerCaches->constBegin(); scIterator != mPluginsServerCaches->constEnd(); ++scIterator )
{
QByteArray content = scIterator.value()->getCachedDocument( project, request, key );
if ( !content.isEmpty() )
{
QDomDocument doc;
if ( doc.setContent( content ) )
{
return &doc;
}
}
}
return nullptr;
}
//! Updates or inserts the document in cache like capabilities
bool QgsServerCacheManager::setCachedDocument( const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
QgsServerCacheFilterMap::const_iterator scIterator;
for ( scIterator = mPluginsServerCaches->constBegin(); scIterator != mPluginsServerCaches->constEnd(); ++scIterator )
{
if ( scIterator.value()->setCachedDocument( doc, project, request, key ) )
{
return true;
}
}
return false;
}
//! Deletes the cached document
bool QgsServerCacheManager::deleteCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const
{
QgsServerCacheFilterMap::const_iterator scIterator;
for ( scIterator = mPluginsServerCaches->constBegin(); scIterator != mPluginsServerCaches->constEnd(); ++scIterator )
{
if ( scIterator.value()->deleteCachedDocument( project, request, key ) )
{
return true;
}
}
return false;
}
//! Register a new access control filter
void QgsServerCacheManager::registerServerCache( QgsServerCacheFilter *serverCache, int priority )
{
mPluginsServerCaches->insert( priority, serverCache );
}

View File

@ -0,0 +1,111 @@
/***************************************************************************
qgsservercachemanager.h
-----------------------
begin : 2018-07-05
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSSERVERCACHEMANAGER_H
#define QGSSERVERCACHEMANAGER_H
#include "qgsservercachefilter.h"
#include "qgsserverrequest.h"
#include <QMultiMap>
#include <QDomDocument>
#include "qgsproject.h"
#include "qgis_server.h"
#include "qgis_sip.h"
SIP_IF_MODULE( HAVE_SERVER_PYTHON_PLUGINS )
class QgsServerCachePlugin;
/**
* \ingroup server
* \class QgsServerCacheManager
* \brief A helper class that centralizes caches accesses given by all the server cache filter plugins.
* \since QGIS 3.4
*/
class SERVER_EXPORT QgsServerCacheManager
{
#ifdef SIP_RUN
#include "qgsservercachefilter.h"
#endif
public:
//! Constructor
QgsServerCacheManager()
{
mPluginsServerCaches = new QgsServerCacheFilterMap();
mResolved = false;
}
//! Constructor
QgsServerCacheManager( const QgsServerCacheManager &copy )
{
mPluginsServerCaches = new QgsServerCacheFilterMap( *copy.mPluginsServerCaches );
mResolved = copy.mResolved;
}
~QgsServerCacheManager()
{
delete mPluginsServerCaches;
}
/**
* Returns cached document (or 0 if document not in cache) like capabilities
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns the cached document or 0 if no corresponding document found
*/
const QDomDocument *getCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
/**
* Updates or inserts the document in cache like capabilities
* \param doc the document to cache
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns true if the document has been cached
*/
bool setCachedDocument( const QDomDocument *doc, const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
/**
* Deletes the cached document
* \param project the project used to generate the document to provide path
* \param request the request used to generate the document to provider parameters or data
* \param key the key provided by the access control to identify differents documents for the same request
* \returns true if the document has been deleted
*/
bool deleteCachedDocument( const QgsProject *project, const QgsServerRequest &request, const QString &key ) const;
/**
* Register a server cache filter
* \param serverCache the server cache to add
* \param priority the priority used to define the order
*/
void registerServerCache( QgsServerCacheFilter *serverCache, int priority = 0 );
private:
//! The ServerCache plugins registry
QgsServerCacheFilterMap *mPluginsServerCaches = nullptr;
bool mResolved;
};
#endif

View File

@ -30,9 +30,13 @@
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrolfilter.h"
#include "qgsaccesscontrol.h"
#include "qgsservercachefilter.h"
#include "qgsservercachemanager.h"
#else
class QgsAccessControl;
class QgsAccessControlFilter;
class QgsServerCacheManager;
class QgsServerCacheFilter;
#endif
#include "qgsserviceregistry.h"
#include "qgis_server.h"
@ -118,6 +122,16 @@ class SERVER_EXPORT QgsServerInterface
//! Gets the registered access control filters
virtual QgsAccessControl *accessControls() const = 0;
/**
* Register a server cache filter
* \param serverCache the server cache to register
* \param priority the priority used to order them
*/
virtual void registerServerCache( QgsServerCacheFilter *serverCache SIP_TRANSFER, int priority = 0 ) = 0;
//! Gets the registered server cache filters
virtual QgsServerCacheManager *cacheManager() const = 0;
//! Returns an enrironment variable, used to pass environment variables to Python
virtual QString getEnv( const QString &name ) const = 0;

View File

@ -29,8 +29,10 @@ QgsServerInterfaceImpl::QgsServerInterfaceImpl( QgsCapabilitiesCache *capCache,
mRequestHandler = nullptr;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
mAccessControls = new QgsAccessControl();
mCacheManager = new QgsServerCacheManager();
#else
mAccessControls = nullptr;
mCacheManager = nullptr;
#endif
}
@ -84,6 +86,17 @@ void QgsServerInterfaceImpl::registerAccessControl( QgsAccessControlFilter *acce
#endif
}
//! Register a new access control filter
void QgsServerInterfaceImpl::registerServerCache( QgsServerCacheFilter *serverCache, int priority )
{
#ifdef HAVE_SERVER_PYTHON_PLUGINS
mCacheManager->registerServerCache( serverCache, priority );
#else
Q_UNUSED( serverCache );
Q_UNUSED( priority );
#endif
}
void QgsServerInterfaceImpl::removeConfigCacheEntry( const QString &path )
{

View File

@ -50,8 +50,8 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QgsRequestHandler *requestHandler() override { return mRequestHandler; }
void registerFilter( QgsServerFilter *filter, int priority = 0 ) override;
QgsServerFiltersMap filters() override { return mFilters; }
//! Register an access control filter
//
void registerAccessControl( QgsAccessControlFilter *accessControl, int priority = 0 ) override;
/**
@ -59,6 +59,16 @@ class QgsServerInterfaceImpl : public QgsServerInterface
* \returns the access control helper
*/
QgsAccessControl *accessControls() const override { return mAccessControls; }
//! Register a server cache filter
void registerServerCache( QgsServerCacheFilter *serverCache, int priority = 0 ) override;
/**
* Gets the helper over all the registered server cache filters
* \returns the server cache helper
*/
QgsServerCacheManager *cacheManager() const override { return mCacheManager; }
QString getEnv( const QString &name ) const override;
QString configFilePath() override { return mConfigFilePath; }
void setConfigFilePath( const QString &configFilePath ) override;
@ -74,6 +84,7 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QString mConfigFilePath;
QgsServerFiltersMap mFilters;
QgsAccessControl *mAccessControls = nullptr;
QgsServerCacheManager *mCacheManager = nullptr;
QgsCapabilitiesCache *mCapabilitiesCache = nullptr;
QgsRequestHandler *mRequestHandler = nullptr;
QgsServiceRegistry *mServiceRegistry = nullptr;

View File

@ -37,10 +37,43 @@ namespace QgsWcs
void writeGetCapabilities( QgsServerInterface *serverIface, const QgsProject *project, const QString &version,
const QgsServerRequest &request, QgsServerResponse &response )
{
QDomDocument doc = createGetCapabilitiesDocument( serverIface, project, version, request );
QStringList cacheKeyList;
bool cache = true;
QgsAccessControl *accessControl = serverIface->accessControls();
if ( accessControl )
cache = accessControl->fillCacheKey( cacheKeyList );
QDomDocument doc;
QString cacheKey = cacheKeyList.join( QStringLiteral( "-" ) );
const QDomDocument *capabilitiesDocument;
QgsServerCacheManager *cacheManager = serverIface->cacheManager();
if ( cacheManager && cache )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
{
doc = createGetCapabilitiesDocument( serverIface, project, version, request );
if ( cache && cacheManager )
{
if ( cacheManager->setCachedDocument( &doc, project, request, cacheKey ) )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
}
if ( !capabilitiesDocument )
{
doc = doc.cloneNode().toDocument();
capabilitiesDocument = &doc;
}
}
response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
response.write( doc.toByteArray() );
response.write( capabilitiesDocument->toByteArray() );
}

View File

@ -41,10 +41,43 @@ namespace QgsWfs
void writeGetCapabilities( QgsServerInterface *serverIface, const QgsProject *project, const QString &version,
const QgsServerRequest &request, QgsServerResponse &response )
{
QDomDocument doc = createGetCapabilitiesDocument( serverIface, project, version, request );
QStringList cacheKeyList;
bool cache = true;
QgsAccessControl *accessControl = serverIface->accessControls();
if ( accessControl )
cache = accessControl->fillCacheKey( cacheKeyList );
QDomDocument doc;
QString cacheKey = cacheKeyList.join( QStringLiteral( "-" ) );
const QDomDocument *capabilitiesDocument;
QgsServerCacheManager *cacheManager = serverIface->cacheManager();
if ( cacheManager && cache )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
{
doc = createGetCapabilitiesDocument( serverIface, project, version, request );
if ( cache && cacheManager )
{
if ( cacheManager->setCachedDocument( &doc, project, request, cacheKey ) )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
}
if ( !capabilitiesDocument )
{
doc = doc.cloneNode().toDocument();
capabilitiesDocument = &doc;
}
}
response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
response.write( doc.toByteArray() );
response.write( capabilitiesDocument->toByteArray() );
}

View File

@ -43,10 +43,43 @@ namespace QgsWfs
void writeGetCapabilities( QgsServerInterface *serverIface, const QgsProject *project, const QString &version,
const QgsServerRequest &request, QgsServerResponse &response )
{
QDomDocument doc = createGetCapabilitiesDocument( serverIface, project, version, request );
QStringList cacheKeyList;
bool cache = true;
QgsAccessControl *accessControl = serverIface->accessControls();
if ( accessControl )
cache = accessControl->fillCacheKey( cacheKeyList );
QDomDocument doc;
QString cacheKey = cacheKeyList.join( QStringLiteral( "-" ) );
const QDomDocument *capabilitiesDocument;
QgsServerCacheManager *cacheManager = serverIface->cacheManager();
if ( cacheManager && cache )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
{
doc = createGetCapabilitiesDocument( serverIface, project, version, request );
if ( cache && cacheManager )
{
if ( cacheManager->setCachedDocument( &doc, project, request, cacheKey ) )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKey );
}
}
if ( !capabilitiesDocument )
{
doc = doc.cloneNode().toDocument();
capabilitiesDocument = &doc;
}
}
response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
response.write( doc.toByteArray() );
response.write( capabilitiesDocument->toByteArray() );
}

View File

@ -100,15 +100,26 @@ namespace QgsWms
cacheKeyList << request.url().host();
bool cache = true;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsAccessControl *accessControl = serverIface->accessControls();
if ( accessControl )
cache = accessControl->fillCacheKey( cacheKeyList );
#endif
QDomDocument doc;
QString cacheKey = cacheKeyList.join( QStringLiteral( "-" ) );
const QDomDocument *capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
const QDomDocument *capabilitiesDocument;
QgsServerCacheManager *cacheManager = serverIface->cacheManager();
if ( cacheManager && cache )
{
if ( cacheKeyList.count() == 2 )
capabilitiesDocument = cacheManager->getCachedDocument( project, request, QStringLiteral( "" ) );
else if ( cacheKeyList.count() > 2 )
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKeyList.at( 3 ) );
}
if ( !capabilitiesDocument ) //capabilities xml not in cache plugins
capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
if ( !capabilitiesDocument ) //capabilities xml not in cache. Create a new one
{
QgsMessageLog::logMessage( QStringLiteral( "Capabilities document not found in cache" ) );
@ -117,10 +128,26 @@ namespace QgsWms
if ( cache )
{
capabilitiesCache->insertCapabilitiesDocument( configFilePath, cacheKey, &doc );
capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
if ( cacheManager )
{
if ( cacheKeyList.count() == 2 &&
cacheManager->setCachedDocument( &doc, project, request, QStringLiteral( "" ) ) )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, QStringLiteral( "" ) );
}
else if ( cacheKeyList.count() > 2 &&
cacheManager->setCachedDocument( &doc, project, request, cacheKeyList.at( 3 ) ) )
{
capabilitiesDocument = cacheManager->getCachedDocument( project, request, cacheKeyList.at( 3 ) );
}
}
else
{
capabilitiesCache->insertCapabilitiesDocument( configFilePath, cacheKey, &doc );
capabilitiesDocument = capabilitiesCache->searchCapabilitiesDocument( configFilePath, cacheKey );
}
}
else
if ( !capabilitiesDocument )
{
doc = doc.cloneNode().toDocument();
capabilitiesDocument = &doc;

View File

@ -268,6 +268,7 @@ IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServerAccessControlWFS test_qgsserver_accesscontrol_wfs.py)
ADD_PYTHON_TEST(PyQgsServerAccessControlWCS test_qgsserver_accesscontrol_wcs.py)
ADD_PYTHON_TEST(PyQgsServerAccessControlWFSTransactional test_qgsserver_accesscontrol_wfs_transactional.py)
ADD_PYTHON_TEST(PyQgsServerCacheManager test_qgsserver_cachemanager.py)
ADD_PYTHON_TEST(PyQgsServerWFS test_qgsserver_wfs.py)
ADD_PYTHON_TEST(PyQgsServerWFST test_qgsserver_wfst.py)
ADD_PYTHON_TEST(PyQgsOfflineEditingWFS test_offline_editing_wfs.py)

View File

@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsServer.
.. 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__ = 'René-Luc DHONT'
__date__ = '19/07/2018'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
print('CTEST_FULL_OUTPUT')
import qgis # NOQA
import os
import urllib.request
import urllib.parse
import urllib.error
import tempfile
import hashlib
from qgis.testing import unittest
from utilities import unitTestDataPath
from qgis.server import QgsServer, QgsServerCacheFilter, QgsServerRequest, QgsBufferServerRequest, QgsBufferServerResponse
from qgis.core import QgsApplication, QgsFontUtils
from qgis.PyQt.QtCore import QFile, QByteArray
from qgis.PyQt.QtXml import QDomDocument
class PyServerCache(QgsServerCacheFilter):
""" Used to have restriction access """
# Be able to deactivate the access control to have a reference point
_active = False
def __init__(self, server_iface):
super(QgsServerCacheFilter, self).__init__(server_iface)
self._cache_dir = os.path.join(tempfile.gettempdir(), "qgs_server_cache")
if not os.path.exists(self._cache_dir):
os.mkdir(self._cache_dir)
def getCachedDocument(self, project, request, key):
m = hashlib.md5()
paramMap = request.parameters()
urlParam = "&".join(["%s=%s" % (k, paramMap[k]) for k in paramMap.keys()])
m.update(urlParam.encode('utf8'))
if not os.path.exists(os.path.join(self._cache_dir, m.hexdigest() + ".xml")):
return QByteArray()
doc = QDomDocument(m.hexdigest() + ".xml")
with open(os.path.join(self._cache_dir, m.hexdigest() + ".xml"), "r") as f:
statusOK, errorStr, errorLine, errorColumn = doc.setContent(f.read(), True)
if not statusOK:
print("Could not read or find the contents document. Error at line %d, column %d:\n%s" % (errorLine, errorColumn, errorStr))
return QByteArray()
return doc.toByteArray()
def setCachedDocument(self, doc, project, request, key):
m = hashlib.md5()
paramMap = request.parameters()
urlParam = "&".join(["%s=%s" % (k, paramMap[k]) for k in paramMap.keys()])
m.update(urlParam.encode('utf8'))
with open(os.path.join(self._cache_dir, m.hexdigest() + ".xml"), "w") as f:
f.write(doc.toString())
return os.path.exists(os.path.join(self._cache_dir, m.hexdigest() + ".xml"))
class TestQgsServerCacheManager(unittest.TestCase):
@classmethod
def _handle_request(cls, qs, requestMethod=QgsServerRequest.GetMethod, data=None):
if data is not None:
data = data.encode('utf-8')
request = QgsBufferServerRequest(qs, requestMethod, {}, data)
response = QgsBufferServerResponse()
cls._server.handleRequest(request, response)
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())
@classmethod
def setUpClass(cls):
"""Run before all tests"""
cls._app = QgsApplication([], False)
cls._server = QgsServer()
cls._handle_request("")
cls._server_iface = cls._server.serverInterface()
cls._servercache = PyServerCache(cls._server_iface)
cls._server_iface.registerServerCache(cls._servercache, 100)
@classmethod
def tearDownClass(cls):
"""Run after all tests"""
filelist = [f for f in os.listdir(cls._servercache._cache_dir) if f.endswith(".xml")]
for f in filelist:
os.remove(os.path.join(cls._servercache._cache_dir, f))
del cls._server
cls._app.exitQgis
def _result(self, data):
headers = {}
for line in data[0].decode('UTF-8').split("\n"):
if line != "":
header = line.split(":")
self.assertEqual(len(header), 2, line)
headers[str(header[0])] = str(header[1]).strip()
return data[1], headers
def _execute_request(self, qs, requestMethod=QgsServerRequest.GetMethod, data=None):
request = QgsBufferServerRequest(qs, requestMethod, {}, data)
response = QgsBufferServerResponse()
self._server.handleRequest(request, response)
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())
def setUp(self):
"""Create the server instance"""
self.fontFamily = QgsFontUtils.standardTestFontFamily()
QgsFontUtils.loadStandardTestFonts(['All'])
d = unitTestDataPath('qgis_server_accesscontrol') + '/'
self._project_path = os.path.join(d, "project.qgs")
def test_getcapabilities(self):
project = self._project_path
assert os.path.exists(project), "Project file not found: " + project
query_string = '?MAP=%s&SERVICE=WMS&VERSION=1.3.0&REQUEST=%s' % (urllib.parse.quote(project), 'GetCapabilities')
header, body = self._execute_request(query_string)
query_string = '?MAP=%s&SERVICE=WFS&VERSION=1.1.0&REQUEST=%s' % (urllib.parse.quote(project), 'GetCapabilities')
header, body = self._execute_request(query_string)
query_string = '?MAP=%s&SERVICE=WCS&VERSION=1.0.0&REQUEST=%s' % (urllib.parse.quote(project), 'GetCapabilities')
header, body = self._execute_request(query_string)
if __name__ == "__main__":
unittest.main()