@@ -24,6 +24,8 @@
#include < QEventLoop>
#include < QNetworkCacheMetaData>
#include < QCryptographicHash> // just for testin file:// fake_qgis_http_endpoint hack
#include < QFuture>
#include < QtConcurrent>
const qint64 READ_BUFFER_SIZE_HINT = 1024 * 1024 ;
@@ -126,26 +128,95 @@ bool QgsWfsRequest::sendGET( const QUrl &url, bool synchronous, bool forceRefres
request.setAttribute ( QNetworkRequest::CacheSaveControlAttribute, true );
}
mReply = QgsNetworkAccessManager::instance ()->get ( request );
mReply ->setReadBufferSize ( READ_BUFFER_SIZE_HINT );
if ( !mUri .auth ().setAuthorizationReply ( mReply ) )
QWaitCondition waitCondition;
QMutex waitConditionMutex;
std::function<bool ()> downloaderFunction = [ this , request, synchronous, &waitConditionMutex, &waitCondition ]()
{
mErrorCode = QgsWfsRequest::NetworkError;
mErrorMessage = errorMessageFailedAuth ();
QgsMessageLog::logMessage ( mErrorMessage , tr ( " WFS" ) );
return false ;
}
connect ( mReply , &QNetworkReply::finished, this , &QgsWfsRequest::replyFinished );
connect ( mReply , &QNetworkReply::downloadProgress, this , &QgsWfsRequest::replyProgress );
if ( QThread::currentThread () != QgsApplication::instance ()->thread () )
QgsNetworkAccessManager::instance ( Qt::DirectConnection );
if ( !synchronous )
return true ;
bool success = true ;
mReply = QgsNetworkAccessManager::instance ()-> get ( request ) ;
QEventLoop loop;
connect ( this , &QgsWfsRequest::downloadFinished, &loop, &QEventLoop::quit );
loop.exec ( QEventLoop::ExcludeUserInputEvents );
mReply ->setReadBufferSize ( READ_BUFFER_SIZE_HINT );
if ( !mUri .auth ().setAuthorizationReply ( mReply ) )
{
mErrorCode = QgsWfsRequest::NetworkError;
mErrorMessage = errorMessageFailedAuth ();
QgsMessageLog::logMessage ( mErrorMessage , tr ( " WFS" ) );
waitCondition.wakeAll ();
success = false ;
}
else
{
// We are able to use direct connection here, because we
// * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
// * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
connect ( mReply , &QNetworkReply::finished, this , &QgsWfsRequest::replyFinished, Qt::DirectConnection );
connect ( mReply , &QNetworkReply::downloadProgress, this , &QgsWfsRequest::replyProgress, Qt::DirectConnection );
return mErrorMessage .isEmpty ();
if ( synchronous )
{
auto resumeMainThread = [&waitConditionMutex, &waitCondition]()
{
waitConditionMutex.lock ();
waitCondition.wakeAll ();
waitConditionMutex.unlock ();
waitConditionMutex.lock ();
waitCondition.wait ( &waitConditionMutex );
waitConditionMutex.unlock ();
};
connect ( QgsNetworkAccessManager::instance (), &QgsNetworkAccessManager::authenticationRequired, this , resumeMainThread, Qt::DirectConnection );
connect ( QgsNetworkAccessManager::instance (), &QgsNetworkAccessManager::proxyAuthenticationRequired, this , resumeMainThread, Qt::DirectConnection );
#ifndef QT_NO_SSL
connect ( QgsNetworkAccessManager::instance (), &QgsNetworkAccessManager::sslErrors, this , resumeMainThread, Qt::DirectConnection );
#endif
QEventLoop loop;
connect ( this , &QgsWfsRequest::downloadFinished, &loop, &QEventLoop::quit, Qt::DirectConnection );
loop.exec ();
}
}
waitCondition.wakeAll ();
return success;
};
bool success;
if ( synchronous && QThread::currentThread () == QApplication::instance ()->thread () )
{
std::unique_ptr<DownloaderThread> downloaderThread = qgis::make_unique<DownloaderThread>( downloaderFunction );
downloaderThread->start ();
while ( !downloaderThread->isFinished () )
{
waitConditionMutex.lock ();
waitCondition.wait ( &waitConditionMutex );
waitConditionMutex.unlock ();
// If the downloader thread wakes us (the main thread) up and is not yet finished
// he needs the authentication to run.
// The processEvents() call gives the auth manager the chance to show a dialog and
// once done with that, we can wake the downloaderThread again and continue the download.
if ( !downloaderThread->isFinished () )
{
QgsApplication::instance ()->processEvents ();
waitConditionMutex.lock ();
waitCondition.wakeAll ();
waitConditionMutex.unlock ();
}
}
success = downloaderThread->success ();
}
else
{
success = downloaderFunction ();
}
return success && mErrorMessage .isEmpty ();
}
bool QgsWfsRequest::sendPOST ( const QUrl &url, const QString &contentTypeHeader, const QByteArray &data )
This comment has been minimized.
@m-kuhn Hi Matthias, seeing this commit. My eye was pulled yesterday to this log msg which you get when you draw an xyz (eg OSM):
../src/providers/wms/qgswmsprovider.cpp: 617: (draw) [0ms] [thread:0x558840d62910] Trying to draw a WMS image on the main thread. Stop it!
Confirmed with a just compiled version now. Is that a call that a thread is stopped? Or is it asking devs to stop drawing in the main thread?
This comment has been minimized.
@rduivenvoorde in an idea world yes: there should be no blocking downloads done on the main thread.
The message is an advice for developers, no effect at the moment (except for an increased risk of crash in such a scenario).
This pull request is the first brick of a code refactoring to make things more stable (i.e. still not bullet proof, but the risk surface becomes smaller and is limited to cases when authentication is involved).
This comment has been minimized.
@m-kuhn what's the grand plan here?
This comment has been minimized.
This comment has been minimized.
I think indeed the online datasources will become more and more used. That is why I hope we can make QgsNetworkManager more user/python friendly (see qgis/QGIS-Enhancement-Proposals#123).
Also: what is https://qgis.org/pyqgis/master/core/Network/QgsNetworkContentFetcher.html becoming then? Looks to me as a second http-fetch class (though does not do POST?), isn't that a little redundant then?
This comment has been minimized.
The right thing to use in 90% of the cases when you are able and not so lazy to use blocking downloads.