QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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
71  setOriginalUrl( url );
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  {
95  method = PostMethod;
96  }
97  else if ( strcmp( me, "PUT" ) == 0 )
98  {
99  method = PutMethod;
100  }
101  else if ( strcmp( me, "DELETE" ) == 0 )
102  {
103  method = DeleteMethod;
104  }
105  else if ( strcmp( me, "HEAD" ) == 0 )
106  {
107  method = HeadMethod;
108  }
109  else if ( strcmp( me, "PATCH" ) == 0 )
110  {
111  method = PatchMethod;
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  };
215 
216  QgsMessageLog::logMessage( QStringLiteral( "Request URL: %2" ).arg( url.url() ), QStringLiteral( "Server" ), Qgis::Info );
217  QgsMessageLog::logMessage( QStringLiteral( "Environment:" ), QStringLiteral( "Server" ), Qgis::Info );
218  QgsMessageLog::logMessage( QStringLiteral( "------------------------------------------------" ), QStringLiteral( "Server" ), Qgis::Info );
219 
220  for ( const auto &envVar : envVars )
221  {
222  if ( getenv( envVar.toStdString().c_str() ) )
223  {
224  QgsMessageLog::logMessage( QStringLiteral( "%1: %2" ).arg( envVar ).arg( QString( getenv( envVar.toStdString().c_str() ) ) ), QStringLiteral( "Server" ), Qgis::Info );
225  }
226  }
227 }
void setMethod(QgsServerRequest::Method method)
Set the request method.
Qgis::MessageLevel logLevel() const
Gets the current log level.
void setUrl(const QUrl &url)
Set the request url.
Method
HTTP Method (or equivalent) used for the request.
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:88
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).
void setHeader(const QString &name, const QString &value)
Set an header.
static QgsServerLogger * instance()
Gets the singleton instance.
QByteArray data() const override
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerRequest::Method method() const
void setOriginalUrl(const QUrl &url)
Set the request original url (the request url as seen by the web server)