37 #include <QNetworkReply> 38 #include <QThreadStorage> 39 #include <QAuthenticator> 40 #include <QStandardPaths> 43 #include <QSslConfiguration> 52 class QgsNetworkProxyFactory :
public QNetworkProxyFactory
55 QgsNetworkProxyFactory() =
default;
57 QList<QNetworkProxy> queryProxy(
const QNetworkProxyQuery &query = QNetworkProxyQuery() )
override 63 for ( QNetworkProxyFactory *f : constProxyFactories )
65 QList<QNetworkProxy> systemproxies = f->systemProxyForQuery( query );
66 if ( !systemproxies.isEmpty() )
69 QList<QNetworkProxy> proxies = f->queryProxy( query );
70 if ( !proxies.isEmpty() )
75 if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
78 QString url = query.url().toString();
81 for (
const QString &noProxy : constNoProxyList )
83 if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
85 QgsDebugMsgLevel( QStringLiteral(
"don't using any proxy for %1 [exclude %2]" ).arg( url, noProxy ), 4 );
86 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
91 for (
const QString &exclude : constExcludeList )
93 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
95 QgsDebugMsgLevel( QStringLiteral(
"using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
96 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
102 QgsDebugMsgLevel( QStringLiteral(
"requesting system proxy for query %1" ).arg( url ), 4 );
103 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
104 if ( !proxies.isEmpty() )
107 .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
112 QgsDebugMsgLevel( QStringLiteral(
"using fallback proxy for %1" ).arg( url ), 4 );
123 static QThreadStorage<QgsNetworkAccessManager> sInstances;
126 if ( nam->thread() == qApp->thread() )
129 if ( !nam->mInitialized )
136 : QNetworkAccessManager( parent )
138 setProxyFactory(
new QgsNetworkProxyFactory() );
143 Q_ASSERT( sMainNAM ==
this );
144 mSslErrorHandler = std::move( handler );
149 Q_ASSERT( sMainNAM ==
this );
150 mAuthHandler = std::move( handler );
155 mProxyFactories.insert( 0, factory );
160 mProxyFactories.removeAll( factory );
165 return mProxyFactories;
170 return mExcludedURLs;
180 return mFallbackProxy;
185 QgsDebugMsgLevel( QStringLiteral(
"proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
186 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral(
"DefaultProxy" ) :
187 proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral(
"Socks5Proxy" ) :
188 proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral(
"NoProxy" ) :
189 proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral(
"HttpProxy" ) :
190 proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral(
"HttpCachingProxy" ) :
191 proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral(
"FtpCachingProxy" ) :
192 QStringLiteral(
"Undefined" ),
196 proxy.password().isEmpty() ? QStringLiteral(
"not set" ) : QStringLiteral(
"set" ) ), 4 );
198 mFallbackProxy = proxy;
199 mExcludedURLs = excludes;
201 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(),
202 [](
const QString & url )
204 return url.trimmed().isEmpty();
205 } ), mExcludedURLs.end() );
207 mNoProxyURLs = noProxyURLs;
208 mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(),
209 [](
const QString & url )
211 return url.trimmed().isEmpty();
212 } ), mNoProxyURLs.end() );
219 QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) );
221 QString userAgent = s.
value( QStringLiteral(
"/qgis/networkAndProxy/userAgent" ),
"Mozilla/5.0" ).toString();
222 if ( !userAgent.isEmpty() )
225 pReq->setRawHeader(
"User-Agent", userAgent.toUtf8() );
228 bool ishttps = pReq->url().scheme().compare( QLatin1String(
"https" ), Qt::CaseInsensitive ) == 0;
231 QgsDebugMsgLevel( QStringLiteral(
"Adding trusted CA certs to request" ), 3 );
232 QSslConfiguration sslconfig( pReq->sslConfiguration() );
236 QString hostport( QStringLiteral(
"%1:%2" )
237 .arg( pReq->url().host().trimmed() )
238 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
240 if ( !servconfig.
isNull() )
242 QgsDebugMsg( QStringLiteral(
"Adding SSL custom config to request for %1" ).arg( hostport ) );
248 pReq->setSslConfiguration( sslconfig );
252 static QAtomicInt sRequestId = 0;
253 const int requestId = ++sRequestId;
255 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
257 content = buffer->buffer();
264 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
265 reply->setProperty(
"requestId", requestId );
271 connect( reply, &QNetworkReply::downloadProgress,
this, &QgsNetworkAccessManager::onReplyDownloadProgress );
273 connect( reply, &QNetworkReply::sslErrors,
this, &QgsNetworkAccessManager::onReplySslErrors );
279 QTimer *timer =
new QTimer( reply );
280 timer->setObjectName( QStringLiteral(
"timeoutTimer" ) );
281 connect( timer, &QTimer::timeout,
this, &QgsNetworkAccessManager::abortRequest );
282 timer->setSingleShot(
true );
285 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
286 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
287 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
288 QgsDebugMsgLevel( QStringLiteral(
"Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
294 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
296 Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
297 mSslErrorWaitCondition.wakeOne();
301 void QgsNetworkAccessManager::abortRequest()
303 QTimer *timer = qobject_cast<QTimer *>( sender() );
306 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
310 QgsDebugMsgLevel( QStringLiteral(
"Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
317 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
322 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
324 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
331 void QgsNetworkAccessManager::onReplySslErrors(
const QList<QSslError> &errors )
333 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
335 Q_ASSERT( reply->manager() == this );
337 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst SSL error is handled" ) );
338 pauseTimeout( reply );
344 emit sslErrorsOccurred( reply, errors );
345 if (
this != sMainNAM )
349 mSslErrorHandlerMutex.lock();
350 mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
351 mSslErrorHandlerMutex.unlock();
352 afterSslErrorHandled( reply );
356 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
358 if ( reply->manager() == this )
360 restartTimeout( reply );
361 emit sslErrorsHandled( reply );
363 else if (
this == sMainNAM )
370 void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
372 Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
373 mAuthRequestWaitCondition.wakeOne();
376 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
378 if ( reply->manager() == this )
380 restartTimeout( reply );
381 emit authRequestHandled( reply );
383 else if (
this == sMainNAM )
390 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
392 Q_ASSERT( reply->manager() == this );
394 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
395 if ( timer && timer->isActive() )
401 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
403 Q_ASSERT( reply->manager() == this );
405 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
408 Q_ASSERT( !timer->isActive() );
409 QgsDebugMsg( QStringLiteral(
"Restarting network reply timeout" ) );
410 timer->setSingleShot(
true );
415 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
417 return reply->property(
"requestId" ).toInt();
420 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply,
const QList<QSslError> &errors )
422 mSslErrorHandler->handleSslErrors( reply, errors );
423 afterSslErrorHandled( reply );
428 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
431 Q_ASSERT( reply->manager() == this );
433 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst auth request is handled" ) );
434 pauseTimeout( reply );
440 emit authRequestOccurred( reply, auth );
442 if (
this != sMainNAM )
446 mAuthRequestHandlerMutex.lock();
447 mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
448 mAuthRequestHandlerMutex.unlock();
449 afterAuthRequestHandled( reply );
453 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
455 mAuthHandler->handleAuthRequest( reply, auth );
459 afterAuthRequestHandled( reply );
466 case QNetworkRequest::AlwaysNetwork:
467 return QStringLiteral(
"AlwaysNetwork" );
468 case QNetworkRequest::PreferNetwork:
469 return QStringLiteral(
"PreferNetwork" );
470 case QNetworkRequest::PreferCache:
471 return QStringLiteral(
"PreferCache" );
472 case QNetworkRequest::AlwaysCache:
473 return QStringLiteral(
"AlwaysCache" );
475 return QStringLiteral(
"PreferNetwork" );
480 if ( name == QLatin1String(
"AlwaysNetwork" ) )
482 return QNetworkRequest::AlwaysNetwork;
484 else if ( name == QLatin1String(
"PreferNetwork" ) )
486 return QNetworkRequest::PreferNetwork;
488 else if ( name == QLatin1String(
"PreferCache" ) )
490 return QNetworkRequest::PreferCache;
492 else if ( name == QLatin1String(
"AlwaysCache" ) )
494 return QNetworkRequest::AlwaysCache;
496 return QNetworkRequest::PreferNetwork;
502 mUseSystemProxy =
false;
504 Q_ASSERT( sMainNAM );
506 if ( sMainNAM !=
this )
508 connect(
this, &QNetworkAccessManager::proxyAuthenticationRequired,
509 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
527 connect(
this, &QNetworkAccessManager::sslErrors,
528 sMainNAM, &QNetworkAccessManager::sslErrors,
541 setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler>() );
544 connect(
this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
546 connect(
this, &QNetworkAccessManager::authenticationRequired,
this, &QgsNetworkAccessManager::onAuthRequired );
547 connect(
this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
549 connect(
this, &QNetworkAccessManager::finished,
this, &QgsNetworkAccessManager::onReplyFinished );
554 QStringList excludes;
555 QStringList noProxyURLs;
557 bool proxyEnabled = settings.
value( QStringLiteral(
"proxy/proxyEnabled" ),
false ).toBool();
562 excludes = settings.
value( QStringLiteral(
"proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
564 noProxyURLs = settings.
value( QStringLiteral(
"proxy/noProxyUrls" ), QStringList() ).toStringList();
567 QString proxyHost = settings.
value( QStringLiteral(
"proxy/proxyHost" ),
"" ).toString();
568 int proxyPort = settings.
value( QStringLiteral(
"proxy/proxyPort" ),
"" ).toString().toInt();
570 QString proxyUser = settings.
value( QStringLiteral(
"proxy/proxyUser" ),
"" ).toString();
571 QString proxyPassword = settings.
value( QStringLiteral(
"proxy/proxyPassword" ),
"" ).toString();
573 QString proxyTypeString = settings.
value( QStringLiteral(
"proxy/proxyType" ),
"" ).toString();
575 if ( proxyTypeString == QLatin1String(
"DefaultProxy" ) )
577 mUseSystemProxy =
true;
578 QNetworkProxyFactory::setUseSystemConfiguration(
true );
579 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
580 if ( !proxies.isEmpty() )
582 proxy = proxies.first();
588 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
589 if ( proxyTypeString == QLatin1String(
"Socks5Proxy" ) )
591 proxyType = QNetworkProxy::Socks5Proxy;
593 else if ( proxyTypeString == QLatin1String(
"HttpProxy" ) )
595 proxyType = QNetworkProxy::HttpProxy;
597 else if ( proxyTypeString == QLatin1String(
"HttpCachingProxy" ) )
599 proxyType = QNetworkProxy::HttpCachingProxy;
601 else if ( proxyTypeString == QLatin1String(
"FtpCachingProxy" ) )
603 proxyType = QNetworkProxy::FtpCachingProxy;
605 QgsDebugMsg( QStringLiteral(
"setting proxy %1 %2:%3 %4/%5" )
607 .arg( proxyHost ).arg( proxyPort )
608 .arg( proxyUser, proxyPassword )
610 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
613 QString authcfg = settings.
value( QStringLiteral(
"proxy/authcfg" ),
"" ).toString();
614 if ( !authcfg.isEmpty( ) )
616 QgsDebugMsg( QStringLiteral(
"setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
629 QString cacheDirectory = settings.
value( QStringLiteral(
"cache/directory" ) ).toString();
630 if ( cacheDirectory.isEmpty() )
631 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
632 qint64 cacheSize = settings.
value( QStringLiteral(
"cache/size" ), 50 * 1024 * 1024 ).toLongLong();
638 if ( cache() != newcache )
639 setCache( newcache );
644 return QgsSettings().
value( QStringLiteral(
"/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt();
656 br.
get( request, forceRefresh, feedback );
664 br.
post( request, data, forceRefresh, feedback );
674 : mOperation( operation )
675 , mRequest( request )
676 , mOriginatingThreadId( QStringLiteral(
"0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) ) )
677 , mRequestId( requestId )
678 , mContent( content )
679 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
680 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
692 QgsDebugMsg( QStringLiteral(
"SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
702 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() ) );
virtual void handleSslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
Called whenever SSL errors are encountered during a network reply.
bool isNull() const
Whether configuration is null (missing components)
static QList< QSslCertificate > casMerge(const QList< QSslCertificate > &bundle1, const QList< QSslCertificate > &bundle2)
casMerge merges two certificate bundles in a single one removing duplicates, the certificates from th...
static const QString QGIS_VERSION
Version string.
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl control)
Returns the name for QNetworkRequest::CacheLoadControl.
int sslPeerVerifyDepth() const
Number or SSL client's peer to verify in connections.
Q_DECL_DEPRECATED void requestCreated(QNetworkReply *)
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkProxy with an authentication config.
void setCacheDirectory(const QString &cacheDir)
QgsNetworkRequestParameters()=default
Default constructor.
#define Q_NOWARN_DEPRECATED_PUSH
Configuration container for SSL server connection exceptions or overrides.
QStringList excludeList() const
Returns the proxy exclude list.
Encapsulates parameters and properties of a network request.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &name)
Returns QNetworkRequest::CacheLoadControl from a name.
QgsNetworkAccessManager(QObject *parent=nullptr)
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client's peer verify mode to use in connections.
virtual void handleAuthRequest(QNetworkReply *reply, QAuthenticator *auth)
Called whenever network authentication requests are encountered during a network reply.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
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.
void finished(QgsNetworkReplyContent reply)
Emitted whenever a pending network reply is finished.
#define QgsDebugMsgLevel(str, level)
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).
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
QString cacheDirectory() const
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made...
void removeProxyFactory(QNetworkProxyFactory *factory)
Removes a factory from the proxy factories list.
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port) ...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
void setSslErrorHandler(std::unique_ptr< QgsSslErrorHandler > handler)
Sets the application SSL error handler, which is used to respond to SSL errors encountered during net...
Q_DECL_DEPRECATED void requestAboutToBeCreated(QNetworkAccessManager::Operation, const QNetworkRequest &, QIODevice *)
void insertProxyFactory(QNetworkProxyFactory *factory)
Inserts a factory into the proxy factories list.
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs)
Sets the fallback proxy and URLs which shouldn't use it.
#define Q_NOWARN_DEPRECATED_POP
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void requestAuthDetailsAdded(int requestId, const QString &realm, const QString &user, const QString &password)
Emitted when network authentication details have been added to a request.
void setupDefaultProxyAndCache(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Setup the QgsNetworkAccessManager (NAM) according to the user's settings.
void requestEncounteredSslErrors(int requestId, const QList< QSslError > &errors)
Emitted when a network request encounters SSL errors.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
qint64 maximumCacheSize() const
static int timeout()
Returns the network timeout length, in milliseconds.
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
void downloadProgress(int requestId, qint64 bytesReceived, qint64 bytesTotal)
Emitted when a network reply receives a progress report.
bool useSystemProxy() const
Returns whether the system proxy should be used.
void setAuthHandler(std::unique_ptr< QgsNetworkAuthenticationHandler > handler)
Sets the application network authentication handler, which is used to respond to network authenticati...
static QgsNetworkReplyContent blockingPost(QNetworkRequest &request, const QByteArray &data, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Posts a POST request to obtain the contents of the target request, using the given data...
static QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
QStringList noProxyList() const
Returns the no proxy list.
network access manager for QGISThis class implements the QGIS network access manager.
void setMaximumCacheSize(qint64 size)
Wrapper implementation of QNetworkDiskCache with all methods guarded by a mutex soly for internal use...
void requestRequiresAuth(int requestId, const QString &realm)
Emitted when a network request prompts an authentication request.
static void setTimeout(int time)
Sets the maximum timeout time for network requests, in milliseconds.
const QList< QNetworkProxyFactory * > proxyFactories() const
Returns a list of proxy factories used by the manager.