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