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 ]()
194 sendRequestToNetworkAccessManager( request );
202 mErrorMessage = errorMessageFailedAuth();
207 if ( requestMadeFromMainThread )
208 authRequestBufferNotEmpty.wakeAll();
216 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
217 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
218 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
220 if ( request.hasRawHeader(
"Range" ) )
221 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
223 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
227 waitConditionMutex.lock();
228 authRequestBufferNotEmpty.wakeAll();
229 waitConditionMutex.unlock();
234 QMetaObject::Connection authRequestConnection;
235 QMetaObject::Connection proxyAuthenticationConnection;
237 QMetaObject::Connection sslErrorsConnection;
240 if ( requestMadeFromMainThread )
242 authRequestConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred,
this, resumeMainThread, Qt::DirectConnection );
243 proxyAuthenticationConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired,
this, resumeMainThread, Qt::DirectConnection );
253 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
257 if ( requestMadeFromMainThread )
260 disconnect( authRequestConnection );
261 disconnect( proxyAuthenticationConnection );
263 disconnect( sslErrorsConnection );
268 if ( requestMadeFromMainThread )
270 waitConditionMutex.lock();
271 threadFinished =
true;
272 authRequestBufferNotEmpty.wakeAll();
273 waitConditionMutex.unlock();
277 if ( requestMadeFromMainThread )
279 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
280 downloaderThread->start();
284 waitConditionMutex.lock();
285 if ( threadFinished )
287 waitConditionMutex.unlock();
290 authRequestBufferNotEmpty.wait( &waitConditionMutex );
296 if ( !threadFinished )
298 waitConditionMutex.unlock();
300 QgsApplication::processEvents();
306 waitConditionMutex.unlock();
310 downloaderThread->wait();
314 downloaderFunction();
324 mReply->deleteLater();
329void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
331 QgsDebugMsgLevel( u
"%1 of %2 bytes downloaded."_s.arg( bytesReceived ).arg( bytesTotal < 0 ? u
"unknown number of"_s : QString::number( bytesTotal ) ), 2 );
333 if ( bytesReceived != 0 )
334 mGotNonEmptyResponse =
true;
336 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
338 if ( mReply->error() == QNetworkReply::NoError )
340 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
355void QgsBlockingNetworkRequest::replyFinished()
357 if ( !mIsAborted && mReply )
360 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
363 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
368 const QUrl &toUrl = redirect.toUrl();
370 if ( toUrl == mReply->url() )
372 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
377 mReplyContent.clear();
381 QNetworkRequest request( toUrl );
385 mReplyContent.clear();
386 mErrorMessage = errorMessageFailedAuth();
399 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
400 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
401 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
404 if ( mReply->request().hasRawHeader(
"Range" ) )
405 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
407 mReply->deleteLater();
410 QgsDebugMsgLevel( u
"redirected: %1 forceRefresh=%2"_s.arg( redirect.toString() ).arg( mForceRefresh ), 2 );
412 sendRequestToNetworkAccessManager( request );
419 mReplyContent.clear();
420 mErrorMessage = errorMessageFailedAuth();
433 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
434 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
435 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
437 if ( request.hasRawHeader(
"Range" ) )
438 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
449 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
451 QNetworkCacheMetaData::RawHeaderList hl;
452 const auto constRawHeaders = cmd.rawHeaders();
453 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
455 if ( h.first !=
"Cache-Control" )
458 cmd.setRawHeaders( hl );
460 QgsDebugMsgLevel( u
"expirationDate:%1"_s.arg( cmd.expirationDate().toString() ), 2 );
461 if ( cmd.expirationDate().isNull() )
463 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
466 nam->cache()->updateMetaData( cmd );
474 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
478 mReplyContent = QgsNetworkReplyContent( mReply );
479 const QByteArray content = mReply->readAll();
482 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
489 mReplyContent.setContent( content );
494 if ( mReply->error() != QNetworkReply::OperationCanceledError )
496 mErrorMessage = mReply->errorString();
503 mReplyContent = QgsNetworkReplyContent( mReply );
504 mReplyContent.setContent( mReply->readAll() );
512 mReply->deleteLater();
522QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
524 return tr(
"network request update failed for authentication config" );
527void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
529 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
534 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)