[server] Python plugins API cleanup part 2

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.
This commit is contained in:
Alessandro Pasotti 2017-04-30 18:31:33 +02:00
parent b7d6c1e59e
commit 2afcad279d
11 changed files with 47 additions and 39 deletions

View File

@ -21,7 +21,7 @@ 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
@ -29,7 +29,7 @@ class QgsBufferServerRequest : QgsServerRequest
\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
@ -40,9 +40,6 @@ class QgsBufferServerRequest : QgsServerRequest
~QgsBufferServerRequest();
virtual QByteArray data() const;
%Docstring
:rtype: QByteArray
%End
};

View File

@ -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();

View File

@ -23,8 +23,8 @@
#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 )
{
@ -32,8 +32,8 @@ QgsBufferServerRequest::QgsBufferServerRequest( const QString &url, Method metho
}
}
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 )
{

View File

@ -41,7 +41,7 @@ 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
@ -49,7 +49,7 @@ class SERVER_EXPORT QgsBufferServerRequest : public QgsServerRequest
* \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();

View File

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

View File

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

View File

@ -55,7 +55,7 @@ import ssl
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')
@ -86,17 +86,18 @@ if os.environ.get('QGIS_SERVER_HTTP_BASIC_AUTH') is not None:
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)
@ -104,12 +105,16 @@ if os.environ.get('QGIS_SERVER_HTTP_BASIC_AUTH') is not None:
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:
@ -117,16 +122,13 @@ class Handler(BaseHTTPRequestHandler):
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__':

View File

@ -9,7 +9,7 @@ configuration to access an HTTP Basic protected endpoint.
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

View File

@ -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

View File

@ -38,7 +38,7 @@ import urllib.error
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
@ -198,6 +198,16 @@ class TestQgsServer(QgsServerTestBase):
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"""

View File

@ -26,13 +26,10 @@ import urllib.request
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