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, requestFlags );
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;
137 mRequestFlags = requestFlags;
139 mErrorMessage.clear();
141 mForceRefresh = forceRefresh;
142 mReplyContent.
clear();
147 mErrorMessage = errorMessageFailedAuth();
152 QgsDebugMsgLevel( QStringLiteral(
"Calling: %1" ).arg( request.url().toString() ), 2 );
154 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
155 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
157 QWaitCondition authRequestBufferNotEmpty;
158 QMutex waitConditionMutex;
160 bool threadFinished =
false;
161 bool success =
false;
163 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
168 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
178 sendRequestToNetworkAccessManager( request );
186 mErrorMessage = errorMessageFailedAuth();
188 if ( requestMadeFromMainThread )
189 authRequestBufferNotEmpty.wakeAll();
197 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
198 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
199 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
201 if ( request.hasRawHeader(
"Range" ) )
202 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
204 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
208 waitConditionMutex.lock();
209 authRequestBufferNotEmpty.wakeAll();
210 waitConditionMutex.unlock();
215 if ( requestMadeFromMainThread )
228 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
233 if ( requestMadeFromMainThread )
235 waitConditionMutex.lock();
236 threadFinished =
true;
237 authRequestBufferNotEmpty.wakeAll();
238 waitConditionMutex.unlock();
242 if ( requestMadeFromMainThread )
244 std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
245 downloaderThread->start();
249 waitConditionMutex.lock();
250 if ( threadFinished )
252 waitConditionMutex.unlock();
255 authRequestBufferNotEmpty.wait( &waitConditionMutex );
261 if ( !threadFinished )
263 waitConditionMutex.unlock();
265 QgsApplication::processEvents();
271 waitConditionMutex.unlock();
275 downloaderThread->wait();
279 downloaderFunction();
289 mReply->deleteLater();
294void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
296 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
298 if ( bytesReceived != 0 )
299 mGotNonEmptyResponse =
true;
301 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
303 if ( mReply->error() == QNetworkReply::NoError )
305 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
314 if ( mMethod == Put || mMethod == Post )
320void QgsBlockingNetworkRequest::replyFinished()
322 if ( !mIsAborted && mReply )
325 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
328 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
333 const QUrl &toUrl = redirect.toUrl();
335 if ( toUrl == mReply->url() )
337 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
339 mReplyContent.
clear();
343 QNetworkRequest request( toUrl );
347 mReplyContent.
clear();
348 mErrorMessage = errorMessageFailedAuth();
358 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
359 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
362 if ( mReply->request().hasRawHeader(
"Range" ) )
363 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
365 mReply->deleteLater();
368 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
370 sendRequestToNetworkAccessManager( request );
377 mReplyContent.
clear();
378 mErrorMessage = errorMessageFailedAuth();
388 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
389 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
390 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
392 if ( request.hasRawHeader(
"Range" ) )
393 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
404 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
406 QNetworkCacheMetaData::RawHeaderList hl;
407 const auto constRawHeaders = cmd.rawHeaders();
408 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
410 if ( h.first !=
"Cache-Control" )
413 cmd.setRawHeaders( hl );
415 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
416 if ( cmd.expirationDate().isNull() )
418 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
421 nam->cache()->updateMetaData( cmd );
429 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
430 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
434 const QByteArray content = mReply->readAll();
437 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
446 if ( mReply->error() != QNetworkReply::OperationCanceledError )
448 mErrorMessage = mReply->errorString();
453 mReplyContent.
setContent( mReply->readAll() );
461 mReply->deleteLater();
471QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
473 return tr(
"network request update failed for authentication config" );
476void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
478 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
483 mErrorMessage = tr(
"The server does not support range requests" );
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,...
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.
void uploadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when when data are sent during a request.
~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 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)
QFlags< RequestFlag > RequestFlags
@ 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, bool silenceNullWarnings=false)
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)