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