QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  }
110 
111  if ( method == PostMethod || method == PutMethod )
112  {
113  // Get post/put data
114  readData();
115  }
116 
117  setUrl( url );
118  setMethod( method );
119 
120  // Get accept header for content-type negotiation
121  const char *accept = getenv( "HTTP_ACCEPT" );
122  if ( accept )
123  {
124  setHeader( QStringLiteral( "Accept" ), accept );
125  }
126 
127  // Output debug infos
129  if ( logLevel <= Qgis::Info )
130  {
131  printRequestInfos( url );
132  }
133 }
134 
135 QByteArray QgsFcgiServerRequest::data() const
136 {
137  return mData;
138 }
139 
140 // Read post put data
141 void QgsFcgiServerRequest::readData()
142 {
143  // Check if we have CONTENT_LENGTH defined
144  const char *lengthstr = getenv( "CONTENT_LENGTH" );
145  if ( lengthstr )
146  {
147  bool success = false;
148  int length = QString( lengthstr ).toInt( &success );
149  // Note: REQUEST_BODY is not part of CGI standard, and it is not
150  // normally passed by any CGI web server and it is implemented only
151  // to allow unit tests to inject a request body and simulate a POST
152  // request
153  const char *request_body = getenv( "REQUEST_BODY" );
154  if ( success && request_body )
155  {
156  QString body( request_body );
157  body.truncate( length );
158  mData.append( body.toUtf8() );
159  length = 0;
160  }
161 #ifdef QGISDEBUG
162  qDebug() << "fcgi: reading " << lengthstr << " bytes from " << ( request_body ? "REQUEST_BODY" : "stdin" );
163 #endif
164  if ( success )
165  {
166  // XXX This not efficient at all !!
167  for ( int i = 0; i < length; ++i )
168  {
169  mData.append( getchar() );
170  }
171  }
172  else
173  {
174  QgsMessageLog::logMessage( "fcgi: Failed to parse CONTENT_LENGTH",
175  QStringLiteral( "Server" ), Qgis::Critical );
176  mHasError = true;
177  }
178  }
179  else
180  {
181  QgsMessageLog::logMessage( "fcgi: No POST data" );
182  }
183 }
184 
185 void QgsFcgiServerRequest::printRequestInfos( const QUrl &url )
186 {
187  QgsMessageLog::logMessage( QStringLiteral( "******************** New request ***************" ), QStringLiteral( "Server" ), Qgis::Info );
188 
189  const QStringList envVars
190  {
191  QStringLiteral( "SERVER_NAME" ),
192  QStringLiteral( "REQUEST_URI" ),
193  QStringLiteral( "SCRIPT_NAME" ),
194  QStringLiteral( "HTTPS" ),
195  QStringLiteral( "REMOTE_ADDR" ),
196  QStringLiteral( "REMOTE_HOST" ),
197  QStringLiteral( "SERVER_PORT" ),
198  QStringLiteral( "QUERY_STRING" ),
199  QStringLiteral( "REMOTE_USER" ),
200  QStringLiteral( "REMOTE_IDENT" ),
201  QStringLiteral( "CONTENT_TYPE" ),
202  QStringLiteral( "REQUEST_METHOD" ),
203  QStringLiteral( "AUTH_TYPE" ),
204  QStringLiteral( "HTTP_ACCEPT" ),
205  QStringLiteral( "HTTP_USER_AGENT" ),
206  QStringLiteral( "HTTP_PROXY" ),
207  QStringLiteral( "NO_PROXY" ),
208  QStringLiteral( "HTTP_AUTHORIZATION" ),
209  QStringLiteral( "QGIS_PROJECT_FILE" )
210  };
211 
212  QgsMessageLog::logMessage( QStringLiteral( "Request URL: %2" ).arg( url.url() ), QStringLiteral( "Server" ), Qgis::Info );
213  QgsMessageLog::logMessage( QStringLiteral( "Environment:" ), QStringLiteral( "Server" ), Qgis::Info );
214  QgsMessageLog::logMessage( QStringLiteral( "------------------------------------------------" ), QStringLiteral( "Server" ), Qgis::Info );
215 
216  for ( const auto &envVar : envVars )
217  {
218  if ( getenv( envVar.toStdString().c_str() ) )
219  {
220  QgsMessageLog::logMessage( QStringLiteral( "%1: %2" ).arg( envVar ).arg( QString( getenv( envVar.toStdString().c_str() ) ) ), QStringLiteral( "Server" ), Qgis::Info );
221  }
222  }
223 }
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:67
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)