From 8b0526d678b5381936e3e894fdd87620213a3b6e Mon Sep 17 00:00:00 2001 From: David Marteau Date: Wed, 11 Jan 2017 22:59:27 +0100 Subject: [PATCH] Revisit server exceptions - Add server exception base class - Enable per service exception definition - Handle QgsException gracefully (error 500) - Handle OGC exception versioning --- python/server/qgsrequesthandler.sip | 45 +---- python/server/qgsserverexception.sip | 56 ++++++ python/server/qgsserverresponse.sip | 5 + python/server/server.sip | 1 + src/server/CMakeLists.txt | 2 +- src/server/qgsfcgiserverresponse.cpp | 10 +- src/server/qgsfcgiserverresponse.h | 8 +- src/server/qgsmapserviceexception.h | 17 +- src/server/qgsrequesthandler.cpp | 30 +-- src/server/qgsrequesthandler.h | 6 +- src/server/qgsserver.cpp | 179 ++++++++++-------- src/server/qgsserverexception.cpp | 77 ++++++++ src/server/qgsserverexception.h | 84 ++++++++ src/server/qgsserverresponse.cpp | 20 +- src/server/qgsserverresponse.h | 7 + src/server/services/wms/qgswms.cpp | 10 +- .../services/wms/qgswmsdescribelayer.cpp | 24 +-- .../services/wms/qgswmsgetcapabilities.cpp | 22 +-- src/server/services/wms/qgswmsgetcontext.cpp | 27 ++- .../services/wms/qgswmsgetfeatureinfo.cpp | 18 +- .../services/wms/qgswmsgetlegendgraphics.cpp | 29 ++- src/server/services/wms/qgswmsgetmap.cpp | 22 +-- src/server/services/wms/qgswmsgetprint.cpp | 77 ++++---- .../services/wms/qgswmsgetschemaextension.cpp | 24 +-- src/server/services/wms/qgswmsgetstyle.cpp | 26 +-- src/server/services/wms/qgswmsgetstyles.cpp | 14 +- .../services/wms/qgswmsservertransitional.cpp | 66 +++---- .../wms/qgswmsserviceexception.h} | 39 +++- src/server/services/wms/qgswmsutils.cpp | 29 +-- src/server/services/wms/qgswmsutils.h | 6 +- 30 files changed, 561 insertions(+), 419 deletions(-) create mode 100644 python/server/qgsserverexception.sip create mode 100644 src/server/qgsserverexception.cpp create mode 100644 src/server/qgsserverexception.h rename src/server/{qgsmapserviceexception.cpp => services/wms/qgswmsserviceexception.h} (51%) diff --git a/python/server/qgsrequesthandler.sip b/python/server/qgsrequesthandler.sip index 3470c43bc40..238408ad8f3 100644 --- a/python/server/qgsrequesthandler.sip +++ b/python/server/qgsrequesthandler.sip @@ -20,45 +20,14 @@ class QgsRequestHandler /Abstract/ { %TypeHeaderCode -#include "qgsmapserviceexception.h" +#include "qgsserverexception.h" #include "qgsrequesthandler.h" %End public: - /** Parses the input and creates a request neutral Parameter/Value map - * @note not available in Python bindings - */ - // virtual void parseInput() = 0; - - /** Sends the map image back to the client - * @note not available in Python bindings - */ - // virtual void setGetMapResponse( const QString& service, QImage* img, int imageQuality ) = 0; - - //! @note not available in Python bindings - // virtual void setGetCapabilitiesResponse( const QDomDocument& doc ) = 0; - - //! @note not available in Python bindings - // virtual void setGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) = 0; - - /** Allow plugins to return a QgsMapServiceException*/ - void setServiceException( const QgsMapServiceException& ex /Transfer/ ); - - //! @note not available in Python bindings - // virtual void setXmlResponse( const QDomDocument& doc ) = 0; - - //! @note not available in Python bindings - // virtual void setXmlResponse( const QDomDocument& doc, const QString& mimeType ) = 0; - - //! @note not available in Python bindings - // virtual void setGetPrintResponse( QByteArray* b ) = 0; - - //! @note not available in Python bindings - // virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) = 0; - - //! @note not available in Python bindings - // virtual void setGetFeatureResponse( QByteArray* ba ) = 0; + /** Allow plugins to return a QgsServerException*/ + void setServiceException( const QgsServerException& ex ); //! @note not available in Python bindings void endGetFeatureResponse( QByteArray* ba ); @@ -116,12 +85,4 @@ class QgsRequestHandler /Abstract/ /** Return true if the HTTP headers were already sent to the client*/ bool headersSent() const; - - - //! @note not available in Python bindings - // virtual QPair getResponse() = 0; - -private: - /** Parses the input and creates a request neutral Parameter/Value map*/ - void parseInput(); }; diff --git a/python/server/qgsserverexception.sip b/python/server/qgsserverexception.sip new file mode 100644 index 00000000000..4baa3ea4190 --- /dev/null +++ b/python/server/qgsserverexception.sip @@ -0,0 +1,56 @@ +/*************************************************************************** + qgsserverexception.sip + ------------------------ + begin : January 11 2017 + copyright : (C) 2017 by David Marteau + email : david dot marteau 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. * + * * + ***************************************************************************/ + + +/** \ingroup server + * \class QgsServerException + * \brief server Exception base class. + */ + +class QgsServerException +{ +%TypeHeaderCode +#include +%End + public: + QgsServerException( const QString& message, int responseCode = 500 ); + + int responseCode() const; + + virtual QByteArray formatResponse( QString& responseFormat ) const; +}; + + +class QgsOgcServiceException +{ +%TypeHeaderCode +#include +%End + public: + QgsOgcServiceException( const QString& code, const QString& message, const QString& locator = QString(), + int responseCode = 200, const QString& version = "1.3.0" ); + + QString message() const; + QString code() const; + QString locator() const; + + virtual QByteArray formatResponse( QString& responseFormat / Out / ) const; +}; + + + + diff --git a/python/server/qgsserverresponse.sip b/python/server/qgsserverresponse.sip index 9b4467954bc..6581b740871 100644 --- a/python/server/qgsserverresponse.sip +++ b/python/server/qgsserverresponse.sip @@ -92,6 +92,11 @@ class QgsServerResponse */ virtual qint64 write( const QByteArray& byteArray ); + /** + * Write server exception + */ + virtual void write( const QgsServerException& ex ); + /** * Return the underlying QIODevice */ diff --git a/python/server/server.sip b/python/server/server.sip index e75d5dcba03..5fd5429f3eb 100644 --- a/python/server/server.sip +++ b/python/server/server.sip @@ -32,6 +32,7 @@ %Include qgsserverrequest.sip %Include qgsserverresponse.sip +%Include qgsserverexception.sip %Include qgsservice.sip %Include qgsservicemodule.sip %Include qgsserviceregistry.sip diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 2a25f635451..b7f74caefdc 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -28,7 +28,7 @@ SET ( qgis_mapserv_SRCS qgswfsserver.cpp qgswcsserver.cpp qgsserversettings.cpp - qgsmapserviceexception.cpp + qgsserverexception.cpp qgsmslayercache.cpp qgsmslayerbuilder.cpp qgshostedvdsbuilder.cpp diff --git a/src/server/qgsfcgiserverresponse.cpp b/src/server/qgsfcgiserverresponse.cpp index 90d0c1ee2d7..7c977d283c6 100644 --- a/src/server/qgsfcgiserverresponse.cpp +++ b/src/server/qgsfcgiserverresponse.cpp @@ -30,16 +30,14 @@ // QgsFcgiServerResponse::QgsFcgiServerResponse( QgsServerRequest::Method method ) + : mMethod( method ) { mBuffer.open( QIODevice::ReadWrite ); - mHeadersSent = false; - mFinished = false; - mMethod = method; + setDefaultHeaders(); } QgsFcgiServerResponse::~QgsFcgiServerResponse() { - } void QgsFcgiServerResponse::clearHeader( const QString& key ) @@ -82,7 +80,6 @@ void QgsFcgiServerResponse::sendError( int code, const QString& message ) } clear(); - setDefaultHeaders(); setReturnCode( code ); setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html;charset=utf-8" ) ); write( QStringLiteral( "%1" ).arg( message ) ); @@ -157,6 +154,9 @@ void QgsFcgiServerResponse::clear() mHeaders.clear(); mBuffer.seek( 0 ); mBuffer.buffer().clear(); + + // Restore default headers + setDefaultHeaders(); } void QgsFcgiServerResponse::setDefaultHeaders() diff --git a/src/server/qgsfcgiserverresponse.h b/src/server/qgsfcgiserverresponse.h index 591f6143abd..c09ccc8c0e5 100644 --- a/src/server/qgsfcgiserverresponse.h +++ b/src/server/qgsfcgiserverresponse.h @@ -61,13 +61,13 @@ class QgsFcgiServerResponse: public QgsServerResponse /** * Set the default headers */ - virtual void setDefaultHeaders(); + void setDefaultHeaders(); private: QMap mHeaders; - QBuffer mBuffer; - bool mFinished; - bool mHeadersSent; + QBuffer mBuffer; + bool mFinished = false; + bool mHeadersSent = false; QgsServerRequest::Method mMethod; }; diff --git a/src/server/qgsmapserviceexception.h b/src/server/qgsmapserviceexception.h index 686b3b321f7..219b275215a 100644 --- a/src/server/qgsmapserviceexception.h +++ b/src/server/qgsmapserviceexception.h @@ -20,12 +20,14 @@ #include -#include "qgsexception.h" +#include "qgsserverexception.h" #include "qgis_server.h" /** \ingroup server * \class QgsMapServiceException - * \brief Exception class for WMS service exceptions. + * \brief Exception class for WMS service exceptions (for compatibility only). + * + * \deprecated Use QsgServerException * * The most important codes are: * * "InvalidFormat" @@ -34,15 +36,12 @@ * * "OperationNotSupported" */ -class SERVER_EXPORT QgsMapServiceException : public QgsException +class SERVER_EXPORT QgsMapServiceException : public QgsOgcServiceException { public: - QgsMapServiceException( const QString& code, const QString& message ); - QString code() const {return mCode;} - QString message() const {return mMessage;} - private: - QString mCode; - QString mMessage; + QgsMapServiceException( const QString& code, const QString& message ) + : QgsOgcServiceException( code, message ) + {} }; #endif diff --git a/src/server/qgsrequesthandler.cpp b/src/server/qgsrequesthandler.cpp index c0d7ef9e399..d418350dcc0 100644 --- a/src/server/qgsrequesthandler.cpp +++ b/src/server/qgsrequesthandler.cpp @@ -24,7 +24,7 @@ #include "qgshttptransaction.h" #endif #include "qgsmessagelog.h" -#include "qgsmapserviceexception.h" +#include "qgsserverexception.h" #include "qgsserverrequest.h" #include "qgsserverresponse.h" #include @@ -38,7 +38,7 @@ #include QgsRequestHandler::QgsRequestHandler( QgsServerRequest& request, QgsServerResponse& response ) - : mException( nullptr ) + : mExceptionRaised( false ) , mRequest( request ) , mResponse( response ) { @@ -46,7 +46,6 @@ QgsRequestHandler::QgsRequestHandler( QgsServerRequest& request, QgsServerRespon QgsRequestHandler::~QgsRequestHandler() { - delete mException; } QMap QgsRequestHandler::parameterMap() const @@ -70,7 +69,7 @@ void QgsRequestHandler::setHttpResponse( const QByteArray& ba, const QString &fo bool QgsRequestHandler::exceptionRaised() const { - return mException; + return mExceptionRaised; } void QgsRequestHandler::setHeader( const QString &name, const QString &value ) @@ -169,28 +168,11 @@ void QgsRequestHandler::setXmlResponse( const QDomDocument& doc, const QString& setHttpResponse( ba, mimeType ); } -void QgsRequestHandler::setServiceException( const QgsMapServiceException& ex ) +void QgsRequestHandler::setServiceException( const QgsServerException& ex ) { // Safety measure to avoid potential leaks if called repeatedly - delete mException; - mException = new QgsMapServiceException( ex ); - //create Exception DOM document - QDomDocument exceptionDoc; - QDomElement serviceExceptionReportElem = exceptionDoc.createElement( QStringLiteral( "ServiceExceptionReport" ) ); - serviceExceptionReportElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.3.0" ) ); - serviceExceptionReportElem.setAttribute( QStringLiteral( "xmlns" ), QStringLiteral( "http://www.opengis.net/ogc" ) ); - exceptionDoc.appendChild( serviceExceptionReportElem ); - QDomElement serviceExceptionElem = exceptionDoc.createElement( QStringLiteral( "ServiceException" ) ); - serviceExceptionElem.setAttribute( QStringLiteral( "code" ), ex.code() ); - QDomText messageText = exceptionDoc.createTextNode( ex.message() ); - serviceExceptionElem.appendChild( messageText ); - serviceExceptionReportElem.appendChild( serviceExceptionElem ); - - QByteArray ba = exceptionDoc.toByteArray(); - // Clear response headers and body and set new exception - // TODO: check for headersSent() - clear(); - setHttpResponse( ba, QStringLiteral( "text/xml" ) ); + mExceptionRaised = true; + mResponse.write( ex ); } bool QgsRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) diff --git a/src/server/qgsrequesthandler.h b/src/server/qgsrequesthandler.h index d55c2d33133..ea418979fc6 100644 --- a/src/server/qgsrequesthandler.h +++ b/src/server/qgsrequesthandler.h @@ -31,7 +31,7 @@ class QDomDocument; class QImage; -class QgsMapServiceException; +class QgsServerException; class QgsServerRequest; class QgsServerResponse; @@ -58,7 +58,7 @@ class SERVER_EXPORT QgsRequestHandler void setGetCapabilitiesResponse( const QDomDocument& doc ); //! Allow plugins to return a QgsMapServiceException - void setServiceException( const QgsMapServiceException &ex ); + void setServiceException( const QgsServerException &ex ); //! @note not available in Python bindings void setXmlResponse( const QDomDocument& doc ); @@ -154,7 +154,7 @@ class SERVER_EXPORT QgsRequestHandler QString mFormatString; //format string as it is passed in the request (with base) QString mService; QString mInfoFormat; - QgsMapServiceException* mException; // Stores the exception + bool mExceptionRaised; QgsServerRequest& mRequest; QgsServerResponse& mResponse; diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index a32ac62f5c3..d3496449fa3 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -396,100 +396,114 @@ void QgsServer::handleRequest( QgsServerRequest& request, QgsServerResponse& res // Call requestReady() method (if enabled) theResponse.start(); - QMap parameterMap = request.parameters(); - printRequestParameters( parameterMap, logLevel ); - - QgsAccessControl* accessControl = sServerInterface->accessControls(); - - //Config file path - QString configFilePath = configPath( *sConfigFilePath, parameterMap ); - - sServerInterface->setConfigFilePath( configFilePath ); - - //Service parameter - QString serviceString = parameterMap.value( QStringLiteral( "SERVICE" ) ); - - if ( serviceString.isEmpty() ) - { - // SERVICE not mandatory for WMS 1.3.0 GetMap & GetFeatureInfo - QString requestString = parameterMap.value( QStringLiteral( "REQUEST" ) ); - if ( requestString == QLatin1String( "GetMap" ) || requestString == QLatin1String( "GetFeatureInfo" ) ) - { - serviceString = QStringLiteral( "WMS" ); - } - } - - QString versionString = parameterMap.value( QStringLiteral( "VERSION" ) ); - - //possibility for client to suggest a download filename - QString outputFileName = parameterMap.value( QStringLiteral( "FILE_NAME" ) ); - if ( !outputFileName.isEmpty() ) - { - theRequestHandler.setHeader( QStringLiteral( "Content-Disposition" ), "attachment; filename=\"" + outputFileName + "\"" ); - } - - // Enter core services main switch + // Plugins may have set exceptions if ( !theRequestHandler.exceptionRaised() ) { - // Lookup for service + try + { + QMap parameterMap = request.parameters(); + printRequestParameters( parameterMap, logLevel ); - QgsService* service = sServiceRegistry.getService( serviceString, versionString ); - if ( service ) - { - service->executeRequest( request, theResponse ); - } - else if ( serviceString == QLatin1String( "WCS" ) ) - { - QgsWCSProjectParser* p = QgsConfigCache::instance()->wcsConfiguration( - configFilePath - , accessControl - ); - if ( !p ) + QgsAccessControl* accessControl = sServerInterface->accessControls(); + + //Config file path + QString configFilePath = configPath( *sConfigFilePath, parameterMap ); + + sServerInterface->setConfigFilePath( configFilePath ); + + //Service parameter + QString serviceString = parameterMap.value( QStringLiteral( "SERVICE" ) ); + + if ( serviceString.isEmpty() ) { - theRequestHandler.setServiceException( QgsMapServiceException( QStringLiteral( "Project file error" ), QStringLiteral( "Error reading the project file" ) ) ); + // SERVICE not mandatory for WMS 1.3.0 GetMap & GetFeatureInfo + QString requestString = parameterMap.value( QStringLiteral( "REQUEST" ) ); + if ( requestString == QLatin1String( "GetMap" ) || requestString == QLatin1String( "GetFeatureInfo" ) ) + { + serviceString = QStringLiteral( "WMS" ); + } + } + + QString versionString = parameterMap.value( QStringLiteral( "VERSION" ) ); + + //possibility for client to suggest a download filename + QString outputFileName = parameterMap.value( QStringLiteral( "FILE_NAME" ) ); + if ( !outputFileName.isEmpty() ) + { + theRequestHandler.setHeader( QStringLiteral( "Content-Disposition" ), "attachment; filename=\"" + outputFileName + "\"" ); + } + + // Lookup for service + QgsService* service = sServiceRegistry.getService( serviceString, versionString ); + if ( service ) + { + service->executeRequest( request, theResponse ); + } + else if ( serviceString == QLatin1String( "WCS" ) ) + { + + QgsWCSProjectParser* p = QgsConfigCache::instance()->wcsConfiguration( + configFilePath + , accessControl + ); + if ( !p ) + { + theRequestHandler.setServiceException( QgsMapServiceException( QStringLiteral( "Project file error" ), + QStringLiteral( "Error reading the project file" ) ) ); + } + else + { + QgsWCSServer wcsServer( + configFilePath + , sSettings + , parameterMap + , p + , &theRequestHandler + , accessControl + ); + wcsServer.executeRequest(); + } + } + else if ( serviceString == QLatin1String( "WFS" ) ) + { + QgsWfsProjectParser* p = QgsConfigCache::instance()->wfsConfiguration( + configFilePath + , accessControl + ); + if ( !p ) + { + theRequestHandler.setServiceException( QgsMapServiceException( QStringLiteral( "Project file error" ), + QStringLiteral( "Error reading the project file" ) ) ); + } + else + { + QgsWfsServer wfsServer( + configFilePath + , sSettings + , parameterMap + , p + , &theRequestHandler + , accessControl + ); + wfsServer.executeRequest(); + } } else { - QgsWCSServer wcsServer( - configFilePath - , sSettings - , parameterMap - , p - , &theRequestHandler - , accessControl - ); - wcsServer.executeRequest(); + throw QgsOgcServiceException( QStringLiteral( "Service configuration error" ), + QStringLiteral( "Service unknown or unsupported" ) ) ; } } - else if ( serviceString == QLatin1String( "WFS" ) ) + catch ( QgsServerException& ex ) { - QgsWfsProjectParser* p = QgsConfigCache::instance()->wfsConfiguration( - configFilePath - , accessControl - ); - if ( !p ) - { - theRequestHandler.setServiceException( QgsMapServiceException( QStringLiteral( "Project file error" ), QStringLiteral( "Error reading the project file" ) ) ); - } - else - { - QgsWfsServer wfsServer( - configFilePath - , sSettings - , parameterMap - , p - , &theRequestHandler - , accessControl - ); - wfsServer.executeRequest(); - } + theResponse.write( ex ); } - else + catch ( QgsException& ex ) { - theRequestHandler.setServiceException( QgsMapServiceException( QStringLiteral( "Service configuration error" ), QStringLiteral( "Service unknown or unsupported" ) ) ); - } // end switch - } // end if not exception raised - + // Internal server error + theResponse.sendError( 500, ex.what() ); + } + } // Terminate the response theResponse.finish(); @@ -497,7 +511,6 @@ void QgsServer::handleRequest( QgsServerRequest& request, QgsServerResponse& res // to a deleted request handler from Python bindings sServerInterface->clearRequestHandler(); - if ( logLevel == QgsMessageLog::INFO ) { QgsMessageLog::logMessage( "Request finished in " + QString::number( time.elapsed() ) + " ms", QStringLiteral( "Server" ), QgsMessageLog::INFO ); diff --git a/src/server/qgsserverexception.cpp b/src/server/qgsserverexception.cpp new file mode 100644 index 00000000000..c903effb501 --- /dev/null +++ b/src/server/qgsserverexception.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + qgsservervexception.h + ------------------- + begin : January 11, 2017 + copyright : (C) 2017 David Marteau + email : david dot marteau 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 "qgsserverexception.h" + +#include + +// QgsServerException +QgsServerException::QgsServerException( const QString& message, int responseCode ) + : QgsException( message ) + , mResponseCode( responseCode ) +{ + +} + +QByteArray QgsServerException::formatResponse( QString& responseFormat ) const +{ + QDomDocument doc; + QDomElement root = doc.createElement( QStringLiteral( "ServerException" ) ); + doc.appendChild( root ); + root.appendChild( doc.createTextNode( what() ) ); + + responseFormat = QStringLiteral( "text/xml; charset=utf-8" ); + return doc.toByteArray(); +} + + +// QgsOgcServiceException +QgsOgcServiceException:: QgsOgcServiceException( const QString& code, const QString& message, const QString& locator, + int responseCode, const QString& version ) + : QgsServerException( message, responseCode ) + , mCode( code ) + , mMessage( message ) + , mLocator( locator ) + , mVersion( version ) +{ + +} + +QByteArray QgsOgcServiceException::formatResponse( QString& responseFormat ) const +{ + QDomDocument doc; + QDomElement root = doc.createElement( QStringLiteral( "ServiceExceptionReport" ) ); + root.setAttribute( QStringLiteral( "version" ), mVersion ); + root.setAttribute( QStringLiteral( "xmlns" ) , QStringLiteral( "http://www.opengis.net/ogc" ) ); + doc.appendChild( root ); + + QDomElement elem = doc.createElement( QStringLiteral( "ServiceException" ) ); + elem.setAttribute( QStringLiteral( "code" ), mCode ); + elem.appendChild( doc.createTextNode( mMessage ) ); + root.appendChild( elem ); + + if ( ! mLocator.isEmpty() ) + { + elem.setAttribute( QStringLiteral( "locator" ), mLocator ); + } + + responseFormat = QStringLiteral( "text/xml; charset=utf-8" ); + return doc.toByteArray(); +} + + + diff --git a/src/server/qgsserverexception.h b/src/server/qgsserverexception.h new file mode 100644 index 00000000000..2895d9233d2 --- /dev/null +++ b/src/server/qgsserverexception.h @@ -0,0 +1,84 @@ +/*************************************************************************** + qgserverexception.h + ------------------------ + begin : January 11, 2017 + copyright : (C) 2017 by David Marteau + email : david dot marteau 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 QGSSERVEREXCEPTION_H +#define QGSSERVEREXCEPTION_H + +#include +#include + +#include "qgsexception.h" +#include "qgis_server.h" + +/** \ingroup server + * \class QgsServerException + * \brief Exception base class for server exceptions. + */ +class SERVER_EXPORT QgsServerException : public QgsException +{ + public: + QgsServerException( const QString& message, int responseCode = 500 ); + + /** + * @return the return HTTP response code associated with this exception + */ + int responseCode() const { return mResponseCode; } + + /** Format the exception for sending to client + * + * @param responseFormat QString to store the content type of the response format. + * @return QByteArray the fermatted response. + * + * The defaolt implementation return text/xml format. + */ + virtual QByteArray formatResponse( QString& responseFormat ) const; + + private: + int mResponseCode; +}; + +/** \ingroup server + * \class QgsOcgServiceException + * \brief Exception base class for service exceptions. + * + * Note that this exeception is assaciated with a default return code 200 which may be + * not appropriate in some situations. + */ +class SERVER_EXPORT QgsOgcServiceException : public QgsServerException +{ + public: + QgsOgcServiceException( const QString& code, const QString& message, const QString& locator = QString(), + int responseCode = 200, const QString& version = QStringLiteral( "1.3.0" ) ); + + QString message() const { return mMessage; } + QString code() const { return mCode; } + QString locator() const { return mLocator; } + QString version() const { return mVersion; } + + virtual QByteArray formatResponse( QString& responseFormat ) const override; + + private: + QString mCode; + QString mMessage; + QString mLocator; + QString mVersion; + + +}; + +#endif + diff --git a/src/server/qgsserverresponse.cpp b/src/server/qgsserverresponse.cpp index 738aa5f8a92..4e0c3398a0f 100644 --- a/src/server/qgsserverresponse.cpp +++ b/src/server/qgsserverresponse.cpp @@ -19,7 +19,7 @@ #include "qgsserverresponse.h" #include "qgsmessagelog.h" -//#include +#include "qgsserverexception.h" //! constructor QgsServerResponse::QgsServerResponse() @@ -75,3 +75,21 @@ qint64 QgsServerResponse::write( const char* data ) return 0; } +void QgsServerResponse::write( const QgsServerException& ex ) +{ + QString responseFormat; + QByteArray ba = ex.formatResponse( responseFormat ); + + if ( headersSent() ) + { + QgsMessageLog::logMessage( QStringLiteral( "Error: Cannot write exception after header sent !" ) ); + return; + } + + clear(); + setReturnCode( ex.responseCode() ); + setHeader( "Content-Type", responseFormat ); + write( ba ); +} + + diff --git a/src/server/qgsserverresponse.h b/src/server/qgsserverresponse.h index ea4ca8140fd..dd4261d3333 100644 --- a/src/server/qgsserverresponse.h +++ b/src/server/qgsserverresponse.h @@ -24,6 +24,8 @@ #include #include +class QgsServerException; + /** * \ingroup server * QgsServerResponse @@ -126,6 +128,11 @@ class SERVER_EXPORT QgsServerResponse */ virtual qint64 write( const char* data ); + /** + * Write server exception + */ + virtual void write( const QgsServerException& ex ); + /** * Return the underlying QIODevice */ diff --git a/src/server/services/wms/qgswms.cpp b/src/server/services/wms/qgswms.cpp index e2a06b92766..b94c128d683 100644 --- a/src/server/services/wms/qgswms.cpp +++ b/src/server/services/wms/qgswms.cpp @@ -79,9 +79,8 @@ namespace QgsWms QString req = params.value( QStringLiteral( "REQUEST" ) ); if ( req.isEmpty() ) { - writeError( response, QStringLiteral( "OperationNotSupported" ), - QStringLiteral( "Please check the value of the REQUEST parameter" ) ); - return; + throw QgsServiceException( QStringLiteral( "OperationNotSupported" ), + QStringLiteral( "Please check the value of the REQUEST parameter" ) ); } if (( QSTR_COMPARE( mVersion, "1.1.1" ) && QSTR_COMPARE( req, "capabilities" ) ) @@ -142,9 +141,8 @@ namespace QgsWms else { // Operation not supported - writeError( response, QStringLiteral( "OperationNotSupported" ), - QString( "Request %1 is not supported" ).arg( req ) ); - return; + throw QgsServiceException( QStringLiteral( "OperationNotSupported" ), + QString( "Request %1 is not supported" ).arg( req ) ); } } diff --git a/src/server/services/wms/qgswmsdescribelayer.cpp b/src/server/services/wms/qgswmsdescribelayer.cpp index a8131a5284e..d1e67f1ccfe 100644 --- a/src/server/services/wms/qgswmsdescribelayer.cpp +++ b/src/server/services/wms/qgswmsdescribelayer.cpp @@ -30,21 +30,15 @@ namespace QgsWms { Q_UNUSED( version ); QgsServerRequest::Parameters params = request.parameters(); - try - { - QgsWmsServer server( serverIface->configFilePath(), - *serverIface->serverSettings(), - params, - getConfigParser( serverIface ), - serverIface->accessControls() ); - QDomDocument doc = server.describeLayer(); - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } + + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), + params, + getConfigParser( serverIface ), + serverIface->accessControls() ); + QDomDocument doc = server.describeLayer(); + response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); + response.write( doc.toByteArray() ); } diff --git a/src/server/services/wms/qgswmsgetcapabilities.cpp b/src/server/services/wms/qgswmsgetcapabilities.cpp index fc2053e1a31..d0412eacfdd 100644 --- a/src/server/services/wms/qgswmsgetcapabilities.cpp +++ b/src/server/services/wms/qgswmsgetcapabilities.cpp @@ -49,20 +49,14 @@ namespace QgsWms { QgsMessageLog::logMessage( QStringLiteral( "Capabilities document not found in cache" ) ); QDomDocument doc; - try - { - QgsWmsServer server( configFilePath, - *serverSettings, - params, - getConfigParser( serverIface ), - accessControl ); - doc = server.getCapabilities( version, projectSettings ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - return; - } + QgsWmsServer server( configFilePath, + *serverSettings, + params, + getConfigParser( serverIface ), + accessControl ); + + doc = server.getCapabilities( version, projectSettings ); + if ( cache ) { capabilitiesCache->insertCapabilitiesDocument( configFilePath, cacheKey, &doc ); diff --git a/src/server/services/wms/qgswmsgetcontext.cpp b/src/server/services/wms/qgswmsgetcontext.cpp index 538f517dc60..c6b136a7079 100644 --- a/src/server/services/wms/qgswmsgetcontext.cpp +++ b/src/server/services/wms/qgswmsgetcontext.cpp @@ -29,22 +29,17 @@ namespace QgsWms const QgsServerRequest& request, QgsServerResponse& response ) { QgsServerRequest::Parameters params = request.parameters(); - try - { - Q_UNUSED( version ); - QgsWmsServer server( serverIface->configFilePath(), - *serverIface->serverSettings(), - params, - getConfigParser( serverIface ), - serverIface->accessControls() ); - QDomDocument doc = server.getContext(); - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } + Q_UNUSED( version ); + + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), + params, + getConfigParser( serverIface ), + serverIface->accessControls() ); + + QDomDocument doc = server.getContext(); + response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); + response.write( doc.toByteArray() ); } diff --git a/src/server/services/wms/qgswmsgetfeatureinfo.cpp b/src/server/services/wms/qgswmsgetfeatureinfo.cpp index bc34a4332bc..2e28186b80f 100644 --- a/src/server/services/wms/qgswmsgetfeatureinfo.cpp +++ b/src/server/services/wms/qgswmsgetfeatureinfo.cpp @@ -146,9 +146,8 @@ namespace QgsWms } else //unsupported format, set exception { - writeError( response, QStringLiteral( "InvalidFormat" ), - QString( "Feature info format '%1' is not supported. Possibilities are 'text/plain', 'text/html' or 'text/xml'." ).arg( infoFormat ) ); - return; + throw QgsServiceException( QStringLiteral( "InvalidFormat" ), + QString( "Feature info format '%1' is not supported. Possibilities are 'text/plain', 'text/html' or 'text/xml'." ).arg( infoFormat ) ); } response.setHeader( QStringLiteral( "Content-Type" ), infoFormat + QStringLiteral( "; charset=utf-8" ) ); @@ -165,17 +164,10 @@ namespace QgsWms *serverIface->serverSettings(), params, getConfigParser( serverIface ), serverIface->accessControls() ); - try - { - QDomDocument doc = server.getFeatureInfo( version ); - QString outputFormat = params.value( QStringLiteral( "INFO_FORMAT" ), QStringLiteral( "text/plain" ) ); - writeInfoResponse( doc, response, outputFormat ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } + QDomDocument doc = server.getFeatureInfo( version ); + QString outputFormat = params.value( QStringLiteral( "INFO_FORMAT" ), QStringLiteral( "text/plain" ) ); + writeInfoResponse( doc, response, outputFormat ); } diff --git a/src/server/services/wms/qgswmsgetlegendgraphics.cpp b/src/server/services/wms/qgswmsgetlegendgraphics.cpp index b55e523c2c5..b5c37acd909 100644 --- a/src/server/services/wms/qgswmsgetlegendgraphics.cpp +++ b/src/server/services/wms/qgswmsgetlegendgraphics.cpp @@ -33,25 +33,22 @@ namespace QgsWms QgsServerRequest::Parameters params = request.parameters(); QgsWmsConfigParser* parser = getConfigParser( serverIface ); - QgsWmsServer server( serverIface->configFilePath(), *serverIface->serverSettings(), - params, parser, serverIface->accessControls() ); - try + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), + params, parser, + serverIface->accessControls() ); + + QScopedPointer result( server.getLegendGraphics() ); + + if ( !result.isNull() ) { - QScopedPointer result( server.getLegendGraphics() ); - if ( !result.isNull() ) - { - QString format = params.value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) ); - writeImage( response, *result, format, server.getImageQuality() ); - } - else - { - writeError( response, QStringLiteral( "UnknownError" ), - QStringLiteral( "Failed to compute GetLegendGraphics image" ) ); - } + QString format = params.value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) ); + writeImage( response, *result, format, server.getImageQuality() ); } - catch ( QgsMapServiceException& ex ) + else { - writeError( response, ex.code(), ex.message() ); + throw QgsServiceException( QStringLiteral( "UnknownError" ), + QStringLiteral( "Failed to compute GetLegendGraphics image" ) ); } } diff --git a/src/server/services/wms/qgswmsgetmap.cpp b/src/server/services/wms/qgswmsgetmap.cpp index a24d66f39b3..ef3e178312a 100644 --- a/src/server/services/wms/qgswmsgetmap.cpp +++ b/src/server/services/wms/qgswmsgetmap.cpp @@ -37,23 +37,17 @@ namespace QgsWms QgsWmsServer server( serverIface->configFilePath(), *serverIface->serverSettings(), params, parser, serverIface->accessControls() ); - try + + QScopedPointer result( server.getMap() ); + if ( !result.isNull() ) { - QScopedPointer result( server.getMap() ); - if ( !result.isNull() ) - { - QString format = params.value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) ); - writeImage( response, *result, format, server.getImageQuality() ); - } - else - { - writeError( response, QStringLiteral( "UnknownError" ), - QStringLiteral( "Failed to compute GetMap image" ) ); - } + QString format = params.value( QStringLiteral( "FORMAT" ), QStringLiteral( "PNG" ) ); + writeImage( response, *result, format, server.getImageQuality() ); } - catch ( QgsMapServiceException& ex ) + else { - writeError( response, ex.code(), ex.message() ); + throw QgsServiceException( QStringLiteral( "UnknownError" ), + QStringLiteral( "Failed to compute GetMap image" ) ); } } diff --git a/src/server/services/wms/qgswmsgetprint.cpp b/src/server/services/wms/qgswmsgetprint.cpp index a957252afc5..9790735e703 100644 --- a/src/server/services/wms/qgswmsgetprint.cpp +++ b/src/server/services/wms/qgswmsgetprint.cpp @@ -28,51 +28,46 @@ namespace QgsWms const QgsServerRequest& request, QgsServerResponse& response ) { QgsServerRequest::Parameters params = request.parameters(); - try + + Q_UNUSED( version ); + + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), params, + getConfigParser( serverIface ), + serverIface->accessControls() ); + + QString format = params.value( "FORMAT" ); + QString contentType; + + // GetPrint supports svg/png/pdf + if ( format.compare( QStringLiteral( "image/png" ), Qt::CaseInsensitive ) == 0 || + format.compare( QStringLiteral( "png" ), Qt::CaseInsensitive ) == 0 ) { - Q_UNUSED( version ); - QgsWmsServer server( serverIface->configFilePath(), - *serverIface->serverSettings(), params, - getConfigParser( serverIface ), - serverIface->accessControls() ); - - QString format = params.value( "FORMAT" ); - QString contentType; - - // GetPrint supports svg/png/pdf - if ( format.compare( QStringLiteral( "image/png" ), Qt::CaseInsensitive ) == 0 || - format.compare( QStringLiteral( "png" ), Qt::CaseInsensitive ) == 0 ) - { - format = "png"; - contentType = "image/png"; - } - else if ( format.compare( QStringLiteral( "image/svg" ), Qt::CaseInsensitive ) == 0 || - format.compare( QStringLiteral( "image/svg+xml" ), Qt::CaseInsensitive ) == 0 || - format.compare( QStringLiteral( "svg" ), Qt::CaseInsensitive ) == 0 ) - { - format = "svg"; - contentType = "image/svg+xml"; - } - else if ( format.compare( QStringLiteral( "application/pdf" ), Qt::CaseInsensitive ) == 0 || - format.compare( QStringLiteral( "pdf" ), Qt::CaseInsensitive ) == 0 ) - { - format = "pdf"; - contentType = "application/pdf"; - } - else - { - writeError( response, QStringLiteral( "InvalidFormat" ), - QString( "Output format %1 is not supported by the GetPrint request" ).arg( format ) ); - } - - QScopedPointer result( server.getPrint( format ) ); - response.setHeader( QStringLiteral( "Content-Type" ), contentType ); - response.write( *result ); + format = "png"; + contentType = "image/png"; } - catch ( QgsMapServiceException& ex ) + else if ( format.compare( QStringLiteral( "image/svg" ), Qt::CaseInsensitive ) == 0 || + format.compare( QStringLiteral( "image/svg+xml" ), Qt::CaseInsensitive ) == 0 || + format.compare( QStringLiteral( "svg" ), Qt::CaseInsensitive ) == 0 ) { - writeError( response, ex.code(), ex.message() ); + format = "svg"; + contentType = "image/svg+xml"; } + else if ( format.compare( QStringLiteral( "application/pdf" ), Qt::CaseInsensitive ) == 0 || + format.compare( QStringLiteral( "pdf" ), Qt::CaseInsensitive ) == 0 ) + { + format = "pdf"; + contentType = "application/pdf"; + } + else + { + throw QgsServiceException( QStringLiteral( "InvalidFormat" ), + QString( "Output format %1 is not supported by the GetPrint request" ).arg( format ) ); + } + + QScopedPointer result( server.getPrint( format ) ); + response.setHeader( QStringLiteral( "Content-Type" ), contentType ); + response.write( *result ); } } // samespace QgsWms diff --git a/src/server/services/wms/qgswmsgetschemaextension.cpp b/src/server/services/wms/qgswmsgetschemaextension.cpp index 84e4e74555c..5934dc2b1b2 100644 --- a/src/server/services/wms/qgswmsgetschemaextension.cpp +++ b/src/server/services/wms/qgswmsgetschemaextension.cpp @@ -30,22 +30,16 @@ namespace QgsWms { Q_UNUSED( version ); QgsServerRequest::Parameters params = request.parameters(); - try - { - QgsWmsServer server( serverIface->configFilePath(), - *serverIface->serverSettings(), params, - getConfigParser( serverIface ), - serverIface->accessControls() ); - QDomDocument doc = server.getSchemaExtension(); - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } - } + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), params, + getConfigParser( serverIface ), + serverIface->accessControls() ); + + QDomDocument doc = server.getSchemaExtension(); + response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); + response.write( doc.toByteArray() ); + } } // samespace QgsWms diff --git a/src/server/services/wms/qgswmsgetstyle.cpp b/src/server/services/wms/qgswmsgetstyle.cpp index 287fcb240f9..281eaa729fe 100644 --- a/src/server/services/wms/qgswmsgetstyle.cpp +++ b/src/server/services/wms/qgswmsgetstyle.cpp @@ -30,23 +30,17 @@ namespace QgsWms { QgsServerRequest::Parameters params = request.parameters(); - try - { - Q_UNUSED( version ); - QgsWmsServer server( serverIface->configFilePath(), - *serverIface->serverSettings(), params, - getConfigParser( serverIface ), - serverIface->accessControls() ); - QDomDocument doc = server.getStyle(); - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } - } + Q_UNUSED( version ); + QgsWmsServer server( serverIface->configFilePath(), + *serverIface->serverSettings(), params, + getConfigParser( serverIface ), + serverIface->accessControls() ); + + QDomDocument doc = server.getStyle(); + response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); + response.write( doc.toByteArray() ); + } } // samespace QgsWms diff --git a/src/server/services/wms/qgswmsgetstyles.cpp b/src/server/services/wms/qgswmsgetstyles.cpp index 32d819080e0..c0779eebda9 100644 --- a/src/server/services/wms/qgswmsgetstyles.cpp +++ b/src/server/services/wms/qgswmsgetstyles.cpp @@ -34,16 +34,10 @@ namespace QgsWms *serverIface->serverSettings(), params, getConfigParser( serverIface ), serverIface->accessControls() ); - try - { - QDomDocument doc = server.getStyles(); - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - catch ( QgsMapServiceException& ex ) - { - writeError( response, ex.code(), ex.message() ); - } + + QDomDocument doc = server.getStyles(); + response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); + response.write( doc.toByteArray() ); } diff --git a/src/server/services/wms/qgswmsservertransitional.cpp b/src/server/services/wms/qgswmsservertransitional.cpp index 7b9d15aff11..29b7359c414 100644 --- a/src/server/services/wms/qgswmsservertransitional.cpp +++ b/src/server/services/wms/qgswmsservertransitional.cpp @@ -42,7 +42,6 @@ #include "qgsvectorlayer.h" #include "qgslogger.h" #include "qgsmessagelog.h" -#include "qgsmapserviceexception.h" #include "qgssldconfigparser.h" #include "qgssymbol.h" #include "qgsrenderer.h" @@ -53,6 +52,7 @@ #include "qgsaccesscontrol.h" #include "qgsfeaturerequest.h" #include "qgsmaprendererjobproxy.h" +#include "qgswmsserviceexception.h" #include #include @@ -405,7 +405,7 @@ namespace QgsWms ok = true; if ( d[2] <= d[0] || d[3] <= d[1] ) { - throw QgsMapServiceException( "InvalidParameterValue", "BBOX is empty" ); + throw QgsServiceException( "InvalidParameterValue", "BBOX is empty" ); } return QgsRectangle( d[0], d[1], d[2], d[3] ); } @@ -419,11 +419,11 @@ namespace QgsWms } if ( !mParameters.contains( QStringLiteral( "LAYER" ) ) && !mParameters.contains( QStringLiteral( "LAYERS" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "LAYER is mandatory for GetLegendGraphic operation" ) ); + throw QgsServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "LAYER is mandatory for GetLegendGraphic operation" ) ); } if ( !mParameters.contains( QStringLiteral( "FORMAT" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "FormatNotSpecified" ), QStringLiteral( "FORMAT is mandatory for GetLegendGraphic operation" ) ); + throw QgsServiceException( QStringLiteral( "FormatNotSpecified" ), QStringLiteral( "FORMAT is mandatory for GetLegendGraphic operation" ) ); } bool contentBasedLegend = false; @@ -436,10 +436,10 @@ namespace QgsWms bool bboxOk; contentBasedLegendExtent = _parseBBOX( mParameters[QStringLiteral( "BBOX" )], bboxOk ); if ( !bboxOk || contentBasedLegendExtent.isEmpty() ) - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Invalid BBOX parameter" ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Invalid BBOX parameter" ) ); if ( mParameters.contains( QStringLiteral( "RULE" ) ) ) - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "BBOX parameter cannot be combined with RULE" ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "BBOX parameter cannot be combined with RULE" ) ); } QStringList layersList, stylesList; @@ -635,7 +635,7 @@ namespace QgsWms #ifdef HAVE_SERVER_PYTHON_PLUGINS if ( !mAccessControl->layerReadPermission( nodeLayer->layer() ) ) { - throw QgsMapServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + nodeLayer->layer()->name() ); + throw QgsServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + nodeLayer->layer()->name() ); } #endif @@ -877,12 +877,12 @@ namespace QgsWms QDomDocument doc; if ( !mParameters.contains( QStringLiteral( "STYLE" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "StyleNotSpecified" ), QStringLiteral( "Style is mandatory for GetStyle operation" ) ); + throw QgsServiceException( QStringLiteral( "StyleNotSpecified" ), QStringLiteral( "Style is mandatory for GetStyle operation" ) ); } if ( !mParameters.contains( QStringLiteral( "LAYER" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layer is mandatory for GetStyle operation" ) ); + throw QgsServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layer is mandatory for GetStyle operation" ) ); } QString styleName = mParameters[ QStringLiteral( "STYLE" )]; @@ -897,13 +897,13 @@ namespace QgsWms QDomDocument doc; if ( !mParameters.contains( QStringLiteral( "LAYERS" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layers is mandatory for GetStyles operation" ) ); + throw QgsServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layers is mandatory for GetStyles operation" ) ); } QStringList layersList = mParameters[ QStringLiteral( "LAYERS" )].split( QStringLiteral( "," ), QString::SkipEmptyParts ); if ( layersList.size() < 1 ) { - throw QgsMapServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layers is mandatory for GetStyles operation" ) ); + throw QgsServiceException( QStringLiteral( "LayerNotSpecified" ), QStringLiteral( "Layers is mandatory for GetStyles operation" ) ); } return mConfigParser->getStyles( layersList ); @@ -914,22 +914,22 @@ namespace QgsWms { if ( !mParameters.contains( QStringLiteral( "SLD_VERSION" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "MissingParameterValue" ), QStringLiteral( "SLD_VERSION is mandatory for DescribeLayer operation" ) ); + throw QgsServiceException( QStringLiteral( "MissingParameterValue" ), QStringLiteral( "SLD_VERSION is mandatory for DescribeLayer operation" ) ); } if ( mParameters[ QStringLiteral( "SLD_VERSION" )] != QLatin1String( "1.1.0" ) ) { - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "SLD_VERSION = %1 is not supported" ).arg( mParameters[ QStringLiteral( "SLD_VERSION" )] ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "SLD_VERSION = %1 is not supported" ).arg( mParameters[ QStringLiteral( "SLD_VERSION" )] ) ); } if ( !mParameters.contains( QStringLiteral( "LAYERS" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "MissingParameterValue" ), QStringLiteral( "LAYERS is mandatory for DescribeLayer operation" ) ); + throw QgsServiceException( QStringLiteral( "MissingParameterValue" ), QStringLiteral( "LAYERS is mandatory for DescribeLayer operation" ) ); } QStringList layersList = mParameters[ QStringLiteral( "LAYERS" )].split( QStringLiteral( "," ), QString::SkipEmptyParts ); if ( layersList.size() < 1 ) { - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Layers is empty" ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Layers is empty" ) ); } //Prepare url @@ -958,7 +958,7 @@ namespace QgsWms { if ( !mAccessControl->layerReadPermission( layer ) ) { - throw QgsMapServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + layer->name() ); + throw QgsServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + layer->name() ); } } #endif @@ -979,7 +979,7 @@ namespace QgsWms if ( !mParameters.contains( QStringLiteral( "TEMPLATE" ) ) ) { clearFeatureSelections( selectedLayerIdList ); - throw QgsMapServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "The TEMPLATE parameter is required for the GetPrint request" ) ); + throw QgsServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "The TEMPLATE parameter is required for the GetPrint request" ) ); } QList< QPair< QgsVectorLayer*, QgsFeatureRenderer*> > bkVectorRenderers; @@ -1056,7 +1056,7 @@ namespace QgsWms { restoreOpacities( bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies ); clearFeatureSelections( selectedLayerIdList ); - throw QgsMapServiceException( QStringLiteral( "InvalidFormat" ), "Output format '" + formatString + "' is not supported in the GetPrint request" ); + throw QgsServiceException( QStringLiteral( "InvalidFormat" ), "Output format '" + formatString + "' is not supported in the GetPrint request" ); } restoreOpacities( bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies ); @@ -1095,7 +1095,7 @@ namespace QgsWms { if ( !checkMaximumWidthHeight() ) { - throw QgsMapServiceException( QStringLiteral( "Size error" ), QStringLiteral( "The requested map size is too large" ) ); + throw QgsServiceException( QStringLiteral( "Size error" ), QStringLiteral( "The requested map size is too large" ) ); } QStringList layersList, stylesList, layerIdList; QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, mapSettings ); @@ -1116,7 +1116,7 @@ namespace QgsWms { if ( !mAccessControl->layerReadPermission( layer ) ) { - throw QgsMapServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + layer->name() ); + throw QgsServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + layer->name() ); } } #endif @@ -1193,7 +1193,7 @@ namespace QgsWms //check if i, j are in the pixel range of the image if ( i < 0 || i > mapSettings.outputSize().width() || j < 0 || j > mapSettings.outputSize().height() ) { - throw QgsMapServiceException( "InvalidPoint", "I/J parameters not within the pixel range" ); + throw QgsServiceException( "InvalidPoint", "I/J parameters not within the pixel range" ); } double xRes = mapSettings.extent().width() / mapSettings.outputSize().width(); @@ -1250,13 +1250,13 @@ namespace QgsWms //read QUERY_LAYERS if ( !mParameters.contains( QStringLiteral( "QUERY_LAYERS" ) ) ) { - throw QgsMapServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "No QUERY_LAYERS" ) ); + throw QgsServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "No QUERY_LAYERS" ) ); } QStringList queryLayerList = mParameters[ QStringLiteral( "QUERY_LAYERS" )].split( QStringLiteral( "," ), QString::SkipEmptyParts ); if ( queryLayerList.size() < 1 ) { - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Malformed QUERY_LAYERS" ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Malformed QUERY_LAYERS" ) ); } //read I,J resp. X,Y @@ -1297,7 +1297,7 @@ namespace QgsWms } else { - throw QgsMapServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) ); + throw QgsServiceException( QStringLiteral( "ParameterMissing" ), QStringLiteral( "I/J parameters are required for GetFeatureInfo" ) ); } } else @@ -1388,7 +1388,7 @@ namespace QgsWms #ifdef HAVE_SERVER_PYTHON_PLUGINS if ( !mAccessControl->layerReadPermission( currentLayer ) ) { - throw QgsMapServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + currentLayer->name() ); + throw QgsServiceException( QStringLiteral( "Security" ), "You are not allowed to access to the layer: " + currentLayer->name() ); } #endif @@ -1706,12 +1706,12 @@ namespace QgsWms if ( !bboxOk ) { //throw a service exception - throw QgsMapServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Invalid BBOX parameter" ) ); + throw QgsServiceException( QStringLiteral( "InvalidParameterValue" ), QStringLiteral( "Invalid BBOX parameter" ) ); } if ( mParameters.contains( "BBOX" ) && mapExtent.isEmpty() ) { - throw QgsMapServiceException( "InvalidParameterValue", "BBOX is empty" ); + throw QgsServiceException( "InvalidParameterValue", "BBOX is empty" ); } QgsUnitTypes::DistanceUnit mapUnits = QgsUnitTypes::DistanceDegrees; @@ -1743,7 +1743,7 @@ namespace QgsWms if ( !outputCRS.isValid() ) { QgsMessageLog::logMessage( QStringLiteral( "Error, could not create output CRS from EPSG" ) ); - throw QgsMapServiceException( QStringLiteral( "InvalidCRS" ), QStringLiteral( "Could not create output CRS" ) ); + throw QgsServiceException( QStringLiteral( "InvalidCRS" ), QStringLiteral( "Could not create output CRS" ) ); } //then set destinationCrs @@ -2220,7 +2220,7 @@ namespace QgsWms else { QgsMessageLog::logMessage( QStringLiteral( "Layer or style not defined, aborting" ) ); - throw QgsMapServiceException( QStringLiteral( "LayerNotDefined" ), "Layer '" + *llstIt + "' and/or style '" + styleName + "' not defined" ); + throw QgsServiceException( QStringLiteral( "LayerNotDefined" ), "Layer '" + *llstIt + "' and/or style '" + styleName + "' not defined" ); } } @@ -2256,10 +2256,10 @@ namespace QgsWms //filter string could be unsafe (danger of sql injection) if ( !testFilterStringSafety( eqSplit.at( 1 ) ) ) { - throw QgsMapServiceException( QStringLiteral( "Filter string rejected" ), "The filter string " + eqSplit.at( 1 ) + - " has been rejected because of security reasons. Note: Text strings have to be enclosed in single or double quotes. " + - "A space between each word / special character is mandatory. Allowed Keywords and special characters are " + - "AND,OR,IN,<,>=,>,>=,!=,',',(,),DMETAPHONE,SOUNDEX. Not allowed are semicolons in the filter expression." ); + throw QgsServiceException( QStringLiteral( "Filter string rejected" ), "The filter string " + eqSplit.at( 1 ) + + " has been rejected because of security reasons. Note: Text strings have to be enclosed in single or double quotes. " + + "A space between each word / special character is mandatory. Allowed Keywords and special characters are " + + "AND,OR,IN,<,>=,>,>=,!=,',',(,),DMETAPHONE,SOUNDEX. Not allowed are semicolons in the filter expression." ); } //we need to find the maplayer objects matching the layer name diff --git a/src/server/qgsmapserviceexception.cpp b/src/server/services/wms/qgswmsserviceexception.h similarity index 51% rename from src/server/qgsmapserviceexception.cpp rename to src/server/services/wms/qgswmsserviceexception.h index 57559e1622e..34cfa6caf23 100644 --- a/src/server/qgsmapserviceexception.cpp +++ b/src/server/services/wms/qgswmsserviceexception.h @@ -1,6 +1,6 @@ /*************************************************************************** - qgsmapserviceexception.h - ------------------- + qgsserviceexception.h + ------------------------ begin : June 13, 2006 copyright : (C) 2006 by Marco Hugentobler email : marco dot hugentobler at karto dot baug dot ethz dot ch @@ -15,11 +15,36 @@ * * ***************************************************************************/ -#include "qgsmapserviceexception.h" +#ifndef QGSWMSSERVICEEXCEPTION_H +#define QGSWMSSERVICEEXCEPTION_H -QgsMapServiceException::QgsMapServiceException( const QString& code, const QString& message ): - QgsException( message ), - mCode( code ), mMessage( message ) +#include + +#include "qgsserverexception.h" + +namespace QgsWms { -} + /** \ingroup server + * \class QgsserviceException + * \brief Exception class for WMS service exceptions. + * + * The most important codes are: + * * "InvalidFormat" + * * "Invalid CRS" + * * "LayerNotDefined" / "StyleNotDefined" + * * "OperationNotSupported" + */ + class QgsServiceException : public QgsOgcServiceException + { + public: + QgsServiceException( const QString& code, const QString& message, const QString& locator = QString(), + int responseCode = 200 ) + : QgsOgcServiceException( code, message, locator, responseCode, QStringLiteral( "1.3.0" ) ) + {} + }; + + +} // namespace QgsWms + +#endif diff --git a/src/server/services/wms/qgswmsutils.cpp b/src/server/services/wms/qgswmsutils.cpp index cb697d75d63..187e74ac5d8 100644 --- a/src/server/services/wms/qgswmsutils.cpp +++ b/src/server/services/wms/qgswmsutils.cpp @@ -25,7 +25,6 @@ namespace QgsWms { - typedef QList< QPair > QgsColorBox; //Color / number of pixels typedef QMultiMap< int, QgsColorBox > QgsColorBoxMap; // sum of pixels / color box @@ -42,35 +41,13 @@ namespace QgsWms QgsWmsConfigParser* parser = QgsConfigCache::instance()->wmsConfiguration( configFilePath, serverIface->accessControls() ); if ( !parser ) { - throw QgsMapServiceException( + throw QgsServiceException( QStringLiteral( "WMS configuration error" ), QStringLiteral( "There was an error reading the project file or the SLD configuration" ) ); - } return parser; } - // Output a wms standard error - void writeError( QgsServerResponse& response, const QString& code, const QString& message ) - { - // WMS errors return erros with http 200 - // XXX Do we really need to use a QDomDocument here ? - QDomDocument doc; - QDomElement root = doc.createElement( QStringLiteral( "ServiceExceptionReport" ) ); - root.setAttribute( QStringLiteral( "version" ), ImplementationVersion() ); - root.setAttribute( QStringLiteral( "xmlns" ) , QStringLiteral( "http://www.opengis.net/ogc" ) ); - doc.appendChild( root ); - - QDomElement elem = doc.createElement( QStringLiteral( "ServiceException" ) ); - elem.setAttribute( QStringLiteral( "code" ), code ); - QDomText messageText = doc.createTextNode( message ); - elem.appendChild( messageText ); - root.appendChild( elem ); - - response.setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/xml; charset=utf-8" ) ); - response.write( doc.toByteArray() ); - } - ImageOutputFormat parseImageFormat( const QString& format ) { if ( format.compare( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 || @@ -164,8 +141,8 @@ namespace QgsWms } else { - writeError( response, "InvalidFormat", - QString( "Output format '%1' is not supported in the GetMap request" ).arg( formatStr ) ); + throw QgsServiceException( "InvalidFormat", + QString( "Output format '%1' is not supported in the GetMap request" ).arg( formatStr ) ); } } diff --git a/src/server/services/wms/qgswmsutils.h b/src/server/services/wms/qgswmsutils.h index aa56b121cf5..8bc145e427e 100644 --- a/src/server/services/wms/qgswmsutils.h +++ b/src/server/services/wms/qgswmsutils.h @@ -25,7 +25,7 @@ #include "qgsmodule.h" #include "qgswmsconfigparser.h" -#include "qgsmapserviceexception.h" +#include "qgswmsserviceexception.h" /** * \ingroup server @@ -51,10 +51,6 @@ namespace QgsWms */ QString ImplementationVersion(); - /** Send WMS standard XML Error respons - */ - void writeError( QgsServerResponse& response, const QString& code, const QString& message ); - /** Parse image format parameter * @return OutputFormat */