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