37 #include <QNetworkReply>
38 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
41 #include <QRecursiveMutex>
43 #include <QThreadStorage>
44 #include <QAuthenticator>
45 #include <QStandardPaths>
49 #include <QSslConfiguration>
57 static std::vector< std::pair< QString, std::function< void( QNetworkRequest * ) > > > sCustomPreprocessors;
58 static std::vector< std::pair< QString, std::function< void(
const QNetworkRequest &, QNetworkReply * ) > > > sCustomReplyPreprocessors;
61 class QgsNetworkProxyFactory :
public QNetworkProxyFactory
64 QgsNetworkProxyFactory() =
default;
66 QList<QNetworkProxy> queryProxy(
const QNetworkProxyQuery &query = QNetworkProxyQuery() )
override
72 for ( QNetworkProxyFactory *f : constProxyFactories )
74 QList<QNetworkProxy> systemproxies = QNetworkProxyFactory::systemProxyForQuery( query );
75 if ( !systemproxies.isEmpty() )
78 QList<QNetworkProxy> proxies = f->queryProxy( query );
79 if ( !proxies.isEmpty() )
84 if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
87 const QString url = query.url().toString();
90 for (
const QString &noProxy : constNoProxyList )
92 if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
94 QgsDebugMsgLevel( QStringLiteral(
"don't using any proxy for %1 [exclude %2]" ).arg( url, noProxy ), 4 );
95 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
100 for (
const QString &exclude : constExcludeList )
102 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
104 QgsDebugMsgLevel( QStringLiteral(
"using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
105 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
111 QgsDebugMsgLevel( QStringLiteral(
"requesting system proxy for query %1" ).arg( url ), 4 );
112 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
113 if ( !proxies.isEmpty() )
116 .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
121 QgsDebugMsgLevel( QStringLiteral(
"using fallback proxy for %1" ).arg( url ), 4 );
128 class QgsNetworkCookieJar :
public QNetworkCookieJar
134 : QNetworkCookieJar( parent )
136 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
137 , mMutex( QMutex::Recursive )
141 bool deleteCookie(
const QNetworkCookie &cookie )
override
143 const QMutexLocker locker( &mMutex );
144 if ( QNetworkCookieJar::deleteCookie( cookie ) )
146 emit mNam->cookiesChanged( allCookies() );
151 bool insertCookie(
const QNetworkCookie &cookie )
override
153 const QMutexLocker locker( &mMutex );
154 if ( QNetworkCookieJar::insertCookie( cookie ) )
156 emit mNam->cookiesChanged( allCookies() );
161 bool setCookiesFromUrl(
const QList<QNetworkCookie> &cookieList,
const QUrl &url )
override
163 const QMutexLocker locker( &mMutex );
164 return QNetworkCookieJar::setCookiesFromUrl( cookieList, url );
166 bool updateCookie(
const QNetworkCookie &cookie )
override
168 const QMutexLocker locker( &mMutex );
169 if ( QNetworkCookieJar::updateCookie( cookie ) )
171 emit mNam->cookiesChanged( allCookies() );
178 QList<QNetworkCookie> allCookies()
const
180 const QMutexLocker locker( &mMutex );
181 return QNetworkCookieJar::allCookies();
183 void setAllCookies(
const QList<QNetworkCookie> &cookieList )
185 const QMutexLocker locker( &mMutex );
186 QNetworkCookieJar::setAllCookies( cookieList );
190 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
191 mutable QMutex mMutex;
193 mutable QRecursiveMutex mMutex;
204 static QThreadStorage<QgsNetworkAccessManager> sInstances;
207 if ( nam->thread() == qApp->thread() )
210 if ( !nam->mInitialized )
220 : QNetworkAccessManager( parent )
221 , mAuthRequestHandlerSemaphore( 1 )
223 setProxyFactory(
new QgsNetworkProxyFactory() );
224 setCookieJar(
new QgsNetworkCookieJar(
this ) );
229 Q_ASSERT( sMainNAM ==
this );
230 mSslErrorHandler = std::move( handler );
235 Q_ASSERT( sMainNAM ==
this );
236 mAuthHandler = std::move( handler );
241 mProxyFactories.insert( 0, factory );
246 mProxyFactories.removeAll( factory );
251 return mProxyFactories;
256 return mExcludedURLs;
266 return mFallbackProxy;
271 QgsDebugMsgLevel( QStringLiteral(
"proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
272 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral(
"DefaultProxy" ) :
273 proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral(
"Socks5Proxy" ) :
274 proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral(
"NoProxy" ) :
275 proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral(
"HttpProxy" ) :
276 proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral(
"HttpCachingProxy" ) :
277 proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral(
"FtpCachingProxy" ) :
278 QStringLiteral(
"Undefined" ),
282 proxy.password().isEmpty() ? QStringLiteral(
"not set" ) : QStringLiteral(
"set" ) ), 4 );
284 mFallbackProxy = proxy;
285 mExcludedURLs = excludes;
287 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(),
288 [](
const QString & url )
290 return url.trimmed().isEmpty();
291 } ), mExcludedURLs.end() );
293 mNoProxyURLs = noProxyURLs;
294 mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(),
295 [](
const QString & url )
297 return url.trimmed().isEmpty();
298 } ), mNoProxyURLs.end() );
305 QNetworkRequest *pReq(
const_cast< QNetworkRequest *
>( &req ) );
307 QString userAgent = s.
value( QStringLiteral(
"/qgis/networkAndProxy/userAgent" ),
"Mozilla/5.0" ).toString();
308 if ( !userAgent.isEmpty() )
310 userAgent += QStringLiteral(
"QGIS/%1/%2" ).arg(
Qgis::versionInt() ).arg( QSysInfo::prettyProductName() );
311 pReq->setRawHeader(
"User-Agent", userAgent.toLatin1() );
314 const bool ishttps = pReq->url().scheme().compare( QLatin1String(
"https" ), Qt::CaseInsensitive ) == 0;
317 QgsDebugMsgLevel( QStringLiteral(
"Adding trusted CA certs to request" ), 3 );
318 QSslConfiguration sslconfig( pReq->sslConfiguration() );
322 const QString hostport( QStringLiteral(
"%1:%2" )
323 .arg( pReq->url().host().trimmed() )
324 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
326 if ( !servconfig.
isNull() )
328 QgsDebugMsg( QStringLiteral(
"Adding SSL custom config to request for %1" ).arg( hostport ) );
334 pReq->setSslConfiguration( sslconfig );
338 if ( sMainNAM->mCacheDisabled )
341 pReq->setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
342 pReq->setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
345 for (
const auto &preprocessor : sCustomPreprocessors )
347 preprocessor.second( pReq );
350 static QAtomicInt sRequestId = 0;
351 const int requestId = ++sRequestId;
353 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
355 content = buffer->buffer();
362 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
363 reply->setProperty(
"requestId", requestId );
369 connect( reply, &QNetworkReply::downloadProgress,
this, &QgsNetworkAccessManager::onReplyDownloadProgress );
371 connect( reply, &QNetworkReply::sslErrors,
this, &QgsNetworkAccessManager::onReplySslErrors );
374 for (
const auto &replyPreprocessor : sCustomReplyPreprocessors )
376 replyPreprocessor.second( req, reply );
384 QTimer *timer =
new QTimer( reply );
385 timer->setObjectName( QStringLiteral(
"timeoutTimer" ) );
386 connect( timer, &QTimer::timeout,
this, &QgsNetworkAccessManager::abortRequest );
387 timer->setSingleShot(
true );
390 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
391 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
392 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
394 QgsDebugMsgLevel( QStringLiteral(
"Created [reply:%1]" ).arg(
reinterpret_cast< qint64
>( reply ), 0, 16 ), 3 );
400 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
402 Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
403 mSslErrorWaitCondition.wakeOne();
407 void QgsNetworkAccessManager::abortRequest()
409 QTimer *timer = qobject_cast<QTimer *>( sender() );
412 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
416 QgsDebugMsgLevel( QStringLiteral(
"Abort [reply:%1] %2" ).arg(
reinterpret_cast< qint64
>( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
423 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
428 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
430 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
437 void QgsNetworkAccessManager::onReplySslErrors(
const QList<QSslError> &errors )
439 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
441 Q_ASSERT( reply->manager() ==
this );
443 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst SSL error is handled" ) );
444 pauseTimeout( reply );
450 emit sslErrorsOccurred( reply, errors );
451 if (
this != sMainNAM )
455 mSslErrorHandlerMutex.lock();
456 mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
457 mSslErrorHandlerMutex.unlock();
458 afterSslErrorHandled( reply );
462 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
464 if ( reply->manager() ==
this )
466 restartTimeout( reply );
467 emit sslErrorsHandled( reply );
469 else if (
this == sMainNAM )
472 qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled();
476 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
478 if ( reply->manager() ==
this )
480 restartTimeout( reply );
481 emit authRequestHandled( reply );
485 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
487 Q_ASSERT( reply->manager() ==
this );
489 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
490 if ( timer && timer->isActive() )
496 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
498 Q_ASSERT( reply->manager() ==
this );
500 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral(
"timeoutTimer" ) );
503 Q_ASSERT( !timer->isActive() );
504 QgsDebugMsg( QStringLiteral(
"Restarting network reply timeout" ) );
505 timer->setSingleShot(
true );
510 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
512 return reply->property(
"requestId" ).toInt();
515 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply,
const QList<QSslError> &errors )
517 mSslErrorHandler->handleSslErrors( reply, errors );
518 afterSslErrorHandled( reply );
523 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
526 Q_ASSERT( reply->manager() ==
this );
528 QgsDebugMsg( QStringLiteral(
"Stopping network reply timeout whilst auth request is handled" ) );
529 pauseTimeout( reply );
533 mAuthRequestHandlerSemaphore.acquire();
536 emit authRequestOccurred( reply, auth );
538 if (
this != sMainNAM )
542 mAuthRequestHandlerSemaphore.acquire();
543 mAuthRequestHandlerSemaphore.release();
544 afterAuthRequestHandled( reply );
550 if (
this != sMainNAM )
556 mAuthHandler->handleAuthRequestOpenBrowser( url );
561 if (
this != sMainNAM )
567 mAuthHandler->handleAuthRequestCloseBrowser();
572 if (
this != sMainNAM )
579 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
581 mAuthHandler->handleAuthRequest( reply, auth );
585 afterAuthRequestHandled( reply );
586 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mAuthRequestHandlerSemaphore.release();
593 case QNetworkRequest::AlwaysNetwork:
594 return QStringLiteral(
"AlwaysNetwork" );
595 case QNetworkRequest::PreferNetwork:
596 return QStringLiteral(
"PreferNetwork" );
597 case QNetworkRequest::PreferCache:
598 return QStringLiteral(
"PreferCache" );
599 case QNetworkRequest::AlwaysCache:
600 return QStringLiteral(
"AlwaysCache" );
602 return QStringLiteral(
"PreferNetwork" );
607 if ( name == QLatin1String(
"AlwaysNetwork" ) )
609 return QNetworkRequest::AlwaysNetwork;
611 else if ( name == QLatin1String(
"PreferNetwork" ) )
613 return QNetworkRequest::PreferNetwork;
615 else if ( name == QLatin1String(
"PreferCache" ) )
617 return QNetworkRequest::PreferCache;
619 else if ( name == QLatin1String(
"AlwaysCache" ) )
621 return QNetworkRequest::AlwaysCache;
623 return QNetworkRequest::PreferNetwork;
629 mUseSystemProxy =
false;
631 Q_ASSERT( sMainNAM );
633 if ( sMainNAM !=
this )
635 connect(
this, &QNetworkAccessManager::proxyAuthenticationRequired,
636 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
654 connect(
this, &QNetworkAccessManager::sslErrors,
655 sMainNAM, &QNetworkAccessManager::sslErrors,
670 setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
673 connect(
this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
675 connect(
this, &QNetworkAccessManager::authenticationRequired,
this, &QgsNetworkAccessManager::onAuthRequired );
676 connect(
this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
678 connect(
this, &QNetworkAccessManager::finished,
this, &QgsNetworkAccessManager::onReplyFinished );
683 QStringList excludes;
684 QStringList noProxyURLs;
686 const bool proxyEnabled = settings.
value( QStringLiteral(
"proxy/proxyEnabled" ),
false ).toBool();
691 excludes = settings.
value( QStringLiteral(
"proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
693 noProxyURLs = settings.
value( QStringLiteral(
"proxy/noProxyUrls" ), QStringList() ).toStringList();
696 const QString proxyHost = settings.
value( QStringLiteral(
"proxy/proxyHost" ),
"" ).toString();
697 const int proxyPort = settings.
value( QStringLiteral(
"proxy/proxyPort" ),
"" ).toString().toInt();
699 const QString proxyUser = settings.
value( QStringLiteral(
"proxy/proxyUser" ),
"" ).toString();
700 const QString proxyPassword = settings.
value( QStringLiteral(
"proxy/proxyPassword" ),
"" ).toString();
702 const QString proxyTypeString = settings.
value( QStringLiteral(
"proxy/proxyType" ),
"" ).toString();
704 if ( proxyTypeString == QLatin1String(
"DefaultProxy" ) )
706 mUseSystemProxy =
true;
707 QNetworkProxyFactory::setUseSystemConfiguration(
true );
708 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
709 if ( !proxies.isEmpty() )
711 proxy = proxies.first();
717 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
718 if ( proxyTypeString == QLatin1String(
"Socks5Proxy" ) )
720 proxyType = QNetworkProxy::Socks5Proxy;
722 else if ( proxyTypeString == QLatin1String(
"HttpProxy" ) )
724 proxyType = QNetworkProxy::HttpProxy;
726 else if ( proxyTypeString == QLatin1String(
"HttpCachingProxy" ) )
728 proxyType = QNetworkProxy::HttpCachingProxy;
730 else if ( proxyTypeString == QLatin1String(
"FtpCachingProxy" ) )
732 proxyType = QNetworkProxy::FtpCachingProxy;
734 QgsDebugMsg( QStringLiteral(
"setting proxy %1 %2:%3 %4/%5" )
736 .arg( proxyHost ).arg( proxyPort )
737 .arg( proxyUser, proxyPassword )
739 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
742 const QString authcfg = settings.
value( QStringLiteral(
"proxy/authcfg" ),
"" ).toString();
743 if ( !authcfg.isEmpty( ) )
745 QgsDebugMsg( QStringLiteral(
"setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
758 QString cacheDirectory = settings.
value( QStringLiteral(
"cache/directory" ) ).toString();
759 if ( cacheDirectory.isEmpty() )
760 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
761 const qint64 cacheSize = settings.
value( QStringLiteral(
"cache/size" ), 256 * 1024 * 1024 ).toLongLong();
767 if ( cache() != newcache )
768 setCache( newcache );
770 if (
this != sMainNAM )
772 static_cast<QgsNetworkCookieJar *
>( cookieJar() )->setAllCookies(
static_cast<QgsNetworkCookieJar *
>( sMainNAM->cookieJar() )->allCookies() );
776 void QgsNetworkAccessManager::syncCookies(
const QList<QNetworkCookie> &cookies )
778 if ( sender() !=
this )
780 static_cast<QgsNetworkCookieJar *
>( cookieJar() )->setAllCookies( cookies );
781 if (
this == sMainNAM )
802 br.
get( request, forceRefresh, feedback );
810 br.
post( request, data, forceRefresh, feedback );
816 QString
id = QUuid::createUuid().toString();
817 sCustomPreprocessors.emplace_back( std::make_pair(
id, processor ) );
823 const size_t prevCount = sCustomPreprocessors.size();
824 sCustomPreprocessors.erase( std::remove_if( sCustomPreprocessors.begin(), sCustomPreprocessors.end(), [
id]( std::pair< QString, std::function<
void( QNetworkRequest * ) > > &a )
826 return a.first == id;
827 } ), sCustomPreprocessors.end() );
828 return prevCount != sCustomPreprocessors.size();
833 QString
id = QUuid::createUuid().toString();
834 sCustomReplyPreprocessors.emplace_back( std::make_pair(
id, processor ) );
840 const size_t prevCount = sCustomReplyPreprocessors.size();
841 sCustomReplyPreprocessors.erase( std::remove_if( sCustomReplyPreprocessors.begin(), sCustomReplyPreprocessors.end(), [
id]( std::pair< QString, std::function<
void(
const QNetworkRequest &, QNetworkReply * ) > > &a )
843 return a.first == id;
844 } ), sCustomReplyPreprocessors.end() );
845 return prevCount != sCustomReplyPreprocessors.size();
850 for (
const auto &preprocessor : sCustomPreprocessors )
852 preprocessor.second( req );
862 : mOperation( operation )
863 , mRequest( request )
864 , mOriginatingThreadId( QStringLiteral(
"0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) ) )
865 , mRequestId( requestId )
866 , mContent( content )
867 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
868 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >(
QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
880 QgsDebugMsg( QStringLiteral(
"SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
890 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() ) );
896 QgsDebugMsg( QStringLiteral(
"Network authentication required external browser to open URL %1, but no handler was in place" ).arg( url.toString() ) );
901 QgsDebugMsg( QStringLiteral(
"Network authentication required external browser closed, but no handler was in place" ) );
905 #include "qgsnetworkaccessmanager.moc"