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