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