17#include "moc_qgsblockingnetworkrequest.cpp" 
   26#include <QNetworkRequest> 
   27#include <QNetworkReply> 
   29#include <QWaitCondition> 
   30#include <QNetworkCacheMetaData> 
   31#include <QAuthenticator> 
   44void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
 
   46  if ( 
reply == mReply )
 
   67  QByteArray ldata( data );
 
   68  QBuffer buffer( &ldata );
 
   69  buffer.open( QIODevice::ReadOnly );
 
   70  return post( request, &buffer, forceRefresh, feedback );
 
 
   86  QByteArray ldata( data );
 
   87  QBuffer buffer( &ldata );
 
   88  buffer.open( QIODevice::ReadOnly );
 
   89  return put( request, &buffer, feedback );
 
 
  103void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager( 
const QNetworkRequest &request )
 
  132  mFeedback = feedback;
 
  137  mGotNonEmptyResponse = 
false;
 
  138  mRequestFlags = requestFlags;
 
  140  mErrorMessage.clear();
 
  142  mForceRefresh = forceRefresh;
 
  143  mReplyContent.
clear();
 
  148    mErrorMessage = errorMessageFailedAuth();
 
  153  QgsDebugMsgLevel( QStringLiteral( 
"Calling: %1" ).arg( request.url().toString() ), 2 );
 
  155  request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
 
  156  request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  157  request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  159  QWaitCondition authRequestBufferNotEmpty;
 
  160  QMutex waitConditionMutex;
 
  162  bool threadFinished = 
false;
 
  163  bool success = 
false;
 
  165  const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
 
  170  const std::function<void()> downloaderFunction = [ 
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
 
  180    sendRequestToNetworkAccessManager( request );
 
  188      mErrorMessage = errorMessageFailedAuth();
 
  190      if ( requestMadeFromMainThread )
 
  191        authRequestBufferNotEmpty.wakeAll();
 
  199      connect( mReply, &QNetworkReply::finished, 
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
 
  200      connect( mReply, &QNetworkReply::downloadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  201      connect( mReply, &QNetworkReply::uploadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  203      if ( request.hasRawHeader( 
"Range" ) )
 
  204        connect( mReply, &QNetworkReply::metaDataChanged, 
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
 
  206      auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
 
  210        waitConditionMutex.lock();
 
  211        authRequestBufferNotEmpty.wakeAll();
 
  212        waitConditionMutex.unlock();
 
  217      QMetaObject::Connection authRequestConnection;
 
  218      QMetaObject::Connection proxyAuthenticationConnection;
 
  220      QMetaObject::Connection sslErrorsConnection;
 
  223      if ( requestMadeFromMainThread )
 
  225        authRequestConnection = connect( 
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred, 
this, resumeMainThread, Qt::DirectConnection );
 
  226        proxyAuthenticationConnection = connect( 
QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, 
this, resumeMainThread, Qt::DirectConnection );
 
  236      connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
 
  240      if ( requestMadeFromMainThread )
 
  243        disconnect( authRequestConnection );
 
  244        disconnect( proxyAuthenticationConnection );
 
  246        disconnect( sslErrorsConnection );
 
  251    if ( requestMadeFromMainThread )
 
  253      waitConditionMutex.lock();
 
  254      threadFinished = 
true;
 
  255      authRequestBufferNotEmpty.wakeAll();
 
  256      waitConditionMutex.unlock();
 
  260  if ( requestMadeFromMainThread )
 
  262    auto downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
 
  263    downloaderThread->start();
 
  267      waitConditionMutex.lock();
 
  268      if ( threadFinished )
 
  270        waitConditionMutex.unlock();
 
  273      authRequestBufferNotEmpty.wait( &waitConditionMutex );
 
  279      if ( !threadFinished )
 
  281        waitConditionMutex.unlock();
 
  283        QgsApplication::processEvents();
 
  289        waitConditionMutex.unlock();
 
  293    downloaderThread->wait();
 
  297    downloaderFunction();
 
  307    mReply->deleteLater();
 
 
  312void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
 
  314  QgsDebugMsgLevel( QStringLiteral( 
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( 
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
 
  316  if ( bytesReceived != 0 )
 
  317    mGotNonEmptyResponse = 
true;
 
  319  if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
 
  321    if ( mReply->error() == QNetworkReply::NoError )
 
  323      const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
 
  338void QgsBlockingNetworkRequest::replyFinished()
 
  340  if ( !mIsAborted && mReply )
 
  343    if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
 
  346      const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
 
  351        const QUrl &toUrl = redirect.toUrl();
 
  353        if ( toUrl == mReply->url() )
 
  355          mErrorMessage = tr( 
"Redirect loop detected: %1" ).arg( toUrl.toString() );
 
  357          mReplyContent.
clear();
 
  361          QNetworkRequest request( toUrl );
 
  365            mReplyContent.
clear();
 
  366            mErrorMessage = errorMessageFailedAuth();
 
  376          request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy );
 
  377          request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  378          request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  381          if ( mReply->request().hasRawHeader( 
"Range" ) )
 
  382            request.setRawHeader( 
"Range", mReply->request().rawHeader( 
"Range" ) );
 
  384          mReply->deleteLater();
 
  387          QgsDebugMsgLevel( QStringLiteral( 
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
 
  389          sendRequestToNetworkAccessManager( request );
 
  396            mReplyContent.
clear();
 
  397            mErrorMessage = errorMessageFailedAuth();
 
  407          connect( mReply, &QNetworkReply::finished, 
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
 
  408          connect( mReply, &QNetworkReply::downloadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  409          connect( mReply, &QNetworkReply::uploadProgress, 
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
 
  411          if ( request.hasRawHeader( 
"Range" ) )
 
  412            connect( mReply, &QNetworkReply::metaDataChanged, 
this, &QgsBlockingNetworkRequest::abortIfNotPartialContentReturned, Qt::DirectConnection );
 
  423          QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
 
  425          QNetworkCacheMetaData::RawHeaderList hl;
 
  426          const auto constRawHeaders = cmd.rawHeaders();
 
  427          for ( 
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
 
  429            if ( h.first != 
"Cache-Control" )
 
  432          cmd.setRawHeaders( hl );
 
  434          QgsDebugMsgLevel( QStringLiteral( 
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
 
  435          if ( cmd.expirationDate().isNull() )
 
  437            cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
 
  440          nam->cache()->updateMetaData( cmd );
 
  448        const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
 
  449        QgsDebugMsgLevel( QStringLiteral( 
"Reply was cached: %1" ).arg( fromCache ), 2 );
 
  453        const QByteArray content = mReply->readAll();
 
  456          mErrorMessage = tr( 
"empty response: %1" ).arg( mReply->errorString() );
 
  465      if ( mReply->error() != QNetworkReply::OperationCanceledError )
 
  467        mErrorMessage = mReply->errorString();
 
  472      mReplyContent.
setContent( mReply->readAll() );
 
  480    mReply->deleteLater();
 
  490QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
 
  492  return tr( 
"network request update failed for authentication config" );
 
  495void QgsBlockingNetworkRequest::abortIfNotPartialContentReturned()
 
  497  if ( mReply && mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
 
  502    mErrorMessage = tr( 
"The server does not support range 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,...
 
QgsBlockingNetworkRequest()
Constructor for QgsBlockingNetworkRequest.
 
ErrorCode put(QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
 
void uploadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when when data are sent during a request.
 
~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.
 
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).
 
QNetworkAccessManager with additional QGIS specific logic.
 
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.
 
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)