QGIS API Documentation 3.99.0-Master (d270888f95f)
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
24#include "qgis.h"
25#include "qgsapplication.h"
26#include "qgsauthmanager.h"
28#include "qgslogger.h"
29#include "qgsmessagelog.h"
30#include "qgsnetworkdiskcache.h"
31#include "qgsnetworkreply.h"
32#include "qgssettings.h"
35#include "qgssettingstree.h"
36
37#include <QAuthenticator>
38#include <QBuffer>
39#include <QNetworkReply>
40#include <QRecursiveMutex>
41#include <QStandardPaths>
42#include <QString>
43#include <QThreadStorage>
44#include <QTimer>
45#include <QUrl>
46#include <QUuid>
47
48#include "moc_qgsnetworkaccessmanager.cpp"
49
50using namespace Qt::StringLiterals;
51
52const QgsSettingsEntryInteger *QgsNetworkAccessManager::settingsNetworkTimeout = new QgsSettingsEntryInteger( u"network-timeout"_s, QgsSettingsTree::sTreeNetwork, 60000, QObject::tr( "Network timeout" ) );
53
54#ifndef QT_NO_SSL
55#include <QSslConfiguration>
56#endif
57
58#include "qgsnetworkdiskcache.h"
59#include "qgsauthmanager.h"
60
61QgsNetworkAccessManager *QgsNetworkAccessManager::sMainNAM = nullptr;
62
63static std::vector< std::pair< QString, std::function< void( QNetworkRequest * ) > > > sCustomPreprocessors;
64static std::vector< std::pair< QString, std::function< void( QNetworkRequest *, int &op, QByteArray *data ) > > > sCustomAdvancedPreprocessors;
65static std::vector< std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > > sCustomReplyPreprocessors;
66
68class QgsNetworkProxyFactory : public QNetworkProxyFactory
69{
70 public:
71 QgsNetworkProxyFactory() = default;
72
73 QList<QNetworkProxy> queryProxy( const QNetworkProxyQuery &query = QNetworkProxyQuery() ) override
74 {
75 QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
76
77 // iterate proxies factories and take first non empty list
78 const auto constProxyFactories = nam->proxyFactories();
79 for ( QNetworkProxyFactory *f : constProxyFactories )
80 {
81 QList<QNetworkProxy> systemproxies = QNetworkProxyFactory::systemProxyForQuery( query );
82 if ( !systemproxies.isEmpty() )
83 return systemproxies;
84
85 QList<QNetworkProxy> proxies = f->queryProxy( query );
86 if ( !proxies.isEmpty() )
87 return proxies;
88 }
89
90 // no proxies from the proxy factory list check for excludes
91 if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
92 return QList<QNetworkProxy>() << nam->fallbackProxy();
93
94 const QString url = query.url().toString();
95
96 const auto constNoProxyList = nam->noProxyList();
97 for ( const QString &noProxy : constNoProxyList )
98 {
99 if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
100 {
101 QgsDebugMsgLevel( u"don't using any proxy for %1 [exclude %2]"_s.arg( url, noProxy ), 4 );
102 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
103 }
104 }
105
106 const auto constExcludeList = nam->excludeList();
107 for ( const QString &exclude : constExcludeList )
108 {
109 if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
110 {
111 QgsDebugMsgLevel( u"using default proxy for %1 [exclude %2]"_s.arg( url, exclude ), 4 );
112 return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
113 }
114 }
115
116 if ( nam->useSystemProxy() )
117 {
118 QgsDebugMsgLevel( u"requesting system proxy for query %1"_s.arg( url ), 4 );
119 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
120 if ( !proxies.isEmpty() )
121 {
122 QgsDebugMsgLevel( u"using system proxy %1:%2 for query"_s
123 .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
124 return proxies;
125 }
126 }
127
128 QgsDebugMsgLevel( u"using fallback proxy for %1"_s.arg( url ), 4 );
129 return QList<QNetworkProxy>() << nam->fallbackProxy();
130 }
131};
133
135class QgsNetworkCookieJar : public QNetworkCookieJar
136{
137 Q_OBJECT
138
139 public:
140 QgsNetworkCookieJar( QgsNetworkAccessManager *parent )
141 : QNetworkCookieJar( parent )
142 , mNam( parent )
143 {}
144
145 bool deleteCookie( const QNetworkCookie &cookie ) override
146 {
147 const QMutexLocker locker( &mMutex );
148 if ( QNetworkCookieJar::deleteCookie( cookie ) )
149 {
150 emit mNam->cookiesChanged( allCookies() );
151 return true;
152 }
153 return false;
154 }
155 bool insertCookie( const QNetworkCookie &cookie ) override
156 {
157 const QMutexLocker locker( &mMutex );
158 if ( QNetworkCookieJar::insertCookie( cookie ) )
159 {
160 emit mNam->cookiesChanged( allCookies() );
161 return true;
162 }
163 return false;
164 }
165 bool setCookiesFromUrl( const QList<QNetworkCookie> &cookieList, const QUrl &url ) override
166 {
167 const QMutexLocker locker( &mMutex );
168 return QNetworkCookieJar::setCookiesFromUrl( cookieList, url );
169 }
170 bool updateCookie( const QNetworkCookie &cookie ) override
171 {
172 const QMutexLocker locker( &mMutex );
173 if ( QNetworkCookieJar::updateCookie( cookie ) )
174 {
175 emit mNam->cookiesChanged( allCookies() );
176 return true;
177 }
178 return false;
179 }
180
181 // Override these to make them public
182 QList<QNetworkCookie> allCookies() const
183 {
184 const QMutexLocker locker( &mMutex );
185 return QNetworkCookieJar::allCookies();
186 }
187 void setAllCookies( const QList<QNetworkCookie> &cookieList )
188 {
189 const QMutexLocker locker( &mMutex );
190 QNetworkCookieJar::setAllCookies( cookieList );
191 }
192
193 QgsNetworkAccessManager *mNam = nullptr;
194 mutable QRecursiveMutex mMutex;
195};
197
198
199//
200// Static calls to enforce singleton behavior
201//
203{
204 static QThreadStorage<QgsNetworkAccessManager> sInstances;
205 QgsNetworkAccessManager *nam = &sInstances.localData();
206
207 if ( nam->thread() == qApp->thread() )
208 sMainNAM = nam;
209
210 if ( !nam->mInitialized )
211 {
212 nam->setupDefaultProxyAndCache( connectionType );
213 nam->setCacheDisabled( sMainNAM->cacheDisabled() );
214 }
215
216 return nam;
217}
218
220 : QNetworkAccessManager( parent )
221 , mSslErrorHandlerSemaphore( 1 )
222 , mAuthRequestHandlerSemaphore( 1 )
223{
224 setRedirectPolicy( QNetworkRequest::NoLessSafeRedirectPolicy );
225 setProxyFactory( new QgsNetworkProxyFactory() );
226 setCookieJar( new QgsNetworkCookieJar( this ) );
227 enableStrictTransportSecurityStore( true );
228 setStrictTransportSecurityEnabled( true );
229}
230
231void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
232{
233 Q_ASSERT( sMainNAM == this );
234 mSslErrorHandler = std::move( handler );
235}
236
237void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
238{
239 Q_ASSERT( sMainNAM == this );
240 mAuthHandler = std::move( handler );
241}
242
243void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
244{
245 mProxyFactories.insert( 0, factory );
246}
247
248void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
249{
250 mProxyFactories.removeAll( factory );
251}
252
253const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
254{
255 return mProxyFactories;
256}
257
259{
260 return mExcludedURLs;
261}
262
264{
265 return mNoProxyURLs;
266}
267
268const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
269{
270 return mFallbackProxy;
271}
272
273void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
274{
275 QgsDebugMsgLevel( u"proxy settings: (type:%1 host: %2:%3, user:%4, password:%5"_s
276 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? u"DefaultProxy"_s :
277 proxy.type() == QNetworkProxy::Socks5Proxy ? u"Socks5Proxy"_s :
278 proxy.type() == QNetworkProxy::NoProxy ? u"NoProxy"_s :
279 proxy.type() == QNetworkProxy::HttpProxy ? u"HttpProxy"_s :
280 proxy.type() == QNetworkProxy::HttpCachingProxy ? u"HttpCachingProxy"_s :
281 proxy.type() == QNetworkProxy::FtpCachingProxy ? u"FtpCachingProxy"_s :
282 u"Undefined"_s,
283 proxy.hostName() )
284 .arg( proxy.port() )
285 .arg( proxy.user(),
286 proxy.password().isEmpty() ? u"not set"_s : u"set"_s ), 4 );
287
288 mFallbackProxy = proxy;
289 mExcludedURLs = excludes;
290 // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
291 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
292 []( const QString & url )
293 {
294 return url.trimmed().isEmpty();
295 } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
296
297 mNoProxyURLs = noProxyURLs;
298 mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(), // clazy:exclude=detaching-member
299 []( const QString & url )
300 {
301 return url.trimmed().isEmpty();
302 } ), mNoProxyURLs.end() ); // clazy:exclude=detaching-member
303}
304
305QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
306{
307 const QgsSettings s;
308
309 QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
310
311 QString userAgent = s.value( u"/qgis/networkAndProxy/userAgent"_s, "Mozilla/5.0" ).toString();
312 if ( !userAgent.isEmpty() )
313 userAgent += ' ';
314 userAgent += u"QGIS/%1/%2"_s.arg( Qgis::versionInt() ).arg( QSysInfo::prettyProductName() );
315 pReq->setRawHeader( "User-Agent", userAgent.toLatin1() );
316
317#ifndef QT_NO_SSL
318 const bool ishttps = pReq->url().scheme().compare( "https"_L1, Qt::CaseInsensitive ) == 0;
319 if ( ishttps && !QgsApplication::authManager()->isDisabled() )
320 {
321 QgsDebugMsgLevel( u"Adding trusted CA certs to request"_s, 3 );
322 QSslConfiguration sslconfig( pReq->sslConfiguration() );
323 // Merge trusted CAs with any additional CAs added by the authentication methods
324 sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
325 // check for SSL cert custom config
326 const QString hostport( u"%1:%2"_s
327 .arg( pReq->url().host().trimmed() )
328 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
329 const QgsAuthConfigSslServer servconfig = QgsApplication::authManager()->sslCertCustomConfigByHost( hostport.trimmed() );
330 if ( !servconfig.isNull() )
331 {
332 QgsDebugMsgLevel( u"Adding SSL custom config to request for %1"_s.arg( hostport ), 2 );
333 sslconfig.setProtocol( servconfig.sslProtocol() );
334 sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
335 sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
336 }
337
338 pReq->setSslConfiguration( sslconfig );
339 }
340#endif
341
342 if ( sMainNAM->mCacheDisabled )
343 {
344 // if caching is disabled then we override whatever the request actually has set!
345 pReq->setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
346 pReq->setAttribute( QNetworkRequest::CacheSaveControlAttribute, false );
347 }
348
349 for ( const auto &preprocessor : sCustomPreprocessors )
350 {
351 preprocessor.second( pReq );
352 }
353
354 static QAtomicInt sRequestId = 0;
355 const int requestId = ++sRequestId;
356 QByteArray content;
357 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
358 {
359 content = buffer->buffer();
360 }
361
362 for ( const auto &preprocessor : sCustomAdvancedPreprocessors )
363 {
364 int intOp = static_cast< int >( op );
365 preprocessor.second( pReq, intOp, &content );
366 op = static_cast< QNetworkAccessManager::Operation >( intOp );
367 }
368
369 emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
371 emit requestAboutToBeCreated( op, req, outgoingData );
373 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
374 reply->setProperty( "requestId", requestId );
375
376 emit requestCreated( QgsNetworkRequestParameters( op, reply->request(), requestId, content ) );
378 emit requestCreated( reply );
380
381 connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
382#ifndef QT_NO_SSL
383 connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
384#endif
385
386 for ( const auto &replyPreprocessor : sCustomReplyPreprocessors )
387 {
388 replyPreprocessor.second( req, reply );
389 }
390
391 // The timer will call abortRequest slot to abort the connection if needed.
392 // The timer is stopped by the finished signal and is restarted on downloadProgress and
393 // uploadProgress.
394 if ( timeout() )
395 {
396 QTimer *timer = new QTimer( reply );
397 timer->setObjectName( u"timeoutTimer"_s );
398 connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
399 timer->setSingleShot( true );
400 timer->start( timeout() );
401
402 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
403 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
404 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
405 }
406 QgsDebugMsgLevel( u"Created [reply:%1]"_s.arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
407
408 return reply;
409}
410
411void QgsNetworkAccessManager::abortRequest()
412{
413 QTimer *timer = qobject_cast<QTimer *>( sender() );
414 Q_ASSERT( timer );
415
416 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
417 Q_ASSERT( reply );
418
419 reply->abort();
420 QgsDebugMsgLevel( u"Abort [reply:%1] %2"_s.arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
421 QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
422 // Notify the application
423 emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
424 emit requestTimedOut( reply );
425}
426
427void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
428{
429 emit finished( QgsNetworkReplyContent( reply ) );
430}
431
432void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
433{
434 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
435 {
436 emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
437 }
438}
439
440int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
441{
442 return reply->property( "requestId" ).toInt();
443}
444
445void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
446{
447 Q_ASSERT( reply->manager() == this );
448
449 QTimer *timer = reply->findChild<QTimer *>( u"timeoutTimer"_s );
450 if ( timer && timer->isActive() )
451 {
452 timer->stop();
453 }
454}
455
456void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
457{
458 Q_ASSERT( reply->manager() == this );
459 // restart reply timeout
460 QTimer *timer = reply->findChild<QTimer *>( u"timeoutTimer"_s );
461 if ( timer )
462 {
463 Q_ASSERT( !timer->isActive() );
464 QgsDebugMsgLevel( u"Restarting network reply timeout"_s, 2 );
465 timer->setSingleShot( true );
466 timer->start( timeout() );
467 }
468}
469
470void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
471{
472 if ( reply->manager() == this )
473 {
474 restartTimeout( reply );
475 emit authRequestHandled( reply );
476 }
477}
478
479#ifndef QT_NO_SSL
480void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
481{
482 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
483 Q_ASSERT( reply );
484 Q_ASSERT( reply->manager() == this );
485
486 QgsDebugMsgLevel( u"Stopping network reply timeout whilst SSL error is handled"_s, 2 );
487 pauseTimeout( reply );
488
489 emit requestEncounteredSslErrors( getRequestId( reply ), errors );
490
491 // acquire semaphore a first time, so we block next acquire until release is called
492 mSslErrorHandlerSemaphore.acquire();
493
494 // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
495 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
496 emit sslErrorsOccurred( reply, errors );
497 if ( this != sMainNAM )
498 {
499 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
500 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
501 mSslErrorHandlerSemaphore.acquire();
502 mSslErrorHandlerSemaphore.release();
503 afterSslErrorHandled( reply );
504 }
505}
506
507void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
508{
509 if ( reply->manager() == this )
510 {
511 restartTimeout( reply );
512 emit sslErrorsHandled( reply );
513 }
514}
515
516void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
517{
518 mSslErrorHandler->handleSslErrors( reply, errors );
519 afterSslErrorHandled( reply );
520 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mSslErrorHandlerSemaphore.release();
521}
522
523#endif
524
525void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
526{
527 Q_ASSERT( reply );
528 Q_ASSERT( reply->manager() == this );
529
530 QgsDebugMsgLevel( u"Stopping network reply timeout whilst auth request is handled"_s, 2 );
531 pauseTimeout( reply );
532
533 emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
534
535 // acquire semaphore a first time, so we block next acquire until release is called
536 mAuthRequestHandlerSemaphore.acquire();
537
538 // in main thread this will trigger auth handler immediately and return once the request is satisfied,
539 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
540 emit authRequestOccurred( reply, auth );
541
542 if ( this != sMainNAM )
543 {
544 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
545 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
546 mAuthRequestHandlerSemaphore.acquire();
547 mAuthRequestHandlerSemaphore.release();
548 afterAuthRequestHandled( reply );
549 }
550}
551
553{
554 if ( this != sMainNAM )
555 {
556 sMainNAM->requestAuthOpenBrowser( url );
558 return;
559 }
560 mAuthHandler->handleAuthRequestOpenBrowser( url );
561}
562
564{
565 if ( this != sMainNAM )
566 {
567 sMainNAM->requestAuthCloseBrowser();
569 return;
570 }
571 mAuthHandler->handleAuthRequestCloseBrowser();
572}
573
575{
576 if ( this != sMainNAM )
577 {
579 }
580 emit authBrowserAborted();
581}
582
583void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
584{
585 mAuthHandler->handleAuthRequest( reply, auth );
586
587 emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
588
589 afterAuthRequestHandled( reply );
590 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mAuthRequestHandlerSemaphore.release();
591}
592
593QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
594{
595 switch ( control )
596 {
597 case QNetworkRequest::AlwaysNetwork:
598 return u"AlwaysNetwork"_s;
599 case QNetworkRequest::PreferNetwork:
600 return u"PreferNetwork"_s;
601 case QNetworkRequest::PreferCache:
602 return u"PreferCache"_s;
603 case QNetworkRequest::AlwaysCache:
604 return u"AlwaysCache"_s;
605 }
606 return u"PreferNetwork"_s;
607}
608
609QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
610{
611 if ( name == "AlwaysNetwork"_L1 )
612 {
613 return QNetworkRequest::AlwaysNetwork;
614 }
615 else if ( name == "PreferNetwork"_L1 )
616 {
617 return QNetworkRequest::PreferNetwork;
618 }
619 else if ( name == "PreferCache"_L1 )
620 {
621 return QNetworkRequest::PreferCache;
622 }
623 else if ( name == "AlwaysCache"_L1 )
624 {
625 return QNetworkRequest::AlwaysCache;
626 }
627 return QNetworkRequest::PreferNetwork;
628}
629
630void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
631{
632 mInitialized = true;
633 mUseSystemProxy = false;
634
635 Q_ASSERT( sMainNAM );
636
637 if ( sMainNAM != this )
638 {
639 connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
640 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
641 connectionType );
642
643 connect( this, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ),
644 sMainNAM, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ) );
645
646 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ),
647 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ) );
648
649 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ),
650 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
651
652 connect( this, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ),
653 sMainNAM, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ) );
654
655 connect( this, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ),
656 sMainNAM, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ) );
657
659
660#ifndef QT_NO_SSL
661 connect( this, &QNetworkAccessManager::sslErrors,
662 sMainNAM, &QNetworkAccessManager::sslErrors,
663 connectionType );
664
666#endif
667
669 connect( sMainNAM, &QgsNetworkAccessManager::cookiesChanged, this, &QgsNetworkAccessManager::syncCookies );
670 connect( this, &QgsNetworkAccessManager::cookiesChanged, sMainNAM, &QgsNetworkAccessManager::syncCookies );
671 }
672 else
673 {
674#ifndef QT_NO_SSL
675 if ( !mSslErrorHandler )
676 setSslErrorHandler( std::make_unique< QgsSslErrorHandler >() );
677#endif
678 if ( !mAuthHandler )
679 setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
680 }
681#ifndef QT_NO_SSL
682 connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
683#endif
684 connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
685 connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
686
687 connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
688
689 // check if proxy is enabled
690 const QgsSettings settings;
691 QNetworkProxy proxy;
692 QStringList excludes;
693 QStringList noProxyURLs;
694
695 const bool proxyEnabled = settings.value( u"proxy/proxyEnabled"_s, false ).toBool();
696 if ( proxyEnabled )
697 {
698 // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
699 // meaning the system one
700 excludes = settings.value( u"proxy/proxyExcludedUrls"_s, QStringList() ).toStringList();
701
702 noProxyURLs = settings.value( u"proxy/noProxyUrls"_s, QStringList() ).toStringList();
703
704 //read type, host, port, user, passw from settings
705 const QString proxyHost = settings.value( u"proxy/proxyHost"_s, "" ).toString();
706 const int proxyPort = settings.value( u"proxy/proxyPort"_s, "" ).toString().toInt();
707
708 const QString proxyUser = settings.value( u"proxy/proxyUser"_s, "" ).toString();
709 const QString proxyPassword = settings.value( u"proxy/proxyPassword"_s, "" ).toString();
710
711 const QString proxyTypeString = settings.value( u"proxy/proxyType"_s, "" ).toString();
712
713 if ( proxyTypeString == "DefaultProxy"_L1 )
714 {
715 mUseSystemProxy = true;
716 QNetworkProxyFactory::setUseSystemConfiguration( true );
717 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
718 if ( !proxies.isEmpty() )
719 {
720 proxy = proxies.first();
721 }
722 QgsDebugMsgLevel( u"setting default proxy"_s, 4 );
723 }
724 else
725 {
726 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
727 if ( proxyTypeString == "Socks5Proxy"_L1 )
728 {
729 proxyType = QNetworkProxy::Socks5Proxy;
730 }
731 else if ( proxyTypeString == "HttpProxy"_L1 )
732 {
733 proxyType = QNetworkProxy::HttpProxy;
734 }
735 else if ( proxyTypeString == "HttpCachingProxy"_L1 )
736 {
737 proxyType = QNetworkProxy::HttpCachingProxy;
738 }
739 else if ( proxyTypeString == "FtpCachingProxy"_L1 )
740 {
741 proxyType = QNetworkProxy::FtpCachingProxy;
742 }
743 QgsDebugMsgLevel( u"setting proxy %1 %2:%3 %4/%5"_s
744 .arg( proxyType )
745 .arg( proxyHost ).arg( proxyPort )
746 .arg( proxyUser, proxyPassword ), 2
747 );
748 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
749 }
750 // Setup network proxy authentication configuration
751 const QString authcfg = settings.value( u"proxy/authcfg"_s, "" ).toString();
752 if ( !authcfg.isEmpty( ) )
753 {
754#ifdef HAVE_AUTH
755 QgsDebugMsgLevel( u"setting proxy from stored authentication configuration %1"_s.arg( authcfg ), 2 );
756 // Never crash! Never.
757 if ( QgsAuthManager *authManager = QgsApplication::authManager() )
758 authManager->updateNetworkProxy( proxy, authcfg );
759#else
760 QgsDebugError( u"Auth manager is not available - cannot update network proxy for authcfg: %1"_s.arg( authcfg ) );
761#endif
762 }
763 }
764
765 setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
766
767 QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
768 if ( !newcache )
769 newcache = new QgsNetworkDiskCache( this );
770
771 QString cacheDirectory = QgsSettingsRegistryCore::settingsNetworkCacheDirectory->value();
772 if ( cacheDirectory.isEmpty() )
773 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
774 newcache->setCacheDirectory( cacheDirectory );
775 qint64 cacheSize = QgsSettingsRegistryCore::settingsNetworkCacheSize->value();
776 newcache->setMaximumCacheSize( cacheSize );
777
778 QgsDebugMsgLevel( u"cacheDirectory: %1"_s.arg( newcache->cacheDirectory() ), 4 );
779 QgsDebugMsgLevel( u"maximumCacheSize: %1"_s.arg( newcache->maximumCacheSize() ), 4 );
780
781 if ( cache() != newcache )
782 setCache( newcache );
783
784 if ( this != sMainNAM )
785 {
786 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( static_cast<QgsNetworkCookieJar *>( sMainNAM->cookieJar() )->allCookies() );
787 }
788}
789
790void QgsNetworkAccessManager::syncCookies( const QList<QNetworkCookie> &cookies )
791{
792 if ( sender() != this )
793 {
794 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( cookies );
795 if ( this == sMainNAM )
796 {
797 emit cookiesChanged( cookies );
798 }
799 }
800}
801
803{
804 return settingsNetworkTimeout->value();
805}
806
808{
809 settingsNetworkTimeout->setValue( time );
810}
811
812QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback, Qgis::NetworkRequestFlags flags )
813{
814 QgsBlockingNetworkRequest br( flags );
815 br.setAuthCfg( authCfg );
816 br.get( request, forceRefresh, feedback );
817 return br.reply();
818}
819
820QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback, Qgis::NetworkRequestFlags flags )
821{
822 QgsBlockingNetworkRequest br( flags );
823 br.setAuthCfg( authCfg );
824 ( void )br.post( request, data, forceRefresh, feedback );
825 return br.reply();
826}
827
828QString QgsNetworkAccessManager::setRequestPreprocessor( const std::function<void ( QNetworkRequest * )> &processor )
829{
830 QString id = QUuid::createUuid().toString();
831 sCustomPreprocessors.emplace_back( std::make_pair( id, processor ) );
832 return id;
833}
834
836{
837 const size_t prevCount = sCustomPreprocessors.size();
838 sCustomPreprocessors.erase( std::remove_if( sCustomPreprocessors.begin(), sCustomPreprocessors.end(), [id]( std::pair< QString, std::function< void( QNetworkRequest * ) > > &a )
839 {
840 return a.first == id;
841 } ), sCustomPreprocessors.end() );
842 return prevCount != sCustomPreprocessors.size();
843}
844
846{
847 const size_t prevCount = sCustomAdvancedPreprocessors.size();
848 sCustomAdvancedPreprocessors.erase( std::remove_if( sCustomAdvancedPreprocessors.begin(), sCustomAdvancedPreprocessors.end(), [id]( std::pair< QString, std::function< void( QNetworkRequest *, int &, QByteArray * ) > > &a )
849 {
850 return a.first == id;
851 } ), sCustomAdvancedPreprocessors.end() );
852 return prevCount != sCustomAdvancedPreprocessors.size();
853}
854
855QString QgsNetworkAccessManager::setAdvancedRequestPreprocessor( const std::function<void ( QNetworkRequest *, int &, QByteArray * )> &processor )
856{
857 QString id = QUuid::createUuid().toString();
858 sCustomAdvancedPreprocessors.emplace_back( std::make_pair( id, processor ) );
859 return id;
860}
861
862QString QgsNetworkAccessManager::setReplyPreprocessor( const std::function<void ( const QNetworkRequest &, QNetworkReply * )> &processor )
863{
864 QString id = QUuid::createUuid().toString();
865 sCustomReplyPreprocessors.emplace_back( std::make_pair( id, processor ) );
866 return id;
867}
868
870{
871 const size_t prevCount = sCustomReplyPreprocessors.size();
872 sCustomReplyPreprocessors.erase( std::remove_if( sCustomReplyPreprocessors.begin(), sCustomReplyPreprocessors.end(), [id]( std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > &a )
873 {
874 return a.first == id;
875 } ), sCustomReplyPreprocessors.end() );
876 return prevCount != sCustomReplyPreprocessors.size();
877}
878
879void QgsNetworkAccessManager::preprocessRequest( QNetworkRequest *req ) const
880{
881 for ( const auto &preprocessor : sCustomPreprocessors )
882 {
883 preprocessor.second( req );
884 }
885}
886
887
888//
889// QgsNetworkRequestParameters
890//
891
892QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
893 : mOperation( operation )
894 , mRequest( request )
895 , mOriginatingThreadId( u"0x%2"_s.arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, '0'_L1 ) )
896 , mRequestId( requestId )
897 , mContent( content )
898 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
899 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
900{
901}
902
903
904//
905// QgsSslErrorHandler
906//
907
908void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
909{
910 Q_UNUSED( reply )
911 QgsDebugError( u"SSL errors occurred accessing URL:\n%1"_s.arg( reply->request().url().toString() ) );
912}
913
914//
915// QgsNetworkAuthenticationHandler
916//
917
918void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
919{
920 Q_UNUSED( reply )
921 QgsDebugError( u"Network reply required authentication, but no handler was in place to provide this authentication request while accessing the URL:\n%1"_s.arg( reply->request().url().toString() ) );
922}
923
925{
926 Q_UNUSED( url )
927 QgsDebugError( u"Network authentication required external browser to open URL %1, but no handler was in place"_s.arg( url.toString() ) );
928}
929
931{
932 QgsDebugError( u"Network authentication required external browser closed, but no handler was in place"_s );
933}
934
935// For QgsNetworkCookieJar
936#include "qgsnetworkaccessmanager.moc"
QFlags< NetworkRequestFlag > NetworkRequestFlags
Flags controlling behavior of network requests.
Definition qgis.h:184
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition qgis.cpp:687
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
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 which offers an interface to manage the authentication configuration database and to utiliz...
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
QNetworkAccessManager with additional QGIS specific logic.
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.
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 QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
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.
static bool removeAdvancedRequestPreprocessor(const QString &id)
Removes an advanced request pre-processor function with matching id.
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 QString setAdvancedRequestPreprocessor(const std::function< void(QNetworkRequest *, int &op, QByteArray *data)> &processor)
Sets an advanced request pre-processor function, which allows manipulation of a network request befor...
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
static QgsNetworkReplyContent blockingPost(QNetworkRequest &request, const QByteArray &data, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a POST request to obtain the contents of the target request, using the given data,...
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 solely for internal u...
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.
int requestId() const
Returns a unique ID identifying the request.
@ AttributeInitiatorClass
Class name of original object which created the request.
@ AttributeInitiatorRequestId
Internal ID used by originator object to identify requests.
QNetworkAccessManager::Operation operation() const
Returns the request operation, e.g.
QNetworkRequest request() const
Returns the network request.
QByteArray content() const
Returns the request's content.
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
Stores settings for use within QGIS.
Definition qgssettings.h:68
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:7451
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59