Skip to content

Commit

Permalink
[server] Python plugins API cleanup part 2
Browse files Browse the repository at this point in the history
This part adds the headers as an optional argument
to the request and start using the handleRequest(request, response)
call in the python tests.

Some additional tests are also added.
  • Loading branch information
elpaso committed May 3, 2017
1 parent b7d6c1e commit 2afcad2
Show file tree
Hide file tree
Showing 11 changed files with 47 additions and 39 deletions.
7 changes: 2 additions & 5 deletions python/server/qgsbufferserverrequest.sip
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class QgsBufferServerRequest : QgsServerRequest
%End
public:

QgsBufferServerRequest( const QString &url, Method method = GetMethod, QByteArray *data = 0 );
QgsBufferServerRequest( const QString &url, Method method = GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers( ), QByteArray *data = 0 );
%Docstring
Constructor

\param url the url string
\param method the request method
%End

QgsBufferServerRequest( const QUrl &url, Method method = GetMethod, QByteArray *data = 0 );
QgsBufferServerRequest( const QUrl &url, Method method = GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers( ), QByteArray *data = 0 );
%Docstring
Constructor

Expand All @@ -40,9 +40,6 @@ class QgsBufferServerRequest : QgsServerRequest
~QgsBufferServerRequest();

virtual QByteArray data() const;
%Docstring
:rtype: QByteArray
%End

};

Expand Down
3 changes: 2 additions & 1 deletion python/server/qgsserver.sip
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ class QgsServer
* \param urlstr QString containing the request url (simple quely string must be preceded by '?')
* \param requestMethod QgsServerRequest::Method that indicates the method. Only "GET" or "POST" are supported.
* \param data array of bytes containing post data
* \param map of request headers
* \returns the response headers and body QPair of QByteArray
*/
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const char *data = 0 );
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers(), const char *data = nullptr );

/** Returns a pointer to the server interface */
QgsServerInterface *serverInterface();
Expand Down
8 changes: 4 additions & 4 deletions src/server/qgsbufferserverrequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@

#include <QDebug>

QgsBufferServerRequest::QgsBufferServerRequest( const QString &url, Method method, QByteArray *data )
: QgsServerRequest( url, method )
QgsBufferServerRequest::QgsBufferServerRequest( const QString &url, Method method, const QgsServerRequest::Headers &headers, QByteArray *data )
: QgsServerRequest( url, method, headers )
{
if ( data )
{
mData = *data;
}
}

QgsBufferServerRequest::QgsBufferServerRequest( const QUrl &url, Method method, QByteArray *data )
: QgsServerRequest( url, method )
QgsBufferServerRequest::QgsBufferServerRequest( const QUrl &url, Method method, const QgsServerRequest::Headers &headers, QByteArray *data )
: QgsServerRequest( url, method, headers )
{
if ( data )
{
Expand Down
4 changes: 2 additions & 2 deletions src/server/qgsbufferserverrequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ class SERVER_EXPORT QgsBufferServerRequest : public QgsServerRequest
* \param url the url string
* \param method the request method
*/
QgsBufferServerRequest( const QString &url, Method method = GetMethod, QByteArray *data = nullptr );
QgsBufferServerRequest( const QString &url, Method method = GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers( ), QByteArray *data = nullptr );

/**
* Constructor
*
* \param url QUrl
* \param method the request method
*/
QgsBufferServerRequest( const QUrl &url, Method method = GetMethod, QByteArray *data = nullptr );
QgsBufferServerRequest( const QUrl &url, Method method = GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers( ), QByteArray *data = nullptr );

~QgsBufferServerRequest();

Expand Down
4 changes: 2 additions & 2 deletions src/server/qgsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res
}
}

QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod, const char *data )
QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod, const QgsServerRequest::Headers &headers, const char *data )
{

QUrl url( urlstr );
Expand All @@ -446,7 +446,7 @@ QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString &urlstr, c
throw QgsServerException( QStringLiteral( "Invalid method in handleRequest(): only GET or POST is supported" ) );
}

QgsBufferServerRequest request( url, requestMethod, &ba );
QgsBufferServerRequest request( url, requestMethod, headers, &ba );
QgsBufferServerResponse response;

handleRequest( request, response );
Expand Down
3 changes: 2 additions & 1 deletion src/server/qgsserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ class SERVER_EXPORT QgsServer
* \param urlstr QString containing the request url (simple quely string must be preceded by '?')
* \param requestMethod QgsServerRequest::Method that indicates the method. Only "GET" or "POST" are supported.
* \param data array of bytes containing post data
* \param map of request headers
* \returns the response headers and body QPair of QByteArray
*/
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const char *data = nullptr );
QPair<QByteArray, QByteArray> handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const QgsServerRequest::Headers &headers = QgsServerRequest::Headers(), const char *data = nullptr );

//! Returns a pointer to the server interface
QgsServerInterfaceImpl *serverInterface() { return sServerInterface; }
Expand Down
38 changes: 20 additions & 18 deletions tests/src/python/qgis_wrapped_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
import urllib.parse
from http.server import BaseHTTPRequestHandler, HTTPServer
from qgis.core import QgsApplication
from qgis.server import QgsServer
from qgis.server import QgsServer, QgsServerRequest, QgsBufferServerRequest, QgsBufferServerResponse

QGIS_SERVER_PORT = int(os.environ.get('QGIS_SERVER_PORT', '8081'))
QGIS_SERVER_HOST = os.environ.get('QGIS_SERVER_HOST', '127.0.0.1')
Expand Down Expand Up @@ -86,47 +86,49 @@
class HTTPBasicFilter(QgsServerFilter):

def responseComplete(self):
request = self.serverInterface().requestHandler()
if self.serverInterface().getEnv('HTTP_AUTHORIZATION'):
username, password = base64.b64decode(self.serverInterface().getEnv('HTTP_AUTHORIZATION')[6:]).split(b':')
handler = self.serverInterface().requestHandler()
auth = self.serverInterface().requestHandler().requestHeader('HTTP_AUTHORIZATION')
if auth:
username, password = base64.b64decode(auth[6:]).split(b':')
if (username.decode('utf-8') == os.environ.get('QGIS_SERVER_USERNAME', 'username') and
password.decode('utf-8') == os.environ.get('QGIS_SERVER_PASSWORD', 'password')):
return
# No auth ...
request.clear()
request.setResponseHeader('Status', '401 Authorization required')
request.setResponseHeader('WWW-Authenticate', 'Basic realm="QGIS Server"')
request.appendBody(b'<h1>Authorization required</h1>')
handler.clear()
handler.setResponseHeader('Status', '401 Authorization required')
handler.setResponseHeader('WWW-Authenticate', 'Basic realm="QGIS Server"')
handler.appendBody(b'<h1>Authorization required</h1>')

filter = HTTPBasicFilter(qgs_server.serverInterface())
qgs_server.serverInterface().registerFilter(filter)


class Handler(BaseHTTPRequestHandler):

def do_GET(self):
def do_GET(self, post_body=None):
# CGI vars:
headers = {}
for k, v in self.headers.items():
qgs_server.putenv('HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper(), v)
headers, body = qgs_server.handleRequest(self.path)
headers_dict = dict(h.split(': ', 1) for h in headers.decode().split('\n') if h)
headers['HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper()] = v
request = QgsBufferServerRequest(self.path, (QgsServerRequest.PostMethod if post_body is not None else QgsServerRequest.GetMethod), headers, post_body)
response = QgsBufferServerResponse()
qgs_server.handleRequest(request, response)

headers_dict = response.headers()
try:
self.send_response(int(headers_dict['Status'].split(' ')[0]))
except:
self.send_response(200)
for k, v in headers_dict.items():
self.send_header(k, v)
self.end_headers()
self.wfile.write(body)
self.wfile.write(response.body())
return

def do_POST(self):
content_len = int(self.headers.get('content-length', 0))
post_body = self.rfile.read(content_len).decode()
request = post_body[1:post_body.find(' ')]
self.path = self.path + '&REQUEST_BODY=' + \
post_body.replace('&amp;', '') + '&REQUEST=' + request
return self.do_GET()
post_body = self.rfile.read(content_len)
return self.do_GET(post_body)


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion tests/src/python/test_authmanager_password_ows.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
From build dir, run from test directory:
LC_ALL=EN ctest -R PyQgsAuthManagerPasswordOWSTest -V
LC_ALL=en_US.UTF-8 ctest -R PyQgsAuthManagerPasswordOWSTest -V
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
2 changes: 1 addition & 1 deletion tests/src/python/test_qgsfiledownloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Test the QgsFileDownloader class
Run test with:
LC_ALL=EN ctest -V -R PyQgsFileDownloader
LC_ALL=en_US.UTF-8 ctest -V -R PyQgsFileDownloader
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
12 changes: 11 additions & 1 deletion tests/src/python/test_qgsserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import email

from io import StringIO
from qgis.server import QgsServer, QgsServerRequest
from qgis.server import QgsServer, QgsServerRequest, QgsBufferServerRequest, QgsBufferServerResponse
from qgis.core import QgsRenderChecker, QgsApplication
from qgis.testing import unittest
from qgis.PyQt.QtCore import QSize
Expand Down Expand Up @@ -198,6 +198,16 @@ def test_multiple_servers(self):
locals()["s%s" % i] = QgsServer()
locals()["s%s" % i].handleRequest("")

def test_requestHandler(self):
"""Test request handler"""
headers = {'header-key-1': 'header-value-1', 'header-key-2': 'header-value-2'}
request = QgsBufferServerRequest('http://somesite.com/somepath', QgsServerRequest.GetMethod, headers)
response = QgsBufferServerResponse()
self.server.handleRequest(request, response)
self.assertEqual(bytes(response.body()), b'<ServerException>Project file error</ServerException>\n')
self.assertEqual(response.headers(), {'Content-Length': '54', 'Content-Type': 'text/xml; charset=utf-8'})
self.assertEqual(response.statusCode(), 500)

def test_api(self):
"""Using an empty query string (returns an XML exception)
we are going to test if headers and body are returned correctly"""
Expand Down
3 changes: 0 additions & 3 deletions tests/src/python/test_qgsserver_wms.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,10 @@
import urllib.parse
import urllib.error

from qgis.core import QgsRenderChecker
from qgis.testing import unittest
from qgis.PyQt.QtCore import QSize

import osgeo.gdal # NOQA
import tempfile
import base64

from test_qgsserver import QgsServerTestBase

Expand Down

0 comments on commit 2afcad2

Please sign in to comment.