QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  // Output debug infos
122  if ( logLevel <= Qgis::Info )
123  {
124  printRequestInfos();
125  }
126 }
127 
128 QByteArray QgsFcgiServerRequest::data() const
129 {
130  return mData;
131 }
132 
133 // Read post put data
134 void QgsFcgiServerRequest::readData()
135 {
136  // Check if we have CONTENT_LENGTH defined
137  const char *lengthstr = getenv( "CONTENT_LENGTH" );
138  if ( lengthstr )
139  {
140  bool success = false;
141  int length = QString( lengthstr ).toInt( &success );
142  // Note: REQUEST_BODY is not part of CGI standard, and it is not
143  // normally passed by any CGI web server and it is implemented only
144  // to allow unit tests to inject a request body and simulate a POST
145  // request
146  const char *request_body = getenv( "REQUEST_BODY" );
147  if ( success && request_body )
148  {
149  QString body( request_body );
150  body.truncate( length );
151  mData.append( body.toUtf8() );
152  length = 0;
153  }
154 #ifdef QGISDEBUG
155  qDebug() << "fcgi: reading " << lengthstr << " bytes from " << ( request_body ? "REQUEST_BODY" : "stdin" );
156 #endif
157  if ( success )
158  {
159  // XXX This not efficient at all !!
160  for ( int i = 0; i < length; ++i )
161  {
162  mData.append( getchar() );
163  }
164  }
165  else
166  {
167  QgsMessageLog::logMessage( "fcgi: Failed to parse CONTENT_LENGTH",
168  QStringLiteral( "Server" ), Qgis::Critical );
169  mHasError = true;
170  }
171  }
172  else
173  {
174  QgsMessageLog::logMessage( "fcgi: No POST data" );
175  }
176 }
177 
178 void QgsFcgiServerRequest::printRequestInfos()
179 {
180  QgsMessageLog::logMessage( QStringLiteral( "******************** New request ***************" ), QStringLiteral( "Server" ), Qgis::Info );
181 
182  const QStringList envVars
183  {
184  QStringLiteral( "SERVER_NAME" ),
185  QStringLiteral( "REQUEST_URI" ),
186  QStringLiteral( "REMOTE_ADDR" ),
187  QStringLiteral( "REMOTE_HOST" ),
188  QStringLiteral( "REMOTE_USER" ),
189  QStringLiteral( "REMOTE_IDENT" ),
190  QStringLiteral( "CONTENT_TYPE" ),
191  QStringLiteral( "AUTH_TYPE" ),
192  QStringLiteral( "HTTP_USER_AGENT" ),
193  QStringLiteral( "HTTP_PROXY" ),
194  QStringLiteral( "NO_PROXY" ),
195  QStringLiteral( "HTTP_AUTHORIZATION" )
196  };
197 
198  for ( const auto &envVar : envVars )
199  {
200  if ( getenv( envVar.toStdString().c_str() ) )
201  {
202  QgsMessageLog::logMessage( QStringLiteral( "%1: %2" ).arg( envVar ).arg( QString( getenv( envVar.toStdString().c_str() ) ) ), QStringLiteral( "Server" ), Qgis::Info );
203  }
204  }
205 }
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:66
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).
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)