26#include <QAuthenticator>
29#include <QNetworkCacheMetaData>
30#include <QNetworkReply>
31#include <QNetworkRequest>
34#include <QWaitCondition>
36#include "moc_qgsblockingnetworkrequest.cpp"
38using namespace Qt::StringLiterals;
51void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
53 if (
reply == mReply )
74 QByteArray ldata( data );
75 QBuffer buffer( &ldata );
76 buffer.open( QIODevice::ReadOnly );
77 return post( request, &buffer, forceRefresh, feedback );
84 mPayloadData =
nullptr;
95 QByteArray ldata( data );
96 QBuffer buffer( &ldata );
97 buffer.open( QIODevice::ReadOnly );
98 return put( request, &buffer, feedback );
105 mPayloadData =
nullptr;
114void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
143 mFeedback = feedback;
148 mGotNonEmptyResponse =
false;
149 mRequestFlags = requestFlags;
151 mErrorMessage.clear();
153 mForceRefresh = forceRefresh;
154 mReplyContent.clear();
159 mErrorMessage = errorMessageFailedAuth();
169 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
170 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
171 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
173 QWaitCondition authRequestBufferNotEmpty;
174 QMutex waitConditionMutex;
176 bool threadFinished =
false;
177 bool success =
false;
179 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
184 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread]() {
193 sendRequestToNetworkAccessManager( request );
201 mErrorMessage = errorMessageFailedAuth();
206 if ( requestMadeFromMainThread )
207 authRequestBufferNotEmpty.wakeAll();
215 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
216 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
217 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
219 if ( request.hasRawHeader(
"Range" ) )
220 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
222 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty]() {
225 waitConditionMutex.lock();
226 authRequestBufferNotEmpty.wakeAll();
227 waitConditionMutex.unlock();
232 QMetaObject::Connection authRequestConnection;
233 QMetaObject::Connection proxyAuthenticationConnection;
235 QMetaObject::Connection sslErrorsConnection;
238 if ( requestMadeFromMainThread )
240 authRequestConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred,
this, resumeMainThread, Qt::DirectConnection );
241 proxyAuthenticationConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired,
this, resumeMainThread, Qt::DirectConnection );
251 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
255 if ( requestMadeFromMainThread )
258 disconnect( authRequestConnection );
259 disconnect( proxyAuthenticationConnection );
261 disconnect( sslErrorsConnection );
266 if ( requestMadeFromMainThread )
268 waitConditionMutex.lock();
269 threadFinished =
true;
270 authRequestBufferNotEmpty.wakeAll();
271 waitConditionMutex.unlock();
275 if ( requestMadeFromMainThread )
277 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
278 downloaderThread->start();
282 waitConditionMutex.lock();
283 if ( threadFinished )
285 waitConditionMutex.unlock();
288 authRequestBufferNotEmpty.wait( &waitConditionMutex );
294 if ( !threadFinished )
296 waitConditionMutex.unlock();
298 QgsApplication::processEvents();
304 waitConditionMutex.unlock();
308 downloaderThread->wait();
312 downloaderFunction();
322 mReply->deleteLater();
327void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
329 QgsDebugMsgLevel( u
"%1 of %2 bytes downloaded."_s.arg( bytesReceived ).arg( bytesTotal < 0 ? u
"unknown number of"_s : QString::number( bytesTotal ) ), 2 );
331 if ( bytesReceived != 0 )
332 mGotNonEmptyResponse =
true;
334 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
336 if ( mReply->error() == QNetworkReply::NoError )
338 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
353void QgsBlockingNetworkRequest::replyFinished()
355 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( u
"redirected: %1 forceRefresh=%2"_s.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( u
"expirationDate:%1"_s.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();
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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)