QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsblockingnetworkrequest.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsblockingnetworkrequest.cpp
3 -----------------------------
4 begin : November 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsapplication.h"
19#include "qgsauthmanager.h"
20#include "qgsfeedback.h"
21#include "qgslogger.h"
22#include "qgsmessagelog.h"
24#include "qgsvariantutils.h"
25
26#include <QAuthenticator>
27#include <QBuffer>
28#include <QMutex>
29#include <QNetworkCacheMetaData>
30#include <QNetworkReply>
31#include <QNetworkRequest>
32#include <QUrl>
33#include <QWaitCondition>
34
35#include "moc_qgsblockingnetworkrequest.cpp"
36
38 : mFlags( flags )
39{
40 connect( QgsNetworkAccessManager::instance(), qOverload< QNetworkReply * >( &QgsNetworkAccessManager::requestTimedOut ), this, &QgsBlockingNetworkRequest::requestTimedOut );
41}
42
47
48void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
49{
50 if ( reply == mReply )
51 mTimedout = true;
52}
53
55{
56 return mAuthCfg;
57}
58
60{
61 mAuthCfg = authCfg;
62}
63
64QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::get( QNetworkRequest &request, bool forceRefresh, QgsFeedback *feedback, RequestFlags requestFlags )
65{
66 return doRequest( Qgis::HttpMethod::Get, request, forceRefresh, feedback, requestFlags );
67}
68
69QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::post( QNetworkRequest &request, const QByteArray &data, bool forceRefresh, QgsFeedback *feedback )
70{
71 QByteArray ldata( data );
72 QBuffer buffer( &ldata );
73 buffer.open( QIODevice::ReadOnly );
74 return post( request, &buffer, forceRefresh, feedback );
75}
76
77QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::post( QNetworkRequest &request, QIODevice *data, bool forceRefresh, QgsFeedback *feedback )
78{
79 mPayloadData = data;
80 const QgsBlockingNetworkRequest::ErrorCode res = doRequest( Qgis::HttpMethod::Post, request, forceRefresh, feedback );
81 mPayloadData = nullptr;
82 return res;
83}
84
85QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::head( QNetworkRequest &request, bool forceRefresh, QgsFeedback *feedback )
86{
87 return doRequest( Qgis::HttpMethod::Head, request, forceRefresh, feedback );
88}
89
90QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::put( QNetworkRequest &request, const QByteArray &data, QgsFeedback *feedback )
91{
92 QByteArray ldata( data );
93 QBuffer buffer( &ldata );
94 buffer.open( QIODevice::ReadOnly );
95 return put( request, &buffer, feedback );
96}
97
98QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::put( QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback )
99{
100 mPayloadData = data;
101 const QgsBlockingNetworkRequest::ErrorCode res = doRequest( Qgis::HttpMethod::Put, request, true, feedback );
102 mPayloadData = nullptr;
103 return res;
104}
105
107{
108 return doRequest( Qgis::HttpMethod::Delete, request, true, feedback );
109}
110
111void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager( const QNetworkRequest &request )
112{
113 switch ( mMethod )
114 {
116 mReply = QgsNetworkAccessManager::instance()->get( request );
117 break;
118
120 mReply = QgsNetworkAccessManager::instance()->post( request, mPayloadData );
121 break;
122
124 mReply = QgsNetworkAccessManager::instance()->head( request );
125 break;
126
128 mReply = QgsNetworkAccessManager::instance()->put( request, mPayloadData );
129 break;
130
132 mReply = QgsNetworkAccessManager::instance()->deleteResource( request );
133 break;
134 };
135}
136
137QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::doRequest( Qgis::HttpMethod method, QNetworkRequest &request, bool forceRefresh, QgsFeedback *feedback, RequestFlags requestFlags )
138{
139 mMethod = method;
140 mFeedback = feedback;
141
142 abort(); // cancel previous
143 mIsAborted = false;
144 mTimedout = false;
145 mGotNonEmptyResponse = false;
146 mRequestFlags = requestFlags;
147
148 mErrorMessage.clear();
149 mErrorCode = NoError;
150 mForceRefresh = forceRefresh;
151 mReplyContent.clear();
152
153 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
154 {
155 mErrorCode = NetworkError;
156 mErrorMessage = errorMessageFailedAuth();
157 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
158 {
159 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
160 }
161 return NetworkError;
162 }
163
164 QgsDebugMsgLevel( QStringLiteral( "Calling: %1" ).arg( request.url().toString() ), 2 );
165
166 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
167 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
168 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
169
170 QWaitCondition authRequestBufferNotEmpty;
171 QMutex waitConditionMutex;
172
173 bool threadFinished = false;
174 bool success = false;
175
176 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
177
178 if ( mFeedback )
179 connect( mFeedback, &QgsFeedback::canceled, this, &QgsBlockingNetworkRequest::abort );
180
181 const std::function<void()> downloaderFunction = [ this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
182 {
183 // this function will always be run in worker threads -- either the blocking call is being made in a worker thread,
184 // or the blocking call has been made from the main thread and we've fired up a new thread for this function
185 Q_ASSERT( QThread::currentThread() != QgsApplication::instance()->thread() );
186
187 QgsNetworkAccessManager::instance( Qt::DirectConnection );
188
189 success = true;
190
191 sendRequestToNetworkAccessManager( request );
192
193 if ( mFeedback )
194 connect( mFeedback, &QgsFeedback::canceled, mReply, &QNetworkReply::abort );
195
196 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
197 {
198 mErrorCode = NetworkError;
199 mErrorMessage = errorMessageFailedAuth();
200 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
201 {
202 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
203 }
204 if ( requestMadeFromMainThread )
205 authRequestBufferNotEmpty.wakeAll();
206 success = false;
207 }
208 else
209 {
210 // We are able to use direct connection here, because we
211 // * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
212 // * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
213 connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
214 connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
215 connect( mReply, &QNetworkReply::uploadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
216
217 if ( request.hasRawHeader( "Range" ) )
218 connect( mReply, &QNetworkReply::metaDataChanged, this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
219
220 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
221 {
222 // when this method is called we have "produced" a single authentication request -- so the buffer is now full
223 // and it's time for the "consumer" (main thread) to do its part
224 waitConditionMutex.lock();
225 authRequestBufferNotEmpty.wakeAll();
226 waitConditionMutex.unlock();
227
228 // note that we don't need to handle waking this thread back up - that's done automatically by QgsNetworkAccessManager
229 };
230
231 QMetaObject::Connection authRequestConnection;
232 QMetaObject::Connection proxyAuthenticationConnection;
233#ifndef QT_NO_SSL
234 QMetaObject::Connection sslErrorsConnection;
235#endif
236
237 if ( requestMadeFromMainThread )
238 {
239 authRequestConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred, this, resumeMainThread, Qt::DirectConnection );
240 proxyAuthenticationConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, this, resumeMainThread, Qt::DirectConnection );
241
242#ifndef QT_NO_SSL
243 sslErrorsConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::sslErrorsOccurred, this, resumeMainThread, Qt::DirectConnection );
244#endif
245 }
246 QEventLoop loop;
247 // connecting to aboutToQuit avoids an on-going request to remain stalled
248 // when QThreadPool::globalInstance()->waitForDone()
249 // is called at process termination
250 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
251 connect( this, &QgsBlockingNetworkRequest::finished, &loop, &QEventLoop::quit, Qt::DirectConnection );
252 loop.exec();
253
254 if ( requestMadeFromMainThread )
255 {
256 // event loop exited - need to disconnect as to not leave functor hanging to receive signals in future
257 disconnect( authRequestConnection );
258 disconnect( proxyAuthenticationConnection );
259#ifndef QT_NO_SSL
260 disconnect( sslErrorsConnection );
261#endif
262 }
263 }
264
265 if ( requestMadeFromMainThread )
266 {
267 waitConditionMutex.lock();
268 threadFinished = true;
269 authRequestBufferNotEmpty.wakeAll();
270 waitConditionMutex.unlock();
271 }
272 };
273
274 if ( requestMadeFromMainThread )
275 {
276 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
277 downloaderThread->start();
278
279 while ( true )
280 {
281 waitConditionMutex.lock();
282 if ( threadFinished )
283 {
284 waitConditionMutex.unlock();
285 break;
286 }
287 authRequestBufferNotEmpty.wait( &waitConditionMutex );
288
289 // If the downloader thread wakes us (the main thread) up and is not yet finished
290 // then it has "produced" an authentication request which we need to now "consume".
291 // The processEvents() call gives the auth manager the chance to show a dialog and
292 // once done with that, we can wake the downloaderThread again and continue the download.
293 if ( !threadFinished )
294 {
295 waitConditionMutex.unlock();
296
297 QgsApplication::processEvents();
298 // we don't need to wake up the worker thread - it will automatically be woken when
299 // the auth request has been dealt with by QgsNetworkAccessManager
300 }
301 else
302 {
303 waitConditionMutex.unlock();
304 }
305 }
306 // wait for thread to gracefully exit
307 downloaderThread->wait();
308 }
309 else
310 {
311 downloaderFunction();
312 }
313 return mErrorCode;
314}
315
317{
318 mIsAborted = true;
319 if ( mReply )
320 {
321 mReply->deleteLater();
322 mReply = nullptr;
323 }
324}
325
326void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
327{
328 QgsDebugMsgLevel( QStringLiteral( "%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( "unknown number of" ) : QString::number( bytesTotal ) ), 2 );
329
330 if ( bytesReceived != 0 )
331 mGotNonEmptyResponse = true;
332
333 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
334 {
335 if ( mReply->error() == QNetworkReply::NoError )
336 {
337 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
338 if ( !QgsVariantUtils::isNull( redirect ) )
339 {
340 // We don't want to emit downloadProgress() for a redirect
341 return;
342 }
343 }
344 }
345
346 if ( mMethod == Qgis::HttpMethod::Put || mMethod == Qgis::HttpMethod::Post )
347 emit uploadProgress( bytesReceived, bytesTotal );
348 else
349 emit downloadProgress( bytesReceived, bytesTotal );
350}
351
352void QgsBlockingNetworkRequest::replyFinished()
353{
354 if ( !mIsAborted && mReply )
355 {
356
357 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
358 {
359 QgsDebugMsgLevel( QStringLiteral( "reply OK" ), 2 );
360 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
361 if ( !QgsVariantUtils::isNull( redirect ) )
362 {
363 QgsDebugMsgLevel( QStringLiteral( "Request redirected." ), 2 );
364
365 const QUrl &toUrl = redirect.toUrl();
366 mReply->request();
367 if ( toUrl == mReply->url() )
368 {
369 mErrorMessage = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() );
370 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
371 {
372 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
373 }
374 mReplyContent.clear();
375 }
376 else
377 {
378 QNetworkRequest request( toUrl );
379
380 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
381 {
382 mReplyContent.clear();
383 mErrorMessage = errorMessageFailedAuth();
384 mErrorCode = NetworkError;
385 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
386 {
387 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
388 }
389 emit finished();
391 emit downloadFinished();
393 return;
394 }
395
396 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
397 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
398 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
399
400 // if that was a range request, use the same range for the redirected request
401 if ( mReply->request().hasRawHeader( "Range" ) )
402 request.setRawHeader( "Range", mReply->request().rawHeader( "Range" ) );
403
404 mReply->deleteLater();
405 mReply = nullptr;
406
407 QgsDebugMsgLevel( QStringLiteral( "redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
408
409 sendRequestToNetworkAccessManager( request );
410
411 if ( mFeedback )
412 connect( mFeedback, &QgsFeedback::canceled, mReply, &QNetworkReply::abort );
413
414 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
415 {
416 mReplyContent.clear();
417 mErrorMessage = errorMessageFailedAuth();
418 mErrorCode = NetworkError;
419 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
420 {
421 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
422 }
423 emit finished();
425 emit downloadFinished();
427 return;
428 }
429
430 connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
431 connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
432 connect( mReply, &QNetworkReply::uploadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
433
434 if ( request.hasRawHeader( "Range" ) )
435 connect( mReply, &QNetworkReply::metaDataChanged, this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
436
437 return;
438 }
439 }
440 else
441 {
442 const QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
443
444 if ( nam->cache() )
445 {
446 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
447
448 QNetworkCacheMetaData::RawHeaderList hl;
449 const auto constRawHeaders = cmd.rawHeaders();
450 for ( const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
451 {
452 if ( h.first != "Cache-Control" )
453 hl.append( h );
454 }
455 cmd.setRawHeaders( hl );
456
457 QgsDebugMsgLevel( QStringLiteral( "expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
458 if ( cmd.expirationDate().isNull() )
459 {
460 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
461 }
462
463 nam->cache()->updateMetaData( cmd );
464 }
465 else
466 {
467 QgsDebugMsgLevel( QStringLiteral( "No cache!" ), 2 );
468 }
469
470#ifdef QGISDEBUG
471 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
472 QgsDebugMsgLevel( QStringLiteral( "Reply was cached: %1" ).arg( fromCache ), 2 );
473#endif
474
475 mReplyContent = QgsNetworkReplyContent( mReply );
476 const QByteArray content = mReply->readAll();
477 if ( !( mRequestFlags & RequestFlag::EmptyResponseIsValid ) && content.isEmpty() && !mGotNonEmptyResponse && mMethod == Qgis::HttpMethod::Get )
478 {
479 mErrorMessage = tr( "empty response: %1" ).arg( mReply->errorString() );
480 mErrorCode = ServerExceptionError;
481 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
482 {
483 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
484 }
485 }
486 mReplyContent.setContent( content );
487 }
488 }
489 else
490 {
491 if ( mReply->error() != QNetworkReply::OperationCanceledError )
492 {
493 mErrorMessage = mReply->errorString();
494 mErrorCode = ServerExceptionError;
495 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
496 {
497 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
498 }
499 }
500 mReplyContent = QgsNetworkReplyContent( mReply );
501 mReplyContent.setContent( mReply->readAll() );
502 }
503 }
504 if ( mTimedout )
505 mErrorCode = TimeoutError;
506
507 if ( mReply )
508 {
509 mReply->deleteLater();
510 mReply = nullptr;
511 }
512
513 emit finished();
515 emit downloadFinished();
517}
518
519QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
520{
521 return tr( "network request update failed for authentication config" );
522}
523
524void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
525{
526 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
527 {
528 // We're expecting a 206 - Partial Content but the server returned 200
529 // It seems it does not support range requests and is returning the whole file!
530 mReply->abort();
531 mErrorMessage = tr( "The server does not support range requests" );
532 mErrorCode = ServerExceptionError;
533 }
534}
@ DisableMessageLogging
If present, indicates that no message logging should be performed when network errors are encountered...
Definition qgis.h:172
QFlags< NetworkRequestFlag > NetworkRequestFlags
Flags controlling behavior of network requests.
Definition qgis.h:181
HttpMethod
Different methods of HTTP requests.
Definition qgis.h:1037
@ Post
POST method.
Definition qgis.h:1039
@ Head
HEAD method.
Definition qgis.h:1040
@ Get
GET method.
Definition qgis.h:1038
@ Put
PUT method.
Definition qgis.h:1041
@ Delete
DELETE method.
Definition qgis.h:1042
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkReply with an authentication config (used to skip known SSL errors,...
ErrorCode put(QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
QgsBlockingNetworkRequest(Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Constructor for QgsBlockingNetworkRequest.
void uploadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when when data are sent during a request.
Qgis::NetworkRequestFlags flags() const
Returns the network request flags.
ErrorCode head(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "head" operation on the specified request.
void abort()
Aborts the network request immediately.
Q_DECL_DEPRECATED void downloadFinished()
Emitted once a request has finished downloading.
ErrorCode post(QNetworkRequest &request, QIODevice *data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
ErrorCode deleteResource(QNetworkRequest &request, QgsFeedback *feedback=nullptr)
Performs a "delete" operation on the specified request.
void finished()
Emitted once a request has finished.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
QString authCfg() const
Returns the authentication config id which will be used during the request.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when when data arrives during a request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
@ EmptyResponseIsValid
Do not generate an error if getting an empty response (e.g. HTTP 204).
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
void canceled()
Internal routines can connect to this signal if they use event loop.
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.
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61