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/
{
%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 );
Expand Down Expand Up @@ -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<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 );

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

/**
* 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 qgsserverresponse.sip
%Include qgsserverexception.sip
%Include qgsservice.sip
%Include qgsservicemodule.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
qgswcsserver.cpp
qgsserversettings.cpp
qgsmapserviceexception.cpp
qgsserverexception.cpp
qgsmslayercache.cpp
qgsmslayerbuilder.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 )
: mMethod( method )
{
mBuffer.open( QIODevice::ReadWrite );
mHeadersSent = false;
mFinished = false;
mMethod = method;
setDefaultHeaders();
}

QgsFcgiServerResponse::~QgsFcgiServerResponse()
{

}

void QgsFcgiServerResponse::clearHeader( const QString& key )
Expand Down Expand Up @@ -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( "<html><body>%1</body></html>" ).arg( message ) );
Expand Down Expand Up @@ -157,6 +154,9 @@ void QgsFcgiServerResponse::clear()
mHeaders.clear();
mBuffer.seek( 0 );
mBuffer.buffer().clear();

// Restore default headers
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
*/
virtual void setDefaultHeaders();
void setDefaultHeaders();

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

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

#include <QString>

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

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

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

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
{
return mException;
return mExceptionRaised;
}

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 );
}

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 )
Expand Down
6 changes: 3 additions & 3 deletions src/server/qgsrequesthandler.h
Expand Up @@ -31,7 +31,7 @@

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

Expand All @@ -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 );
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 mService;
QString mInfoFormat;
QgsMapServiceException* mException; // Stores the exception
bool mExceptionRaised;

QgsServerRequest& mRequest;
QgsServerResponse& mResponse;
Expand Down

0 comments on commit 8b0526d

Please sign in to comment.