26#include <QAuthenticator>
29#include <QNetworkCacheMetaData>
30#include <QNetworkReply>
31#include <QNetworkRequest>
33#include <QWaitCondition>
35#include "moc_qgsblockingnetworkrequest.cpp"
48void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
50 if (
reply == mReply )
71 QByteArray ldata( data );
72 QBuffer buffer( &ldata );
73 buffer.open( QIODevice::ReadOnly );
74 return post( request, &buffer, forceRefresh, feedback );
81 mPayloadData =
nullptr;
92 QByteArray ldata( data );
93 QBuffer buffer( &ldata );
94 buffer.open( QIODevice::ReadOnly );
95 return put( request, &buffer, feedback );
102 mPayloadData =
nullptr;
111void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
140 mFeedback = feedback;
145 mGotNonEmptyResponse =
false;
146 mRequestFlags = requestFlags;
148 mErrorMessage.clear();
150 mForceRefresh = forceRefresh;
151 mReplyContent.clear();
156 mErrorMessage = errorMessageFailedAuth();
164 QgsDebugMsgLevel( QStringLiteral(
"Calling: %1" ).arg( request.url().toString() ), 2 );
166 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
167 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
168 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
170 QWaitCondition authRequestBufferNotEmpty;
171 QMutex waitConditionMutex;
173 bool threadFinished =
false;
174 bool success =
false;
176 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
181 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
191 sendRequestToNetworkAccessManager( request );
199 mErrorMessage = errorMessageFailedAuth();
204 if ( requestMadeFromMainThread )
205 authRequestBufferNotEmpty.wakeAll();
213 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
214 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
215 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
217 if ( request.hasRawHeader(
"Range" ) )
218 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
220 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
224 waitConditionMutex.lock();
225 authRequestBufferNotEmpty.wakeAll();
226 waitConditionMutex.unlock();
231 QMetaObject::Connection authRequestConnection;
232 QMetaObject::Connection proxyAuthenticationConnection;
234 QMetaObject::Connection sslErrorsConnection;
237 if ( requestMadeFromMainThread )
239 authRequestConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred,
this, resumeMainThread, Qt::DirectConnection );
240 proxyAuthenticationConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired,
this, resumeMainThread, Qt::DirectConnection );
250 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
254 if ( requestMadeFromMainThread )
257 disconnect( authRequestConnection );
258 disconnect( proxyAuthenticationConnection );
260 disconnect( sslErrorsConnection );
265 if ( requestMadeFromMainThread )
267 waitConditionMutex.lock();
268 threadFinished =
true;
269 authRequestBufferNotEmpty.wakeAll();
270 waitConditionMutex.unlock();
274 if ( requestMadeFromMainThread )
276 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
277 downloaderThread->start();
281 waitConditionMutex.lock();
282 if ( threadFinished )
284 waitConditionMutex.unlock();
287 authRequestBufferNotEmpty.wait( &waitConditionMutex );
293 if ( !threadFinished )
295 waitConditionMutex.unlock();
297 QgsApplication::processEvents();
303 waitConditionMutex.unlock();
307 downloaderThread->wait();
311 downloaderFunction();
321 mReply->deleteLater();
326void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
328 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
330 if ( bytesReceived != 0 )
331 mGotNonEmptyResponse =
true;
333 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
335 if ( mReply->error() == QNetworkReply::NoError )
337 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
352void QgsBlockingNetworkRequest::replyFinished()
354 if ( !mIsAborted && mReply )
357 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
360 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
365 const QUrl &toUrl = redirect.toUrl();
367 if ( toUrl == mReply->url() )
369 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
374 mReplyContent.clear();
378 QNetworkRequest request( toUrl );
382 mReplyContent.clear();
383 mErrorMessage = errorMessageFailedAuth();
396 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
397 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
398 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
401 if ( mReply->request().hasRawHeader(
"Range" ) )
402 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
404 mReply->deleteLater();
407 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
409 sendRequestToNetworkAccessManager( request );
416 mReplyContent.clear();
417 mErrorMessage = errorMessageFailedAuth();
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 );
434 if ( request.hasRawHeader(
"Range" ) )
435 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
446 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
448 QNetworkCacheMetaData::RawHeaderList hl;
449 const auto constRawHeaders = cmd.rawHeaders();
450 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
452 if ( h.first !=
"Cache-Control" )
455 cmd.setRawHeaders( hl );
457 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
458 if ( cmd.expirationDate().isNull() )
460 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
463 nam->cache()->updateMetaData( cmd );
471 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
472 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
475 mReplyContent = QgsNetworkReplyContent( mReply );
476 const QByteArray content = mReply->readAll();
479 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
486 mReplyContent.setContent( content );
491 if ( mReply->error() != QNetworkReply::OperationCanceledError )
493 mErrorMessage = mReply->errorString();
500 mReplyContent = QgsNetworkReplyContent( mReply );
501 mReplyContent.setContent( mReply->readAll() );
509 mReply->deleteLater();
519QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
521 return tr(
"network request update failed for authentication config" );
524void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
526 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
531 mErrorMessage = tr(
"The server does not support range requests" );
@ DisableMessageLogging
If present, indicates that no message logging should be performed when network errors are encountered...
QFlags< NetworkRequestFlag > NetworkRequestFlags
Flags controlling behavior of network requests.
HttpMethod
Different methods of HTTP 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,...
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.
~QgsBlockingNetworkRequest() override
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).
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).
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
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugMsgLevel(str, level)