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