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::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
156 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
158 QWaitCondition authRequestBufferNotEmpty;
159 QMutex waitConditionMutex;
161 bool threadFinished =
false;
162 bool success =
false;
164 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
169 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
179 sendRequestToNetworkAccessManager( request );
187 mErrorMessage = errorMessageFailedAuth();
189 if ( requestMadeFromMainThread )
190 authRequestBufferNotEmpty.wakeAll();
198 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
199 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
200 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
202 if ( request.hasRawHeader(
"Range" ) )
203 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
205 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
209 waitConditionMutex.lock();
210 authRequestBufferNotEmpty.wakeAll();
211 waitConditionMutex.unlock();
216 if ( requestMadeFromMainThread )
229 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
234 if ( requestMadeFromMainThread )
236 waitConditionMutex.lock();
237 threadFinished =
true;
238 authRequestBufferNotEmpty.wakeAll();
239 waitConditionMutex.unlock();
243 if ( requestMadeFromMainThread )
245 std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
246 downloaderThread->start();
250 waitConditionMutex.lock();
251 if ( threadFinished )
253 waitConditionMutex.unlock();
256 authRequestBufferNotEmpty.wait( &waitConditionMutex );
262 if ( !threadFinished )
264 waitConditionMutex.unlock();
266 QgsApplication::processEvents();
272 waitConditionMutex.unlock();
276 downloaderThread->wait();
280 downloaderFunction();
290 mReply->deleteLater();
295void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
297 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
299 if ( bytesReceived != 0 )
300 mGotNonEmptyResponse =
true;
302 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
304 if ( mReply->error() == QNetworkReply::NoError )
306 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
315 if ( mMethod == Put || mMethod == Post )
321void QgsBlockingNetworkRequest::replyFinished()
323 if ( !mIsAborted && mReply )
326 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
329 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
334 const QUrl &toUrl = redirect.toUrl();
336 if ( toUrl == mReply->url() )
338 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
340 mReplyContent.
clear();
344 QNetworkRequest request( toUrl );
348 mReplyContent.
clear();
349 mErrorMessage = errorMessageFailedAuth();
359 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
360 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
363 if ( mReply->request().hasRawHeader(
"Range" ) )
364 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
366 mReply->deleteLater();
369 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
371 sendRequestToNetworkAccessManager( request );
378 mReplyContent.
clear();
379 mErrorMessage = errorMessageFailedAuth();
389 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
390 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
391 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
393 if ( request.hasRawHeader(
"Range" ) )
394 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
405 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
407 QNetworkCacheMetaData::RawHeaderList hl;
408 const auto constRawHeaders = cmd.rawHeaders();
409 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
411 if ( h.first !=
"Cache-Control" )
414 cmd.setRawHeaders( hl );
416 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
417 if ( cmd.expirationDate().isNull() )
419 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
422 nam->cache()->updateMetaData( cmd );
430 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
431 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
435 const QByteArray content = mReply->readAll();
438 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
447 if ( mReply->error() != QNetworkReply::OperationCanceledError )
449 mErrorMessage = mReply->errorString();
454 mReplyContent.
setContent( mReply->readAll() );
462 mReply->deleteLater();
472QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
474 return tr(
"network request update failed for authentication config" );
477void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
479 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
484 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)