QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsserver.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsserver.cpp
3 A server application supporting WMS / WFS / WCS
4 -------------------
5 begin : July 04, 2006
6 copyright : (C) 2006 by Marco Hugentobler & Ionut Iosifescu Enescu
7 : (C) 2015 by Alessandro Pasotti
8 email : marco dot hugentobler at karto dot baug dot ethz dot ch
9 : elpaso at itopen dot it
10 ***************************************************************************/
11
12/***************************************************************************
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 ***************************************************************************/
20
21//for CMAKE_INSTALL_PREFIX
22#include "qgsconfig.h"
23#include "qgsserver.h"
24
25#include "qgsapplication.h"
26#include "qgsauthmanager.h"
30#include "qgsfontutils.h"
31#include "qgslogger.h"
34#include "qgsnetworkdiskcache.h"
35#include "qgsproject.h"
36#include "qgsproviderregistry.h"
37#include "qgsrequesthandler.h"
38#include "qgsruntimeprofiler.h"
39#include "qgsserverapi.h"
40#include "qgsserverapicontext.h"
41#include "qgsserverlogger.h"
42#include "qgsserverparameters.h"
43#include "qgsserverrequest.h"
44#include "qgsservice.h"
45#include "qgsversion.h"
46
47#include <QDomDocument>
48#include <QElapsedTimer>
49#include <QNetworkDiskCache>
50#include <QSettings>
51
52// TODO: remove, it's only needed by a single debug message
53#include <fcgi_stdio.h>
54#include <cstdlib>
55
56
57// Server status static initializers.
58// Default values are for C++, SIP bindings will override their
59// options in in init()
60
61QString *QgsServer::sConfigFilePath = nullptr;
62QgsCapabilitiesCache *QgsServer::sCapabilitiesCache = nullptr;
63QgsServerInterfaceImpl *QgsServer::sServerInterface = nullptr;
64// Initialization must run once for all servers
65bool QgsServer::sInitialized = false;
66
67QgsServiceRegistry *QgsServer::sServiceRegistry = nullptr;
68
70
72{
73 // QgsApplication must exist
74 if ( !qobject_cast<QgsApplication *>( qApp ) )
75 {
76 qFatal( "A QgsApplication must exist before a QgsServer instance can be created." );
77 abort();
78 }
79 init();
80 mConfigCache = QgsConfigCache::instance();
81}
82
83QFileInfo QgsServer::defaultAdminSLD()
84{
85 return QFileInfo( QStringLiteral( "admin.sld" ) );
86}
87
88void QgsServer::setupNetworkAccessManager()
89{
90 const QSettings settings;
91 QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
92 QNetworkDiskCache *cache = new QNetworkDiskCache( nullptr );
93 const QString cacheDirectory = sSettings()->cacheDirectory();
94 cache->setCacheDirectory( cacheDirectory );
95 qint64 cacheSize = sSettings()->cacheSize();
96 cache->setMaximumCacheSize( cacheSize );
97 QgsMessageLog::logMessage( QStringLiteral( "cacheDirectory: %1" ).arg( cache->cacheDirectory() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
98 QgsMessageLog::logMessage( QStringLiteral( "maximumCacheSize: %1" ).arg( cache->maximumCacheSize() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
99 nam->setCache( cache );
100}
101
102QFileInfo QgsServer::defaultProjectFile()
103{
104 const QDir currentDir;
105 fprintf( FCGI_stderr, "current directory: %s\n", currentDir.absolutePath().toUtf8().constData() );
106 QStringList nameFilterList;
107 nameFilterList << QStringLiteral( "*.qgs" )
108 << QStringLiteral( "*.qgz" );
109 const QFileInfoList projectFiles = currentDir.entryInfoList( nameFilterList, QDir::Files, QDir::Name );
110 for ( int x = 0; x < projectFiles.size(); x++ )
111 {
112 QgsMessageLog::logMessage( projectFiles.at( x ).absoluteFilePath(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
113 }
114 if ( projectFiles.isEmpty() )
115 {
116 return QFileInfo();
117 }
118 return projectFiles.at( 0 );
119}
120
121void QgsServer::printRequestParameters( const QMap<QString, QString> &parameterMap, Qgis::MessageLevel logLevel )
122{
123 if ( logLevel > Qgis::MessageLevel::Info )
124 {
125 return;
126 }
127
128 QMap<QString, QString>::const_iterator pIt = parameterMap.constBegin();
129 for ( ; pIt != parameterMap.constEnd(); ++pIt )
130 {
131 QgsMessageLog::logMessage( pIt.key() + ":" + pIt.value(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
132 }
133}
134
135QString QgsServer::configPath( const QString &defaultConfigPath, const QString &configPath )
136{
137 QString cfPath( defaultConfigPath );
138 const QString projectFile = sSettings()->projectFile();
139 if ( !projectFile.isEmpty() )
140 {
141 cfPath = projectFile;
142 QgsDebugMsgLevel( QStringLiteral( "QGIS_PROJECT_FILE:%1" ).arg( cfPath ), 2 );
143 }
144 else
145 {
146 if ( configPath.isEmpty() )
147 {
148 // Read it from the environment, because a rewrite rule may have rewritten it
149 if ( getenv( "QGIS_PROJECT_FILE" ) )
150 {
151 cfPath = getenv( "QGIS_PROJECT_FILE" );
152 QgsMessageLog::logMessage( QStringLiteral( "Using configuration file path from environment: %1" ).arg( cfPath ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
153 }
154 else if ( !defaultConfigPath.isEmpty() )
155 {
156 QgsMessageLog::logMessage( QStringLiteral( "Using default configuration file path: %1" ).arg( defaultConfigPath ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
157 }
158 }
159 else
160 {
161 cfPath = configPath;
162 QgsDebugMsgLevel( QStringLiteral( "MAP:%1" ).arg( cfPath ), 2 );
163 }
164 }
165 return cfPath;
166}
167
168void QgsServer::initLocale()
169{
170 // System locale override
171 if ( !sSettings()->overrideSystemLocale().isEmpty() )
172 {
173 QLocale::setDefault( QLocale( sSettings()->overrideSystemLocale() ) );
174 }
175 // Number group separator settings
176 QLocale currentLocale;
177 if ( sSettings()->showGroupSeparator() )
178 {
179 currentLocale.setNumberOptions( currentLocale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
180 }
181 else
182 {
183 currentLocale.setNumberOptions( currentLocale.numberOptions() |= QLocale::NumberOption::OmitGroupSeparator );
184 }
185 QLocale::setDefault( currentLocale );
186}
187
188bool QgsServer::init()
189{
190 if ( sInitialized )
191 {
192 return false;
193 }
194
195 QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
196 QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
197 QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
198
199 // TODO: remove QGIS_OPTIONS_PATH from settings and rely on QgsApplication's env var QGIS_CUSTOM_CONFIG_PATH
200 // Note that QGIS_CUSTOM_CONFIG_PATH gives /tmp/qt_temp-rUpsId/profiles/default/QGIS/QGIS3.ini
201 // while QGIS_OPTIONS_PATH gives /tmp/qt_temp-rUpsId/QGIS/QGIS3.ini
202 QgsApplication::init( qgetenv( "QGIS_OPTIONS_PATH" ) );
203
204#if defined( SERVER_SKIP_ECW )
205 QgsMessageLog::logMessage( "Skipping GDAL ECW drivers in server.", "Server", Qgis::MessageLevel::Info );
208#endif
209
210 // reload settings to take into account QCoreApplication and QgsApplication
211 // configuration
212 sSettings()->load();
213
214 // init and configure logger
216 QgsServerLogger::instance()->setLogLevel( sSettings()->logLevel() );
217 if ( !sSettings()->logFile().isEmpty() )
218 {
219 QgsServerLogger::instance()->setLogFile( sSettings()->logFile() );
220 }
221 else if ( sSettings()->logStderr() )
222 {
224 }
225
226 // Logging handlers for CRS grid issues
227 QgsCoordinateTransform::setCustomMissingRequiredGridHandler( []( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::GridDetails &grid ) {
228 QgsServerLogger::instance()->logMessage( QStringLiteral( "Cannot use project transform between %1 and %2 - missing grid %3" ).arg( sourceCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::ShortString ), destinationCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::ShortString ), grid.shortName ), QStringLiteral( "QGIS Server" ), Qgis::MessageLevel::Warning );
229 } );
230
231
232 QgsCoordinateTransform::setCustomMissingGridUsedByContextHandler( []( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &details ) {
233 QString gridMessage;
234 for ( const QgsDatumTransform::GridDetails &grid : details.grids )
235 {
236 if ( !grid.isAvailable )
237 {
238 gridMessage.append( QStringLiteral( "This transformation requires the grid file '%1', which is not available for use on the system.\n" ).arg( grid.shortName ) );
239 }
240 }
241 QgsServerLogger::instance()->logMessage( QStringLiteral( "Cannot use project transform between %1 and %2 - %3.\n%4" ).arg( sourceCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::ShortString ), destinationCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::ShortString ), details.name, gridMessage ), QStringLiteral( "QGIS Server" ), Qgis::MessageLevel::Warning );
242 } );
243
244
245 QgsCoordinateTransform::setCustomMissingPreferredGridHandler( []( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &preferredOperation, const QgsDatumTransform::TransformDetails &availableOperation ) {
246 QString gridMessage;
247 for ( const QgsDatumTransform::GridDetails &grid : preferredOperation.grids )
248 {
249 if ( !grid.isAvailable )
250 {
251 gridMessage.append( QStringLiteral( "This transformation requires the grid file '%1', which is not available for use on the system.\n" ).arg( grid.shortName ) );
252 if ( !grid.url.isEmpty() )
253 {
254 if ( !grid.packageName.isEmpty() )
255 {
256 gridMessage.append( QStringLiteral( "This grid is part of the '%1' package, available for download from %2.\n" ).arg( grid.packageName, grid.url ) );
257 }
258 else
259 {
260 gridMessage.append( QStringLiteral( "This grid is available for download from %1.\n" ).arg( grid.url ) );
261 }
262 }
263 }
264 }
265
266 QString accuracyMessage;
267 if ( availableOperation.accuracy >= 0 && preferredOperation.accuracy >= 0 )
268 accuracyMessage = QStringLiteral( "Current transform '%1' has an accuracy of %2 meters, while the preferred transformation '%3' has accuracy %4 meters.\n" ).arg( availableOperation.name ).arg( availableOperation.accuracy ).arg( preferredOperation.name ).arg( preferredOperation.accuracy );
269 else if ( preferredOperation.accuracy >= 0 )
270 accuracyMessage = QStringLiteral( "Current transform '%1' has an unknown accuracy, while the preferred transformation '%2' has accuracy %3 meters.\n" )
271 .arg( availableOperation.name, preferredOperation.name )
272 .arg( preferredOperation.accuracy );
273
274 const QString longMessage = QStringLiteral( "The preferred transform between '%1' and '%2' is not available for use on the system.\n" ).arg( sourceCrs.userFriendlyIdentifier(), destinationCrs.userFriendlyIdentifier() )
275 + gridMessage + accuracyMessage;
276
277 QgsServerLogger::instance()->logMessage( longMessage, QStringLiteral( "QGIS Server" ), Qgis::MessageLevel::Warning );
278 } );
279
280 QgsCoordinateTransform::setCustomCoordinateOperationCreationErrorHandler( []( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &error ) {
281 const QString longMessage = QStringLiteral( "No transform is available between %1 and %2: %3" )
282 .arg( sourceCrs.userFriendlyIdentifier(), destinationCrs.userFriendlyIdentifier(), error );
283 QgsServerLogger::instance()->logMessage( longMessage, QStringLiteral( "QGIS Server" ), Qgis::MessageLevel::Warning );
284 } );
285
286 // Configure locale
287 initLocale();
288
289 QgsMessageLog::logMessage( QStringLiteral( "QGIS Server Starting : %1 (%2)" ).arg( _QGIS_VERSION, QGSVERSION ), "Server", Qgis::MessageLevel::Info );
290
291 // log settings currently used
292 sSettings()->logSummary();
293
294 setupNetworkAccessManager();
295 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
296
297 // Instantiate the plugin directory so that providers are loaded
299 QgsMessageLog::logMessage( "Prefix PATH: " + QgsApplication::prefixPath(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
300 QgsMessageLog::logMessage( "Plugin PATH: " + QgsApplication::pluginPath(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
301 QgsMessageLog::logMessage( "PkgData PATH: " + QgsApplication::pkgDataPath(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
302 QgsMessageLog::logMessage( "User DB PATH: " + QgsApplication::qgisUserDatabaseFilePath(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
303 QgsMessageLog::logMessage( "Auth DB URI: " + QgsApplication::qgisAuthDatabaseUri(), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
304 QgsMessageLog::logMessage( "SVG PATHS: " + QgsApplication::svgPaths().join( QDir::listSeparator() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
305
306 QgsApplication::createDatabase(); //init qgis.db (e.g. necessary for user crs)
307
308 // Sets up the authentication system
309 // creates or uses qgis-auth.db in ~/.qgis3/ or directory defined by QGIS_AUTH_DB_DIR_PATH env variable
310 // or QGIS_AUTH_DB_URI env variable.
311 // set the master password as first line of file defined by QGIS_AUTH_PASSWORD_FILE env variable
312 // (QGIS_AUTH_PASSWORD_FILE variable removed from environment after accessing)
314
315 QString defaultConfigFilePath;
316 const QFileInfo projectFileInfo = defaultProjectFile(); //try to find a .qgs/.qgz file in the server directory
317 if ( projectFileInfo.exists() )
318 {
319 defaultConfigFilePath = projectFileInfo.absoluteFilePath();
320 QgsMessageLog::logMessage( "Using default project file: " + defaultConfigFilePath, QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
321 }
322 else
323 {
324 const QFileInfo adminSLDFileInfo = defaultAdminSLD();
325 if ( adminSLDFileInfo.exists() )
326 {
327 defaultConfigFilePath = adminSLDFileInfo.absoluteFilePath();
328 }
329 }
330 // Store the config file path
331 sConfigFilePath = new QString( defaultConfigFilePath );
332
333 //create cache for capabilities XML
334 sCapabilitiesCache = new QgsCapabilitiesCache( sSettings()->capabilitiesCacheSize() );
335
336 QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Roman" ) << QStringLiteral( "Bold" ) );
337
338 sServiceRegistry = new QgsServiceRegistry();
339
340 sServerInterface = new QgsServerInterfaceImpl( sCapabilitiesCache, sServiceRegistry, sSettings() );
341
342 // Load service module
343 const QString modulePath = QgsApplication::libexecPath() + "server";
344 // qDebug() << QStringLiteral( "Initializing server modules from: %1" ).arg( modulePath );
345 sServiceRegistry->init( modulePath, sServerInterface );
346
347 // Initialize config cache
348 QgsConfigCache::initialize( sSettings );
349
351
352 sInitialized = true;
353 QgsMessageLog::logMessage( QStringLiteral( "Server initialized" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
354 return true;
355}
356
357
358void QgsServer::putenv( const QString &var, const QString &val )
359{
360 if ( val.isEmpty() )
361 {
362 qunsetenv( var.toUtf8().data() );
363 }
364 else
365 {
366 qputenv( var.toUtf8().data(), val.toUtf8() );
367 }
368 sSettings()->load( var );
369}
370
372{
374 {
375 const QgsScopedRuntimeProfile profiler { QStringLiteral( "handleRequest" ), QStringLiteral( "server" ) };
376
377 qApp->processEvents();
378
379 response.clear();
380
381 // Clean up qgis access control filter's cache to prevent side effects
382 // across requests
383#ifdef HAVE_SERVER_PYTHON_PLUGINS
384 QgsAccessControl *accessControls = sServerInterface->accessControls();
385 if ( accessControls )
386 {
387 accessControls->unresolveFilterFeatures();
388 }
389#endif
390
391 // Pass the filters to the requestHandler, this is needed for the following reasons:
392 // Allow server request to call sendResponse plugin hook if enabled
393 QgsFilterResponseDecorator responseDecorator( sServerInterface->filters(), response );
394
395 //Request handler
396 QgsRequestHandler requestHandler( request, response );
397
398 if ( !response.feedback()
399 || !response.feedback()->isCanceled() ) // to avoid to much log when request is canceled
400 {
401 try
402 {
403 // TODO: split parse input into plain parse and processing from specific services
404 requestHandler.parseInput();
405 }
406 catch ( QgsMapServiceException &e )
407 {
408 QgsMessageLog::logMessage( "Parse input exception: " + e.message(), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
409 requestHandler.setServiceException( e );
410 }
411 }
412
413 // Set the request handler into the interface for plugins to manipulate it
414 sServerInterface->setRequestHandler( &requestHandler );
415
416 // Initialize configfilepath so that is is available
417 // before calling plugin methods
418 // Note that plugins may still change that value using
419 // setConfigFilePath() interface method
420 if ( !project )
421 {
422 const QString configFilePath = configPath( *sConfigFilePath, request.serverParameters().map() );
423 sServerInterface->setConfigFilePath( configFilePath );
424 }
425 else
426 {
427 sServerInterface->setConfigFilePath( project->fileName() );
428 }
429
430 // Call requestReady() method (if enabled)
431 // This may also throw exceptions if there are errors in python plugins code
432 try
433 {
434 responseDecorator.start();
435 }
436 catch ( QgsException &ex )
437 {
438 // Internal server error
439 response.sendError( 500, QStringLiteral( "Internal Server Error" ) );
440 QgsMessageLog::logMessage( ex.what(), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
441 }
442
443 // Plugins may have set exceptions
444 if ( !requestHandler.exceptionRaised() && ( !response.feedback() || !response.feedback()->isCanceled() ) )
445 {
446 try
447 {
448 const QgsServerParameters params = request.serverParameters();
449 printRequestParameters( params.toMap(), logLevel );
450
451 // Setup project (config file path)
452 if ( !project )
453 {
454 const QString configFilePath = configPath( *sConfigFilePath, params.map() );
455
456 // load the project if needed and not empty
457 if ( !configFilePath.isEmpty() )
458 {
459 // Note that QgsConfigCache::project( ... ) call QgsProject::setInstance(...)
460 project = mConfigCache->project( configFilePath, sServerInterface->serverSettings() );
461 }
462 }
463
464 // Set the current project instance
465 QgsProject::setInstance( const_cast<QgsProject *>( project ) );
466
467 if ( project )
468 {
469 sServerInterface->setConfigFilePath( project->fileName() );
470 }
471 else
472 {
473 sServerInterface->setConfigFilePath( QString() );
474 }
475
476 // Call projectReady() method (if enabled)
477 // This may also throw exceptions if there are errors in python plugins code
478 responseDecorator.ready();
479
480 // Note that at this point we still might not have set a valid project.
481 // There are APIs that work without a project (e.g. the landing page catalog API that
482 // lists the available projects metadata).
483
484 // Dispatcher: if SERVICE is set, we assume a OWS service, if not, let's try an API
485 // TODO: QGIS 4 fix the OWS services and treat them as APIs
486 if ( !response.feedback() || !response.feedback()->isCanceled() )
487 {
488 if ( params.service().isEmpty() )
489 {
490 QgsServerApi *api = sServiceRegistry->apiForRequest( request );
491 if ( api )
492 {
493 const QgsServerApiContext context { api->rootPath(), &request, &responseDecorator, project, sServerInterface };
494 api->executeRequest( context );
495 }
496 else
497 {
499 QStringLiteral( "Service configuration error" ),
500 QStringLiteral( "Service unknown or unsupported. Current supported services "
501 "(case-sensitive): WMS WFS WCS WMTS SampleService, or use a WFS3 "
502 "(OGC API Features) endpoint" )
503 );
504 }
505 }
506 else
507 {
508 // Project is mandatory for OWS at this point
509 if ( !project )
510 {
511 throw QgsServerException(
512 QStringLiteral( "Project file error. For OWS services: please provide a SERVICE "
513 "and a MAP parameter pointing to a valid QGIS project file" )
514 );
515 }
516
517 if ( !params.fileName().isEmpty() )
518 {
519 const QString value = QString( "attachment; filename=\"%1\"" ).arg( params.fileName() );
520 requestHandler.setResponseHeader( QStringLiteral( "Content-Disposition" ), value );
521 }
522
523 // Lookup for service
524 QgsService *service = sServiceRegistry->getService( params.service(), params.version() );
525 if ( service )
526 {
527 service->executeRequest( request, responseDecorator, project );
528 }
529 else
530 {
532 QStringLiteral( "Service configuration error" ),
533 QStringLiteral( "Service unknown or unsupported. Current supported services "
534 "(case-sensitive): WMS WFS WCS WMTS SampleService, or use a WFS3 "
535 "(OGC API Features) endpoint" )
536 );
537 }
538 }
539 }
540 }
541 catch ( QgsServerException &ex )
542 {
543 responseDecorator.write( ex );
544 QString format;
545 QgsMessageLog::logMessage( ex.formatResponse( format ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
546 }
547 catch ( QgsException &ex )
548 {
549 // Internal server error
550 response.sendError( 500, QStringLiteral( "Internal Server Error" ) );
551 QgsMessageLog::logMessage( ex.what(), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
552 }
553 }
554
555 // Terminate the response
556 // This may also throw exceptions if there are errors in python plugins code
557 try
558 {
559 responseDecorator.finish();
560 }
561 catch ( QgsException &ex )
562 {
563 // Internal server error
564 response.sendError( 500, QStringLiteral( "Internal Server Error" ) );
565 QgsMessageLog::logMessage( ex.what(), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
566 }
567
568 // We are done using requestHandler in plugins, make sure we don't access
569 // to a deleted request handler from Python bindings
570 sServerInterface->clearRequestHandler();
571 }
572
573 if ( logLevel == Qgis::MessageLevel::Info )
574 {
575 QgsMessageLog::logMessage( "Request finished in " + QString::number( QgsApplication::profiler()->profileTime( QStringLiteral( "handleRequest" ), QStringLiteral( "server" ) ) * 1000.0 ) + " ms", QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
576 if ( sSettings->logProfile() )
577 {
578 std::function<void( const QModelIndex &, int )> profileFormatter;
579 profileFormatter = [&profileFormatter]( const QModelIndex &idx, int level ) {
580 QgsMessageLog::logMessage( QStringLiteral( "Profile: %1%2, %3 : %4 ms" ).arg( level > 0 ? QString().fill( '-', level ) + ' ' : QString() ).arg( QgsApplication::profiler()->data( idx, static_cast<int>( QgsRuntimeProfilerNode::CustomRole::Group ) ).toString() ).arg( QgsApplication::profiler()->data( idx, static_cast<int>( QgsRuntimeProfilerNode::CustomRole::Name ) ).toString() ).arg( QString::number( QgsApplication::profiler()->data( idx, static_cast<int>( QgsRuntimeProfilerNode::CustomRole::Elapsed ) ).toDouble() * 1000.0 ) ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
581
582 for ( int subRow = 0; subRow < QgsApplication::profiler()->rowCount( idx ); subRow++ )
583 {
584 const auto subIdx { QgsApplication::profiler()->index( subRow, 0, idx ) };
585 profileFormatter( subIdx, level + 1 );
586 }
587 };
588
589 for ( int row = 0; row < QgsApplication::profiler()->rowCount(); row++ )
590 {
591 const auto idx { QgsApplication::profiler()->index( row, 0 ) };
592 profileFormatter( idx, 0 );
593 }
594 }
595 }
596
597
598 // Clear the profiler content after each request
599 QgsApplication::profiler()->clear( QStringLiteral( "startup" ) );
600 QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
601 QgsApplication::profiler()->clear( QStringLiteral( "rendering" ) );
602 QgsApplication::profiler()->clear( QStringLiteral( "server" ) );
603}
604
605
606#ifdef HAVE_SERVER_PYTHON_PLUGINS
607void QgsServer::initPython()
608{
609 // Init plugins
610 if ( !QgsServerPlugins::initPlugins( sServerInterface ) )
611 {
612 QgsMessageLog::logMessage( QStringLiteral( "No server python plugins are available" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
613 }
614 else
615 {
616 QgsMessageLog::logMessage( QStringLiteral( "Server python plugins loaded" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
617 }
618}
619#endif
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
Definition qgis.h:2418
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition qgis.h:156
@ Warning
Warning message.
Definition qgis.h:158
@ Critical
Critical/error message.
Definition qgis.h:159
@ Info
Information message.
Definition qgis.h:157
A helper class that centralizes restrictions given by all the access control filter plugins.
void unresolveFilterFeatures()
Clear expression's cache computed from resolveFilterFeatures.
static void skipGdalDriver(const QString &driver)
Sets the GDAL_SKIP environment variable to include the specified driver and then calls GDALDriverMana...
static QString pluginPath()
Returns the path to the application plugin directory.
static bool createDatabase(QString *errorMessage=nullptr)
initialize qgis.db
static void init(QString profileFolder=QString())
This method initializes paths etc for QGIS.
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QString libexecPath()
Returns the path with utility executables (help viewer, crssync, ...).
static QStringList svgPaths()
Returns the paths to svg directories.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
static const char * QGIS_APPLICATION_NAME
static const char * QGIS_ORGANIZATION_DOMAIN
static QString prefixPath()
Returns the path to the application prefix directory.
static const char * QGIS_ORGANIZATION_NAME
static QString qgisAuthDatabaseUri()
Returns the URI to the user authentication database.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
void setup(const QString &pluginPath=QString(), const QString &authDatabasePath=QString())
Sets up the authentication manager configuration.
A cache for capabilities xml documents (by configuration file path).
void removeCapabilitiesDocument(const QString &path)
Removes capabilities document.
static QgsConfigCache * instance()
Returns the current instance.
static void initialize(QgsServerSettings *settings)
Initialize from settings.
void projectRemovedFromCache(const QString &path)
Emitted whenever a project is removed from the cache.
QString userFriendlyIdentifier(Qgis::CrsIdentifierType type=Qgis::CrsIdentifierType::MediumString) const
Returns a user friendly identifier for the CRS.
static void setCustomMissingRequiredGridHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::GridDetails &grid)> &handler)
Sets a custom handler to use when a coordinate transform is created between sourceCrs and destination...
static void setCustomCoordinateOperationCreationErrorHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &error)> &handler)
Sets a custom handler to use when a coordinate transform was required between sourceCrs and destinati...
static void setCustomMissingPreferredGridHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &preferredOperation, const QgsDatumTransform::TransformDetails &availableOperation)> &handler)
Sets a custom handler to use when a coordinate transform is created between sourceCrs and destination...
static void setCustomMissingGridUsedByContextHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &desiredOperation)> &handler)
Sets a custom handler to use when a coordinate operation was specified for use between sourceCrs and ...
Defines a QGIS exception class.
QString what() const
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
A decorator for calling filter's hooks.
void start()
Call filters requestReady() method.
void finish() override
Finish the response, ending the transaction.
void ready()
Call filters projectReady() method.
static bool loadStandardTestFonts(const QStringList &loadstyles)
Loads standard test fonts from filesystem or qrc resource.
Exception class for WMS service exceptions (for compatibility only).
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Exception base class for service exceptions.
QString message() const
Returns the exception message.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
QString fileName
Definition qgsproject.h:113
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
An interface hiding the details of reading input and writing output from/to a wms request mechanism.
bool exceptionRaised() const
Pointer to last raised exception.
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 setResponseHeader(const QString &name, const QString &value)
Sets an HTTP response header.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void clear(const QString &group="startup")
clear Clear all profile data.
Scoped object for logging of the runtime for a single operation or group of operations.
Encapsulates the resources for a particular client request.
Server generic API endpoint abstract base class.
virtual void executeRequest(const QgsServerApiContext &context) const =0
Executes a request by passing the given context to the API handlers.
virtual const QString rootPath() const =0
Returns the root path for the API.
Exception base class for server exceptions.
virtual QByteArray formatResponse(QString &responseFormat) const
Formats the exception for sending to client.
Interfaces exposed by QGIS Server and made available to plugins.
static QgsServerLogger * instance()
Gets the singleton instance.
Qgis::MessageLevel logLevel() const
Gets the current log level.
void setLogLevel(Qgis::MessageLevel level)
Set the current log level.
void setLogFile(const QString &filename=QString())
Set the current log file.
void setLogStderr()
Activates logging to stderr.
void logMessage(const QString &message, const QString &tag, Qgis::MessageLevel level) override
Log a message from the server context.
Provides an interface to retrieve and manipulate global parameters received from the client.
QMap< QString, QString > toMap() const
Returns all parameters in a map.
QString map() const
Returns MAP parameter as a string or an empty string if not defined.
QString service() const
Returns SERVICE parameter as a string or an empty string if not defined.
QString fileName() const
Returns FILE_NAME parameter as a string or an empty string if not defined.
virtual QString version() const
Returns VERSION parameter as a string or an empty string if not defined.
static bool initPlugins(QgsServerInterface *interface)
Initializes the Python plugins.
Defines requests passed to QgsService classes.
QgsServerParameters serverParameters() const
Returns parameters.
Defines the response interface passed to QgsService.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual QgsFeedback * feedback() const
Returns the socket feedback if any.
virtual void clear()=0
Reset all headers and content for this response.
virtual void sendError(int code, const QString &message)=0
Send error This method delegates error handling at the server level.
Provides a way to retrieve settings by prioritizing according to environment variables,...
QgsServer()
Creates the server instance.
Definition qgsserver.cpp:71
void handleRequest(QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project=nullptr)
Handles the request.
void putenv(const QString &var, const QString &val)
Set environment variable.
A registry manager for QGIS server services.
Defines interfaces for QGIS server services.
Definition qgsservice.h:38
virtual void executeRequest(const QgsServerRequest &request, QgsServerResponse &response, const QgsProject *project)=0
Executes the requests and sets result in QgsServerRequest.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
Q_GLOBAL_STATIC(QgsServerSettings, sSettings)
QString shortName
Short name of transform grid.
bool isAvailable
true if grid is currently available for use
QString packageName
Name of package the grid is included within.
QString url
Url to download grid from.
double accuracy
Transformation accuracy (in meters).
QString name
Display name of transform operation.
QList< QgsDatumTransform::GridDetails > grids
Contains a list of transform grids used by the operation.