QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsrequesthandler.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgshttprequesthandler.cpp
3  -------------------------
4  begin : June 29, 2007
5  copyright : (C) 2007 by Marco Hugentobler
6  (C) 2014 by Alessandro Pasotti
7  email : marco dot hugentobler at karto dot baug dot ethz dot ch
8  a dot pasotti at itopen dot it
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 "qgsrequesthandler.h"
22 #include "qgsmessagelog.h"
23 #include "qgsserverrequest.h"
24 #include "qgsserverresponse.h"
25 #include <QByteArray>
26 #include <QDomDocument>
27 #include <QUrl>
28 #include <QUrlQuery>
29 
31  : mExceptionRaised( false )
32  , mRequest( request )
33  , mResponse( response )
34 {
35 }
36 
37 QMap<QString, QString> QgsRequestHandler::parameterMap() const
38 {
39  return mRequest.parameters();
40 }
41 
43 {
44  return mExceptionRaised;
45 }
46 
47 void QgsRequestHandler::setResponseHeader( const QString &name, const QString &value )
48 {
49  mResponse.setHeader( name, value );
50 }
51 
53 {
54  mResponse.clear();
55 }
56 
57 void QgsRequestHandler::removeResponseHeader( const QString &name )
58 {
59  mResponse.removeHeader( name );
60 }
61 
62 QString QgsRequestHandler::responseHeader( const QString &name ) const
63 {
64  return mResponse.header( name );
65 }
66 
67 QMap<QString, QString> QgsRequestHandler::responseHeaders() const
68 {
69  return mResponse.headers();
70 }
71 
72 void QgsRequestHandler::setRequestHeader( const QString &name, const QString &value )
73 {
74  mRequest.setHeader( name, value );
75 }
76 
77 void QgsRequestHandler::removeRequestHeader( const QString &name )
78 {
79  mRequest.removeHeader( name );
80 }
81 
82 QString QgsRequestHandler::requestHeader( const QString &name ) const
83 {
84  return mRequest.header( name );
85 }
86 
87 
88 QMap<QString, QString> QgsRequestHandler::requestHeaders() const
89 {
90  return mRequest.headers();
91 }
92 
93 
95 {
96  return mResponse.headersSent();
97 }
98 
99 void QgsRequestHandler::appendBody( const QByteArray &body )
100 {
101  mResponse.write( body );
102 }
103 
105 {
106  mResponse.truncate();
107 }
108 
109 QByteArray QgsRequestHandler::body() const
110 {
111  return mResponse.data();
112 }
113 
114 QByteArray QgsRequestHandler::data() const
115 {
116  return mRequest.data();
117 }
118 
119 QString QgsRequestHandler::url() const
120 {
121  return mRequest.url().toString();
122 }
123 
124 QString QgsRequestHandler::path() const
125 {
126  return mRequest.url().path();
127 }
128 
130 {
131  mResponse.setStatusCode( code );
132 }
133 
135 {
136  return mResponse.statusCode();
137 }
138 
140 {
141  // Send data to output
142  mResponse.flush();
143 }
144 
146 {
147  // Safety measure to avoid potential leaks if called repeatedly
148  mExceptionRaised = true;
149  mResponse.write( ex );
150 }
151 
152 void QgsRequestHandler::setupParameters()
153 {
154  const QgsServerRequest::Parameters parameters = mRequest.parameters();
155 
156  //feature info format?
157  const QString infoFormat = parameters.value( QStringLiteral( "INFO_FORMAT" ) );
158  if ( !infoFormat.isEmpty() )
159  {
160  mFormat = infoFormat;
161  }
162  else //capabilities format or GetMap format
163  {
164  mFormatString = parameters.value( QStringLiteral( "FORMAT" ) );
165  QString formatString = mFormatString;
166  if ( !formatString.isEmpty() )
167  {
168  //remove the image/ in front of the format
169  if ( formatString.contains( QLatin1String( "image/png" ), Qt::CaseInsensitive ) || formatString.compare( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 )
170  {
171  formatString = QStringLiteral( "PNG" );
172  }
173  else if ( formatString.contains( QLatin1String( "image/jpeg" ), Qt::CaseInsensitive ) || formatString.contains( QLatin1String( "image/jpg" ), Qt::CaseInsensitive )
174  || formatString.compare( QLatin1String( "jpg" ), Qt::CaseInsensitive ) == 0 )
175  {
176  formatString = QStringLiteral( "JPG" );
177  }
178  else if ( formatString.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
179  {
180  formatString = QStringLiteral( "SVG" );
181  }
182  else if ( formatString.contains( QLatin1String( "pdf" ), Qt::CaseInsensitive ) )
183  {
184  formatString = QStringLiteral( "PDF" );
185  }
186 
187  mFormat = formatString;
188  }
189  }
190 
191 }
192 
194 {
195  if ( mRequest.method() == QgsServerRequest::PostMethod ||
196  mRequest.method() == QgsServerRequest::PutMethod ||
197  mRequest.method() == QgsServerRequest::PatchMethod )
198  {
199  if ( mRequest.header( QStringLiteral( "Content-Type" ) ).contains( QStringLiteral( "json" ) ) )
200  {
201  setupParameters();
202  }
203  else
204  {
205  QString inputString( mRequest.data() );
206  QDomDocument doc;
207  QString errorMsg;
208  int line = -1;
209  int column = -1;
210  if ( !doc.setContent( inputString, true, &errorMsg, &line, &column ) )
211  {
212  // XXX Output error but continue processing request ?
213  QgsMessageLog::logMessage( QStringLiteral( "Warning: error parsing post data as XML: at line %1, column %2: %3. Assuming urlencoded query string sent in the post body." )
214  .arg( line ).arg( column ).arg( errorMsg ) );
215 
216  // Process input string as a simple query text
217 
218  typedef QPair<QString, QString> pair_t;
219  const QUrlQuery query( inputString );
220  const QList<pair_t> items = query.queryItems();
221  for ( const pair_t &pair : items )
222  {
223  mRequest.setParameter( pair.first, pair.second );
224  }
225  setupParameters();
226  }
227  else
228  {
229  // we have an XML document
230 
231  setupParameters();
232 
233  const QDomElement docElem = doc.documentElement();
234  // the document element tag name is the request
235  mRequest.setParameter( QStringLiteral( "REQUEST" ), docElem.tagName() );
236  // loop through the attributes which are the parameters
237  // excepting the attributes started by xmlns or xsi
238  const QDomNamedNodeMap map = docElem.attributes();
239  for ( int i = 0 ; i < map.length() ; ++i )
240  {
241  if ( map.item( i ).isNull() )
242  continue;
243 
244  const QDomNode attrNode = map.item( i );
245  const QDomAttr attr = attrNode.toAttr();
246  if ( attr.isNull() )
247  continue;
248 
249  const QString attrName = attr.name();
250  if ( attrName.startsWith( "xmlns" ) || attrName.startsWith( "xsi:" ) )
251  continue;
252 
253  mRequest.setParameter( attrName.toUpper(), attr.value() );
254  }
255  mRequest.setParameter( QStringLiteral( "REQUEST_BODY" ), inputString.replace( '+', QLatin1String( "%2B" ) ) );
256  }
257  }
258  }
259  else
260  {
261  setupParameters();
262  }
263 }
264 
265 void QgsRequestHandler::setParameter( const QString &key, const QString &value )
266 {
267  if ( !( key.isEmpty() || value.isEmpty() ) )
268  {
269  // Warn for potential breaking change if plugin set the MAP parameter
270  // expecting changing the config file path, see PR #9773
271  if ( key.compare( QLatin1String( "MAP" ), Qt::CaseInsensitive ) == 0 )
272  {
273  QgsMessageLog::logMessage( QStringLiteral( "Changing the 'MAP' parameter will have no effect on config path: use QgsSerververInterface::setConfigFilePath instead" ),
274  "Server", Qgis::MessageLevel::Warning );
275  }
276  mRequest.setParameter( key, value );
277  }
278 }
279 
280 
281 QString QgsRequestHandler::parameter( const QString &key ) const
282 {
283  return mRequest.parameter( key );
284 }
285 
286 void QgsRequestHandler::removeParameter( const QString &key )
287 {
288  mRequest.removeParameter( key );
289 }
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
void removeParameter(const QString &key)
Remove a request parameter.
void removeRequestHeader(const QString &name)
Remove an HTTP request header.
QString responseHeader(const QString &name) const
Retrieve response header value.
void clearBody()
Clear response buffer.
QString requestHeader(const QString &name) const
Retrieve request header value.
QMap< QString, QString > parameterMap() const
Returns the parsed parameters as a key-value pair, to modify a parameter setParameter( const QString ...
QString path() const
Returns the path component of the request URL.
QByteArray data() const
Returns the request POST data (can be null)
bool exceptionRaised() const
Pointer to last raised exception.
bool headersSent() const
Returns true if the HTTP headers were already sent to the client.
void parseInput()
Parses the input and creates a request neutral Parameter/Value map.
void setServiceException(const QgsServerException &ex)
Allow plugins to return a QgsMapServiceException.
void removeResponseHeader(const QString &name)
Remove an HTTP response header.
void sendResponse()
Send out HTTP headers and flush output buffer.
QMap< QString, QString > responseHeaders() const
Returns the response headers.
void setRequestHeader(const QString &name, const QString &value)
Sets an HTTP request header.
void appendBody(const QByteArray &body)
Sets the info format string such as "text/xml".
QString parameter(const QString &key) const
Returns a request parameter.
QMap< QString, QString > requestHeaders() const
Returns the the Request headers.
QString url() const
Returns the request url.
void setStatusCode(int code)
Sets response http status code.
QgsRequestHandler(QgsServerRequest &request, QgsServerResponse &response)
Constructor.
QByteArray body() const
Returns the response body data.
void clear()
Clears the response body and headers.
void setResponseHeader(const QString &name, const QString &value)
Sets an HTTP response header.
int statusCode() const
Returns the response http status code.
void setParameter(const QString &key, const QString &value)
Sets a request parameter.
Exception base class for server exceptions.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
virtual QString header(const QString &name) const
Returns the header value.
QString parameter(const QString &key, const QString &defaultValue=QString()) const
Gets a parameter value.
virtual void setParameter(const QString &key, const QString &value)
Set a parameter.
QMap< QString, QString > headers() const
Returns the header map.
QMap< QString, QString > Parameters
QgsServerRequest::Method method() const
void removeHeader(const QString &name)
Remove an header.
virtual void removeParameter(const QString &key)
Remove a parameter.
void setHeader(const QString &name, const QString &value)
Set an header.
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void removeHeader(const QString &key)=0
Clear header Undo a previous 'setHeader' call.
virtual void flush() SIP_THROW(QgsServerException)
Flushes the current output buffer to the network.
virtual int statusCode() const =0
Returns the http status code.
virtual void truncate()=0
Truncate data.
virtual QByteArray data() const =0
Gets the data written so far.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
virtual void clear()=0
Reset all headers and content for this response.
virtual QMap< QString, QString > headers() const =0
Returns the header value.
virtual bool headersSent() const =0
Returns true if the headers have already been sent.
virtual QString header(const QString &key) const =0
Returns the header value.
virtual void setStatusCode(int code)=0
Set the http status code.