QGIS API Documentation 4.1.0-Master (60fea48833c)
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 // this function will always be run in worker threads -- either the blocking call is being made in a worker thread,
186 // or the blocking call has been made from the main thread and we've fired up a new thread for this function
187 Q_ASSERT( QThread::currentThread() != QgsApplication::instance()->thread() );
188
189 QgsNetworkAccessManager::instance( Qt::DirectConnection );
190
191 success = true;
192
193 sendRequestToNetworkAccessManager( request );
194
195 if ( mFeedback )
196 connect( mFeedback, &QgsFeedback::canceled, mReply, &QNetworkReply::abort );
197
198 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
199 {
200 mErrorCode = NetworkError;
201 mErrorMessage = errorMessageFailedAuth();
202 if ( !mFlags.testFlag( Qgis::NetworkRequestFlag::DisableMessageLogging ) )
203 {
204 QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
205 }
206 if ( requestMadeFromMainThread )
207 authRequestBufferNotEmpty.wakeAll();
208 success = false;
209 }
210 else
211 {
212 // We are able to use direct connection here, because we
213 // * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
214 // * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
215 connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
216 connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
217 connect( mReply, &QNetworkReply::uploadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
218
219 if ( request.hasRawHeader( "Range" ) )
220 connect( mReply, &QNetworkReply::metaDataChanged, this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
221
222 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty]() {
223 // when this method is called we have "produced" a single authentication request -- so the buffer is now full
224 // and it's time for the "consumer" (main thread) to do its part
225 waitConditionMutex.lock();
226 authRequestBufferNotEmpty.wakeAll();
227 waitConditionMutex.unlock();
228
229 // note that we don't need to handle waking this thread back up - that's done automatically by QgsNetworkAccessManager
230 };
231
232 QMetaObject::Connection authRequestConnection;
233 QMetaObject::Connection proxyAuthenticationConnection;
234#ifndef QT_NO_SSL
235 QMetaObject::Connection sslErrorsConnection;
236#endif
237
238 if ( requestMadeFromMainThread )
239 {
240 authRequestConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred, this, resumeMainThread, Qt::DirectConnection );
241 proxyAuthenticationConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, this, resumeMainThread, Qt::DirectConnection );
242
243#ifndef QT_NO_SSL
244 sslErrorsConnection = connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::sslErrorsOccurred, this, resumeMainThread, Qt::DirectConnection );
245#endif
246 }
247 QEventLoop loop;
248 // connecting to aboutToQuit avoids an on-going request to remain stalled
249 // when QThreadPool::globalInstance()->waitForDone()
250 // is called at process termination
251 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
252 connect( this, &QgsBlockingNetworkRequest::finished, &loop, &QEventLoop::quit, Qt::DirectConnection );
253 loop.exec();
254
255 if ( requestMadeFromMainThread )
256 {
257 // event loop exited - need to disconnect as to not leave functor hanging to receive signals in future
258 disconnect( authRequestConnection );
259 disconnect( proxyAuthenticationConnection );
260#ifndef QT_NO_SSL
261 disconnect( sslErrorsConnection );
262#endif
263 }
264 }
265
266 if ( requestMadeFromMainThread )
267 {
268 waitConditionMutex.lock();
269 threadFinished = true;
270 authRequestBufferNotEmpty.wakeAll();
271 waitConditionMutex.unlock();
272 }
273 };
274
275 if ( requestMadeFromMainThread )
276 {
277 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
278 downloaderThread->start();
279
280 while ( true )
281 {
282 waitConditionMutex.lock();
283 if ( threadFinished )
284 {
285 waitConditionMutex.unlock();
286 break;
287 }
288 authRequestBufferNotEmpty.wait( &waitConditionMutex );
289
290 // If the downloader thread wakes us (the main thread) up and is not yet finished
291 // then it has "produced" an authentication request which we need to now "consume".
292 // The processEvents() call gives the auth manager the chance to show a dialog and
293 // once done with that, we can wake the downloaderThread again and continue the download.
294 if ( !threadFinished )
295 {
296 waitConditionMutex.unlock();
297
298 QgsApplication::processEvents();
299 // we don't need to wake up the worker thread - it will automatically be woken when
300 // the auth request has been dealt with by QgsNetworkAccessManager
301 }
302 else
303 {
304 waitConditionMutex.unlock();
305 }
306 }
307 // wait for thread to gracefully exit
308 downloaderThread->wait();
309 }
310 else
311 {
312 downloaderFunction();
313 }
314 return mErrorCode;
315}
316
318{
319 mIsAborted = true;
320 if ( mReply )
321 {
322 mReply->deleteLater();
323 mReply = nullptr;
324 }
325}
326
327void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
328{
329 QgsDebugMsgLevel( u"%1 of %2 bytes downloaded."_s.arg( bytesReceived ).arg( bytesTotal < 0 ? u"unknown number of"_s : QString::number( bytesTotal ) ), 2 );
330
331 if ( bytesReceived != 0 )
332 mGotNonEmptyResponse = true;
333
334 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
335 {
336 if ( mReply->error() == QNetworkReply::NoError )
337 {
338 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
339 if ( !QgsVariantUtils::isNull( redirect ) )
340 {
341 // We don't want to emit downloadProgress() for a redirect
342 return;
343 }
344 }
345 }
346
347 if ( mMethod == Qgis::HttpMethod::Put || mMethod == Qgis::HttpMethod::Post )
348 emit uploadProgress( bytesReceived, bytesTotal );
349 else
350 emit downloadProgress( bytesReceived, bytesTotal );
351}
352
353void QgsBlockingNetworkRequest::replyFinished()
354{
355 if ( !mIsAborted && mReply )
356 {
357 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
358 {
359 QgsDebugMsgLevel( u"reply OK"_s, 2 );
360 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
361 if ( !QgsVariantUtils::isNull( redirect ) )
362 {
363 QgsDebugMsgLevel( u"Request redirected."_s, 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( u"redirected: %1 forceRefresh=%2"_s.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( u"expirationDate:%1"_s.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( u"No cache!"_s, 2 );
468 }
469
470#ifdef QGISDEBUG
471 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
472 QgsDebugMsgLevel( u"Reply was cached: %1"_s.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:188
QFlags< NetworkRequestFlag > NetworkRequestFlags
Flags controlling behavior of network requests.
Definition qgis.h:197
HttpMethod
Different methods of HTTP requests.
Definition qgis.h:1063
@ Post
POST method.
Definition qgis.h:1065
@ Head
HEAD method.
Definition qgis.h:1066
@ Get
GET method.
Definition qgis.h:1064
@ Put
PUT method.
Definition qgis.h:1067
@ Delete
DELETE method.
Definition qgis.h:1068
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63