Skip to content

Commit

Permalink
Revisit server exceptions
Browse files Browse the repository at this point in the history
    - Add server exception base class
    - Enable per service exception definition
    - Handle QgsException gracefully (error 500)
    - Handle OGC exception versioning
  • Loading branch information
dmarteau committed Jan 14, 2017
1 parent c128d13 commit 8b0526d
Show file tree
Hide file tree
Showing 30 changed files with 552 additions and 410 deletions.
45 changes: 3 additions & 42 deletions python/server/qgsrequesthandler.sip
Expand Up @@ -20,45 +20,14 @@
class QgsRequestHandler /Abstract/ class QgsRequestHandler /Abstract/
{ {
%TypeHeaderCode %TypeHeaderCode
#include "qgsmapserviceexception.h" #include "qgsserverexception.h"
#include "qgsrequesthandler.h" #include "qgsrequesthandler.h"
%End %End


public: public:


/** Parses the input and creates a request neutral Parameter/Value map /** Allow plugins to return a QgsServerException*/
* @note not available in Python bindings void setServiceException( const QgsServerException& ex );
*/
// 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;


//! @note not available in Python bindings //! @note not available in Python bindings
void endGetFeatureResponse( QByteArray* ba ); void endGetFeatureResponse( QByteArray* ba );
Expand Down Expand Up @@ -116,12 +85,4 @@ class QgsRequestHandler /Abstract/


/** Return true if the HTTP headers were already sent to the client*/ /** Return true if the HTTP headers were already sent to the client*/
bool headersSent() const; bool headersSent() const;


//! @note not available in Python bindings
// virtual QPair<QByteArray, QByteArray> getResponse() = 0;

private:
/** Parses the input and creates a request neutral Parameter/Value map*/
void parseInput();
}; };
56 changes: 56 additions & 0 deletions 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 <qgsserverexception.h>
%End
public:
QgsServerException( const QString& message, int responseCode = 500 );

int responseCode() const;

virtual QByteArray formatResponse( QString& responseFormat ) const;
};


class QgsOgcServiceException
{
%TypeHeaderCode
#include <qgsserverexception.h>
%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;
};




5 changes: 5 additions & 0 deletions python/server/qgsserverresponse.sip
Expand Up @@ -92,6 +92,11 @@ class QgsServerResponse
*/ */
virtual qint64 write( const QByteArray& byteArray ); virtual qint64 write( const QByteArray& byteArray );


/**
* Write server exception
*/
virtual void write( const QgsServerException& ex );

/** /**
* Return the underlying QIODevice * Return the underlying QIODevice
*/ */
Expand Down
1 change: 1 addition & 0 deletions python/server/server.sip
Expand Up @@ -32,6 +32,7 @@


%Include qgsserverrequest.sip %Include qgsserverrequest.sip
%Include qgsserverresponse.sip %Include qgsserverresponse.sip
%Include qgsserverexception.sip
%Include qgsservice.sip %Include qgsservice.sip
%Include qgsservicemodule.sip %Include qgsservicemodule.sip
%Include qgsserviceregistry.sip %Include qgsserviceregistry.sip
Expand Down
2 changes: 1 addition & 1 deletion src/server/CMakeLists.txt
Expand Up @@ -28,7 +28,7 @@ SET ( qgis_mapserv_SRCS
qgswfsserver.cpp qgswfsserver.cpp
qgswcsserver.cpp qgswcsserver.cpp
qgsserversettings.cpp qgsserversettings.cpp
qgsmapserviceexception.cpp qgsserverexception.cpp
qgsmslayercache.cpp qgsmslayercache.cpp
qgsmslayerbuilder.cpp qgsmslayerbuilder.cpp
qgshostedvdsbuilder.cpp qgshostedvdsbuilder.cpp
Expand Down
10 changes: 5 additions & 5 deletions src/server/qgsfcgiserverresponse.cpp
Expand Up @@ -30,16 +30,14 @@
// //


QgsFcgiServerResponse::QgsFcgiServerResponse( QgsServerRequest::Method method ) QgsFcgiServerResponse::QgsFcgiServerResponse( QgsServerRequest::Method method )
: mMethod( method )
{ {
mBuffer.open( QIODevice::ReadWrite ); mBuffer.open( QIODevice::ReadWrite );
mHeadersSent = false; setDefaultHeaders();
mFinished = false;
mMethod = method;
} }


QgsFcgiServerResponse::~QgsFcgiServerResponse() QgsFcgiServerResponse::~QgsFcgiServerResponse()
{ {

} }


void QgsFcgiServerResponse::clearHeader( const QString& key ) void QgsFcgiServerResponse::clearHeader( const QString& key )
Expand Down Expand Up @@ -82,7 +80,6 @@ void QgsFcgiServerResponse::sendError( int code, const QString& message )
} }


clear(); clear();
setDefaultHeaders();
setReturnCode( code ); setReturnCode( code );
setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html;charset=utf-8" ) ); setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html;charset=utf-8" ) );
write( QStringLiteral( "<html><body>%1</body></html>" ).arg( message ) ); write( QStringLiteral( "<html><body>%1</body></html>" ).arg( message ) );
Expand Down Expand Up @@ -157,6 +154,9 @@ void QgsFcgiServerResponse::clear()
mHeaders.clear(); mHeaders.clear();
mBuffer.seek( 0 ); mBuffer.seek( 0 );
mBuffer.buffer().clear(); mBuffer.buffer().clear();

// Restore default headers
setDefaultHeaders();
} }


void QgsFcgiServerResponse::setDefaultHeaders() void QgsFcgiServerResponse::setDefaultHeaders()
Expand Down
8 changes: 4 additions & 4 deletions src/server/qgsfcgiserverresponse.h
Expand Up @@ -61,13 +61,13 @@ class QgsFcgiServerResponse: public QgsServerResponse
/** /**
* Set the default headers * Set the default headers
*/ */
virtual void setDefaultHeaders(); void setDefaultHeaders();


private: private:
QMap<QString, QString> mHeaders; QMap<QString, QString> mHeaders;
QBuffer mBuffer; QBuffer mBuffer;
bool mFinished; bool mFinished = false;
bool mHeadersSent; bool mHeadersSent = false;
QgsServerRequest::Method mMethod; QgsServerRequest::Method mMethod;
}; };


Expand Down
17 changes: 8 additions & 9 deletions src/server/qgsmapserviceexception.h
Expand Up @@ -20,12 +20,14 @@


#include <QString> #include <QString>


#include "qgsexception.h" #include "qgsserverexception.h"
#include "qgis_server.h" #include "qgis_server.h"


/** \ingroup server /** \ingroup server
* \class QgsMapServiceException * \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: * The most important codes are:
* * "InvalidFormat" * * "InvalidFormat"
Expand All @@ -34,15 +36,12 @@
* * "OperationNotSupported" * * "OperationNotSupported"
*/ */


class SERVER_EXPORT QgsMapServiceException : public QgsException class SERVER_EXPORT QgsMapServiceException : public QgsOgcServiceException
{ {
public: public:
QgsMapServiceException( const QString& code, const QString& message ); QgsMapServiceException( const QString& code, const QString& message )
QString code() const {return mCode;} : QgsOgcServiceException( code, message )
QString message() const {return mMessage;} {}
private:
QString mCode;
QString mMessage;
}; };


#endif #endif
30 changes: 6 additions & 24 deletions src/server/qgsrequesthandler.cpp
Expand Up @@ -24,7 +24,7 @@
#include "qgshttptransaction.h" #include "qgshttptransaction.h"
#endif #endif
#include "qgsmessagelog.h" #include "qgsmessagelog.h"
#include "qgsmapserviceexception.h" #include "qgsserverexception.h"
#include "qgsserverrequest.h" #include "qgsserverrequest.h"
#include "qgsserverresponse.h" #include "qgsserverresponse.h"
#include <QBuffer> #include <QBuffer>
Expand All @@ -38,15 +38,14 @@
#include <QUrlQuery> #include <QUrlQuery>


QgsRequestHandler::QgsRequestHandler( QgsServerRequest& request, QgsServerResponse& response ) QgsRequestHandler::QgsRequestHandler( QgsServerRequest& request, QgsServerResponse& response )
: mException( nullptr ) : mExceptionRaised( false )
, mRequest( request ) , mRequest( request )
, mResponse( response ) , mResponse( response )
{ {
} }


QgsRequestHandler::~QgsRequestHandler() QgsRequestHandler::~QgsRequestHandler()
{ {
delete mException;
} }


QMap<QString, QString> QgsRequestHandler::parameterMap() const QMap<QString, QString> QgsRequestHandler::parameterMap() const
Expand All @@ -70,7 +69,7 @@ void QgsRequestHandler::setHttpResponse( const QByteArray& ba, const QString &fo


bool QgsRequestHandler::exceptionRaised() const bool QgsRequestHandler::exceptionRaised() const
{ {
return mException; return mExceptionRaised;
} }


void QgsRequestHandler::setHeader( const QString &name, const QString &value ) void QgsRequestHandler::setHeader( const QString &name, const QString &value )
Expand Down Expand Up @@ -169,28 +168,11 @@ void QgsRequestHandler::setXmlResponse( const QDomDocument& doc, const QString&
setHttpResponse( ba, mimeType ); setHttpResponse( ba, mimeType );
} }


void QgsRequestHandler::setServiceException( const QgsMapServiceException& ex ) void QgsRequestHandler::setServiceException( const QgsServerException& ex )
{ {
// Safety measure to avoid potential leaks if called repeatedly // Safety measure to avoid potential leaks if called repeatedly
delete mException; mExceptionRaised = true;
mException = new QgsMapServiceException( ex ); mResponse.write( 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" ) );
} }


bool QgsRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) bool QgsRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat )
Expand Down
6 changes: 3 additions & 3 deletions src/server/qgsrequesthandler.h
Expand Up @@ -31,7 +31,7 @@


class QDomDocument; class QDomDocument;
class QImage; class QImage;
class QgsMapServiceException; class QgsServerException;
class QgsServerRequest; class QgsServerRequest;
class QgsServerResponse; class QgsServerResponse;


Expand All @@ -58,7 +58,7 @@ class SERVER_EXPORT QgsRequestHandler
void setGetCapabilitiesResponse( const QDomDocument& doc ); void setGetCapabilitiesResponse( const QDomDocument& doc );


//! Allow plugins to return a QgsMapServiceException //! Allow plugins to return a QgsMapServiceException
void setServiceException( const QgsMapServiceException &ex ); void setServiceException( const QgsServerException &ex );


//! @note not available in Python bindings //! @note not available in Python bindings
void setXmlResponse( const QDomDocument& doc ); void setXmlResponse( const QDomDocument& doc );
Expand Down Expand Up @@ -154,7 +154,7 @@ class SERVER_EXPORT QgsRequestHandler
QString mFormatString; //format string as it is passed in the request (with base) QString mFormatString; //format string as it is passed in the request (with base)
QString mService; QString mService;
QString mInfoFormat; QString mInfoFormat;
QgsMapServiceException* mException; // Stores the exception bool mExceptionRaised;


QgsServerRequest& mRequest; QgsServerRequest& mRequest;
QgsServerResponse& mResponse; QgsServerResponse& mResponse;
Expand Down

0 comments on commit 8b0526d

Please sign in to comment.