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