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 );
82 mPayloadDataVariant = data;
88 mPayloadDataVariant = data;
99 QByteArray ldata( data );
100 QBuffer buffer( &ldata );
101 buffer.open( QIODevice::ReadOnly );
102 return put( request, &buffer, feedback );
107 mPayloadDataVariant = data;
116void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
125 if ( std::holds_alternative<QHttpMultiPart *>( mPayloadDataVariant ) )
129 else if ( std::holds_alternative<QIODevice *>( mPayloadDataVariant ) )
136 QgsDebugError( QString(
"Not implemented std::variant type" ) );
157 mFeedback = feedback;
162 mGotNonEmptyResponse =
false;
163 mRequestFlags = requestFlags;
165 mErrorMessage.clear();
167 mForceRefresh = forceRefresh;
168 mReplyContent.clear();
173 mErrorMessage = errorMessageFailedAuth();
183 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
184 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
185 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
187 QWaitCondition authRequestBufferNotEmpty;
188 QMutex waitConditionMutex;
190 bool threadFinished =
false;
191 bool success =
false;
193 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
198 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread]() {
207 sendRequestToNetworkAccessManager( request );
215 mErrorMessage = errorMessageFailedAuth();
220 if ( requestMadeFromMainThread )
221 authRequestBufferNotEmpty.wakeAll();
229 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
230 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
231 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
233 if ( request.hasRawHeader(
"Range" ) )
234 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
236 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty]() {
239 waitConditionMutex.lock();
240 authRequestBufferNotEmpty.wakeAll();
241 waitConditionMutex.unlock();
246 QMetaObject::Connection authRequestConnection;
247 QMetaObject::Connection proxyAuthenticationConnection;
249 QMetaObject::Connection sslErrorsConnection;
252 if ( requestMadeFromMainThread )
254 authRequestConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred,
this, resumeMainThread, Qt::DirectConnection );
255 proxyAuthenticationConnection = connect(
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired,
this, resumeMainThread, Qt::DirectConnection );
265 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
269 if ( requestMadeFromMainThread )
272 disconnect( authRequestConnection );
273 disconnect( proxyAuthenticationConnection );
275 disconnect( sslErrorsConnection );
280 if ( requestMadeFromMainThread )
282 waitConditionMutex.lock();
283 threadFinished =
true;
284 authRequestBufferNotEmpty.wakeAll();
285 waitConditionMutex.unlock();
289 if ( requestMadeFromMainThread )
291 auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
292 downloaderThread->start();
296 waitConditionMutex.lock();
297 if ( threadFinished )
299 waitConditionMutex.unlock();
302 authRequestBufferNotEmpty.wait( &waitConditionMutex );
308 if ( !threadFinished )
310 waitConditionMutex.unlock();
312 QgsApplication::processEvents();
318 waitConditionMutex.unlock();
322 downloaderThread->wait();
326 downloaderFunction();
336 mReply->deleteLater();
341void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
343 QgsDebugMsgLevel( u
"%1 of %2 bytes downloaded."_s.arg( bytesReceived ).arg( bytesTotal < 0 ? u
"unknown number of"_s : QString::number( bytesTotal ) ), 2 );
345 if ( bytesReceived != 0 )
346 mGotNonEmptyResponse =
true;
348 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
350 if ( mReply->error() == QNetworkReply::NoError )
352 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
367void QgsBlockingNetworkRequest::replyFinished()
369 if ( !mIsAborted && mReply )
371 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
374 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
379 const QUrl &toUrl = redirect.toUrl();
381 if ( toUrl == mReply->url() )
383 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
388 mReplyContent.clear();
392 QNetworkRequest request( toUrl );
396 mReplyContent.clear();
397 mErrorMessage = errorMessageFailedAuth();
410 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
411 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
412 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
415 if ( mReply->request().hasRawHeader(
"Range" ) )
416 request.setRawHeader(
"Range", mReply->request().rawHeader(
"Range" ) );
418 mReply->deleteLater();
421 QgsDebugMsgLevel( u
"redirected: %1 forceRefresh=%2"_s.arg( redirect.toString() ).arg( mForceRefresh ), 2 );
423 sendRequestToNetworkAccessManager( request );
430 mReplyContent.clear();
431 mErrorMessage = errorMessageFailedAuth();
444 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
445 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
446 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
448 if ( request.hasRawHeader(
"Range" ) )
449 connect( mReply, &QNetworkReply::metaDataChanged,
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
460 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
462 QNetworkCacheMetaData::RawHeaderList hl;
463 const auto constRawHeaders = cmd.rawHeaders();
464 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
466 if ( h.first !=
"Cache-Control" )
469 cmd.setRawHeaders( hl );
471 QgsDebugMsgLevel( u
"expirationDate:%1"_s.arg( cmd.expirationDate().toString() ), 2 );
472 if ( cmd.expirationDate().isNull() )
474 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
477 nam->cache()->updateMetaData( cmd );
485 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
489 mReplyContent = QgsNetworkReplyContent( mReply );
490 const QByteArray content = mReply->readAll();
493 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
500 mReplyContent.setContent( content );
505 if ( mReply->error() != QNetworkReply::OperationCanceledError && ( !mFeedback || !mFeedback->isCanceled() ) )
507 mErrorMessage = mReply->errorString();
514 mReplyContent = QgsNetworkReplyContent( mReply );
515 mReplyContent.setContent( mReply->readAll() );
523 mReply->deleteLater();
533QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
535 return tr(
"network request update failed for authentication config" );
538void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
540 if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
545 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)
#define QgsDebugError(str)