QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
20 #include "qgis.h"
21 #include "qgsfcgiserverrequest.h"
22 #include "qgsserverlogger.h"
23 #include "qgsmessagelog.h"
24 #include <fcgi_stdio.h>
25 #include <QDebug>
26 
28 {
29 
30  // Get the REQUEST_URI from the environment
31  QUrl url;
32  QString uri = getenv( "REQUEST_URI" );
33 
34  if ( uri.isEmpty() )
35  {
36  uri = getenv( "SCRIPT_NAME" );
37  }
38 
39  url.setUrl( uri );
40 
41  // Check if host is defined
42  if ( url.host().isEmpty() )
43  {
44  url.setHost( getenv( "SERVER_NAME" ) );
45  }
46 
47  // Port ?
48  if ( url.port( -1 ) == -1 )
49  {
50  QString portString = getenv( "SERVER_PORT" );
51  if ( !portString.isEmpty() )
52  {
53  bool portOk;
54  int portNumber = portString.toInt( &portOk );
55  if ( portOk && portNumber != 80 )
56  {
57  url.setPort( portNumber );
58  }
59  }
60  }
61 
62  // scheme
63  if ( url.scheme().isEmpty() )
64  {
65  QString( getenv( "HTTPS" ) ).compare( QLatin1String( "on" ), Qt::CaseInsensitive ) == 0
66  ? url.setScheme( QStringLiteral( "https" ) )
67  : url.setScheme( QStringLiteral( "http" ) );
68  }
69 
70  // Store the URL before the server rewrite that could have been set in QUERY_STRING
72 
73  // OGC parameters are passed with the query string, which is normally part of
74  // the REQUEST_URI, we override the query string url in case it is defined
75  // independently of REQUEST_URI
76  const char *qs = getenv( "QUERY_STRING" );
77  if ( qs )
78  {
79  url.setQuery( qs );
80  }
81 
82 #ifdef QGISDEBUG
83  qDebug() << "fcgi query string: " << url.query();
84 #endif
85 
87 
88  // Get method
89  const char *me = getenv( "REQUEST_METHOD" );
90 
91  if ( me )
92  {
93  if ( strcmp( me, "POST" ) == 0 )
94  {
96  }
97  else if ( strcmp( me, "PUT" ) == 0 )
98  {
99  method = PutMethod;
100  }
101  else if ( strcmp( me, "DELETE" ) == 0 )
102  {
104  }
105  else if ( strcmp( me, "HEAD" ) == 0 )
106  {
107  method = HeadMethod;
108  }
109  else if ( strcmp( me, "PATCH" ) == 0 )
110  {
112  }
113  }
114 
115  if ( method == PostMethod || method == PutMethod )
116  {
117  // Get post/put data
118  readData();
119  }
120 
121  setUrl( url );
122  setMethod( method );
123 
124  // Get accept header for content-type negotiation
125  const char *accept = getenv( "HTTP_ACCEPT" );
126  if ( accept )
127  {
128  setHeader( QStringLiteral( "Accept" ), accept );
129  }
130 
131  // Output debug infos
133  if ( logLevel <= Qgis::Info )
134  {
135  printRequestInfos( url );
136  }
137 }
138 
139 QByteArray QgsFcgiServerRequest::data() const
140 {
141  return mData;
142 }
143 
144 // Read post put data
145 void QgsFcgiServerRequest::readData()
146 {
147  // Check if we have CONTENT_LENGTH defined
148  const char *lengthstr = getenv( "CONTENT_LENGTH" );
149  if ( lengthstr )
150  {
151  bool success = false;
152  int length = QString( lengthstr ).toInt( &success );
153  // Note: REQUEST_BODY is not part of CGI standard, and it is not
154  // normally passed by any CGI web server and it is implemented only
155  // to allow unit tests to inject a request body and simulate a POST
156  // request
157  const char *request_body = getenv( "REQUEST_BODY" );
158  if ( success && request_body )
159  {
160  QString body( request_body );
161  body.truncate( length );
162  mData.append( body.toUtf8() );
163  length = 0;
164  }
165 #ifdef QGISDEBUG
166  qDebug() << "fcgi: reading " << lengthstr << " bytes from " << ( request_body ? "REQUEST_BODY" : "stdin" );
167 #endif
168  if ( success )
169  {
170  // XXX This not efficient at all !!
171  for ( int i = 0; i < length; ++i )
172  {
173  mData.append( getchar() );
174  }
175  }
176  else
177  {
178  QgsMessageLog::logMessage( "fcgi: Failed to parse CONTENT_LENGTH",
179  QStringLiteral( "Server" ), Qgis::Critical );
180  mHasError = true;
181  }
182  }
183  else
184  {
185  QgsMessageLog::logMessage( "fcgi: No POST data" );
186  }
187 }
188 
189 void QgsFcgiServerRequest::printRequestInfos( const QUrl &url )
190 {
191  QgsMessageLog::logMessage( QStringLiteral( "******************** New request ***************" ), QStringLiteral( "Server" ), Qgis::Info );
192 
193  const QStringList envVars
194  {
195  QStringLiteral( "SERVER_NAME" ),
196  QStringLiteral( "REQUEST_URI" ),
197  QStringLiteral( "SCRIPT_NAME" ),
198  QStringLiteral( "HTTPS" ),
199  QStringLiteral( "REMOTE_ADDR" ),
200  QStringLiteral( "REMOTE_HOST" ),
201  QStringLiteral( "SERVER_PORT" ),
202  QStringLiteral( "QUERY_STRING" ),
203  QStringLiteral( "REMOTE_USER" ),
204  QStringLiteral( "REMOTE_IDENT" ),
205  QStringLiteral( "CONTENT_TYPE" ),
206  QStringLiteral( "REQUEST_METHOD" ),
207  QStringLiteral( "AUTH_TYPE" ),
208  QStringLiteral( "HTTP_ACCEPT" ),
209  QStringLiteral( "HTTP_USER_AGENT" ),
210  QStringLiteral( "HTTP_PROXY" ),
211  QStringLiteral( "NO_PROXY" ),
212  QStringLiteral( "HTTP_AUTHORIZATION" ),
213  QStringLiteral( "QGIS_PROJECT_FILE" ),
214  QStringLiteral( "QGIS_SERVER_IGNORE_BAD_LAYERS" )
215  };
216 
217  QgsMessageLog::logMessage( QStringLiteral( "Request URL: %2" ).arg( url.url() ), QStringLiteral( "Server" ), Qgis::Info );
218  QgsMessageLog::logMessage( QStringLiteral( "Environment:" ), QStringLiteral( "Server" ), Qgis::Info );
219  QgsMessageLog::logMessage( QStringLiteral( "------------------------------------------------" ), QStringLiteral( "Server" ), Qgis::Info );
220 
221  for ( const auto &envVar : envVars )
222  {
223  if ( getenv( envVar.toStdString().c_str() ) )
224  {
225  QgsMessageLog::logMessage( QStringLiteral( "%1: %2" ).arg( envVar ).arg( QString( getenv( envVar.toStdString().c_str() ) ) ), QStringLiteral( "Server" ), Qgis::Info );
226  }
227  }
228 }
QgsServerRequest::GetMethod
@ GetMethod
Definition: qgsserverrequest.h:54
QgsServerRequest::setHeader
void setHeader(const QString &name, const QString &value)
Set an header.
Definition: qgsserverrequest.cpp:49
QgsServerRequest::setOriginalUrl
void setOriginalUrl(const QUrl &url)
Set the request original url (the request url as seen by the web server)
Definition: qgsserverrequest.cpp:75
qgis.h
QgsServerRequest::HeadMethod
@ HeadMethod
Definition: qgsserverrequest.h:52
QgsFcgiServerRequest::QgsFcgiServerRequest
QgsFcgiServerRequest()
Definition: qgsfcgiserverrequest.cpp:27
QgsServerRequest::setMethod
void setMethod(QgsServerRequest::Method method)
Set the request method.
Definition: qgsserverrequest.cpp:129
QgsServerLogger::instance
static QgsServerLogger * instance()
Gets the singleton instance.
Definition: qgsserverlogger.cpp:22
Qgis::Info
@ Info
Definition: qgis.h:90
QgsServerRequest::DeleteMethod
@ DeleteMethod
Definition: qgsserverrequest.h:56
QgsServerRequest::PostMethod
@ PostMethod
Definition: qgsserverrequest.h:55
QgsServerRequest::method
QgsServerRequest::Method method() const
Definition: qgsserverrequest.cpp:80
qgsserverlogger.h
Qgis::MessageLevel
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:89
QgsFcgiServerRequest::data
QByteArray data() const override
Returns post/put data Check for QByteArray::isNull() to check if data is available.
Definition: qgsfcgiserverrequest.cpp:139
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsServerLogger::logLevel
Qgis::MessageLevel logLevel() const
Gets the current log level.
Definition: qgsserverlogger.h:50
QgsServerRequest::url
QUrl url() const
Definition: qgsserverrequest.cpp:65
QgsServerRequest::Method
Method
HTTP Method (or equivalent) used for the request.
Definition: qgsserverrequest.h:51
Qgis::Critical
@ Critical
Definition: qgis.h:92
QgsServerRequest::PatchMethod
@ PatchMethod
Definition: qgsserverrequest.h:57
qgsfcgiserverrequest.h
QgsServerRequest::setUrl
void setUrl(const QUrl &url)
Set the request url.
Definition: qgsserverrequest.cpp:122
QgsServerRequest::PutMethod
@ PutMethod
Definition: qgsserverrequest.h:53
qgsmessagelog.h