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 &exclude : constExcludeList )
83 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
85 QgsDebugMsgLevel( QStringLiteral(
"using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
86 return QList<QNetworkProxy>() << QNetworkProxy();
92 QgsDebugMsgLevel( QStringLiteral(
"requesting system proxy for query %1" ).arg( url ), 4 );
93 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
94 if ( !proxies.isEmpty() )
97 .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
102 QgsDebugMsgLevel( QStringLiteral(
"using fallback proxy for %1" ).arg( url ), 4 );
113 static QThreadStorage<QgsNetworkAccessManager> sInstances;
116 if ( nam->thread() == qApp->thread() )
119 if ( !nam->mInitialized )
126 : QNetworkAccessManager( parent )
128 setProxyFactory(
new QgsNetworkProxyFactory() );
133 Q_ASSERT( sMainNAM ==
this );
134 mSslErrorHandler = std::move( handler );
139 Q_ASSERT( sMainNAM ==
this );
140 mAuthHandler = std::move( handler );
145 mProxyFactories.insert( 0, factory );
150 mProxyFactories.removeAll( factory );
155 return mProxyFactories;
160 return mExcludedURLs;
165 return mFallbackProxy;
170 QgsDebugMsgLevel( QStringLiteral(
"proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
171 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral(
"DefaultProxy" ) :
172 proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral(
"Socks5Proxy" ) :
173 proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral(
"NoProxy" ) :
174 proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral(
"HttpProxy" ) :
175 proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral(
"HttpCachingProxy" ) :
176 proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral(
"FtpCachingProxy" ) :
177 QStringLiteral(
"Undefined" ),
181 proxy.password().isEmpty() ? QStringLiteral(
"not set" ) : QStringLiteral(
"set" ) ), 4 );
183 mFallbackProxy = proxy;
184 mExcludedURLs = excludes;
186 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(),
187 [](
const QString & url )
189 return url.trimmed().isEmpty();
190 } ), mExcludedURLs.end() );
198 QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) );
200 QString userAgent = s.
value( QStringLiteral(
"/qgis/networkAndProxy/userAgent" ),
"Mozilla/5.0" ).toString();
201 if ( !userAgent.isEmpty() )
204 pReq->setRawHeader(
"User-Agent", userAgent.toUtf8() );
207 bool ishttps = pReq->url().scheme().compare( QLatin1String(
"https" ), Qt::CaseInsensitive ) == 0;
210 QgsDebugMsgLevel( QStringLiteral(
"Adding trusted CA certs to request" ), 3 );
211 QSslConfiguration sslconfig( pReq->sslConfiguration() );
215 QString hostport( QStringLiteral(
"%1:%2" )
216 .arg( pReq->url().host().trimmed() )
217 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
219 if ( !servconfig.
isNull() )
221 QgsDebugMsg( QStringLiteral(
"Adding SSL custom config to request for %1" ).arg( hostport ) );
227 pReq->setSslConfiguration( sslconfig );
231 static QAtomicInt sRequestId = 0;
232 const int requestId = ++sRequestId;
234 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
236 content = buffer->buffer();
243 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
244 reply->setProperty(
"requestId", requestId );
250 connect( reply, &QNetworkReply::downloadProgress,
this, &QgsNetworkAccessManager::onReplyDownloadProgress );
252 connect( reply, &QNetworkReply::sslErrors,
this, &QgsNetworkAccessManager::onReplySslErrors );
258 QTimer *timer =
new QTimer( reply );
259 timer->setObjectName( QStringLiteral(
"timeoutTimer" ) );
260 connect( timer, &QTimer::timeout,
this, &QgsNetworkAccessManager::abortRequest );
261 timer->setSingleShot(
true );
264 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
265 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
266 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
267 QgsDebugMsgLevel( QStringLiteral(
"Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
273 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
275 Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
276 mSslErrorWaitCondition.wakeOne();
280 void QgsNetworkAccessManager::abortRequest()
282 QTimer *timer = qobject_cast<QTimer *>( sender() );
285 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
289 QgsDebugMsgLevel( QStringLiteral(
"Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
296 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
301 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
303 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
310 void QgsNetworkAccessManager::onReplySslErrors(
const QList<QSslError> &errors )
312 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
314 Q_ASSERT( reply->manager() == this );
316 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst SSL error is handled" ) );
317 pauseTimeout( reply );
323 emit sslErrorsOccurred( reply, errors );
324 if (
this != sMainNAM )
328 mSslErrorHandlerMutex.lock();
329 mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
330 mSslErrorHandlerMutex.unlock();
331 afterSslErrorHandled( reply );
335 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
337 if ( reply->manager() == this )
339 restartTimeout( reply );
340 emit sslErrorsHandled( reply );
342 else if (
this == sMainNAM )
349 void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
351 Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
352 mAuthRequestWaitCondition.wakeOne();
355 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
357 if ( reply->manager() == this )
359 restartTimeout( reply );
360 emit authRequestHandled( reply );
362 else if (
this == sMainNAM )
369 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
371 Q_ASSERT( reply->manager() == this );
373 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
374 if ( timer && timer->isActive() )
380 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
382 Q_ASSERT( reply->manager() == this );
384 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
387 Q_ASSERT( !timer->isActive() );
388 QgsDebugMsg( QStringLiteral(
"Restarting network reply timeout" ) );
389 timer->setSingleShot(
true );
394 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
396 return reply->property(
"requestId" ).toInt();
399 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply,
const QList<QSslError> &errors )
401 mSslErrorHandler->handleSslErrors( reply, errors );
402 afterSslErrorHandled( reply );
407 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
410 Q_ASSERT( reply->manager() == this );
412 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst auth request is handled" ) );
413 pauseTimeout( reply );
419 emit authRequestOccurred( reply, auth );
421 if (
this != sMainNAM )
425 mAuthRequestHandlerMutex.lock();
426 mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
427 mAuthRequestHandlerMutex.unlock();
428 afterAuthRequestHandled( reply );
432 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
434 mAuthHandler->handleAuthRequest( reply, auth );
438 afterAuthRequestHandled( reply );
445 case QNetworkRequest::AlwaysNetwork:
446 return QStringLiteral(
"AlwaysNetwork" );
447 case QNetworkRequest::PreferNetwork:
448 return QStringLiteral(
"PreferNetwork" );
449 case QNetworkRequest::PreferCache:
450 return QStringLiteral(
"PreferCache" );
451 case QNetworkRequest::AlwaysCache:
452 return QStringLiteral(
"AlwaysCache" );
454 return QStringLiteral(
"PreferNetwork" );
459 if ( name == QLatin1String(
"AlwaysNetwork" ) )
461 return QNetworkRequest::AlwaysNetwork;
463 else if ( name == QLatin1String(
"PreferNetwork" ) )
465 return QNetworkRequest::PreferNetwork;
467 else if ( name == QLatin1String(
"PreferCache" ) )
469 return QNetworkRequest::PreferCache;
471 else if ( name == QLatin1String(
"AlwaysCache" ) )
473 return QNetworkRequest::AlwaysCache;
475 return QNetworkRequest::PreferNetwork;
481 mUseSystemProxy =
false;
483 Q_ASSERT( sMainNAM );
485 if ( sMainNAM !=
this )
487 connect(
this, &QNetworkAccessManager::proxyAuthenticationRequired,
488 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
506 connect(
this, &QNetworkAccessManager::sslErrors,
507 sMainNAM, &QNetworkAccessManager::sslErrors,
520 setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler>() );
523 connect(
this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
525 connect(
this, &QNetworkAccessManager::authenticationRequired,
this, &QgsNetworkAccessManager::onAuthRequired );
526 connect(
this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
528 connect(
this, &QNetworkAccessManager::finished,
this, &QgsNetworkAccessManager::onReplyFinished );
533 QStringList excludes;
535 bool proxyEnabled = settings.
value( QStringLiteral(
"proxy/proxyEnabled" ),
false ).toBool();
538 excludes = settings.
value( QStringLiteral(
"proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
541 QString proxyHost = settings.
value( QStringLiteral(
"proxy/proxyHost" ),
"" ).toString();
542 int proxyPort = settings.
value( QStringLiteral(
"proxy/proxyPort" ),
"" ).toString().toInt();
544 QString proxyUser = settings.
value( QStringLiteral(
"proxy/proxyUser" ),
"" ).toString();
545 QString proxyPassword = settings.
value( QStringLiteral(
"proxy/proxyPassword" ),
"" ).toString();
547 QString proxyTypeString = settings.
value( QStringLiteral(
"proxy/proxyType" ),
"" ).toString();
549 if ( proxyTypeString == QLatin1String(
"DefaultProxy" ) )
551 mUseSystemProxy =
true;
552 QNetworkProxyFactory::setUseSystemConfiguration(
true );
553 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
554 if ( !proxies.isEmpty() )
556 proxy = proxies.first();
562 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
563 if ( proxyTypeString == QLatin1String(
"Socks5Proxy" ) )
565 proxyType = QNetworkProxy::Socks5Proxy;
567 else if ( proxyTypeString == QLatin1String(
"HttpProxy" ) )
569 proxyType = QNetworkProxy::HttpProxy;
571 else if ( proxyTypeString == QLatin1String(
"HttpCachingProxy" ) )
573 proxyType = QNetworkProxy::HttpCachingProxy;
575 else if ( proxyTypeString == QLatin1String(
"FtpCachingProxy" ) )
577 proxyType = QNetworkProxy::FtpCachingProxy;
579 QgsDebugMsg( QStringLiteral(
"setting proxy %1 %2:%3 %4/%5" )
581 .arg( proxyHost ).arg( proxyPort )
582 .arg( proxyUser, proxyPassword )
584 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
587 QString authcfg = settings.
value( QStringLiteral(
"proxy/authcfg" ),
"" ).toString();
588 if ( !authcfg.isEmpty( ) )
590 QgsDebugMsg( QStringLiteral(
"setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
603 QString cacheDirectory = settings.
value( QStringLiteral(
"cache/directory" ) ).toString();
604 if ( cacheDirectory.isEmpty() )
605 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
606 qint64 cacheSize = settings.
value( QStringLiteral(
"cache/size" ), 50 * 1024 * 1024 ).toLongLong();
612 if ( cache() != newcache )
613 setCache( newcache );
618 return QgsSettings().
value( QStringLiteral(
"/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt();
630 br.
get( request, forceRefresh, feedback );
638 br.
post( request, data, forceRefresh, feedback );
648 : mOperation( operation )
649 , mRequest( request )
650 , mOriginatingThreadId( QStringLiteral(
"0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) ) )
651 , mRequestId( requestId )
652 , mContent( content )
653 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
654 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
666 QgsDebugMsg( QStringLiteral(
"SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
676 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.
#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...
network access manager for QGISThis class implements the QGIS network access manager.
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes)
Sets the fallback proxy and URLs which shouldn't use it.
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.