Skip to content
Permalink
Browse files

Merge pull request #8830 from elpaso/bugfix-server-fcgi-rewritten-uri

[server] Use REQUEST_URI as default URL for FCGI requests
  • Loading branch information
elpaso committed Jan 11, 2019
1 parent 5c24b4e commit dedbbd4ad3c0c2c08cce8926f80790373ec18174
@@ -0,0 +1,57 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/server/qgsfcgiserverrequest.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsFcgiServerRequest: QgsServerRequest
{
%Docstring
Class defining fcgi request

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsfcgiserverrequest.h"
%End
public:
QgsFcgiServerRequest();

virtual QByteArray data() const;


bool hasError() const;
%Docstring
Returns true if an error occurred during initialization
%End

virtual QUrl url() const;

%Docstring

:return: the request url

Overrides base implementation because FCGI is typically behind
a proxy server and QGIS Server will see a rewritten QUERY_STRING.
FCGI implementation stores the REQUEST_URI (which is the URL seen
by the proxy before it gets rewritten) and returns it instead of
the rewritten one.
%End


};

/************************************************************************
* This file has been generated automatically from *
* *
* src/server/qgsfcgiserverrequest.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -56,10 +56,14 @@ Constructor

virtual ~QgsServerRequest();

QUrl url() const;
virtual QUrl url() const;
%Docstring

:return: the request url

Subclasses may override in case the original URL (not rewritten, e.g.from
a web server rewrite module) needs to be returned instead of the URL
seen by QGIS server.
%End

QgsServerRequest::Method method() const;
@@ -8,6 +8,7 @@
%Include auto_generated/qgsserverparameters.sip
%Include auto_generated/qgsbufferserverrequest.sip
%Include auto_generated/qgsbufferserverresponse.sip
%Include auto_generated/qgsfcgiserverrequest.sip
%Include auto_generated/qgsrequesthandler.sip
%Include auto_generated/qgsserver.sip
%Include auto_generated/qgsserverexception.sip
@@ -29,13 +29,11 @@

QgsFcgiServerRequest::QgsFcgiServerRequest()
{
mHasError = false;

// Rebuild the full URL

// Get the REQUEST_URI from the environment
QUrl url;
QString uri = getenv( "REQUEST_URI" );

if ( uri.isEmpty() )
{
uri = getenv( "SCRIPT_NAME" );
@@ -72,9 +70,12 @@ QgsFcgiServerRequest::QgsFcgiServerRequest()
: url.setScheme( QStringLiteral( "http" ) );
}

// XXX OGC paremetrs are passed with the query string
// we override the query string url in case it is
// defined independently of REQUEST_URI
// Store the URL before the server rewrite that could have been set in QUERY_STRING
mOriginalUrl = url;

// OGC parameters are passed with the query string, which is normally part of
// the REQUEST_URI, we override the query string url in case it is defined
// independently of REQUEST_URI
const char *qs = getenv( "QUERY_STRING" );
if ( qs )
{
@@ -132,6 +133,11 @@ QByteArray QgsFcgiServerRequest::data() const
return mData;
}

QUrl QgsFcgiServerRequest::url() const
{
return mOriginalUrl.isEmpty() ? QgsServerRequest::url() : mOriginalUrl;
}

// Read post put data
void QgsFcgiServerRequest::readData()
{
@@ -168,48 +174,28 @@ void QgsFcgiServerRequest::readData()
void QgsFcgiServerRequest::printRequestInfos()
{
QgsMessageLog::logMessage( QStringLiteral( "******************** New request ***************" ), QStringLiteral( "Server" ), Qgis::Info );
if ( getenv( "REMOTE_ADDR" ) )
{
QgsMessageLog::logMessage( "REMOTE_ADDR: " + QString( getenv( "REMOTE_ADDR" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "REMOTE_HOST" ) )
{
QgsMessageLog::logMessage( "REMOTE_HOST: " + QString( getenv( "REMOTE_HOST" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "REMOTE_USER" ) )
{
QgsMessageLog::logMessage( "REMOTE_USER: " + QString( getenv( "REMOTE_USER" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "REMOTE_IDENT" ) )
{
QgsMessageLog::logMessage( "REMOTE_IDENT: " + QString( getenv( "REMOTE_IDENT" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "CONTENT_TYPE" ) )
{
QgsMessageLog::logMessage( "CONTENT_TYPE: " + QString( getenv( "CONTENT_TYPE" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "AUTH_TYPE" ) )
{
QgsMessageLog::logMessage( "AUTH_TYPE: " + QString( getenv( "AUTH_TYPE" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "HTTP_USER_AGENT" ) )
{
QgsMessageLog::logMessage( "HTTP_USER_AGENT: " + QString( getenv( "HTTP_USER_AGENT" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "HTTP_PROXY" ) )
{
QgsMessageLog::logMessage( "HTTP_PROXY: " + QString( getenv( "HTTP_PROXY" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "HTTPS_PROXY" ) )
{
QgsMessageLog::logMessage( "HTTPS_PROXY: " + QString( getenv( "HTTPS_PROXY" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "NO_PROXY" ) )
{
QgsMessageLog::logMessage( "NO_PROXY: " + QString( getenv( "NO_PROXY" ) ), QStringLiteral( "Server" ), Qgis::Info );
}
if ( getenv( "HTTP_AUTHORIZATION" ) )
{
QgsMessageLog::logMessage( "HTTP_AUTHORIZATION: " + QString( getenv( "HTTP_AUTHORIZATION" ) ), QStringLiteral( "Server" ), Qgis::Info );

const QStringList envVars
{
QStringLiteral( "SERVER_NAME" ),
QStringLiteral( "REQUEST_URI" ),
QStringLiteral( "REMOTE_ADDR" ),
QStringLiteral( "REMOTE_HOST" ),
QStringLiteral( "REMOTE_USER" ),
QStringLiteral( "REMOTE_IDENT" ),
QStringLiteral( "CONTENT_TYPE" ),
QStringLiteral( "AUTH_TYPE" ),
QStringLiteral( "HTTP_USER_AGENT" ),
QStringLiteral( "HTTP_PROXY" ),
QStringLiteral( "NO_PROXY" ),
QStringLiteral( "HTTP_AUTHORIZATION" )
};

for ( const auto &envVar : envVars )
{
if ( getenv( envVar.toStdString().c_str() ) )
{
QgsMessageLog::logMessage( envVar + QString( getenv( envVar.toStdString().c_str() ) ), QStringLiteral( "Server" ), Qgis::Info );
}
}
}
@@ -19,8 +19,6 @@
#ifndef QGSFCGISERVERREQUEST_H
#define QGSFCGISERVERREQUEST_H

#define SIP_NO_FILE


#include "qgsserverrequest.h"

@@ -44,6 +42,18 @@ class SERVER_EXPORT QgsFcgiServerRequest: public QgsServerRequest
*/
bool hasError() const { return mHasError; }

/**
* \returns the request url
*
* Overrides base implementation because FCGI is typically behind
* a proxy server and QGIS Server will see a rewritten QUERY_STRING.
* FCGI implementation stores the REQUEST_URI (which is the URL seen
* by the proxy before it gets rewritten) and returns it instead of
* the rewritten one.
*/
QUrl url() const override;


private:
void readData();

@@ -53,7 +63,9 @@ class SERVER_EXPORT QgsFcgiServerRequest: public QgsServerRequest


QByteArray mData;
bool mHasError;
bool mHasError = false;
//! Url before the server rewrite
QUrl mOriginalUrl;
};

#endif
@@ -83,8 +83,12 @@ class SERVER_EXPORT QgsServerRequest

/**
* \returns the request url
*
* Subclasses may override in case the original URL (not rewritten, e.g.from
* a web server rewrite module) needs to be returned instead of the URL
* seen by QGIS server.
*/
QUrl url() const;
virtual QUrl url() const;

/**
* \returns the request method
@@ -10,6 +10,8 @@
"""
import unittest
import os
from urllib.parse import parse_qs, urlparse

__author__ = 'Alessandro Pasotti'
__date__ = '29/04/2017'
@@ -19,7 +21,7 @@


from qgis.PyQt.QtCore import QUrl
from qgis.server import QgsServerRequest
from qgis.server import QgsServerRequest, QgsFcgiServerRequest


class QgsServerRequestTest(unittest.TestCase):
@@ -66,6 +68,47 @@ def test_requestMethod(self):
request.setMethod(QgsServerRequest.PostMethod)
self.assertEqual(request.method(), QgsServerRequest.PostMethod)

def test_fcgiRequest(self):
"""Test various combinations of FCGI env parameters with rewritten urls"""

def _test_url(url, env={}):
for k in ('QUERY_STRING', 'REQUEST_URI', 'SERVER_NAME', 'SERVER_PORT', 'SCRIPT_NAME'):
try:
del os.environ[k]
except KeyError:
pass
try:
os.environ[k] = env[k]
except KeyError:
pass
request = QgsFcgiServerRequest()
self.assertEqual(request.url().toString(), url)
# Check MAP
if 'QUERY_STRING' in env:
map = {k.upper(): v[0] for k, v in parse_qs(env['QUERY_STRING']).items()}['MAP']
else:
map = {k.upper(): v[0] for k, v in parse_qs(urlparse(env['REQUEST_URI']).query).items()}['MAP']
self.assertEqual(request.parameter('MAP'), map)

_test_url('http://somesite.com/somepath/index.html?map=/my/path.qgs', {
'REQUEST_URI': '/somepath/index.html?map=/my/path.qgs',
'SERVER_NAME': 'somesite.com',
})
_test_url('http://somesite.com/somepath?map=/my/path.qgs', {
'REQUEST_URI': '/somepath?map=/my/path.qgs',
'SERVER_NAME': 'somesite.com',
})
_test_url('http://somesite.com/somepath/path', {
'REQUEST_URI': '/somepath/path',
'SERVER_NAME': 'somesite.com',
'QUERY_STRING': 'map=/my/path.qgs'
})
_test_url('http://somesite.com/somepath/path/?token=QGIS2019', {
'REQUEST_URI': '/somepath/path/?token=QGIS2019',
'SERVER_NAME': 'somesite.com',
'QUERY_STRING': 'map=/my/path.qgs',
})


if __name__ == '__main__':
unittest.main()

1 comment on commit dedbbd4

@andreassteffens

This comment has been minimized.

Copy link

@andreassteffens andreassteffens commented on dedbbd4 Jan 19, 2019

Unfortunately this seems to break the server in cases where a POST is used to transmit the query parameters. In this case QgsServerRequest analyzes the posted parameters and appends them to the original url so the server services can build QgsWmsParameters, QgsWfsParameters etc. from the query string. With the overriden url() getter in QgsFcgiServerRequest the original url is returned to the services which in a POST scenario has no query resulting in the services (at least in their current implementation) to not be able to get the posted parameters.

Please sign in to comment.
You can’t perform that action at this time.