17#include "moc_qgsblockingnetworkrequest.cpp"
26#include <QNetworkRequest>
27#include <QNetworkReply>
29#include <QWaitCondition>
30#include <QNetworkCacheMetaData>
31#include <QAuthenticator>
44void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
46 if (
reply == mReply )
62 return doRequest( Get, request, forceRefresh, feedback, requestFlags );
67 QByteArray ldata( data );
68 QBuffer buffer( &ldata );
69 buffer.open( QIODevice::ReadOnly );
70 return post( request, &buffer, forceRefresh, feedback );
76 return doRequest( Post, request, forceRefresh, feedback );
81 return doRequest( Head, request, forceRefresh, feedback );
86 QByteArray ldata( data );
87 QBuffer buffer( &ldata );
88 buffer.open( QIODevice::ReadOnly );
89 return put( request, &buffer, feedback );
95 return doRequest( Put, request,
true, feedback );
100 return doRequest( Delete, request,
true, feedback );
103void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
132 mFeedback = feedback;
137 mGotNonEmptyResponse =
false;
138 mRequestFlags = requestFlags;
140 mErrorMessage.clear();
142 mForceRefresh = forceRefresh;
143 mReplyContent.
clear();
148 mErrorMessage = errorMessageFailedAuth();
153 QgsDebugMsgLevel( QStringLiteral(
"Calling: %1" ).arg( request.url().toString() ), 2 );
155 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
156 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
157 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
159 QWaitCondition authRequestBufferNotEmpty;
160 QMutex waitConditionMutex;
162 bool threadFinished =
false;
163 bool success =
false;
165 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
170 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
180 sendRequestToNetworkAccessManager( request );
188 mErrorMessage = errorMessageFailedAuth();
190 if ( requestMadeFromMainThread )
191 authRequestBufferNotEmpty.wakeAll();
199 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
200 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
201 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
203 if ( request.hasRawHeader(
"Range" ) )
204 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
206 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
210 waitConditionMutex.lock();
211 authRequestBufferNotEmpty.wakeAll();
212 waitConditionMutex.unlock();
217 QMetaObject::Connection authRequestConnection;
218 QMetaObject::Connection proxyAuthenticationConnection;
220 QMetaObject::Connection sslErrorsConnection;
223 if ( requestMadeFromMainThread )
225 authRequestConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred,
this, resumeMainThread, Qt::DirectConnection );
226 proxyAuthenticationConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired,
this, resumeMainThread, Qt::DirectConnection );
236 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
240 if ( requestMadeFromMainThread )
243 disconnect( authRequestConnection );
244 disconnect( proxyAuthenticationConnection );
246 disconnect( sslErrorsConnection );
251 if ( requestMadeFromMainThread )
253 waitConditionMutex.lock();
254 threadFinished =
true;
255 authRequestBufferNotEmpty.wakeAll();
256 waitConditionMutex.unlock();
260 if ( requestMadeFromMainThread )
262 std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
263 downloaderThread->start();
267 waitConditionMutex.lock();
268 if ( threadFinished )
270 waitConditionMutex.unlock();
273 authRequestBufferNotEmpty.wait( &waitConditionMutex );
279 if ( !threadFinished )
281 waitConditionMutex.unlock();
283 QgsApplication::processEvents();
289 waitConditionMutex.unlock();
293 downloaderThread->wait();
297 downloaderFunction();
307 mReply->deleteLater();
312void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
314 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
316 if ( bytesReceived != 0 )
317 mGotNonEmptyResponse =
true;
319 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
321 if ( mReply->error() == QNetworkReply::NoError )
323 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
332 if ( mMethod == Put || mMethod == Post )
338void QgsBlockingNetworkRequest::replyFinished()
340 if ( !mIsAborted && mReply )
343 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
346 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
351 const QUrl &toUrl = redirect.toUrl();
353 if ( toUrl == mReply->url() )
355 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
357 mReplyContent.
clear();
361 QNetworkRequest request( toUrl );
365 mReplyContent.
clear();
366 mErrorMessage = errorMessageFailedAuth();
376 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
377 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
378 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
381 if ( mReply->request().hasRawHeader(
"Range" ) )
382 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
384 mReply->deleteLater();
387 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
389 sendRequestToNetworkAccessManager( request );
396 mReplyContent.
clear();
397 mErrorMessage = errorMessageFailedAuth();
407 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
408 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
409 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
411 if ( request.hasRawHeader(
"Range" ) )
412 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
423 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
425 QNetworkCacheMetaData::RawHeaderList hl;
426 const auto constRawHeaders = cmd.rawHeaders();
427 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
429 if ( h.first !=
"Cache-Control" )
432 cmd.setRawHeaders( hl );
434 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
435 if ( cmd.expirationDate().isNull() )
437 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
440 nam->cache()->updateMetaData( cmd );
448 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
449 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
453 const QByteArray content = mReply->readAll();
456 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
465 if ( mReply->error() != QNetworkReply::OperationCanceledError )
467 mErrorMessage = mReply->errorString();
472 mReplyContent.
setContent( mReply->readAll() );
480 mReply->deleteLater();
490QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
492 return tr(
"network request update failed for authentication config" );
495void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
497 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
502 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, 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).
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)