QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsnetworkaccessmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsnetworkaccessmanager.cpp
3 This class implements a QNetworkManager with the ability to chain in
4 own proxy factories.
5
6 -------------------
7 begin : 2010-05-08
8 copyright : (C) 2010 by Juergen E. Fischer
9 email : jef at norbit dot de
10
11***************************************************************************/
12
13/***************************************************************************
14 * *
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
19 * *
20 ***************************************************************************/
21
23#include "moc_qgsnetworkaccessmanager.cpp"
24
25#include "qgsapplication.h"
26#include "qgsmessagelog.h"
27#include "qgssettings.h"
29#include "qgslogger.h"
30#include "qgis.h"
31#include "qgsnetworkdiskcache.h"
32#include "qgsauthmanager.h"
33#include "qgsnetworkreply.h"
36#include "qgssettingstree.h"
37
38#include <QUrl>
39#include <QTimer>
40#include <QBuffer>
41#include <QNetworkReply>
42#include <QRecursiveMutex>
43#include <QThreadStorage>
44#include <QAuthenticator>
45#include <QStandardPaths>
46#include <QUuid>
47
48const QgsSettingsEntryInteger *QgsNetworkAccessManager::settingsNetworkTimeout = new QgsSettingsEntryInteger( QStringLiteral( "network-timeout" ), QgsSettingsTree::sTreeNetwork, 60000, QObject::tr( "Network timeout" ) );
49
50#ifndef QT_NO_SSL
51#include <QSslConfiguration>
52#endif
53
54#include "qgsnetworkdiskcache.h"
55#include "qgsauthmanager.h"
56
57QgsNetworkAccessManager *QgsNetworkAccessManager::sMainNAM = nullptr;
58
59static std::vector< std::pair< QString, std::function< void( QNetworkRequest * ) > > > sCustomPreprocessors;
60static std::vector< std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > > sCustomReplyPreprocessors;
61
63class QgsNetworkProxyFactory : public QNetworkProxyFactory
64{
65 public:
66 QgsNetworkProxyFactory() = default;
67
68 QList<QNetworkProxy> queryProxy( const QNetworkProxyQuery &query = QNetworkProxyQuery() ) override
69 {
71
72 // iterate proxies factories and take first non empty list
73 const auto constProxyFactories = nam->proxyFactories();
74 for ( QNetworkProxyFactory *f : constProxyFactories )
75 {
76 QList<QNetworkProxy> systemproxies = QNetworkProxyFactory::systemProxyForQuery( query );
77 if ( !systemproxies.isEmpty() )
78 return systemproxies;
79
80 QList<QNetworkProxy> proxies = f->queryProxy( query );
81 if ( !proxies.isEmpty() )
82 return proxies;
83 }
84
85 // no proxies from the proxy factory list check for excludes
86 if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
87 return QList<QNetworkProxy>() << nam->fallbackProxy();
88
89 const QString url = query.url().toString();
90
91 const auto constNoProxyList = nam->noProxyList();
92 for ( const QString &noProxy : constNoProxyList )
93 {
94 if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
95 {
96 QgsDebugMsgLevel( QStringLiteral( "don't using any proxy for %1 [exclude %2]" ).arg( url, noProxy ), 4 );
97 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
98 }
99 }
100
101 const auto constExcludeList = nam->excludeList();
102 for ( const QString &exclude : constExcludeList )
103 {
104 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
105 {
106 QgsDebugMsgLevel( QStringLiteral( "using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
107 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
108 }
109 }
110
111 if ( nam->useSystemProxy() )
112 {
113 QgsDebugMsgLevel( QStringLiteral( "requesting system proxy for query %1" ).arg( url ), 4 );
114 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
115 if ( !proxies.isEmpty() )
116 {
117 QgsDebugMsgLevel( QStringLiteral( "using system proxy %1:%2 for query" )
118 .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
119 return proxies;
120 }
121 }
122
123 QgsDebugMsgLevel( QStringLiteral( "using fallback proxy for %1" ).arg( url ), 4 );
124 return QList<QNetworkProxy>() << nam->fallbackProxy();
125 }
126};
128
130class QgsNetworkCookieJar : public QNetworkCookieJar
131{
132 Q_OBJECT
133
134 public:
135 QgsNetworkCookieJar( QgsNetworkAccessManager *parent )
136 : QNetworkCookieJar( parent )
137 , mNam( parent )
138 {}
139
140 bool deleteCookie( const QNetworkCookie &cookie ) override
141 {
142 const QMutexLocker locker( &mMutex );
143 if ( QNetworkCookieJar::deleteCookie( cookie ) )
144 {
145 emit mNam->cookiesChanged( allCookies() );
146 return true;
147 }
148 return false;
149 }
150 bool insertCookie( const QNetworkCookie &cookie ) override
151 {
152 const QMutexLocker locker( &mMutex );
153 if ( QNetworkCookieJar::insertCookie( cookie ) )
154 {
155 emit mNam->cookiesChanged( allCookies() );
156 return true;
157 }
158 return false;
159 }
160 bool setCookiesFromUrl( const QList<QNetworkCookie> &cookieList, const QUrl &url ) override
161 {
162 const QMutexLocker locker( &mMutex );
163 return QNetworkCookieJar::setCookiesFromUrl( cookieList, url );
164 }
165 bool updateCookie( const QNetworkCookie &cookie ) override
166 {
167 const QMutexLocker locker( &mMutex );
168 if ( QNetworkCookieJar::updateCookie( cookie ) )
169 {
170 emit mNam->cookiesChanged( allCookies() );
171 return true;
172 }
173 return false;
174 }
175
176 // Override these to make them public
177 QList<QNetworkCookie> allCookies() const
178 {
179 const QMutexLocker locker( &mMutex );
180 return QNetworkCookieJar::allCookies();
181 }
182 void setAllCookies( const QList<QNetworkCookie> &cookieList )
183 {
184 const QMutexLocker locker( &mMutex );
185 QNetworkCookieJar::setAllCookies( cookieList );
186 }
187
188 QgsNetworkAccessManager *mNam = nullptr;
189 mutable QRecursiveMutex mMutex;
190};
192
193
194//
195// Static calls to enforce singleton behavior
196//
198{
199 static QThreadStorage<QgsNetworkAccessManager> sInstances;
200 QgsNetworkAccessManager *nam = &sInstances.localData();
201
202 if ( nam->thread() == qApp->thread() )
203 sMainNAM = nam;
204
205 if ( !nam->mInitialized )
206 {
207 nam->setupDefaultProxyAndCache( connectionType );
208 nam->setCacheDisabled( sMainNAM->cacheDisabled() );
209 }
210
211 return nam;
212}
213
215 : QNetworkAccessManager( parent )
216 , mSslErrorHandlerSemaphore( 1 )
217 , mAuthRequestHandlerSemaphore( 1 )
218{
219 setProxyFactory( new QgsNetworkProxyFactory() );
220 setCookieJar( new QgsNetworkCookieJar( this ) );
221 enableStrictTransportSecurityStore( true );
222 setStrictTransportSecurityEnabled( true );
223}
224
225void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
226{
227 Q_ASSERT( sMainNAM == this );
228 mSslErrorHandler = std::move( handler );
229}
230
231void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
232{
233 Q_ASSERT( sMainNAM == this );
234 mAuthHandler = std::move( handler );
235}
236
237void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
238{
239 mProxyFactories.insert( 0, factory );
240}
241
242void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
243{
244 mProxyFactories.removeAll( factory );
245}
246
247const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
248{
249 return mProxyFactories;
250}
251
253{
254 return mExcludedURLs;
255}
256
258{
259 return mNoProxyURLs;
260}
261
262const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
263{
264 return mFallbackProxy;
265}
266
267void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
268{
269 QgsDebugMsgLevel( QStringLiteral( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
270 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral( "DefaultProxy" ) :
271 proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral( "Socks5Proxy" ) :
272 proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral( "NoProxy" ) :
273 proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral( "HttpProxy" ) :
274 proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral( "HttpCachingProxy" ) :
275 proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral( "FtpCachingProxy" ) :
276 QStringLiteral( "Undefined" ),
277 proxy.hostName() )
278 .arg( proxy.port() )
279 .arg( proxy.user(),
280 proxy.password().isEmpty() ? QStringLiteral( "not set" ) : QStringLiteral( "set" ) ), 4 );
281
282 mFallbackProxy = proxy;
283 mExcludedURLs = excludes;
284 // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
285 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
286 []( const QString & url )
287 {
288 return url.trimmed().isEmpty();
289 } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
290
291 mNoProxyURLs = noProxyURLs;
292 mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(), // clazy:exclude=detaching-member
293 []( const QString & url )
294 {
295 return url.trimmed().isEmpty();
296 } ), mNoProxyURLs.end() ); // clazy:exclude=detaching-member
297}
298
299QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
300{
301 const QgsSettings s;
302
303 QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
304
305 QString userAgent = s.value( QStringLiteral( "/qgis/networkAndProxy/userAgent" ), "Mozilla/5.0" ).toString();
306 if ( !userAgent.isEmpty() )
307 userAgent += ' ';
308 userAgent += QStringLiteral( "QGIS/%1/%2" ).arg( Qgis::versionInt() ).arg( QSysInfo::prettyProductName() );
309 pReq->setRawHeader( "User-Agent", userAgent.toLatin1() );
310
311#ifndef QT_NO_SSL
312 const bool ishttps = pReq->url().scheme().compare( QLatin1String( "https" ), Qt::CaseInsensitive ) == 0;
313 if ( ishttps && !QgsApplication::authManager()->isDisabled() )
314 {
315 QgsDebugMsgLevel( QStringLiteral( "Adding trusted CA certs to request" ), 3 );
316 QSslConfiguration sslconfig( pReq->sslConfiguration() );
317 // Merge trusted CAs with any additional CAs added by the authentication methods
318 sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
319 // check for SSL cert custom config
320 const QString hostport( QStringLiteral( "%1:%2" )
321 .arg( pReq->url().host().trimmed() )
322 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
323 const QgsAuthConfigSslServer servconfig = QgsApplication::authManager()->sslCertCustomConfigByHost( hostport.trimmed() );
324 if ( !servconfig.isNull() )
325 {
326 QgsDebugMsgLevel( QStringLiteral( "Adding SSL custom config to request for %1" ).arg( hostport ), 2 );
327 sslconfig.setProtocol( servconfig.sslProtocol() );
328 sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
329 sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
330 }
331
332 pReq->setSslConfiguration( sslconfig );
333 }
334#endif
335
336 if ( sMainNAM->mCacheDisabled )
337 {
338 // if caching is disabled then we override whatever the request actually has set!
339 pReq->setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
340 pReq->setAttribute( QNetworkRequest::CacheSaveControlAttribute, false );
341 }
342
343 for ( const auto &preprocessor : sCustomPreprocessors )
344 {
345 preprocessor.second( pReq );
346 }
347
348 static QAtomicInt sRequestId = 0;
349 const int requestId = ++sRequestId;
350 QByteArray content;
351 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
352 {
353 content = buffer->buffer();
354 }
355
356 emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
358 emit requestAboutToBeCreated( op, req, outgoingData );
360 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
361 reply->setProperty( "requestId", requestId );
362
363 emit requestCreated( QgsNetworkRequestParameters( op, reply->request(), requestId, content ) );
365 emit requestCreated( reply );
367
368 connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
369#ifndef QT_NO_SSL
370 connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
371#endif
372
373 for ( const auto &replyPreprocessor : sCustomReplyPreprocessors )
374 {
375 replyPreprocessor.second( req, reply );
376 }
377
378 // The timer will call abortRequest slot to abort the connection if needed.
379 // The timer is stopped by the finished signal and is restarted on downloadProgress and
380 // uploadProgress.
381 if ( timeout() )
382 {
383 QTimer *timer = new QTimer( reply );
384 timer->setObjectName( QStringLiteral( "timeoutTimer" ) );
385 connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
386 timer->setSingleShot( true );
387 timer->start( timeout() );
388
389 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
390 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
391 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
392 }
393 QgsDebugMsgLevel( QStringLiteral( "Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
394
395 return reply;
396}
397
398void QgsNetworkAccessManager::abortRequest()
399{
400 QTimer *timer = qobject_cast<QTimer *>( sender() );
401 Q_ASSERT( timer );
402
403 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
404 Q_ASSERT( reply );
405
406 reply->abort();
407 QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
408 QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
409 // Notify the application
410 emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
411 emit requestTimedOut( reply );
412}
413
414void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
415{
416 emit finished( QgsNetworkReplyContent( reply ) );
417}
418
419void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
420{
421 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
422 {
423 emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
424 }
425}
426
427#ifndef QT_NO_SSL
428void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
429{
430 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
431 Q_ASSERT( reply );
432 Q_ASSERT( reply->manager() == this );
433
434 QgsDebugMsgLevel( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ), 2 );
435 pauseTimeout( reply );
436
437 emit requestEncounteredSslErrors( getRequestId( reply ), errors );
438
439 // acquire semaphore a first time, so we block next acquire until release is called
440 mSslErrorHandlerSemaphore.acquire();
441
442 // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
443 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
444 emit sslErrorsOccurred( reply, errors );
445 if ( this != sMainNAM )
446 {
447 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
448 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
449 mSslErrorHandlerSemaphore.acquire();
450 mSslErrorHandlerSemaphore.release();
451 afterSslErrorHandled( reply );
452 }
453}
454
455void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
456{
457 if ( reply->manager() == this )
458 {
459 restartTimeout( reply );
460 emit sslErrorsHandled( reply );
461 }
462}
463
464void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
465{
466 if ( reply->manager() == this )
467 {
468 restartTimeout( reply );
469 emit authRequestHandled( reply );
470 }
471}
472
473void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
474{
475 Q_ASSERT( reply->manager() == this );
476
477 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
478 if ( timer && timer->isActive() )
479 {
480 timer->stop();
481 }
482}
483
484void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
485{
486 Q_ASSERT( reply->manager() == this );
487 // restart reply timeout
488 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
489 if ( timer )
490 {
491 Q_ASSERT( !timer->isActive() );
492 QgsDebugMsgLevel( QStringLiteral( "Restarting network reply timeout" ), 2 );
493 timer->setSingleShot( true );
494 timer->start( timeout() );
495 }
496}
497
498int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
499{
500 return reply->property( "requestId" ).toInt();
501}
502
503void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
504{
505 mSslErrorHandler->handleSslErrors( reply, errors );
506 afterSslErrorHandled( reply );
507 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mSslErrorHandlerSemaphore.release();
508}
509
510#endif
511
512void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
513{
514 Q_ASSERT( reply );
515 Q_ASSERT( reply->manager() == this );
516
517 QgsDebugMsgLevel( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ), 2 );
518 pauseTimeout( reply );
519
520 emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
521
522 // acquire semaphore a first time, so we block next acquire until release is called
523 mAuthRequestHandlerSemaphore.acquire();
524
525 // in main thread this will trigger auth handler immediately and return once the request is satisfied,
526 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
527 emit authRequestOccurred( reply, auth );
528
529 if ( this != sMainNAM )
530 {
531 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
532 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
533 mAuthRequestHandlerSemaphore.acquire();
534 mAuthRequestHandlerSemaphore.release();
535 afterAuthRequestHandled( reply );
536 }
537}
538
540{
541 if ( this != sMainNAM )
542 {
543 sMainNAM->requestAuthOpenBrowser( url );
545 return;
546 }
547 mAuthHandler->handleAuthRequestOpenBrowser( url );
548}
549
551{
552 if ( this != sMainNAM )
553 {
554 sMainNAM->requestAuthCloseBrowser();
556 return;
557 }
558 mAuthHandler->handleAuthRequestCloseBrowser();
559}
560
562{
563 if ( this != sMainNAM )
564 {
566 }
567 emit authBrowserAborted();
568}
569
570void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
571{
572 mAuthHandler->handleAuthRequest( reply, auth );
573
574 emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
575
576 afterAuthRequestHandled( reply );
577 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mAuthRequestHandlerSemaphore.release();
578}
579
580QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
581{
582 switch ( control )
583 {
584 case QNetworkRequest::AlwaysNetwork:
585 return QStringLiteral( "AlwaysNetwork" );
586 case QNetworkRequest::PreferNetwork:
587 return QStringLiteral( "PreferNetwork" );
588 case QNetworkRequest::PreferCache:
589 return QStringLiteral( "PreferCache" );
590 case QNetworkRequest::AlwaysCache:
591 return QStringLiteral( "AlwaysCache" );
592 }
593 return QStringLiteral( "PreferNetwork" );
594}
595
596QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
597{
598 if ( name == QLatin1String( "AlwaysNetwork" ) )
599 {
600 return QNetworkRequest::AlwaysNetwork;
601 }
602 else if ( name == QLatin1String( "PreferNetwork" ) )
603 {
604 return QNetworkRequest::PreferNetwork;
605 }
606 else if ( name == QLatin1String( "PreferCache" ) )
607 {
608 return QNetworkRequest::PreferCache;
609 }
610 else if ( name == QLatin1String( "AlwaysCache" ) )
611 {
612 return QNetworkRequest::AlwaysCache;
613 }
614 return QNetworkRequest::PreferNetwork;
615}
616
617void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
618{
619 mInitialized = true;
620 mUseSystemProxy = false;
621
622 Q_ASSERT( sMainNAM );
623
624 if ( sMainNAM != this )
625 {
626 connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
627 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
628 connectionType );
629
630 connect( this, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ),
631 sMainNAM, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ) );
632
633 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ),
634 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ) );
635
636 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ),
637 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
638
639 connect( this, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ),
640 sMainNAM, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ) );
641
642 connect( this, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ),
643 sMainNAM, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ) );
644
646
647#ifndef QT_NO_SSL
648 connect( this, &QNetworkAccessManager::sslErrors,
649 sMainNAM, &QNetworkAccessManager::sslErrors,
650 connectionType );
651
653#endif
654
656 connect( sMainNAM, &QgsNetworkAccessManager::cookiesChanged, this, &QgsNetworkAccessManager::syncCookies );
657 connect( this, &QgsNetworkAccessManager::cookiesChanged, sMainNAM, &QgsNetworkAccessManager::syncCookies );
658 }
659 else
660 {
661#ifndef QT_NO_SSL
662 if ( !mSslErrorHandler )
663 setSslErrorHandler( std::make_unique< QgsSslErrorHandler >() );
664#endif
665 if ( !mAuthHandler )
666 setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
667 }
668#ifndef QT_NO_SSL
669 connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
670#endif
671 connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
672 connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
673
674 connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
675
676 // check if proxy is enabled
677 const QgsSettings settings;
678 QNetworkProxy proxy;
679 QStringList excludes;
680 QStringList noProxyURLs;
681
682 const bool proxyEnabled = settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool();
683 if ( proxyEnabled )
684 {
685 // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
686 // meaning the system one
687 excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
688
689 noProxyURLs = settings.value( QStringLiteral( "proxy/noProxyUrls" ), QStringList() ).toStringList();
690
691 //read type, host, port, user, passw from settings
692 const QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
693 const int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
694
695 const QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
696 const QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
697
698 const QString proxyTypeString = settings.value( QStringLiteral( "proxy/proxyType" ), "" ).toString();
699
700 if ( proxyTypeString == QLatin1String( "DefaultProxy" ) )
701 {
702 mUseSystemProxy = true;
703 QNetworkProxyFactory::setUseSystemConfiguration( true );
704 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
705 if ( !proxies.isEmpty() )
706 {
707 proxy = proxies.first();
708 }
709 QgsDebugMsgLevel( QStringLiteral( "setting default proxy" ), 4 );
710 }
711 else
712 {
713 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
714 if ( proxyTypeString == QLatin1String( "Socks5Proxy" ) )
715 {
716 proxyType = QNetworkProxy::Socks5Proxy;
717 }
718 else if ( proxyTypeString == QLatin1String( "HttpProxy" ) )
719 {
720 proxyType = QNetworkProxy::HttpProxy;
721 }
722 else if ( proxyTypeString == QLatin1String( "HttpCachingProxy" ) )
723 {
724 proxyType = QNetworkProxy::HttpCachingProxy;
725 }
726 else if ( proxyTypeString == QLatin1String( "FtpCachingProxy" ) )
727 {
728 proxyType = QNetworkProxy::FtpCachingProxy;
729 }
730 QgsDebugMsgLevel( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
731 .arg( proxyType )
732 .arg( proxyHost ).arg( proxyPort )
733 .arg( proxyUser, proxyPassword ), 2
734 );
735 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
736 }
737 // Setup network proxy authentication configuration
738 const QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
739 if ( !authcfg.isEmpty( ) )
740 {
741 QgsDebugMsgLevel( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ), 2 );
742 // Never crash! Never.
743 if ( QgsAuthManager *authManager = QgsApplication::authManager() )
744 authManager->updateNetworkProxy( proxy, authcfg );
745 }
746 }
747
748 setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
749
750 QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
751 if ( !newcache )
752 newcache = new QgsNetworkDiskCache( this );
753
755 if ( cacheDirectory.isEmpty() )
756 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
757 newcache->setCacheDirectory( cacheDirectory );
759 newcache->setMaximumCacheSize( cacheSize );
760
761 QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 );
762 QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 );
763
764 if ( cache() != newcache )
765 setCache( newcache );
766
767 if ( this != sMainNAM )
768 {
769 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( static_cast<QgsNetworkCookieJar *>( sMainNAM->cookieJar() )->allCookies() );
770 }
771}
772
773void QgsNetworkAccessManager::syncCookies( const QList<QNetworkCookie> &cookies )
774{
775 if ( sender() != this )
776 {
777 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( cookies );
778 if ( this == sMainNAM )
779 {
780 emit cookiesChanged( cookies );
781 }
782 }
783}
784
789
791{
793}
794
795QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
796{
798 br.setAuthCfg( authCfg );
799 br.get( request, forceRefresh, feedback );
800 return br.reply();
801}
802
803QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
804{
806 br.setAuthCfg( authCfg );
807 br.post( request, data, forceRefresh, feedback );
808 return br.reply();
809}
810
811QString QgsNetworkAccessManager::setRequestPreprocessor( const std::function<void ( QNetworkRequest * )> &processor )
812{
813 QString id = QUuid::createUuid().toString();
814 sCustomPreprocessors.emplace_back( std::make_pair( id, processor ) );
815 return id;
816}
817
819{
820 const size_t prevCount = sCustomPreprocessors.size();
821 sCustomPreprocessors.erase( std::remove_if( sCustomPreprocessors.begin(), sCustomPreprocessors.end(), [id]( std::pair< QString, std::function< void( QNetworkRequest * ) > > &a )
822 {
823 return a.first == id;
824 } ), sCustomPreprocessors.end() );
825 return prevCount != sCustomPreprocessors.size();
826}
827
828QString QgsNetworkAccessManager::setReplyPreprocessor( const std::function<void ( const QNetworkRequest &, QNetworkReply * )> &processor )
829{
830 QString id = QUuid::createUuid().toString();
831 sCustomReplyPreprocessors.emplace_back( std::make_pair( id, processor ) );
832 return id;
833}
834
836{
837 const size_t prevCount = sCustomReplyPreprocessors.size();
838 sCustomReplyPreprocessors.erase( std::remove_if( sCustomReplyPreprocessors.begin(), sCustomReplyPreprocessors.end(), [id]( std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > &a )
839 {
840 return a.first == id;
841 } ), sCustomReplyPreprocessors.end() );
842 return prevCount != sCustomReplyPreprocessors.size();
843}
844
845void QgsNetworkAccessManager::preprocessRequest( QNetworkRequest *req ) const
846{
847 for ( const auto &preprocessor : sCustomPreprocessors )
848 {
849 preprocessor.second( req );
850 }
851}
852
853
854//
855// QgsNetworkRequestParameters
856//
857
858QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
859 : mOperation( operation )
860 , mRequest( request )
861 , mOriginatingThreadId( QStringLiteral( "0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
862 , mRequestId( requestId )
863 , mContent( content )
864 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
865 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
866{
867}
868
869
870//
871// QgsSslErrorHandler
872//
873
874void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
875{
876 Q_UNUSED( reply )
877 QgsDebugError( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
878}
879
880//
881// QgsNetworkAuthenticationHandler
882//
883
884void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
885{
886 Q_UNUSED( reply )
887 QgsDebugError( 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() ) );
888}
889
891{
892 Q_UNUSED( url )
893 QgsDebugError( QStringLiteral( "Network authentication required external browser to open URL %1, but no handler was in place" ).arg( url.toString() ) );
894}
895
897{
898 QgsDebugError( QStringLiteral( "Network authentication required external browser closed, but no handler was in place" ) );
899}
900
901// For QgsNetworkCookieJar
902#include "qgsnetworkaccessmanager.moc"
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition qgis.cpp:264
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
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...
Configuration container for SSL server connection exceptions or overrides.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
int sslPeerVerifyDepth() const
Number or SSL client's peer to verify in connections.
bool isNull() const
Whether configuration is null (missing components)
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client's peer verify mode to use in connections.
Singleton offering an interface to manage the authentication configuration database and to utilize co...
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port)
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode post(QNetworkRequest &request, QIODevice *data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
network access manager for QGIS
QStringList noProxyList() const
Returns the no proxy list.
void finished(QgsNetworkReplyContent reply)
Emitted whenever a pending network reply is finished.
static const QgsSettingsEntryInteger * settingsNetworkTimeout
Settings entry network timeout.
void cookiesChanged(const QList< QNetworkCookie > &cookies)
Emitted when the cookies changed.
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...
void insertProxyFactory(QNetworkProxyFactory *factory)
Inserts a factory into the proxy factories list.
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 operation, const QNetworkRequest &request, QIODevice *device)
void abortAuthBrowser()
Abort any outstanding external browser login request.
void setCacheDisabled(bool disabled)
Sets whether all network caching should be disabled.
const QList< QNetworkProxyFactory * > proxyFactories() const
Returns a list of proxy factories used by the manager.
void downloadProgress(int requestId, qint64 bytesReceived, qint64 bytesTotal)
Emitted when a network reply receives a progress report.
void requestAuthOpenBrowser(const QUrl &url) const
Forwards an external browser login url opening request to the authentication handler.
void requestAuthCloseBrowser() const
Forwards an external browser login closure request to the authentication handler.
void requestEncounteredSslErrors(int requestId, const QList< QSslError > &errors)
Emitted when a network request encounters SSL errors.
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl control)
Returns the name for QNetworkRequest::CacheLoadControl.
void requestCreated(const QgsNetworkRequestParameters &request)
Emitted when a network request has been created.
static QString setReplyPreprocessor(const std::function< void(const QNetworkRequest &, QNetworkReply *)> &processor)
Sets a reply pre-processor function, which allows manipulation of QNetworkReply objects after they ar...
static bool removeRequestPreprocessor(const QString &id)
Removes the custom request pre-processor function with matching id.
void requestAuthDetailsAdded(int requestId, const QString &realm, const QString &user, const QString &password)
Emitted when network authentication details have been added to a request.
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &name)
Returns QNetworkRequest::CacheLoadControl from a name.
bool cacheDisabled() const
Returns true if all network caching is disabled.
QgsNetworkAccessManager(QObject *parent=nullptr)
void requestRequiresAuth(int requestId, const QString &realm)
Emitted when a network request prompts an authentication request.
void preprocessRequest(QNetworkRequest *req) const
Preprocesses request.
void setAuthHandler(std::unique_ptr< QgsNetworkAuthenticationHandler > handler)
Sets the application network authentication handler, which is used to respond to network authenticati...
static void setTimeout(int time)
Sets the maximum timeout time for network requests, in milliseconds.
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,...
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
static int timeout()
Returns the network timeout length, in milliseconds.
void setupDefaultProxyAndCache(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Setup the QgsNetworkAccessManager (NAM) according to the user's settings.
static QString setRequestPreprocessor(const std::function< void(QNetworkRequest *request)> &processor)
Sets a request pre-processor function, which allows manipulation of a network request before it is pr...
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs)
Sets the fallback proxy and URLs which shouldn't use it.
static bool removeReplyPreprocessor(const QString &id)
Removes the custom reply pre-processor function with matching id.
QStringList excludeList() const
Returns the proxy exclude list.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void removeProxyFactory(QNetworkProxyFactory *factory)
Removes a factory from the proxy factories list.
void authBrowserAborted()
Emitted when external browser logins are to be aborted.
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
bool useSystemProxy() const
Returns whether the system proxy should be used.
QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
virtual void handleAuthRequest(QNetworkReply *reply, QAuthenticator *auth)
Called whenever network authentication requests are encountered during a network reply.
virtual void handleAuthRequestCloseBrowser()
Called to terminate a network authentication through external browser.
virtual void handleAuthRequestOpenBrowser(const QUrl &url)
Called to initiate a network authentication through external browser url.
Wrapper implementation of QNetworkDiskCache with all methods guarded by a mutex soly for internal use...
void setCacheDirectory(const QString &cacheDir)
qint64 maximumCacheSize() const
void setMaximumCacheSize(qint64 size)
QString cacheDirectory() const
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
Encapsulates parameters and properties of a network request.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
An integer settings entry.
static const QgsSettingsEntryInteger64 * settingsNetworkCacheSize
Settings entry network cache directory.
static const QgsSettingsEntryString * settingsNetworkCacheDirectory
Settings entry network cache directory.
static QgsSettingsTreeNode * sTreeNetwork
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
virtual void handleSslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
Called whenever SSL errors are encountered during a network reply.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6643
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6642
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38