24 #include <QNetworkRequest> 
   25 #include <QNetworkReply> 
   27 #include <QWaitCondition> 
   28 #include <QNetworkCacheMetaData> 
   29 #include <QAuthenticator> 
   41 void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
 
   43   if ( 
reply == mReply )
 
   59   return doRequest( Get, request, forceRefresh, feedback );
 
   65   return doRequest( Post, request, forceRefresh, feedback );
 
   70   return doRequest( Head, request, forceRefresh, feedback );
 
   76   return doRequest( Put, request, 
true, feedback );
 
   81   return doRequest( Delete, request, 
true, feedback );
 
   84 void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager( 
const QNetworkRequest &request )
 
  113   mFeedback = feedback;
 
  118   mGotNonEmptyResponse = 
false;
 
  120   mErrorMessage.clear();
 
  122   mForceRefresh = forceRefresh;
 
  123   mReplyContent.
clear();
 
  128     mErrorMessage = errorMessageFailedAuth();
 
  133   QgsDebugMsgLevel( QStringLiteral( 
"Calling: %1" ).arg( request.url().toString() ), 2 );
 
  135   request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  136   request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  138   QWaitCondition authRequestBufferNotEmpty;
 
  139   QMutex waitConditionMutex;
 
  141   bool threadFinished = 
false;
 
  142   bool success = 
false;
 
  144   const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
 
  149   std::function<void()> downloaderFunction = [ 
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
 
  159     sendRequestToNetworkAccessManager( request );
 
  167       mErrorMessage = errorMessageFailedAuth();
 
  169       if ( requestMadeFromMainThread )
 
  170         authRequestBufferNotEmpty.wakeAll();
 
  178       connect( mReply, &QNetworkReply::finished, 
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
 
  179       connect( mReply, &QNetworkReply::downloadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  181       auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
 
  185         waitConditionMutex.lock();
 
  186         authRequestBufferNotEmpty.wakeAll();
 
  187         waitConditionMutex.unlock();
 
  192       if ( requestMadeFromMainThread )
 
  205       connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
 
  210     if ( requestMadeFromMainThread )
 
  212       waitConditionMutex.lock();
 
  213       threadFinished = 
true;
 
  214       authRequestBufferNotEmpty.wakeAll();
 
  215       waitConditionMutex.unlock();
 
  219   if ( requestMadeFromMainThread )
 
  221     std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
 
  222     downloaderThread->start();
 
  226       waitConditionMutex.lock();
 
  227       if ( threadFinished )
 
  229         waitConditionMutex.unlock();
 
  232       authRequestBufferNotEmpty.wait( &waitConditionMutex );
 
  238       if ( !threadFinished )
 
  240         waitConditionMutex.unlock();
 
  248         waitConditionMutex.unlock();
 
  252     downloaderThread->wait();
 
  256     downloaderFunction();
 
  266     mReply->deleteLater();
 
  271 void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
 
  273   QgsDebugMsgLevel( QStringLiteral( 
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( 
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
 
  275   if ( bytesReceived != 0 )
 
  276     mGotNonEmptyResponse = 
true;
 
  278   if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
 
  280     if ( mReply->error() == QNetworkReply::NoError )
 
  282       QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
 
  283       if ( !redirect.isNull() )
 
  294 void QgsBlockingNetworkRequest::replyFinished()
 
  296   if ( !mIsAborted && mReply )
 
  299     if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
 
  302       QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
 
  303       if ( !redirect.isNull() )
 
  307         const QUrl &toUrl = redirect.toUrl();
 
  309         if ( toUrl == mReply->url() )
 
  311           mErrorMessage = tr( 
"Redirect loop detected: %1" ).arg( toUrl.toString() );
 
  313           mReplyContent.
clear();
 
  317           QNetworkRequest request( toUrl );
 
  321             mReplyContent.
clear();
 
  322             mErrorMessage = errorMessageFailedAuth();
 
  329           request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  330           request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  332           mReply->deleteLater();
 
  335           QgsDebugMsgLevel( QStringLiteral( 
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
 
  337           sendRequestToNetworkAccessManager( request );
 
  344             mReplyContent.
clear();
 
  345             mErrorMessage = errorMessageFailedAuth();
 
  352           connect( mReply, &QNetworkReply::finished, 
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
 
  353           connect( mReply, &QNetworkReply::downloadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  363           QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
 
  365           QNetworkCacheMetaData::RawHeaderList hl;
 
  366           const auto constRawHeaders = cmd.rawHeaders();
 
  367           for ( 
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
 
  369             if ( h.first != 
"Cache-Control" )
 
  372           cmd.setRawHeaders( hl );
 
  374           QgsDebugMsgLevel( QStringLiteral( 
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
 
  375           if ( cmd.expirationDate().isNull() )
 
  377             cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
 
  380           nam->cache()->updateMetaData( cmd );
 
  388         bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
 
  389         QgsDebugMsgLevel( QStringLiteral( 
"Reply was cached: %1" ).arg( fromCache ), 2 );
 
  393         const QByteArray content = mReply->readAll();
 
  394         if ( content.isEmpty() && !mGotNonEmptyResponse && mMethod == Get )
 
  396           mErrorMessage = tr( 
"empty response: %1" ).arg( mReply->errorString() );
 
  405       if ( mReply->error() != QNetworkReply::OperationCanceledError )
 
  407         mErrorMessage = mReply->errorString();
 
  412       mReplyContent.
setContent( mReply->readAll() );
 
  420     mReply->deleteLater();
 
  427 QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
 
  429   return tr( 
"network request update failed for authentication config" );
 
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,...
void downloadProgress(qint64, qint64)
Emitted when when data arrives during a request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
QgsBlockingNetworkRequest()
Constructor for QgsBlockingNetworkRequest.
~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.
ErrorCode put(QNetworkRequest &request, const QByteArray &data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
void downloadFinished()
Emitted once a request has finished downloading.
ErrorCode deleteResource(QNetworkRequest &request, QgsFeedback *feedback=nullptr)
Performs a "delete" operation on the specified request.
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.
ErrorCode post(QNetworkRequest &request, const QByteArray &data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
@ 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() or post() request has been made.
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.
#define QgsDebugMsgLevel(str, level)