25#include <QNetworkRequest>
26#include <QNetworkReply>
28#include <QWaitCondition>
29#include <QNetworkCacheMetaData>
30#include <QAuthenticator>
43void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
45 if (
reply == mReply )
61 return doRequest( Get, request, forceRefresh, feedback );
66 QByteArray ldata( data );
67 QBuffer buffer( &ldata );
68 buffer.open( QIODevice::ReadOnly );
69 return post( request, &buffer, forceRefresh, feedback );
75 return doRequest( Post, request, forceRefresh, feedback );
80 return doRequest( Head, request, forceRefresh, feedback );
85 QByteArray ldata( data );
86 QBuffer buffer( &ldata );
87 buffer.open( QIODevice::ReadOnly );
88 return put( request, &buffer, feedback );
94 return doRequest( Put, request,
true, feedback );
99 return doRequest( Delete, request,
true, feedback );
102void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
131 mFeedback = feedback;
136 mGotNonEmptyResponse =
false;
138 mErrorMessage.clear();
140 mForceRefresh = forceRefresh;
141 mReplyContent.
clear();
146 mErrorMessage = errorMessageFailedAuth();
151 QgsDebugMsgLevel( QStringLiteral(
"Calling: %1" ).arg( request.url().toString() ), 2 );
153 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
154 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
156 QWaitCondition authRequestBufferNotEmpty;
157 QMutex waitConditionMutex;
159 bool threadFinished =
false;
160 bool success =
false;
162 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
167 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
177 sendRequestToNetworkAccessManager( request );
185 mErrorMessage = errorMessageFailedAuth();
187 if ( requestMadeFromMainThread )
188 authRequestBufferNotEmpty.wakeAll();
196 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
197 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
198 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
200 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
204 waitConditionMutex.lock();
205 authRequestBufferNotEmpty.wakeAll();
206 waitConditionMutex.unlock();
211 if ( requestMadeFromMainThread )
224 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
229 if ( requestMadeFromMainThread )
231 waitConditionMutex.lock();
232 threadFinished =
true;
233 authRequestBufferNotEmpty.wakeAll();
234 waitConditionMutex.unlock();
238 if ( requestMadeFromMainThread )
240 std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
241 downloaderThread->start();
245 waitConditionMutex.lock();
246 if ( threadFinished )
248 waitConditionMutex.unlock();
251 authRequestBufferNotEmpty.wait( &waitConditionMutex );
257 if ( !threadFinished )
259 waitConditionMutex.unlock();
261 QgsApplication::processEvents();
267 waitConditionMutex.unlock();
271 downloaderThread->wait();
275 downloaderFunction();
285 mReply->deleteLater();
290void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
292 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
294 if ( bytesReceived != 0 )
295 mGotNonEmptyResponse =
true;
297 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
299 if ( mReply->error() == QNetworkReply::NoError )
301 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
310 if ( mMethod == Put || mMethod == Post )
316void QgsBlockingNetworkRequest::replyFinished()
318 if ( !mIsAborted && mReply )
321 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
324 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
329 const QUrl &toUrl = redirect.toUrl();
331 if ( toUrl == mReply->url() )
333 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
335 mReplyContent.
clear();
339 QNetworkRequest request( toUrl );
343 mReplyContent.
clear();
344 mErrorMessage = errorMessageFailedAuth();
354 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
355 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
357 mReply->deleteLater();
360 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
362 sendRequestToNetworkAccessManager( request );
369 mReplyContent.
clear();
370 mErrorMessage = errorMessageFailedAuth();
380 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
381 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
382 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
392 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
394 QNetworkCacheMetaData::RawHeaderList hl;
395 const auto constRawHeaders = cmd.rawHeaders();
396 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
398 if ( h.first !=
"Cache-Control" )
401 cmd.setRawHeaders( hl );
403 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
404 if ( cmd.expirationDate().isNull() )
406 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
409 nam->cache()->updateMetaData( cmd );
417 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
418 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
422 const QByteArray content = mReply->readAll();
423 if ( content.isEmpty() && !mGotNonEmptyResponse && mMethod == Get )
425 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
434 if ( mReply->error() != QNetworkReply::OperationCanceledError )
436 mErrorMessage = mReply->errorString();
441 mReplyContent.
setContent( mReply->readAll() );
449 mReply->deleteLater();
459QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
461 return tr(
"network request update failed for authentication config" );
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,...
void downloadProgress(qint64, qint64)
Emitted when when data arrives during a request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
QgsBlockingNetworkRequest()
Constructor for QgsBlockingNetworkRequest.
ErrorCode put(QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
~QgsBlockingNetworkRequest() override
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 uploadProgress(qint64, qint64)
Emitted when when data are sent during a request.
@ 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.
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)
Adds a message to the log instance (and creates it if necessary).
network access manager for QGIS
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.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
void setContent(const QByteArray &content)
Sets the reply content.
void clear()
Clears the reply, resetting it back to a default, empty reply.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugMsgLevel(str, level)