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 = qgis::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 )
298 if ( mReply->error() == QNetworkReply::NoError && ( !mFeedback || !mFeedback->isCanceled() ) )
301 QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
302 if ( !redirect.isNull() )
306 const QUrl &toUrl = redirect.toUrl();
308 if ( toUrl == mReply->url() )
310 mErrorMessage = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
312 mReplyContent.
clear();
316 QNetworkRequest request( toUrl );
320 mReplyContent.
clear();
321 mErrorMessage = errorMessageFailedAuth();
328 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
329 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
331 mReply->deleteLater();
334 QgsDebugMsgLevel( QStringLiteral(
"redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
336 sendRequestToNetworkAccessManager( request );
343 mReplyContent.
clear();
344 mErrorMessage = errorMessageFailedAuth();
351 connect( mReply, &QNetworkReply::finished,
this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
352 connect( mReply, &QNetworkReply::downloadProgress,
this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
362 QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
364 QNetworkCacheMetaData::RawHeaderList hl;
365 const auto constRawHeaders = cmd.rawHeaders();
366 for (
const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
368 if ( h.first !=
"Cache-Control" )
371 cmd.setRawHeaders( hl );
373 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
374 if ( cmd.expirationDate().isNull() )
376 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
379 nam->cache()->updateMetaData( cmd );
387 bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
388 QgsDebugMsgLevel( QStringLiteral(
"Reply was cached: %1" ).arg( fromCache ), 2 );
392 const QByteArray content = mReply->readAll();
393 if ( content.isEmpty() && !mGotNonEmptyResponse && mMethod == Get )
395 mErrorMessage = tr(
"empty response: %1" ).arg( mReply->errorString() );
404 if ( mReply->error() != QNetworkReply::OperationCanceledError )
406 mErrorMessage = mReply->errorString();
418 mReply->deleteLater();
425 QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
427 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::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)