mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
Make network authentication request handling thread safe, avoid
races and crashes when auth requests occur in non-main thread Like the recent SSL error handling, this commit abstracts out the network authentication handling into a thread safe approach.
This commit is contained in:
parent
6fa3bf8e8b
commit
8ee2e793f8
@ -268,6 +268,22 @@ This signal is propagated to the main thread QgsNetworkAccessManager instance, s
|
|||||||
only to connect to the main thread's signal in order to receive notifications about requests
|
only to connect to the main thread's signal in order to receive notifications about requests
|
||||||
created in any thread.
|
created in any thread.
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
%End
|
||||||
|
|
||||||
|
void requestRequiresAuth( int requestId, const QString &realm );
|
||||||
|
%Docstring
|
||||||
|
Emitted when a network request prompts an authentication request.
|
||||||
|
|
||||||
|
The ``requestId`` argument reflects the unique ID identifying the original request which the authentication relates to.
|
||||||
|
|
||||||
|
This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
||||||
|
only to connect to the main thread's signal in order to receive notifications about authentication requests
|
||||||
|
from any thread.
|
||||||
|
|
||||||
|
This signal is for debugging and logging purposes only, and cannot be used to respond to the
|
||||||
|
requests. See QgsNetworkAuthenticationHandler for details on how to handle authentication requests.
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
%End
|
%End
|
||||||
|
|
||||||
@ -276,12 +292,15 @@ created in any thread.
|
|||||||
%Docstring
|
%Docstring
|
||||||
Emitted when a network request encounters SSL ``errors``.
|
Emitted when a network request encounters SSL ``errors``.
|
||||||
|
|
||||||
The ``requestId`` argument reflects the unique ID identifying the original request which the progress report relates to.
|
The ``requestId`` argument reflects the unique ID identifying the original request which the SSL error relates to.
|
||||||
|
|
||||||
This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
||||||
only to connect to the main thread's signal in order to receive notifications about SSL errors
|
only to connect to the main thread's signal in order to receive notifications about SSL errors
|
||||||
from any thread.
|
from any thread.
|
||||||
|
|
||||||
|
This signal is for debugging and logging purposes only, and cannot be used to respond to the errors.
|
||||||
|
See QgsSslErrorHandler for details on how to handle SSL errors and potentially ignore them.
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
%End
|
%End
|
||||||
|
|
||||||
@ -296,6 +315,7 @@ from any thread.
|
|||||||
void requestTimedOut( QNetworkReply * );
|
void requestTimedOut( QNetworkReply * );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QNetworkReply *createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0 );
|
virtual QNetworkReply *createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0 );
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ SET(QGIS_APP_SRCS
|
|||||||
qgisappstylesheet.cpp
|
qgisappstylesheet.cpp
|
||||||
qgsabout.cpp
|
qgsabout.cpp
|
||||||
qgsalignrasterdialog.cpp
|
qgsalignrasterdialog.cpp
|
||||||
|
qgsappauthrequesthandler.cpp
|
||||||
qgsappbrowserproviders.cpp
|
qgsappbrowserproviders.cpp
|
||||||
qgsapplayertreeviewmenuprovider.cpp
|
qgsapplayertreeviewmenuprovider.cpp
|
||||||
qgsappwindowmanager.cpp
|
qgsappwindowmanager.cpp
|
||||||
|
@ -142,6 +142,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
|
|||||||
#include "qgisplugin.h"
|
#include "qgisplugin.h"
|
||||||
#include "qgsabout.h"
|
#include "qgsabout.h"
|
||||||
#include "qgsalignrasterdialog.h"
|
#include "qgsalignrasterdialog.h"
|
||||||
|
#include "qgsappauthrequesthandler.h"
|
||||||
#include "qgsappbrowserproviders.h"
|
#include "qgsappbrowserproviders.h"
|
||||||
#include "qgsapplayertreeviewmenuprovider.h"
|
#include "qgsapplayertreeviewmenuprovider.h"
|
||||||
#include "qgsapplication.h"
|
#include "qgsapplication.h"
|
||||||
@ -13883,85 +13884,18 @@ void QgisApp::namSetup()
|
|||||||
{
|
{
|
||||||
QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
|
QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
|
||||||
|
|
||||||
connect( nam, &QNetworkAccessManager::authenticationRequired,
|
|
||||||
this, &QgisApp::namAuthenticationRequired );
|
|
||||||
|
|
||||||
connect( nam, &QNetworkAccessManager::proxyAuthenticationRequired,
|
connect( nam, &QNetworkAccessManager::proxyAuthenticationRequired,
|
||||||
this, &QgisApp::namProxyAuthenticationRequired );
|
this, &QgisApp::namProxyAuthenticationRequired );
|
||||||
|
|
||||||
connect( nam, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ),
|
connect( nam, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ),
|
||||||
this, &QgisApp::namRequestTimedOut );
|
this, &QgisApp::namRequestTimedOut );
|
||||||
|
|
||||||
|
nam->setAuthHandler( qgis::make_unique<QgsAppAuthRequestHandler>() );
|
||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
nam->setSslErrorHandler( qgis::make_unique<QgsAppSslErrorHandler>() );
|
nam->setSslErrorHandler( qgis::make_unique<QgsAppSslErrorHandler>() );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgisApp::namAuthenticationRequired( QNetworkReply *inReply, QAuthenticator *auth )
|
|
||||||
{
|
|
||||||
QPointer<QNetworkReply> reply( inReply );
|
|
||||||
Q_ASSERT( qApp->thread() == QThread::currentThread() );
|
|
||||||
|
|
||||||
QString username = auth->user();
|
|
||||||
QString password = auth->password();
|
|
||||||
|
|
||||||
if ( username.isEmpty() && password.isEmpty() && reply->request().hasRawHeader( "Authorization" ) )
|
|
||||||
{
|
|
||||||
QByteArray header( reply->request().rawHeader( "Authorization" ) );
|
|
||||||
if ( header.startsWith( "Basic " ) )
|
|
||||||
{
|
|
||||||
QByteArray auth( QByteArray::fromBase64( header.mid( 6 ) ) );
|
|
||||||
int pos = auth.indexOf( ':' );
|
|
||||||
if ( pos >= 0 )
|
|
||||||
{
|
|
||||||
username = auth.left( pos );
|
|
||||||
password = auth.mid( pos + 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
{
|
|
||||||
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
|
||||||
ok = QgsCredentials::instance()->get(
|
|
||||||
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
|
||||||
username, password,
|
|
||||||
tr( "Authentication required" ) );
|
|
||||||
}
|
|
||||||
if ( !ok )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( reply.isNull() || reply->isFinished() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( auth->user() != username || ( password != auth->password() && !password.isNull() ) )
|
|
||||||
break;
|
|
||||||
|
|
||||||
// credentials didn't change - stored ones probably wrong? clear password and retry
|
|
||||||
{
|
|
||||||
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
|
||||||
QgsCredentials::instance()->put(
|
|
||||||
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
|
||||||
username, QString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save credentials
|
|
||||||
{
|
|
||||||
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
|
||||||
QgsCredentials::instance()->put(
|
|
||||||
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
|
||||||
username, password
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
auth->setUser( username );
|
|
||||||
auth->setPassword( password );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgisApp::namProxyAuthenticationRequired( const QNetworkProxy &proxy, QAuthenticator *auth )
|
void QgisApp::namProxyAuthenticationRequired( const QNetworkProxy &proxy, QAuthenticator *auth )
|
||||||
{
|
{
|
||||||
QgsSettings settings;
|
QgsSettings settings;
|
||||||
|
@ -879,7 +879,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
void setAppStyleSheet( const QString &stylesheet );
|
void setAppStyleSheet( const QString &stylesheet );
|
||||||
|
|
||||||
//! request credentials for network manager
|
//! request credentials for network manager
|
||||||
void namAuthenticationRequired( QNetworkReply *reply, QAuthenticator *auth );
|
|
||||||
void namProxyAuthenticationRequired( const QNetworkProxy &proxy, QAuthenticator *auth );
|
void namProxyAuthenticationRequired( const QNetworkProxy &proxy, QAuthenticator *auth );
|
||||||
void namRequestTimedOut( const QgsNetworkRequestParameters &request );
|
void namRequestTimedOut( const QgsNetworkRequestParameters &request );
|
||||||
|
|
||||||
|
82
src/app/qgsappauthrequesthandler.cpp
Normal file
82
src/app/qgsappauthrequesthandler.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsappauthrequesthandler.cpp
|
||||||
|
---------------------------
|
||||||
|
begin : January 2019
|
||||||
|
copyright : (C) 2019 by Nyall Dawson
|
||||||
|
email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgsappauthrequesthandler.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
#include "qgsauthcertutils.h"
|
||||||
|
#include "qgsapplication.h"
|
||||||
|
#include "qgsauthmanager.h"
|
||||||
|
#include "qgisapp.h"
|
||||||
|
#include "qgscredentials.h"
|
||||||
|
#include <QAuthenticator>
|
||||||
|
|
||||||
|
void QgsAppAuthRequestHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
|
||||||
|
{
|
||||||
|
QString username = auth->user();
|
||||||
|
QString password = auth->password();
|
||||||
|
|
||||||
|
if ( username.isEmpty() && password.isEmpty() && reply->request().hasRawHeader( "Authorization" ) )
|
||||||
|
{
|
||||||
|
QByteArray header( reply->request().rawHeader( "Authorization" ) );
|
||||||
|
if ( header.startsWith( "Basic " ) )
|
||||||
|
{
|
||||||
|
QByteArray auth( QByteArray::fromBase64( header.mid( 6 ) ) );
|
||||||
|
int pos = auth.indexOf( ':' );
|
||||||
|
if ( pos >= 0 )
|
||||||
|
{
|
||||||
|
username = auth.left( pos );
|
||||||
|
password = auth.mid( pos + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
||||||
|
ok = QgsCredentials::instance()->get(
|
||||||
|
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
||||||
|
username, password,
|
||||||
|
QObject::tr( "Authentication required" ) );
|
||||||
|
}
|
||||||
|
if ( !ok )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( auth->user() != username || ( password != auth->password() && !password.isNull() ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// credentials didn't change - stored ones probably wrong? clear password and retry
|
||||||
|
{
|
||||||
|
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
||||||
|
QgsCredentials::instance()->put(
|
||||||
|
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
||||||
|
username, QString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save credentials
|
||||||
|
{
|
||||||
|
QMutexLocker lock( QgsCredentials::instance()->mutex() );
|
||||||
|
QgsCredentials::instance()->put(
|
||||||
|
QStringLiteral( "%1 at %2" ).arg( auth->realm(), reply->url().host() ),
|
||||||
|
username, password
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auth->setUser( username );
|
||||||
|
auth->setPassword( password );
|
||||||
|
}
|
30
src/app/qgsappauthrequesthandler.h
Normal file
30
src/app/qgsappauthrequesthandler.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsappauthrequesthandler.h
|
||||||
|
-------------------------
|
||||||
|
begin : January 2019
|
||||||
|
copyright : (C) 2019 by Nyall Dawson
|
||||||
|
email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSAPPAUTHREQUESTHANDLER_H
|
||||||
|
#define QGSAPPAUTHREQUESTHANDLER_H
|
||||||
|
|
||||||
|
#include "qgsnetworkaccessmanager.h"
|
||||||
|
|
||||||
|
class QgsAppAuthRequestHandler : public QgsNetworkAuthenticationHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QGSAPPAUTHREQUESTHANDLER_H
|
@ -130,6 +130,12 @@ void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHan
|
|||||||
mSslErrorHandler = std::move( handler );
|
mSslErrorHandler = std::move( handler );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
|
||||||
|
{
|
||||||
|
Q_ASSERT( sMainNAM == this );
|
||||||
|
mAuthHandler = std::move( handler );
|
||||||
|
}
|
||||||
|
|
||||||
void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
|
void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
|
||||||
{
|
{
|
||||||
mProxyFactories.insert( 0, factory );
|
mProxyFactories.insert( 0, factory );
|
||||||
@ -280,7 +286,7 @@ void QgsNetworkAccessManager::abortRequest()
|
|||||||
QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
|
QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
|
||||||
QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
|
QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
|
||||||
// Notify the application
|
// Notify the application
|
||||||
emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), reply->property( "requestId" ).toInt() ) );
|
emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
|
||||||
emit requestTimedOut( reply );
|
emit requestTimedOut( reply );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,10 +299,7 @@ void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qin
|
|||||||
{
|
{
|
||||||
if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
|
if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
|
||||||
{
|
{
|
||||||
bool ok = false;
|
emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
|
||||||
int requestId = reply->property( "requestId" ).toInt( &ok );
|
|
||||||
if ( ok )
|
|
||||||
emit downloadProgress( requestId, bytesReceived, bytesTotal );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,16 +310,10 @@ void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
|
|||||||
Q_ASSERT( reply );
|
Q_ASSERT( reply );
|
||||||
Q_ASSERT( reply->manager() == this );
|
Q_ASSERT( reply->manager() == this );
|
||||||
|
|
||||||
QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
|
|
||||||
if ( timer && timer->isActive() )
|
|
||||||
{
|
|
||||||
QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ) );
|
QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ) );
|
||||||
timer->stop();
|
pauseTimeout( reply );
|
||||||
}
|
|
||||||
bool ok = false;
|
emit requestEncounteredSslErrors( getRequestId( reply ), errors );
|
||||||
int requestId = reply->property( "requestId" ).toInt( &ok );
|
|
||||||
if ( ok )
|
|
||||||
emit requestEncounteredSslErrors( requestId, errors );
|
|
||||||
|
|
||||||
// in main thread this will trigger SSL error handler immediately and return once the errors are handled,
|
// in main thread this will trigger SSL error handler immediately and return once the errors are handled,
|
||||||
// while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
|
// while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
|
||||||
@ -336,6 +333,50 @@ void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
|
|||||||
{
|
{
|
||||||
if ( reply->manager() == this )
|
if ( reply->manager() == this )
|
||||||
{
|
{
|
||||||
|
restartTimeout( reply );
|
||||||
|
emit sslErrorsHandled( reply );
|
||||||
|
}
|
||||||
|
else if ( this == sMainNAM )
|
||||||
|
{
|
||||||
|
// notify other threads to allow them to handle the reply
|
||||||
|
qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled(); // safe to call directly - the other thread will be stuck waiting for us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
|
||||||
|
{
|
||||||
|
Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
|
||||||
|
mAuthRequestWaitCondition.wakeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
|
||||||
|
{
|
||||||
|
if ( reply->manager() == this )
|
||||||
|
{
|
||||||
|
restartTimeout( reply );
|
||||||
|
emit authRequestHandled( reply );
|
||||||
|
}
|
||||||
|
else if ( this == sMainNAM )
|
||||||
|
{
|
||||||
|
// notify other threads to allow them to handle the reply
|
||||||
|
qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterAuthRequestHandled(); // safe to call directly - the other thread will be stuck waiting for us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
|
||||||
|
{
|
||||||
|
Q_ASSERT( reply->manager() == this );
|
||||||
|
|
||||||
|
QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
|
||||||
|
if ( timer && timer->isActive() )
|
||||||
|
{
|
||||||
|
timer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
|
||||||
|
{
|
||||||
|
Q_ASSERT( reply->manager() == this );
|
||||||
// restart reply timeout
|
// restart reply timeout
|
||||||
QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
|
QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
|
||||||
if ( timer )
|
if ( timer )
|
||||||
@ -345,12 +386,11 @@ void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
|
|||||||
timer->setSingleShot( true );
|
timer->setSingleShot( true );
|
||||||
timer->start( QgsSettings().value( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt() );
|
timer->start( QgsSettings().value( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( this == sMainNAM )
|
|
||||||
{
|
int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
|
||||||
// notify other threads to allow them to handle the reply
|
{
|
||||||
qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled(); // safe to call directly - the other thread will be stuck waiting for us
|
return reply->property( "requestId" ).toInt();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
|
void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
|
||||||
@ -358,8 +398,39 @@ void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList
|
|||||||
mSslErrorHandler->handleSslErrors( reply, errors );
|
mSslErrorHandler->handleSslErrors( reply, errors );
|
||||||
afterSslErrorHandled( reply );
|
afterSslErrorHandled( reply );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
|
||||||
|
{
|
||||||
|
Q_ASSERT( reply );
|
||||||
|
Q_ASSERT( reply->manager() == this );
|
||||||
|
|
||||||
|
QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ) );
|
||||||
|
pauseTimeout( reply );
|
||||||
|
|
||||||
|
emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
|
||||||
|
|
||||||
|
// in main thread this will trigger auth handler immediately and return once the request is satisfied,
|
||||||
|
// while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
|
||||||
|
emit authRequestOccurred( reply, auth );
|
||||||
|
if ( this != sMainNAM )
|
||||||
|
{
|
||||||
|
// lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
|
||||||
|
// without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
|
||||||
|
mAuthRequestHandlerMutex.lock();
|
||||||
|
mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
|
||||||
|
mAuthRequestHandlerMutex.unlock();
|
||||||
|
afterAuthRequestHandled( reply );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
|
||||||
|
{
|
||||||
|
mAuthHandler->handleAuthRequest( reply, auth );
|
||||||
|
afterAuthRequestHandled( reply );
|
||||||
|
}
|
||||||
|
|
||||||
QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
|
QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
|
||||||
{
|
{
|
||||||
switch ( control )
|
switch ( control )
|
||||||
@ -406,10 +477,6 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType conn
|
|||||||
|
|
||||||
if ( sMainNAM != this )
|
if ( sMainNAM != this )
|
||||||
{
|
{
|
||||||
connect( this, &QNetworkAccessManager::authenticationRequired,
|
|
||||||
sMainNAM, &QNetworkAccessManager::authenticationRequired,
|
|
||||||
connectionType );
|
|
||||||
|
|
||||||
connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
|
connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
|
||||||
sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
|
sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
|
||||||
connectionType );
|
connectionType );
|
||||||
@ -435,16 +502,22 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType conn
|
|||||||
|
|
||||||
connect( this, &QgsNetworkAccessManager::requestEncounteredSslErrors, sMainNAM, &QgsNetworkAccessManager::requestEncounteredSslErrors );
|
connect( this, &QgsNetworkAccessManager::requestEncounteredSslErrors, sMainNAM, &QgsNetworkAccessManager::requestEncounteredSslErrors );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
connect( this, &QgsNetworkAccessManager::requestRequiresAuth, sMainNAM, &QgsNetworkAccessManager::requestRequiresAuth );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
|
setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
|
||||||
#endif
|
#endif
|
||||||
|
setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler>() );
|
||||||
}
|
}
|
||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
|
connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
|
||||||
#endif
|
#endif
|
||||||
|
connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
|
||||||
|
connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
|
||||||
|
|
||||||
connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
|
connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
|
||||||
|
|
||||||
// check if proxy is enabled
|
// check if proxy is enabled
|
||||||
@ -559,3 +632,13 @@ void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSsl
|
|||||||
Q_UNUSED( reply );
|
Q_UNUSED( reply );
|
||||||
QgsDebugMsg( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
|
QgsDebugMsg( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsNetworkAuthenticationHandler
|
||||||
|
//
|
||||||
|
|
||||||
|
void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
|
||||||
|
{
|
||||||
|
Q_UNUSED( reply );
|
||||||
|
QgsDebugMsg( QStringLiteral( "Network reply required authentication, but no handler was in place to provide this authentication request while accessing the URL:\n%1" ).arg( reply->request().url().toString() ) );
|
||||||
|
}
|
||||||
|
@ -183,6 +183,48 @@ class CORE_EXPORT QgsSslErrorHandler
|
|||||||
*/
|
*/
|
||||||
virtual void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors );
|
virtual void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class QgsNetworkAuthenticationHandler
|
||||||
|
* \brief Network authentication handler, used for responding to network authentication requests during network requests.
|
||||||
|
* \ingroup core
|
||||||
|
*
|
||||||
|
* QgsNetworkAuthenticationHandler responds to authentication requests encountered during network requests. The
|
||||||
|
* base QgsNetworkAuthenticationHandler class responds to requests only by logging the request,
|
||||||
|
* but does not provide any username or password to allow the request to proceed.
|
||||||
|
*
|
||||||
|
* Subclasses can override this behavior by implementing their own handleAuthRequest()
|
||||||
|
* method. QgsNetworkAuthenticationHandler are ONLY ever called from the main thread, so it
|
||||||
|
* is safe to utilize gui widgets and dialogs during handleAuthRequest (e.g. to
|
||||||
|
* present prompts to users requesting the username and password).
|
||||||
|
*
|
||||||
|
* If a reply is coming from background thread, that thread is blocked while handleAuthRequest()
|
||||||
|
* is running.
|
||||||
|
*
|
||||||
|
* An application instance can only have a single network authentication handler. The current
|
||||||
|
* authentication handler is set by calling QgsNetworkAccessManager::setAuthHandler().
|
||||||
|
* By default an instance of the logging-only QgsNetworkAuthenticationHandler base class is used.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.6
|
||||||
|
*/
|
||||||
|
class CORE_EXPORT QgsNetworkAuthenticationHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~QgsNetworkAuthenticationHandler() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever network authentication requests are encountered during a network \a reply.
|
||||||
|
*
|
||||||
|
* Subclasses should reimplement this method to implement their own logic
|
||||||
|
* regarding how to handle the requests and whether they should be presented to users.
|
||||||
|
*
|
||||||
|
* The base class method just logs the request but does not provide any username/password resolution.
|
||||||
|
*/
|
||||||
|
virtual void handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth );
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -251,6 +293,24 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
* \since QGIS 3.6
|
* \since QGIS 3.6
|
||||||
*/
|
*/
|
||||||
void setSslErrorHandler( std::unique_ptr< QgsSslErrorHandler > handler );
|
void setSslErrorHandler( std::unique_ptr< QgsSslErrorHandler > handler );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the application network authentication \a handler, which is used to respond to network
|
||||||
|
* authentication prompts during network requests.
|
||||||
|
*
|
||||||
|
* Ownership of \a handler is transferred to the main thread QgsNetworkAccessManager instance.
|
||||||
|
*
|
||||||
|
* This method must ONLY be called on the main thread QgsNetworkAccessManager. It is not
|
||||||
|
* necessary to set handlers for background threads -- the main thread QgsNetworkAuthenticationHandler will
|
||||||
|
* automatically be used in a thread-safe manner for any authentication requests encountered on background threads.
|
||||||
|
*
|
||||||
|
* The default QgsNetworkAuthenticationHandler responds to request only by logging the request,
|
||||||
|
* but does not provide any username or password resolution.
|
||||||
|
*
|
||||||
|
* \note Not available in Python bindings.
|
||||||
|
* \since QGIS 3.6
|
||||||
|
*/
|
||||||
|
void setAuthHandler( std::unique_ptr< QgsNetworkAuthenticationHandler > handler );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//! insert a factory into the proxy factories list
|
//! insert a factory into the proxy factories list
|
||||||
@ -354,17 +414,36 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
*/
|
*/
|
||||||
void downloadProgress( int requestId, qint64 bytesReceived, qint64 bytesTotal );
|
void downloadProgress( int requestId, qint64 bytesReceived, qint64 bytesTotal );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when a network request prompts an authentication request.
|
||||||
|
*
|
||||||
|
* The \a requestId argument reflects the unique ID identifying the original request which the authentication relates to.
|
||||||
|
*
|
||||||
|
* This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
||||||
|
* only to connect to the main thread's signal in order to receive notifications about authentication requests
|
||||||
|
* from any thread.
|
||||||
|
*
|
||||||
|
* This signal is for debugging and logging purposes only, and cannot be used to respond to the
|
||||||
|
* requests. See QgsNetworkAuthenticationHandler for details on how to handle authentication requests.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.6
|
||||||
|
*/
|
||||||
|
void requestRequiresAuth( int requestId, const QString &realm );
|
||||||
|
|
||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when a network request encounters SSL \a errors.
|
* Emitted when a network request encounters SSL \a errors.
|
||||||
*
|
*
|
||||||
* The \a requestId argument reflects the unique ID identifying the original request which the progress report relates to.
|
* The \a requestId argument reflects the unique ID identifying the original request which the SSL error relates to.
|
||||||
*
|
*
|
||||||
* This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
* This signal is propagated to the main thread QgsNetworkAccessManager instance, so it is necessary
|
||||||
* only to connect to the main thread's signal in order to receive notifications about SSL errors
|
* only to connect to the main thread's signal in order to receive notifications about SSL errors
|
||||||
* from any thread.
|
* from any thread.
|
||||||
*
|
*
|
||||||
|
* This signal is for debugging and logging purposes only, and cannot be used to respond to the errors.
|
||||||
|
* See QgsSslErrorHandler for details on how to handle SSL errors and potentially ignore them.
|
||||||
|
*
|
||||||
* \since QGIS 3.6
|
* \since QGIS 3.6
|
||||||
*/
|
*/
|
||||||
void requestEncounteredSslErrors( int requestId, const QList<QSslError> &errors );
|
void requestEncounteredSslErrors( int requestId, const QList<QSslError> &errors );
|
||||||
@ -386,6 +465,14 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
|
|
||||||
void requestTimedOut( QNetworkReply * );
|
void requestTimedOut( QNetworkReply * );
|
||||||
|
|
||||||
|
#ifndef SIP_RUN
|
||||||
|
///@cond PRIVATE
|
||||||
|
// these signals are for internal use only - it's not safe to connect by external code
|
||||||
|
void authRequestOccurred( QNetworkReply *, QAuthenticator *auth );
|
||||||
|
void authRequestHandled( QNetworkReply *reply );
|
||||||
|
///@endcond
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void abortRequest();
|
void abortRequest();
|
||||||
@ -398,6 +485,10 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
|
|
||||||
void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors );
|
void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void onAuthRequired( QNetworkReply *reply, QAuthenticator *auth );
|
||||||
|
void handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QNetworkReply *createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData = nullptr ) override;
|
QNetworkReply *createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData = nullptr ) override;
|
||||||
|
|
||||||
@ -406,6 +497,14 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
void unlockAfterSslErrorHandled();
|
void unlockAfterSslErrorHandled();
|
||||||
void afterSslErrorHandled( QNetworkReply *reply );
|
void afterSslErrorHandled( QNetworkReply *reply );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void unlockAfterAuthRequestHandled();
|
||||||
|
void afterAuthRequestHandled( QNetworkReply *reply );
|
||||||
|
|
||||||
|
void pauseTimeout( QNetworkReply *reply );
|
||||||
|
void restartTimeout( QNetworkReply *reply );
|
||||||
|
static int getRequestId( QNetworkReply *reply );
|
||||||
|
|
||||||
QList<QNetworkProxyFactory *> mProxyFactories;
|
QList<QNetworkProxyFactory *> mProxyFactories;
|
||||||
QNetworkProxy mFallbackProxy;
|
QNetworkProxy mFallbackProxy;
|
||||||
QStringList mExcludedURLs;
|
QStringList mExcludedURLs;
|
||||||
@ -414,10 +513,18 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
|
|||||||
static QgsNetworkAccessManager *sMainNAM;
|
static QgsNetworkAccessManager *sMainNAM;
|
||||||
// ssl error handler, will be set for main thread ONLY
|
// ssl error handler, will be set for main thread ONLY
|
||||||
std::unique_ptr< QgsSslErrorHandler > mSslErrorHandler;
|
std::unique_ptr< QgsSslErrorHandler > mSslErrorHandler;
|
||||||
// only in use by work threads, unused in main thread
|
// only in use by worker threads, unused in main thread
|
||||||
QMutex mSslErrorHandlerMutex;
|
QMutex mSslErrorHandlerMutex;
|
||||||
// only in use by work threads, unused in main thread
|
// only in use by worker threads, unused in main thread
|
||||||
QWaitCondition mSslErrorWaitCondition;
|
QWaitCondition mSslErrorWaitCondition;
|
||||||
|
|
||||||
|
// auth request handler, will be set for main thread ONLY
|
||||||
|
std::unique_ptr< QgsNetworkAuthenticationHandler > mAuthHandler;
|
||||||
|
// only in use by worker threads, unused in main thread
|
||||||
|
QMutex mAuthRequestHandlerMutex;
|
||||||
|
// only in use by worker threads, unused in main thread
|
||||||
|
QWaitCondition mAuthRequestWaitCondition;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSNETWORKACCESSMANAGER_H
|
#endif // QGSNETWORKACCESSMANAGER_H
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "qgstest.h"
|
#include "qgstest.h"
|
||||||
#include "qgssettings.h"
|
#include "qgssettings.h"
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QAuthenticator>
|
||||||
|
|
||||||
class BackgroundRequest : public QThread
|
class BackgroundRequest : public QThread
|
||||||
{
|
{
|
||||||
@ -65,12 +65,36 @@ class TestSslErrorHandler : public QgsSslErrorHandler
|
|||||||
|
|
||||||
void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors ) override
|
void handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors ) override
|
||||||
{
|
{
|
||||||
|
Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
|
||||||
QCOMPARE( errors.at( 0 ).error(), QSslError::SelfSignedCertificate );
|
QCOMPARE( errors.at( 0 ).error(), QSslError::SelfSignedCertificate );
|
||||||
reply->ignoreSslErrors();
|
reply->ignoreSslErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TestAuthRequestHandler : public QgsNetworkAuthenticationHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
TestAuthRequestHandler( const QString &user, const QString &password )
|
||||||
|
: mUser( user )
|
||||||
|
, mPassword( password )
|
||||||
|
{}
|
||||||
|
|
||||||
|
void handleAuthRequest( QNetworkReply *, QAuthenticator *auth ) override
|
||||||
|
{
|
||||||
|
Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
|
||||||
|
if ( !mUser.isEmpty() )
|
||||||
|
auth->setUser( mUser );
|
||||||
|
if ( !mPassword.isEmpty() )
|
||||||
|
auth->setPassword( mPassword );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString mUser;
|
||||||
|
QString mPassword;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class TestQgsNetworkAccessManager : public QObject
|
class TestQgsNetworkAccessManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -89,6 +113,7 @@ class TestQgsNetworkAccessManager : public QObject
|
|||||||
void fetchPost();
|
void fetchPost();
|
||||||
void fetchBadSsl();
|
void fetchBadSsl();
|
||||||
void testSslErrorHandler();
|
void testSslErrorHandler();
|
||||||
|
void testAuthRequestHandler();
|
||||||
void fetchTimeout();
|
void fetchTimeout();
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -483,6 +508,108 @@ void TestQgsNetworkAccessManager::testSslErrorHandler()
|
|||||||
QgsNetworkAccessManager::instance()->setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
|
QgsNetworkAccessManager::instance()->setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsNetworkAccessManager::testAuthRequestHandler()
|
||||||
|
{
|
||||||
|
if ( QgsTest::isTravis() )
|
||||||
|
QSKIP( "This test is disabled on Travis CI environment" );
|
||||||
|
|
||||||
|
// initially this request should fail -- we aren't providing the username and password required
|
||||||
|
QgsNetworkAccessManager::instance()->setAuthHandler( qgis::make_unique< TestAuthRequestHandler >( QString(), QString() ) );
|
||||||
|
|
||||||
|
QObject context;
|
||||||
|
bool loaded = false;
|
||||||
|
bool gotRequestAboutToBeCreatedSignal = false;
|
||||||
|
bool gotAuthRequest = false;
|
||||||
|
int requestId = -1;
|
||||||
|
QUrl u = QUrl( QStringLiteral( "http://httpbin.org/basic-auth/me/secret" ) );
|
||||||
|
QNetworkReply::NetworkError expectedError = QNetworkReply::NoError;
|
||||||
|
connect( QgsNetworkAccessManager::instance(), qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestAboutToBeCreated ), &context, [&]( const QgsNetworkRequestParameters & params )
|
||||||
|
{
|
||||||
|
gotRequestAboutToBeCreatedSignal = true;
|
||||||
|
requestId = params.requestId();
|
||||||
|
QVERIFY( requestId > 0 );
|
||||||
|
QCOMPARE( params.operation(), QNetworkAccessManager::GetOperation );
|
||||||
|
QCOMPARE( params.request().url(), u );
|
||||||
|
} );
|
||||||
|
connect( QgsNetworkAccessManager::instance(), qgis::overload< QgsNetworkReplyContent >::of( &QgsNetworkAccessManager::finished ), &context, [&]( const QgsNetworkReplyContent & reply )
|
||||||
|
{
|
||||||
|
QCOMPARE( reply.error(), expectedError );
|
||||||
|
QCOMPARE( reply.requestId(), requestId );
|
||||||
|
QCOMPARE( reply.request().url(), u );
|
||||||
|
loaded = true;
|
||||||
|
} );
|
||||||
|
|
||||||
|
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::requestRequiresAuth, &context, [&]( int authRequestId, const QString & realm )
|
||||||
|
{
|
||||||
|
QCOMPARE( authRequestId, requestId );
|
||||||
|
QCOMPARE( realm, QStringLiteral( "Fake Realm" ) );
|
||||||
|
gotAuthRequest = true;
|
||||||
|
} );
|
||||||
|
|
||||||
|
expectedError = QNetworkReply::AuthenticationRequiredError;
|
||||||
|
QgsNetworkAccessManager::instance()->get( QNetworkRequest( u ) );
|
||||||
|
|
||||||
|
while ( !loaded || !gotAuthRequest || !gotRequestAboutToBeCreatedSignal )
|
||||||
|
{
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY( gotRequestAboutToBeCreatedSignal );
|
||||||
|
|
||||||
|
// now try in a thread
|
||||||
|
loaded = false;
|
||||||
|
gotAuthRequest = false;
|
||||||
|
gotRequestAboutToBeCreatedSignal = false;
|
||||||
|
|
||||||
|
|
||||||
|
BackgroundRequest *thread = new BackgroundRequest( QNetworkRequest( u ) );
|
||||||
|
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
while ( !loaded || !gotAuthRequest || !gotRequestAboutToBeCreatedSignal )
|
||||||
|
{
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
QVERIFY( gotRequestAboutToBeCreatedSignal );
|
||||||
|
thread->exit();
|
||||||
|
thread->wait();
|
||||||
|
thread->deleteLater();
|
||||||
|
|
||||||
|
// try with username and password specified
|
||||||
|
QgsNetworkAccessManager::instance()->setAuthHandler( qgis::make_unique< TestAuthRequestHandler >( QStringLiteral( "me" ), QStringLiteral( "secret" ) ) );
|
||||||
|
loaded = false;
|
||||||
|
gotAuthRequest = false;
|
||||||
|
gotRequestAboutToBeCreatedSignal = false;
|
||||||
|
expectedError = QNetworkReply::NoError;
|
||||||
|
QgsNetworkAccessManager::instance()->get( QNetworkRequest( u ) );
|
||||||
|
|
||||||
|
while ( !loaded || !gotAuthRequest || !gotRequestAboutToBeCreatedSignal )
|
||||||
|
{
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// correct username and password, in a thread
|
||||||
|
QgsNetworkAccessManager::instance()->setAuthHandler( qgis::make_unique< TestAuthRequestHandler >( QStringLiteral( "me2" ), QStringLiteral( "secret2" ) ) );
|
||||||
|
u = QUrl( QStringLiteral( "http://httpbin.org/basic-auth/me2/secret2" ) );
|
||||||
|
loaded = false;
|
||||||
|
gotAuthRequest = false;
|
||||||
|
gotRequestAboutToBeCreatedSignal = false;
|
||||||
|
expectedError = QNetworkReply::NoError;
|
||||||
|
|
||||||
|
thread = new BackgroundRequest( QNetworkRequest( u ) );
|
||||||
|
thread->start();
|
||||||
|
while ( !loaded || !gotAuthRequest || !gotRequestAboutToBeCreatedSignal )
|
||||||
|
{
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
QVERIFY( gotRequestAboutToBeCreatedSignal );
|
||||||
|
thread->exit();
|
||||||
|
thread->wait();
|
||||||
|
thread->deleteLater();
|
||||||
|
|
||||||
|
QgsNetworkAccessManager::instance()->setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler >() );
|
||||||
|
}
|
||||||
|
|
||||||
void TestQgsNetworkAccessManager::fetchTimeout()
|
void TestQgsNetworkAccessManager::fetchTimeout()
|
||||||
{
|
{
|
||||||
if ( QgsTest::isTravis() )
|
if ( QgsTest::isTravis() )
|
||||||
|
Loading…
x
Reference in New Issue
Block a user