24 #include <QNetworkRequest>
25 #include <QNetworkReply>
27 #include <QWaitCondition>
28 #include <QNetworkCacheMetaData>
29 #include <QAuthenticator>
42 void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
44 if (
reply == mReply )
60 return doRequest( Get, request, forceRefresh, feedback );
65 QByteArray ldata( data );
66 QBuffer buffer( &ldata );
67 buffer.open( QIODevice::ReadOnly );
68 return post( request, &buffer, forceRefresh, feedback );
74 return doRequest( Post, request, forceRefresh, feedback );
79 return doRequest( Head, request, forceRefresh, feedback );
84 QByteArray ldata( data );
85 QBuffer buffer( &ldata );
86 buffer.open( QIODevice::ReadOnly );
87 return put( request, &buffer, feedback );
93 return doRequest( Put, request,
true, feedback );
98 return doRequest( Delete, request,
true, feedback );
101 void QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager(
const QNetworkRequest &request )
130 mFeedback = feedback;
135 mGotNonEmptyResponse =
false;
137 mErrorMessage.clear();
139 mForceRefresh = forceRefresh;
140 mReplyContent.
clear();
145 mErrorMessage = errorMessageFailedAuth();
150 QgsDebugMsgLevel( QStringLiteral(
"Calling: %1" ).arg( request.url().toString() ), 2 );
152 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
153 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
155 QWaitCondition authRequestBufferNotEmpty;
156 QMutex waitConditionMutex;
158 bool threadFinished =
false;
159 bool success =
false;
161 const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
166 const std::function<void()> downloaderFunction = [
this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
176 sendRequestToNetworkAccessManager( request );
184 mErrorMessage = errorMessageFailedAuth();
186 if ( requestMadeFromMainThread )
187 authRequestBufferNotEmpty.wakeAll();
195 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
196 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
197 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
199 auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
203 waitConditionMutex.lock();
204 authRequestBufferNotEmpty.wakeAll();
205 waitConditionMutex.unlock();
210 if ( requestMadeFromMainThread )
223 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
228 if ( requestMadeFromMainThread )
230 waitConditionMutex.lock();
231 threadFinished =
true;
232 authRequestBufferNotEmpty.wakeAll();
233 waitConditionMutex.unlock();
237 if ( requestMadeFromMainThread )
239 std::unique_ptr<DownloaderThread> downloaderThread = std::make_unique<DownloaderThread>( downloaderFunction );
240 downloaderThread->start();
244 waitConditionMutex.lock();
245 if ( threadFinished )
247 waitConditionMutex.unlock();
250 authRequestBufferNotEmpty.wait( &waitConditionMutex );
256 if ( !threadFinished )
258 waitConditionMutex.unlock();
260 QgsApplication::processEvents();
266 waitConditionMutex.unlock();
270 downloaderThread->wait();
274 downloaderFunction();
284 mReply->deleteLater();
289 void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
291 QgsDebugMsgLevel( QStringLiteral(
"%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) ), 2 );
293 if ( bytesReceived != 0 )
294 mGotNonEmptyResponse =
true;
296 if ( !mIsAborted && mReply && ( !mFeedback || !mFeedback->isCanceled() ) )
298 if ( mReply->error() == QNetworkReply::NoError )
300 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
301 if ( !redirect.isNull() )
309 if ( mMethod == Put || mMethod == Post )
315 void QgsBlockingNetworkRequest::replyFinished()
317 if ( !mIsAborted && mReply )
320 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
323 const QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
324 if ( !redirect.isNull() )
328 const QUrl &toUrl = redirect.toUrl();
330 if ( toUrl == mReply->url() )
332 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
334 mReplyContent.
clear();
338 QNetworkRequest request( toUrl );
342 mReplyContent.
clear();
343 mErrorMessage = errorMessageFailedAuth();
353 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
354 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
356 mReply->deleteLater();
359 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
361 sendRequestToNetworkAccessManager( request );
368 mReplyContent.
clear();
369 mErrorMessage = errorMessageFailedAuth();
379 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
380 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
381 connect( mReply, &QNetworkReply::uploadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
391 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
393 QNetworkCacheMetaData::RawHeaderList hl;
394 const auto constRawHeaders = cmd.rawHeaders();
395 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
397 if ( h.first !=
"Cache-Control" )
400 cmd.setRawHeaders( hl );
402 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
403 if ( cmd.expirationDate().isNull() )
405 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
408 nam->cache()->updateMetaData( cmd );
416 const bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
417 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
421 const QByteArray content = mReply->readAll();
422 if ( content.isEmpty() && !mGotNonEmptyResponse && mMethod == Get )
424 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
433 if ( mReply->error() != QNetworkReply::OperationCanceledError )
435 mErrorMessage = mReply->errorString();
440 mReplyContent.
setContent( mReply->readAll() );
448 mReply->deleteLater();
458 QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
460 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.
ErrorCode put(QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
~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 uploadProgress(qint64, qint64)
Emitted when when data are sent during a request.
@ 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 Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
#define QgsDebugMsgLevel(str, level)