QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsfcgiserverrequest.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfcgiserverrequest.cpp
3
4 Define response wrapper for fcgi request
5 -------------------
6 begin : 2017-01-03
7 copyright : (C) 2017 by David Marteau
8 email : david dot marteau at 3liz dot com
9 ***************************************************************************/
10
11/***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
21
22#include "qgis.h"
23#include "qgsmessagelog.h"
24#include "qgsserverlogger.h"
25#include "qgsstringutils.h"
26
27#include <QDebug>
28
29#include <fcgi_stdio.h>
30
32{
33 // Get the REQUEST_URI from the environment
34 QString uri = getenv( "REQUEST_URI" );
35
36 if ( uri.isEmpty() )
37 {
38 uri = getenv( "SCRIPT_NAME" );
39 }
40
41 QUrl url;
42 url.setUrl( uri );
43 fillUrl( url );
44 // Store the URL before the server rewrite that could have been set in QUERY_STRING
46
47 const QString qs = getenv( "QUERY_STRING" );
48 const QString questionMark = qs.isEmpty() ? QString() : QChar( '?' );
49 const QString extraPath = QStringLiteral( "%1%2%3" ).arg( getenv( "PATH_INFO" ) ).arg( questionMark ).arg( qs );
50
51 QUrl baseUrl;
52 if ( uri.endsWith( extraPath ) )
53 {
54 baseUrl.setUrl( uri.left( uri.length() - extraPath.length() ) );
55 }
56 else
57 {
58 baseUrl.setUrl( uri );
59 }
60 fillUrl( baseUrl );
61 setBaseUrl( url );
62
63 // OGC parameters are passed with the query string, which is normally part of
64 // the REQUEST_URI, we override the query string url in case it is defined
65 // independently of REQUEST_URI
66 if ( !qs.isEmpty() )
67 {
68 url.setQuery( qs );
69 }
70
71#ifdef QGISDEBUG
72 qDebug() << "fcgi query string: " << url.query();
73#endif
74
76
77 // Get method
78 const char *me = getenv( "REQUEST_METHOD" );
79
80 if ( me )
81 {
82 if ( strcmp( me, "POST" ) == 0 )
83 {
85 }
86 else if ( strcmp( me, "PUT" ) == 0 )
87 {
89 }
90 else if ( strcmp( me, "DELETE" ) == 0 )
91 {
93 }
94 else if ( strcmp( me, "HEAD" ) == 0 )
95 {
97 }
98 else if ( strcmp( me, "PATCH" ) == 0 )
99 {
101 }
102 }
103
104 if ( method == PostMethod || method == PutMethod || method == PatchMethod )
105 {
106 // Get post/put/patch data
107 readData();
108 }
109
110 setUrl( url );
111 setMethod( method );
112
113 // Fill the headers dictionary
114 for ( const auto &headerKey : qgsEnumMap<QgsServerRequest::RequestHeader>() )
115 {
116 const QString headerName = QgsStringUtils::capitalize(
117 QString( headerKey ).replace( QLatin1Char( '_' ), QLatin1Char( ' ' ) ), Qgis::Capitalization::TitleCase
118 )
119 .replace( QLatin1Char( ' ' ), QLatin1Char( '-' ) );
120 const char *result = getenv( QStringLiteral( "HTTP_%1" ).arg( headerKey ).toStdString().c_str() );
121 if ( result && strlen( result ) > 0 )
122 {
123 setHeader( headerName, result );
124 }
125 }
126
127 // Output debug infos
129 if ( logLevel <= Qgis::MessageLevel::Info )
130 {
131 printRequestInfos( url );
132 }
133}
134
135void QgsFcgiServerRequest::fillUrl( QUrl &url ) const
136{
137 // Check if host is defined
138 if ( url.host().isEmpty() )
139 {
140 url.setHost( getenv( "SERVER_NAME" ) );
141 }
142
143 // Port ?
144 if ( url.port( -1 ) == -1 )
145 {
146 const QString portString = getenv( "SERVER_PORT" );
147 if ( !portString.isEmpty() )
148 {
149 bool portOk;
150 const int portNumber = portString.toInt( &portOk );
151 if ( portOk && portNumber != 80 )
152 {
153 url.setPort( portNumber );
154 }
155 }
156 }
157
158 // scheme
159 if ( url.scheme().isEmpty() )
160 {
161 QString( getenv( "HTTPS" ) ).compare( QLatin1String( "on" ), Qt::CaseInsensitive ) == 0
162 ? url.setScheme( QStringLiteral( "https" ) )
163 : url.setScheme( QStringLiteral( "http" ) );
164 }
165}
166
168{
169 return mData;
170}
171
172// Read post put data
173void QgsFcgiServerRequest::readData()
174{
175 // Check if we have CONTENT_LENGTH defined
176 const char *lengthstr = getenv( "CONTENT_LENGTH" );
177 if ( lengthstr )
178 {
179 bool success = false;
180 const int length = QString( lengthstr ).toInt( &success );
181 if ( !success || length < 0 )
182 {
183 QgsMessageLog::logMessage( "fcgi: Invalid CONTENT_LENGTH", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
184 mHasError = true;
185 }
186 else
187 {
188 // Note: REQUEST_BODY is not part of CGI standard, and it is not
189 // normally passed by any CGI web server and it is implemented only
190 // to allow unit tests to inject a request body and simulate a POST
191 // request
192 const char *requestBody = getenv( "REQUEST_BODY" );
193
194#ifdef QGISDEBUG
195 qDebug() << "fcgi: reading " << lengthstr << " bytes from " << ( requestBody ? "REQUEST_BODY" : "stdin" );
196#endif
197
198 if ( requestBody )
199 {
200 const size_t requestBodyLength = strlen( requestBody );
201 const int actualLength = static_cast<int>( std::min<size_t>( length, requestBodyLength ) );
202 if ( static_cast<size_t>( actualLength ) < requestBodyLength )
203 {
204 QgsMessageLog::logMessage( "fcgi: CONTENT_LENGTH is larger than actual length of REQUEST_BODY", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
205 mHasError = true;
206 }
207 mData = QByteArray::fromRawData( requestBody, actualLength );
208 }
209 else
210 {
211 mData.resize( length );
212 const int actualLength = static_cast<int>( fread( mData.data(), 1, length, stdin ) );
213 if ( actualLength < length )
214 {
215 mData.resize( actualLength );
216 QgsMessageLog::logMessage( "fcgi: CONTENT_LENGTH is larger than actual length of stdin", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
217 mHasError = true;
218 }
219 }
220 }
221 }
222 else
223 {
224 QgsMessageLog::logMessage( "fcgi: No POST/PUT/PATCH data" );
225 }
226}
227
228void QgsFcgiServerRequest::printRequestInfos( const QUrl &url ) const
229{
230 QgsMessageLog::logMessage( QStringLiteral( "******************** New request ***************" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
231
232 const QStringList envVars {
233 QStringLiteral( "SERVER_NAME" ),
234 QStringLiteral( "REQUEST_URI" ),
235 QStringLiteral( "SCRIPT_NAME" ),
236 QStringLiteral( "PATH_INFO" ),
237 QStringLiteral( "HTTPS" ),
238 QStringLiteral( "REMOTE_ADDR" ),
239 QStringLiteral( "REMOTE_HOST" ),
240 QStringLiteral( "SERVER_PORT" ),
241 QStringLiteral( "QUERY_STRING" ),
242 QStringLiteral( "REMOTE_USER" ),
243 QStringLiteral( "REMOTE_IDENT" ),
244 QStringLiteral( "CONTENT_TYPE" ),
245 QStringLiteral( "REQUEST_METHOD" ),
246 QStringLiteral( "AUTH_TYPE" ),
247 QStringLiteral( "HTTP_PROXY" ),
248 QStringLiteral( "NO_PROXY" ),
249 QStringLiteral( "QGIS_PROJECT_FILE" ),
250 QStringLiteral( "QGIS_SERVER_IGNORE_BAD_LAYERS" ),
251 QStringLiteral( "QGIS_SERVER_SERVICE_URL" ),
252 QStringLiteral( "QGIS_SERVER_WMS_SERVICE_URL" ),
253 QStringLiteral( "QGIS_SERVER_WFS_SERVICE_URL" ),
254 QStringLiteral( "QGIS_SERVER_WMTS_SERVICE_URL" ),
255 QStringLiteral( "QGIS_SERVER_WCS_SERVICE_URL" ),
256 QStringLiteral( "SERVER_PROTOCOL" )
257 };
258
259 QgsMessageLog::logMessage( QStringLiteral( "Request URL: %2" ).arg( url.url() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
260
261 QgsMessageLog::logMessage( QStringLiteral( "Environment:" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
262 QgsMessageLog::logMessage( QStringLiteral( "------------------------------------------------" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
263 for ( const auto &envVar : envVars )
264 {
265 if ( getenv( envVar.toStdString().c_str() ) )
266 {
267 QgsMessageLog::logMessage( QStringLiteral( "%1: %2" ).arg( envVar ).arg( QString( getenv( envVar.toStdString().c_str() ) ) ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
268 }
269 }
270
271 qDebug() << "Headers:";
272 qDebug() << "------------------------------------------------";
273 const QMap<QString, QString> &hdrs = headers();
274 for ( auto it = hdrs.constBegin(); it != hdrs.constEnd(); it++ )
275 {
276 qDebug() << it.key() << ": " << it.value();
277 }
278}
279
280QString QgsFcgiServerRequest::header( const QString &name ) const
281{
282 // Get from internal dictionary
283 QString result = QgsServerRequest::header( name );
284
285 // Or from standard environment variable
286 // https://tools.ietf.org/html/rfc3875#section-4.1.18
287 if ( result.isEmpty() )
288 {
289 result = qgetenv( QStringLiteral( "HTTP_%1" ).arg( name.toUpper().replace( QLatin1Char( '-' ), QLatin1Char( '_' ) ) ).toStdString().c_str() );
290 }
291 return result;
292}
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition qgis.h:156
@ Critical
Critical/error message.
Definition qgis.h:159
@ Info
Information message.
Definition qgis.h:157
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
Definition qgis.h:3395
QString header(const QString &name) const override
Returns the header value.
QByteArray data() const override
Returns post/put data Check for QByteArray::isNull() to check if data is available.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QgsServerLogger * instance()
Gets the singleton instance.
Qgis::MessageLevel logLevel() const
Gets the current log level.
void setOriginalUrl(const QUrl &url)
Set the request original url (the request url as seen by the web server).
Method
HTTP Method (or equivalent) used for the request.
virtual QString header(const QString &name) const
Returns the header value.
virtual void setUrl(const QUrl &url)
Set the request url.
QUrl url() const
Returns the request URL as seen by QGIS server.
QMap< QString, QString > headers() const
Returns the header map.
QUrl baseUrl() const
Returns the base URL of QGIS server.
QgsServerRequest::Method method() const
Returns the request method.
void setMethod(QgsServerRequest::Method method)
Set the request method.
void setBaseUrl(const QUrl &url)
Set the base URL of QGIS server.
void setHeader(const QString &name, const QString &value)
Set an header.
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
const QMap< T, QString > qgsEnumMap()
Returns a map of all enum entries.
Definition qgis.h:6781