QGIS API Documentation 3.41.0-Master (02257426e5a)
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 setRedirectPolicy( QNetworkRequest::NoLessSafeRedirectPolicy );
220 setProxyFactory( new QgsNetworkProxyFactory() );
221 setCookieJar( new QgsNetworkCookieJar( this ) );
222 enableStrictTransportSecurityStore( true );
223 setStrictTransportSecurityEnabled( true );
224}
225
226void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
227{
228 Q_ASSERT( sMainNAM == this );
229 mSslErrorHandler = std::move( handler );
230}
231
232void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
233{
234 Q_ASSERT( sMainNAM == this );
235 mAuthHandler = std::move( handler );
236}
237
238void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
239{
240 mProxyFactories.insert( 0, factory );
241}
242
243void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
244{
245 mProxyFactories.removeAll( factory );
246}
247
248const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
249{
250 return mProxyFactories;
251}
252
254{
255 return mExcludedURLs;
256}
257
259{
260 return mNoProxyURLs;
261}
262
263const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
264{
265 return mFallbackProxy;
266}
267
268void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
269{
270 QgsDebugMsgLevel( QStringLiteral( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
271 .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral( "DefaultProxy" ) :
272 proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral( "Socks5Proxy" ) :
273 proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral( "NoProxy" ) :
274 proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral( "HttpProxy" ) :
275 proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral( "HttpCachingProxy" ) :
276 proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral( "FtpCachingProxy" ) :
277 QStringLiteral( "Undefined" ),
278 proxy.hostName() )
279 .arg( proxy.port() )
280 .arg( proxy.user(),
281 proxy.password().isEmpty() ? QStringLiteral( "not set" ) : QStringLiteral( "set" ) ), 4 );
282
283 mFallbackProxy = proxy;
284 mExcludedURLs = excludes;
285 // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
286 mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
287 []( const QString & url )
288 {
289 return url.trimmed().isEmpty();
290 } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
291
292 mNoProxyURLs = noProxyURLs;
293 mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(), // clazy:exclude=detaching-member
294 []( const QString & url )
295 {
296 return url.trimmed().isEmpty();
297 } ), mNoProxyURLs.end() ); // clazy:exclude=detaching-member
298}
299
300QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
301{
302 const QgsSettings s;
303
304 QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
305
306 QString userAgent = s.value( QStringLiteral( "/qgis/networkAndProxy/userAgent" ), "Mozilla/5.0" ).toString();
307 if ( !userAgent.isEmpty() )
308 userAgent += ' ';
309 userAgent += QStringLiteral( "QGIS/%1/%2" ).arg( Qgis::versionInt() ).arg( QSysInfo::prettyProductName() );
310 pReq->setRawHeader( "User-Agent", userAgent.toLatin1() );
311
312#ifndef QT_NO_SSL
313 const bool ishttps = pReq->url().scheme().compare( QLatin1String( "https" ), Qt::CaseInsensitive ) == 0;
314 if ( ishttps && !QgsApplication::authManager()->isDisabled() )
315 {
316 QgsDebugMsgLevel( QStringLiteral( "Adding trusted CA certs to request" ), 3 );
317 QSslConfiguration sslconfig( pReq->sslConfiguration() );
318 // Merge trusted CAs with any additional CAs added by the authentication methods
319 sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
320 // check for SSL cert custom config
321 const QString hostport( QStringLiteral( "%1:%2" )
322 .arg( pReq->url().host().trimmed() )
323 .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
324 const QgsAuthConfigSslServer servconfig = QgsApplication::authManager()->sslCertCustomConfigByHost( hostport.trimmed() );
325 if ( !servconfig.isNull() )
326 {
327 QgsDebugMsgLevel( QStringLiteral( "Adding SSL custom config to request for %1" ).arg( hostport ), 2 );
328 sslconfig.setProtocol( servconfig.sslProtocol() );
329 sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
330 sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
331 }
332
333 pReq->setSslConfiguration( sslconfig );
334 }
335#endif
336
337 if ( sMainNAM->mCacheDisabled )
338 {
339 // if caching is disabled then we override whatever the request actually has set!
340 pReq->setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
341 pReq->setAttribute( QNetworkRequest::CacheSaveControlAttribute, false );
342 }
343
344 for ( const auto &preprocessor : sCustomPreprocessors )
345 {
346 preprocessor.second( pReq );
347 }
348
349 static QAtomicInt sRequestId = 0;
350 const int requestId = ++sRequestId;
351 QByteArray content;
352 if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
353 {
354 content = buffer->buffer();
355 }
356
357 emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
359 emit requestAboutToBeCreated( op, req, outgoingData );
361 QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
362 reply->setProperty( "requestId", requestId );
363
364 emit requestCreated( QgsNetworkRequestParameters( op, reply->request(), requestId, content ) );
366 emit requestCreated( reply );
368
369 connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
370#ifndef QT_NO_SSL
371 connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
372#endif
373
374 for ( const auto &replyPreprocessor : sCustomReplyPreprocessors )
375 {
376 replyPreprocessor.second( req, reply );
377 }
378
379 // The timer will call abortRequest slot to abort the connection if needed.
380 // The timer is stopped by the finished signal and is restarted on downloadProgress and
381 // uploadProgress.
382 if ( timeout() )
383 {
384 QTimer *timer = new QTimer( reply );
385 timer->setObjectName( QStringLiteral( "timeoutTimer" ) );
386 connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
387 timer->setSingleShot( true );
388 timer->start( timeout() );
389
390 connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
391 connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
392 connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
393 }
394 QgsDebugMsgLevel( QStringLiteral( "Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
395
396 return reply;
397}
398
399void QgsNetworkAccessManager::abortRequest()
400{
401 QTimer *timer = qobject_cast<QTimer *>( sender() );
402 Q_ASSERT( timer );
403
404 QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
405 Q_ASSERT( reply );
406
407 reply->abort();
408 QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
409 QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
410 // Notify the application
411 emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
412 emit requestTimedOut( reply );
413}
414
415void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
416{
417 emit finished( QgsNetworkReplyContent( reply ) );
418}
419
420void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
421{
422 if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
423 {
424 emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
425 }
426}
427
428#ifndef QT_NO_SSL
429void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
430{
431 QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
432 Q_ASSERT( reply );
433 Q_ASSERT( reply->manager() == this );
434
435 QgsDebugMsgLevel( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ), 2 );
436 pauseTimeout( reply );
437
438 emit requestEncounteredSslErrors( getRequestId( reply ), errors );
439
440 // acquire semaphore a first time, so we block next acquire until release is called
441 mSslErrorHandlerSemaphore.acquire();
442
443 // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
444 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
445 emit sslErrorsOccurred( reply, errors );
446 if ( this != sMainNAM )
447 {
448 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
449 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
450 mSslErrorHandlerSemaphore.acquire();
451 mSslErrorHandlerSemaphore.release();
452 afterSslErrorHandled( reply );
453 }
454}
455
456void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
457{
458 if ( reply->manager() == this )
459 {
460 restartTimeout( reply );
461 emit sslErrorsHandled( reply );
462 }
463}
464
465void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
466{
467 if ( reply->manager() == this )
468 {
469 restartTimeout( reply );
470 emit authRequestHandled( reply );
471 }
472}
473
474void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
475{
476 Q_ASSERT( reply->manager() == this );
477
478 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
479 if ( timer && timer->isActive() )
480 {
481 timer->stop();
482 }
483}
484
485void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
486{
487 Q_ASSERT( reply->manager() == this );
488 // restart reply timeout
489 QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
490 if ( timer )
491 {
492 Q_ASSERT( !timer->isActive() );
493 QgsDebugMsgLevel( QStringLiteral( "Restarting network reply timeout" ), 2 );
494 timer->setSingleShot( true );
495 timer->start( timeout() );
496 }
497}
498
499int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
500{
501 return reply->property( "requestId" ).toInt();
502}
503
504void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
505{
506 mSslErrorHandler->handleSslErrors( reply, errors );
507 afterSslErrorHandled( reply );
508 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mSslErrorHandlerSemaphore.release();
509}
510
511#endif
512
513void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
514{
515 Q_ASSERT( reply );
516 Q_ASSERT( reply->manager() == this );
517
518 QgsDebugMsgLevel( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ), 2 );
519 pauseTimeout( reply );
520
521 emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
522
523 // acquire semaphore a first time, so we block next acquire until release is called
524 mAuthRequestHandlerSemaphore.acquire();
525
526 // in main thread this will trigger auth handler immediately and return once the request is satisfied,
527 // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
528 emit authRequestOccurred( reply, auth );
529
530 if ( this != sMainNAM )
531 {
532 // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
533 // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
534 mAuthRequestHandlerSemaphore.acquire();
535 mAuthRequestHandlerSemaphore.release();
536 afterAuthRequestHandled( reply );
537 }
538}
539
541{
542 if ( this != sMainNAM )
543 {
544 sMainNAM->requestAuthOpenBrowser( url );
546 return;
547 }
548 mAuthHandler->handleAuthRequestOpenBrowser( url );
549}
550
552{
553 if ( this != sMainNAM )
554 {
555 sMainNAM->requestAuthCloseBrowser();
557 return;
558 }
559 mAuthHandler->handleAuthRequestCloseBrowser();
560}
561
563{
564 if ( this != sMainNAM )
565 {
567 }
568 emit authBrowserAborted();
569}
570
571void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
572{
573 mAuthHandler->handleAuthRequest( reply, auth );
574
575 emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
576
577 afterAuthRequestHandled( reply );
578 qobject_cast<QgsNetworkAccessManager *>( reply->manager() )->mAuthRequestHandlerSemaphore.release();
579}
580
581QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
582{
583 switch ( control )
584 {
585 case QNetworkRequest::AlwaysNetwork:
586 return QStringLiteral( "AlwaysNetwork" );
587 case QNetworkRequest::PreferNetwork:
588 return QStringLiteral( "PreferNetwork" );
589 case QNetworkRequest::PreferCache:
590 return QStringLiteral( "PreferCache" );
591 case QNetworkRequest::AlwaysCache:
592 return QStringLiteral( "AlwaysCache" );
593 }
594 return QStringLiteral( "PreferNetwork" );
595}
596
597QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
598{
599 if ( name == QLatin1String( "AlwaysNetwork" ) )
600 {
601 return QNetworkRequest::AlwaysNetwork;
602 }
603 else if ( name == QLatin1String( "PreferNetwork" ) )
604 {
605 return QNetworkRequest::PreferNetwork;
606 }
607 else if ( name == QLatin1String( "PreferCache" ) )
608 {
609 return QNetworkRequest::PreferCache;
610 }
611 else if ( name == QLatin1String( "AlwaysCache" ) )
612 {
613 return QNetworkRequest::AlwaysCache;
614 }
615 return QNetworkRequest::PreferNetwork;
616}
617
618void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
619{
620 mInitialized = true;
621 mUseSystemProxy = false;
622
623 Q_ASSERT( sMainNAM );
624
625 if ( sMainNAM != this )
626 {
627 connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
628 sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
629 connectionType );
630
631 connect( this, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ),
632 sMainNAM, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ) );
633
634 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ),
635 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ) );
636
637 connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ),
638 sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
639
640 connect( this, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ),
641 sMainNAM, qOverload< const QgsNetworkRequestParameters & >( &QgsNetworkAccessManager::requestCreated ) );
642
643 connect( this, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ),
644 sMainNAM, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ) );
645
647
648#ifndef QT_NO_SSL
649 connect( this, &QNetworkAccessManager::sslErrors,
650 sMainNAM, &QNetworkAccessManager::sslErrors,
651 connectionType );
652
654#endif
655
657 connect( sMainNAM, &QgsNetworkAccessManager::cookiesChanged, this, &QgsNetworkAccessManager::syncCookies );
658 connect( this, &QgsNetworkAccessManager::cookiesChanged, sMainNAM, &QgsNetworkAccessManager::syncCookies );
659 }
660 else
661 {
662#ifndef QT_NO_SSL
663 if ( !mSslErrorHandler )
664 setSslErrorHandler( std::make_unique< QgsSslErrorHandler >() );
665#endif
666 if ( !mAuthHandler )
667 setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
668 }
669#ifndef QT_NO_SSL
670 connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
671#endif
672 connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
673 connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
674
675 connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
676
677 // check if proxy is enabled
678 const QgsSettings settings;
679 QNetworkProxy proxy;
680 QStringList excludes;
681 QStringList noProxyURLs;
682
683 const bool proxyEnabled = settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool();
684 if ( proxyEnabled )
685 {
686 // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
687 // meaning the system one
688 excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
689
690 noProxyURLs = settings.value( QStringLiteral( "proxy/noProxyUrls" ), QStringList() ).toStringList();
691
692 //read type, host, port, user, passw from settings
693 const QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
694 const int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
695
696 const QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
697 const QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
698
699 const QString proxyTypeString = settings.value( QStringLiteral( "proxy/proxyType" ), "" ).toString();
700
701 if ( proxyTypeString == QLatin1String( "DefaultProxy" ) )
702 {
703 mUseSystemProxy = true;
704 QNetworkProxyFactory::setUseSystemConfiguration( true );
705 QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
706 if ( !proxies.isEmpty() )
707 {
708 proxy = proxies.first();
709 }
710 QgsDebugMsgLevel( QStringLiteral( "setting default proxy" ), 4 );
711 }
712 else
713 {
714 QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
715 if ( proxyTypeString == QLatin1String( "Socks5Proxy" ) )
716 {
717 proxyType = QNetworkProxy::Socks5Proxy;
718 }
719 else if ( proxyTypeString == QLatin1String( "HttpProxy" ) )
720 {
721 proxyType = QNetworkProxy::HttpProxy;
722 }
723 else if ( proxyTypeString == QLatin1String( "HttpCachingProxy" ) )
724 {
725 proxyType = QNetworkProxy::HttpCachingProxy;
726 }
727 else if ( proxyTypeString == QLatin1String( "FtpCachingProxy" ) )
728 {
729 proxyType = QNetworkProxy::FtpCachingProxy;
730 }
731 QgsDebugMsgLevel( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
732 .arg( proxyType )
733 .arg( proxyHost ).arg( proxyPort )
734 .arg( proxyUser, proxyPassword ), 2
735 );
736 proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
737 }
738 // Setup network proxy authentication configuration
739 const QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
740 if ( !authcfg.isEmpty( ) )
741 {
742 QgsDebugMsgLevel( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ), 2 );
743 // Never crash! Never.
744 if ( QgsAuthManager *authManager = QgsApplication::authManager() )
745 authManager->updateNetworkProxy( proxy, authcfg );
746 }
747 }
748
749 setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
750
751 QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
752 if ( !newcache )
753 newcache = new QgsNetworkDiskCache( this );
754
756 if ( cacheDirectory.isEmpty() )
757 cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
758 newcache->setCacheDirectory( cacheDirectory );
760 newcache->setMaximumCacheSize( cacheSize );
761
762 QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 );
763 QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 );
764
765 if ( cache() != newcache )
766 setCache( newcache );
767
768 if ( this != sMainNAM )
769 {
770 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( static_cast<QgsNetworkCookieJar *>( sMainNAM->cookieJar() )->allCookies() );
771 }
772}
773
774void QgsNetworkAccessManager::syncCookies( const QList<QNetworkCookie> &cookies )
775{
776 if ( sender() != this )
777 {
778 static_cast<QgsNetworkCookieJar *>( cookieJar() )->setAllCookies( cookies );
779 if ( this == sMainNAM )
780 {
781 emit cookiesChanged( cookies );
782 }
783 }
784}
785
790
792{
794}
795
796QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
797{
799 br.setAuthCfg( authCfg );
800 br.get( request, forceRefresh, feedback );
801 return br.reply();
802}
803
804QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
805{
807 br.setAuthCfg( authCfg );
808 br.post( request, data, forceRefresh, feedback );
809 return br.reply();
810}
811
812QString QgsNetworkAccessManager::setRequestPreprocessor( const std::function<void ( QNetworkRequest * )> &processor )
813{
814 QString id = QUuid::createUuid().toString();
815 sCustomPreprocessors.emplace_back( std::make_pair( id, processor ) );
816 return id;
817}
818
820{
821 const size_t prevCount = sCustomPreprocessors.size();
822 sCustomPreprocessors.erase( std::remove_if( sCustomPreprocessors.begin(), sCustomPreprocessors.end(), [id]( std::pair< QString, std::function< void( QNetworkRequest * ) > > &a )
823 {
824 return a.first == id;
825 } ), sCustomPreprocessors.end() );
826 return prevCount != sCustomPreprocessors.size();
827}
828
829QString QgsNetworkAccessManager::setReplyPreprocessor( const std::function<void ( const QNetworkRequest &, QNetworkReply * )> &processor )
830{
831 QString id = QUuid::createUuid().toString();
832 sCustomReplyPreprocessors.emplace_back( std::make_pair( id, processor ) );
833 return id;
834}
835
837{
838 const size_t prevCount = sCustomReplyPreprocessors.size();
839 sCustomReplyPreprocessors.erase( std::remove_if( sCustomReplyPreprocessors.begin(), sCustomReplyPreprocessors.end(), [id]( std::pair< QString, std::function< void( const QNetworkRequest &, QNetworkReply * ) > > &a )
840 {
841 return a.first == id;
842 } ), sCustomReplyPreprocessors.end() );
843 return prevCount != sCustomReplyPreprocessors.size();
844}
845
846void QgsNetworkAccessManager::preprocessRequest( QNetworkRequest *req ) const
847{
848 for ( const auto &preprocessor : sCustomPreprocessors )
849 {
850 preprocessor.second( req );
851 }
852}
853
854
855//
856// QgsNetworkRequestParameters
857//
858
859QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
860 : mOperation( operation )
861 , mRequest( request )
862 , mOriginatingThreadId( QStringLiteral( "0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
863 , mRequestId( requestId )
864 , mContent( content )
865 , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
866 , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
867{
868}
869
870
871//
872// QgsSslErrorHandler
873//
874
875void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
876{
877 Q_UNUSED( reply )
878 QgsDebugError( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
879}
880
881//
882// QgsNetworkAuthenticationHandler
883//
884
885void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
886{
887 Q_UNUSED( reply )
888 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() ) );
889}
890
892{
893 Q_UNUSED( url )
894 QgsDebugError( QStringLiteral( "Network authentication required external browser to open URL %1, but no handler was in place" ).arg( url.toString() ) );
895}
896
898{
899 QgsDebugError( QStringLiteral( "Network authentication required external browser closed, but no handler was in place" ) );
900}
901
902// For QgsNetworkCookieJar
903#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, 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).
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:6668
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6667
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40