From 2afcad279d79ee336a83954af4c315ebccb8ff66 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 30 Apr 2017 18:31:33 +0200 Subject: [PATCH] [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. --- python/server/qgsbufferserverrequest.sip | 7 +--- python/server/qgsserver.sip | 3 +- src/server/qgsbufferserverrequest.cpp | 8 ++-- src/server/qgsbufferserverrequest.h | 4 +- src/server/qgsserver.cpp | 4 +- src/server/qgsserver.h | 3 +- tests/src/python/qgis_wrapped_server.py | 38 ++++++++++--------- .../python/test_authmanager_password_ows.py | 2 +- tests/src/python/test_qgsfiledownloader.py | 2 +- tests/src/python/test_qgsserver.py | 12 +++++- tests/src/python/test_qgsserver_wms.py | 3 -- 11 files changed, 47 insertions(+), 39 deletions(-) diff --git a/python/server/qgsbufferserverrequest.sip b/python/server/qgsbufferserverrequest.sip index 707fc290867..48fa1c724e8 100644 --- a/python/server/qgsbufferserverrequest.sip +++ b/python/server/qgsbufferserverrequest.sip @@ -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 }; diff --git a/python/server/qgsserver.sip b/python/server/qgsserver.sip index 2bb8e382bb1..92e972fbdeb 100644 --- a/python/server/qgsserver.sip +++ b/python/server/qgsserver.sip @@ -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 handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const char *data = 0 ); + QPair 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(); diff --git a/src/server/qgsbufferserverrequest.cpp b/src/server/qgsbufferserverrequest.cpp index ffaa6edc91c..9d49a66b52f 100644 --- a/src/server/qgsbufferserverrequest.cpp +++ b/src/server/qgsbufferserverrequest.cpp @@ -23,8 +23,8 @@ #include -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 ) { diff --git a/src/server/qgsbufferserverrequest.h b/src/server/qgsbufferserverrequest.h index 171b48b1854..9a569521bac 100644 --- a/src/server/qgsbufferserverrequest.h +++ b/src/server/qgsbufferserverrequest.h @@ -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(); diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index bcd84457b87..9cf13b6b5c9 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -427,7 +427,7 @@ void QgsServer::handleRequest( QgsServerRequest &request, QgsServerResponse &res } } -QPair QgsServer::handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod, const char *data ) +QPair QgsServer::handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod, const QgsServerRequest::Headers &headers, const char *data ) { QUrl url( urlstr ); @@ -446,7 +446,7 @@ QPair 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 ); diff --git a/src/server/qgsserver.h b/src/server/qgsserver.h index f7a626cd954..0ca1e112917 100644 --- a/src/server/qgsserver.h +++ b/src/server/qgsserver.h @@ -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 handleRequest( const QString &urlstr, const QgsServerRequest::Method requestMethod = QgsServerRequest::GetMethod, const char *data = nullptr ); + QPair 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; } diff --git a/tests/src/python/qgis_wrapped_server.py b/tests/src/python/qgis_wrapped_server.py index dd708b0b638..3b5ad7e16bd 100644 --- a/tests/src/python/qgis_wrapped_server.py +++ b/tests/src/python/qgis_wrapped_server.py @@ -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'

Authorization required

') + handler.clear() + handler.setResponseHeader('Status', '401 Authorization required') + handler.setResponseHeader('WWW-Authenticate', 'Basic realm="QGIS Server"') + handler.appendBody(b'

Authorization required

') 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('&', '') + '&REQUEST=' + request - return self.do_GET() + post_body = self.rfile.read(content_len) + return self.do_GET(post_body) if __name__ == '__main__': diff --git a/tests/src/python/test_authmanager_password_ows.py b/tests/src/python/test_authmanager_password_ows.py index 652b11d9ac3..a33be159b2b 100644 --- a/tests/src/python/test_authmanager_password_ows.py +++ b/tests/src/python/test_authmanager_password_ows.py @@ -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 diff --git a/tests/src/python/test_qgsfiledownloader.py b/tests/src/python/test_qgsfiledownloader.py index 9522ba8574c..7ff71d09ada 100644 --- a/tests/src/python/test_qgsfiledownloader.py +++ b/tests/src/python/test_qgsfiledownloader.py @@ -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 diff --git a/tests/src/python/test_qgsserver.py b/tests/src/python/test_qgsserver.py index 77eca0b7633..bd20dc7d123 100644 --- a/tests/src/python/test_qgsserver.py +++ b/tests/src/python/test_qgsserver.py @@ -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'Project file error\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""" diff --git a/tests/src/python/test_qgsserver_wms.py b/tests/src/python/test_qgsserver_wms.py index a71485f3d66..0c4979c841f 100644 --- a/tests/src/python/test_qgsserver_wms.py +++ b/tests/src/python/test_qgsserver_wms.py @@ -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