Skip to content
Permalink
Browse files
[server] Expose HTTP status code and test refactoring
* Renamed returnCode to statusCode (as per RFC https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
* Expose statusCode to server iface
* API Break: Renamed getHeader() to header() Qt-style
* Split server plugin filter tests in its own test file
* Added tests for clearBody() and body() getter
  • Loading branch information
elpaso committed Apr 22, 2017
1 parent 1083f9f commit d03b00596941ca9f1bb634ae1770b507289cc3df
@@ -33,7 +33,7 @@ class QgsRequestHandler /Abstract/
void setHeader( const QString &name, const QString &value );

//! Retrieve header value
QString getHeader( const QString &name ) const;
QString header( const QString &name ) const;

//! Return the list of all header keys
QList<QString> headerKeys() const;
@@ -50,6 +50,12 @@ class QgsRequestHandler /Abstract/
/** Send out HTTP headers and flush output buffer*/
void sendResponse();

//! Set response http status code
void setStatusCode( int code );

//! Return response http status code
int statusCode( ) const;

/** Pointer to last raised exception*/
bool exceptionRaised() const;

@@ -63,10 +63,14 @@ class QgsServerResponse
*/
virtual bool headersSent() const = 0;

/** Set the http return code
/** Return the http status code
*/
virtual int statusCode( ) const = 0;

/** Set the http status code
* @param code HTTP return code value
*/
virtual void setReturnCode( int code ) = 0;
virtual void setStatusCode( int code ) = 0;

/**
* Send error
@@ -49,7 +49,7 @@ void QgsBufferServerResponse::setHeader( const QString &key, const QString &valu
mHeaders.insert( key, value );
}

void QgsBufferServerResponse::setReturnCode( int code )
void QgsBufferServerResponse::setStatusCode( int code )
{
mReturnCode = code;
}
@@ -78,7 +78,7 @@ void QgsBufferServerResponse::sendError( int code, const QString &message )
}

clear();
setReturnCode( code );
setStatusCode( code );
setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/plain; charset=utf-8" ) );
write( message );
finish();
@@ -48,7 +48,9 @@ class QgsBufferServerResponse: public QgsServerResponse

bool headersSent() const override;

void setReturnCode( int code ) override;
void setStatusCode( int code ) override;

int statusCode( ) const override { return mReturnCode; }

void sendError( int code, const QString &message ) override;

@@ -74,10 +76,6 @@ class QgsBufferServerResponse: public QgsServerResponse
*/
QMap<QString, QString> headers() const { return mHeaders; }

/**
* Return the status code
*/
int returnCode() const { return mReturnCode; }

private:
QMap<QString, QString> mHeaders;
@@ -66,10 +66,12 @@ bool QgsFcgiServerResponse::headersSent() const
return mHeadersSent;
}

void QgsFcgiServerResponse::setReturnCode( int code )
void QgsFcgiServerResponse::setStatusCode( int code )
{
// fcgi applications must return HTTP status in header
mHeaders.insert( QStringLiteral( "Status" ), QStringLiteral( " %1" ).arg( code ) );
// Store the code to make it available for plugins
mStatusCode = code;
}

void QgsFcgiServerResponse::sendError( int code, const QString &message )
@@ -81,7 +83,7 @@ void QgsFcgiServerResponse::sendError( int code, const QString &message )
}

clear();
setReturnCode( code );
setStatusCode( code );
setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html;charset=utf-8" ) );
write( QStringLiteral( "<html><body>%1</body></html>" ).arg( message ) );
finish();
@@ -46,7 +46,9 @@ class SERVER_EXPORT QgsFcgiServerResponse: public QgsServerResponse

bool headersSent() const override;

void setReturnCode( int code ) override;
void setStatusCode( int code ) override;

int statusCode( ) const override { return mStatusCode; }

void sendError( int code, const QString &message ) override;

@@ -73,6 +75,7 @@ class SERVER_EXPORT QgsFcgiServerResponse: public QgsServerResponse
bool mFinished = false;
bool mHeadersSent = false;
QgsServerRequest::Method mMethod;
int mStatusCode = 0;
};

/**
@@ -51,7 +51,9 @@ class QgsFilterResponseDecorator: public QgsServerResponse

bool headersSent() const override { return mResponse.headersSent(); }

void setReturnCode( int code ) override { mResponse.setReturnCode( code ); }
void setStatusCode( int code ) override { mResponse.setStatusCode( code ); }

int statusCode( ) const override { return mResponse.statusCode( ); }

void sendError( int code, const QString &message ) override { mResponse.sendError( code, message ); }

@@ -69,7 +69,7 @@ void QgsRequestHandler::removeHeader( const QString &name )
mResponse.clearHeader( name );
}

QString QgsRequestHandler::getHeader( const QString &name ) const
QString QgsRequestHandler::header( const QString &name ) const
{
return mResponse.getHeader( name );
}
@@ -99,6 +99,16 @@ QByteArray QgsRequestHandler::body() const
return mResponse.data();
}

void QgsRequestHandler::setStatusCode( int code )
{
mResponse.setStatusCode( code );
}

int QgsRequestHandler::statusCode() const
{
return mResponse.statusCode();
}

void QgsRequestHandler::sendResponse()
{
// Send data to output
@@ -71,7 +71,7 @@ class SERVER_EXPORT QgsRequestHandler
void removeHeader( const QString &name );

//! Retrieve header value
QString getHeader( const QString &name ) const;
QString header( const QString &name ) const;

//! Return the list of all header keys
QList<QString> headerKeys() const;
@@ -88,9 +88,15 @@ class SERVER_EXPORT QgsRequestHandler
//! Clear response buffer
void clearBody();

//! Return body data
//! Return response body data
QByteArray body() const;

//! Set response http status code
void setStatusCode( int code );

//! Return response http status code
int statusCode( ) const;

/** Return the parsed parameters as a key-value pair, to modify
* a parameter setParameter( const QString &key, const QString &value)
* and removeParameter(const QString &key) must be used
@@ -87,7 +87,7 @@ void QgsServerResponse::write( const QgsServerException &ex )
}

clear();
setReturnCode( ex.responseCode() );
setStatusCode( ex.responseCode() );
setHeader( "Content-Type", responseFormat );
write( ba );
}
@@ -77,10 +77,14 @@ class SERVER_EXPORT QgsServerResponse
virtual bool headersSent() const = 0;


/** Set the http return code
* \param code HTTP return code value
/** Set the http status code
* \param code HTTP status code value
*/
virtual void setReturnCode( int code ) = 0;
virtual void setStatusCode( int code ) = 0;

/** Return the http status code
*/
virtual int statusCode( ) const = 0;

/**
* Send error
@@ -113,7 +117,7 @@ class SERVER_EXPORT QgsServerResponse
*
* This is a convenient method that will write directly
* to the underlying I/O device
* \returns the number of bytes written
* \returns the number of bytes written
*
* \note not available in Python bindings
*/
@@ -162,7 +166,7 @@ class SERVER_EXPORT QgsServerResponse
* Get the data written so far
*
* This is implementation dependent: some implementations may not
* give access to the underlyng and return an empty array.
* give access to the underlying and return an empty array.
*
* Note that each call to 'flush' may empty the buffer and in case
* of streaming process you may get partial content
@@ -189,7 +189,8 @@ IF (WITH_APIDOC)
ENDIF (WITH_APIDOC)

IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
ADD_PYTHON_TEST(PyQgsServerPlugins test_qgsserver_plugins.py)
ADD_PYTHON_TEST(PyQgsServerSettings test_qgsserver_settings.py)
ADD_PYTHON_TEST(PyQgsServerProjectUtils test_qgsserver_projectutils.py)
ADD_PYTHON_TEST(PyQgsServerSecurity test_qgsserver_security.py)
@@ -135,79 +135,6 @@ def test_api(self):
expected = self.strip_version_xmlns(b'<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc">\n <ServiceException code="Service configuration error">Service unknown or unsupported</ServiceException>\n</ServiceExceptionReport>\n')
self.assertEqual(self.strip_version_xmlns(body), expected)

def test_pluginfilters(self):
"""Test python plugins filters"""
try:
from qgis.server import QgsServerFilter
except ImportError:
print("QGIS Server plugins are not compiled. Skipping test")
return

class SimpleHelloFilter(QgsServerFilter):

def requestReady(self):
QgsMessageLog.logMessage("SimpleHelloFilter.requestReady")

def sendResponse(self):
QgsMessageLog.logMessage("SimpleHelloFilter.sendResponse")

def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
QgsMessageLog.logMessage("SimpleHelloFilter.responseComplete")
if params.get('SERVICE', '').upper() == 'SIMPLE':
request.clear()
request.setHeader('Content-type', 'text/plain')
request.appendBody('Hello from SimpleServer!'.encode('utf-8'))

serverIface = self.server.serverInterface()
filter = SimpleHelloFilter(serverIface)
serverIface.registerFilter(filter, 100)
# Get registered filters
self.assertEqual(filter, serverIface.filters()[100][0])

# Register some more filters
class Filter1(QgsServerFilter):

def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
if params.get('SERVICE', '').upper() == 'SIMPLE':
request.appendBody('Hello from Filter1!'.encode('utf-8'))

class Filter2(QgsServerFilter):

def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
if params.get('SERVICE', '').upper() == 'SIMPLE':
request.appendBody('Hello from Filter2!'.encode('utf-8'))

filter1 = Filter1(serverIface)
filter2 = Filter2(serverIface)
serverIface.registerFilter(filter1, 101)
serverIface.registerFilter(filter2, 200)
serverIface.registerFilter(filter2, 100)
self.assertTrue(filter2 in serverIface.filters()[100])
self.assertEqual(filter1, serverIface.filters()[101][0])
self.assertEqual(filter2, serverIface.filters()[200][0])
header, body = [_v for _v in self.server.handleRequest('?service=simple')]
response = header + body
expected = b'Content-Length: 62\nContent-type: text/plain\n\nHello from SimpleServer!Hello from Filter1!Hello from Filter2!'
self.assertEqual(response, expected)

# Test that the bindings for complex type QgsServerFiltersMap are working
filters = {100: [filter, filter2], 101: [filter1], 200: [filter2]}
serverIface.setFilters(filters)
self.assertTrue(filter in serverIface.filters()[100])
self.assertTrue(filter2 in serverIface.filters()[100])
self.assertEqual(filter1, serverIface.filters()[101][0])
self.assertEqual(filter2, serverIface.filters()[200][0])
header, body = [_v for _v in self.server.handleRequest('?service=simple')]
response = header + body
expected = b'Content-Length: 62\nContent-type: text/plain\n\nHello from SimpleServer!Hello from Filter1!Hello from Filter2!'
self.assertEqual(response, expected)

# WMS tests
def wms_request_compare(self, request, extra=None, reference_file=None):
project = self.testdata_path + "test_project.qgs"
Loading

0 comments on commit d03b005

Please sign in to comment.