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