QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 <QThreadStorage>
39 #include <QAuthenticator>
40 #include <QStandardPaths>
41 
42 #ifndef QT_NO_SSL
43 #include <QSslConfiguration>
44 #endif
45 
46 #include "qgsnetworkdiskcache.h"
47 #include "qgsauthmanager.h"
48 
49 QgsNetworkAccessManager *QgsNetworkAccessManager::sMainNAM = nullptr;
50 
52 class QgsNetworkProxyFactory : public QNetworkProxyFactory
53 {
54  public:
55  QgsNetworkProxyFactory() = default;
56 
57  QList<QNetworkProxy> queryProxy( const QNetworkProxyQuery &query = QNetworkProxyQuery() ) override
58  {
60 
61  // iterate proxies factories and take first non empty list
62  const auto constProxyFactories = nam->proxyFactories();
63  for ( QNetworkProxyFactory *f : constProxyFactories )
64  {
65  QList<QNetworkProxy> systemproxies = f->systemProxyForQuery( query );
66  if ( !systemproxies.isEmpty() )
67  return systemproxies;
68 
69  QList<QNetworkProxy> proxies = f->queryProxy( query );
70  if ( !proxies.isEmpty() )
71  return proxies;
72  }
73 
74  // no proxies from the proxy factory list check for excludes
75  if ( query.queryType() != QNetworkProxyQuery::UrlRequest )
76  return QList<QNetworkProxy>() << nam->fallbackProxy();
77 
78  QString url = query.url().toString();
79 
80  const auto constNoProxyList = nam->noProxyList();
81  for ( const QString &noProxy : constNoProxyList )
82  {
83  if ( !noProxy.trimmed().isEmpty() && url.startsWith( noProxy ) )
84  {
85  QgsDebugMsgLevel( QStringLiteral( "don't using any proxy for %1 [exclude %2]" ).arg( url, noProxy ), 4 );
86  return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::NoProxy );
87  }
88  }
89 
90  const auto constExcludeList = nam->excludeList();
91  for ( const QString &exclude : constExcludeList )
92  {
93  if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
94  {
95  QgsDebugMsgLevel( QStringLiteral( "using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
96  return QList<QNetworkProxy>() << QNetworkProxy( QNetworkProxy::DefaultProxy );
97  }
98  }
99 
100  if ( nam->useSystemProxy() )
101  {
102  QgsDebugMsgLevel( QStringLiteral( "requesting system proxy for query %1" ).arg( url ), 4 );
103  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
104  if ( !proxies.isEmpty() )
105  {
106  QgsDebugMsgLevel( QStringLiteral( "using system proxy %1:%2 for query" )
107  .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
108  return proxies;
109  }
110  }
111 
112  QgsDebugMsgLevel( QStringLiteral( "using fallback proxy for %1" ).arg( url ), 4 );
113  return QList<QNetworkProxy>() << nam->fallbackProxy();
114  }
115 };
117 
118 //
119 // Static calls to enforce singleton behavior
120 //
121 QgsNetworkAccessManager *QgsNetworkAccessManager::instance( Qt::ConnectionType connectionType )
122 {
123  static QThreadStorage<QgsNetworkAccessManager> sInstances;
124  QgsNetworkAccessManager *nam = &sInstances.localData();
125 
126  if ( nam->thread() == qApp->thread() )
127  sMainNAM = nam;
128 
129  if ( !nam->mInitialized )
130  {
131  nam->setupDefaultProxyAndCache( connectionType );
132  nam->setCacheDisabled( sMainNAM->cacheDisabled() );
133  }
134 
135  return nam;
136 }
137 
139  : QNetworkAccessManager( parent )
140 {
141  setProxyFactory( new QgsNetworkProxyFactory() );
142 }
143 
144 void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
145 {
146  Q_ASSERT( sMainNAM == this );
147  mSslErrorHandler = std::move( handler );
148 }
149 
150 void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
151 {
152  Q_ASSERT( sMainNAM == this );
153  mAuthHandler = std::move( handler );
154 }
155 
156 void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
157 {
158  mProxyFactories.insert( 0, factory );
159 }
160 
161 void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
162 {
163  mProxyFactories.removeAll( factory );
164 }
165 
166 const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
167 {
168  return mProxyFactories;
169 }
170 
172 {
173  return mExcludedURLs;
174 }
175 
177 {
178  return mNoProxyURLs;
179 }
180 
181 const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
182 {
183  return mFallbackProxy;
184 }
185 
186 void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes, const QStringList &noProxyURLs )
187 {
188  QgsDebugMsgLevel( QStringLiteral( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
189  .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral( "DefaultProxy" ) :
190  proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral( "Socks5Proxy" ) :
191  proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral( "NoProxy" ) :
192  proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral( "HttpProxy" ) :
193  proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral( "HttpCachingProxy" ) :
194  proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral( "FtpCachingProxy" ) :
195  QStringLiteral( "Undefined" ),
196  proxy.hostName() )
197  .arg( proxy.port() )
198  .arg( proxy.user(),
199  proxy.password().isEmpty() ? QStringLiteral( "not set" ) : QStringLiteral( "set" ) ), 4 );
200 
201  mFallbackProxy = proxy;
202  mExcludedURLs = excludes;
203  // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
204  mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
205  []( const QString & url )
206  {
207  return url.trimmed().isEmpty();
208  } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
209 
210  mNoProxyURLs = noProxyURLs;
211  mNoProxyURLs.erase( std::remove_if( mNoProxyURLs.begin(), mNoProxyURLs.end(), // clazy:exclude=detaching-member
212  []( const QString & url )
213  {
214  return url.trimmed().isEmpty();
215  } ), mNoProxyURLs.end() ); // clazy:exclude=detaching-member
216 }
217 
218 QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
219 {
220  QgsSettings s;
221 
222  QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
223 
224  QString userAgent = s.value( QStringLiteral( "/qgis/networkAndProxy/userAgent" ), "Mozilla/5.0" ).toString();
225  if ( !userAgent.isEmpty() )
226  userAgent += ' ';
227  userAgent += QStringLiteral( "QGIS/%1" ).arg( Qgis::versionInt() );
228  pReq->setRawHeader( "User-Agent", userAgent.toLatin1() );
229 
230 #ifndef QT_NO_SSL
231  bool ishttps = pReq->url().scheme().compare( QLatin1String( "https" ), Qt::CaseInsensitive ) == 0;
232  if ( ishttps && !QgsApplication::authManager()->isDisabled() )
233  {
234  QgsDebugMsgLevel( QStringLiteral( "Adding trusted CA certs to request" ), 3 );
235  QSslConfiguration sslconfig( pReq->sslConfiguration() );
236  // Merge trusted CAs with any additional CAs added by the authentication methods
237  sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
238  // check for SSL cert custom config
239  QString hostport( QStringLiteral( "%1:%2" )
240  .arg( pReq->url().host().trimmed() )
241  .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
243  if ( !servconfig.isNull() )
244  {
245  QgsDebugMsg( QStringLiteral( "Adding SSL custom config to request for %1" ).arg( hostport ) );
246  sslconfig.setProtocol( servconfig.sslProtocol() );
247  sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
248  sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
249  }
250 
251  pReq->setSslConfiguration( sslconfig );
252  }
253 #endif
254 
255  if ( sMainNAM->mCacheDisabled )
256  {
257  // if caching is disabled then we override whatever the request actually has set!
258  pReq->setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
259  pReq->setAttribute( QNetworkRequest::CacheSaveControlAttribute, false );
260  }
261 
262  static QAtomicInt sRequestId = 0;
263  const int requestId = ++sRequestId;
264  QByteArray content;
265  if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
266  {
267  content = buffer->buffer();
268  }
269 
270  emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
272  emit requestAboutToBeCreated( op, req, outgoingData );
274  QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
275  reply->setProperty( "requestId", requestId );
276 
278  emit requestCreated( reply );
280 
281  connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
282 #ifndef QT_NO_SSL
283  connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
284 #endif
285 
286  // The timer will call abortRequest slot to abort the connection if needed.
287  // The timer is stopped by the finished signal and is restarted on downloadProgress and
288  // uploadProgress.
289  if ( timeout() )
290  {
291  QTimer *timer = new QTimer( reply );
292  timer->setObjectName( QStringLiteral( "timeoutTimer" ) );
293  connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
294  timer->setSingleShot( true );
295  timer->start( timeout() );
296 
297  connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
298  connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
299  connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
300  }
301  QgsDebugMsgLevel( QStringLiteral( "Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
302 
303  return reply;
304 }
305 
306 #ifndef QT_NO_SSL
307 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
308 {
309  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
310  mSslErrorWaitCondition.wakeOne();
311 }
312 #endif
313 
314 void QgsNetworkAccessManager::abortRequest()
315 {
316  QTimer *timer = qobject_cast<QTimer *>( sender() );
317  Q_ASSERT( timer );
318 
319  QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
320  Q_ASSERT( reply );
321 
322  reply->abort();
323  QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
324  QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
325  // Notify the application
326  emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
327  emit requestTimedOut( reply );
328 }
329 
330 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
331 {
332  emit finished( QgsNetworkReplyContent( reply ) );
333 }
334 
335 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
336 {
337  if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
338  {
339  emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
340  }
341 }
342 
343 #ifndef QT_NO_SSL
344 void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
345 {
346  QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
347  Q_ASSERT( reply );
348  Q_ASSERT( reply->manager() == this );
349 
350  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ) );
351  pauseTimeout( reply );
352 
353  emit requestEncounteredSslErrors( getRequestId( reply ), errors );
354 
355  // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
356  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
357  emit sslErrorsOccurred( reply, errors );
358  if ( this != sMainNAM )
359  {
360  // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
361  // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
362  mSslErrorHandlerMutex.lock();
363  mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
364  mSslErrorHandlerMutex.unlock();
365  afterSslErrorHandled( reply );
366  }
367 }
368 
369 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
370 {
371  if ( reply->manager() == this )
372  {
373  restartTimeout( reply );
374  emit sslErrorsHandled( reply );
375  }
376  else if ( this == sMainNAM )
377  {
378  // notify other threads to allow them to handle the reply
379  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled(); // safe to call directly - the other thread will be stuck waiting for us
380  }
381 }
382 
383 void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
384 {
385  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
386  mAuthRequestWaitCondition.wakeOne();
387 }
388 
389 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
390 {
391  if ( reply->manager() == this )
392  {
393  restartTimeout( reply );
394  emit authRequestHandled( reply );
395  }
396  else if ( this == sMainNAM )
397  {
398  // notify other threads to allow them to handle the reply
399  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterAuthRequestHandled(); // safe to call directly - the other thread will be stuck waiting for us
400  }
401 }
402 
403 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
404 {
405  Q_ASSERT( reply->manager() == this );
406 
407  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
408  if ( timer && timer->isActive() )
409  {
410  timer->stop();
411  }
412 }
413 
414 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
415 {
416  Q_ASSERT( reply->manager() == this );
417  // restart reply timeout
418  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
419  if ( timer )
420  {
421  Q_ASSERT( !timer->isActive() );
422  QgsDebugMsg( QStringLiteral( "Restarting network reply timeout" ) );
423  timer->setSingleShot( true );
424  timer->start( timeout() );
425  }
426 }
427 
428 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
429 {
430  return reply->property( "requestId" ).toInt();
431 }
432 
433 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
434 {
435  mSslErrorHandler->handleSslErrors( reply, errors );
436  afterSslErrorHandled( reply );
437 }
438 
439 #endif
440 
441 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
442 {
443  Q_ASSERT( reply );
444  Q_ASSERT( reply->manager() == this );
445 
446  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ) );
447  pauseTimeout( reply );
448 
449  emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
450 
451  // in main thread this will trigger auth handler immediately and return once the request is satisfied,
452  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
453  emit authRequestOccurred( reply, auth );
454 
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  mAuthRequestHandlerMutex.lock();
460  mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
461  mAuthRequestHandlerMutex.unlock();
462  afterAuthRequestHandled( reply );
463  }
464 }
465 
467 {
468  if ( this != sMainNAM )
469  {
470  sMainNAM->requestAuthOpenBrowser( url );
472  return;
473  }
474  mAuthHandler->handleAuthRequestOpenBrowser( url );
475 }
476 
478 {
479  if ( this != sMainNAM )
480  {
481  sMainNAM->requestAuthCloseBrowser();
483  return;
484  }
485  mAuthHandler->handleAuthRequestCloseBrowser();
486 }
487 
489 {
490  if ( this != sMainNAM )
491  {
493  }
494  emit authBrowserAborted();
495 }
496 
497 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
498 {
499  mAuthHandler->handleAuthRequest( reply, auth );
500 
501  emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
502 
503  afterAuthRequestHandled( reply );
504 }
505 
506 QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
507 {
508  switch ( control )
509  {
510  case QNetworkRequest::AlwaysNetwork:
511  return QStringLiteral( "AlwaysNetwork" );
512  case QNetworkRequest::PreferNetwork:
513  return QStringLiteral( "PreferNetwork" );
514  case QNetworkRequest::PreferCache:
515  return QStringLiteral( "PreferCache" );
516  case QNetworkRequest::AlwaysCache:
517  return QStringLiteral( "AlwaysCache" );
518  }
519  return QStringLiteral( "PreferNetwork" );
520 }
521 
522 QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
523 {
524  if ( name == QLatin1String( "AlwaysNetwork" ) )
525  {
526  return QNetworkRequest::AlwaysNetwork;
527  }
528  else if ( name == QLatin1String( "PreferNetwork" ) )
529  {
530  return QNetworkRequest::PreferNetwork;
531  }
532  else if ( name == QLatin1String( "PreferCache" ) )
533  {
534  return QNetworkRequest::PreferCache;
535  }
536  else if ( name == QLatin1String( "AlwaysCache" ) )
537  {
538  return QNetworkRequest::AlwaysCache;
539  }
540  return QNetworkRequest::PreferNetwork;
541 }
542 
543 void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
544 {
545  mInitialized = true;
546  mUseSystemProxy = false;
547 
548  Q_ASSERT( sMainNAM );
549 
550  if ( sMainNAM != this )
551  {
552  connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
553  sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
554  connectionType );
555 
556  connect( this, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ),
557  sMainNAM, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ) );
558 
559  connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ),
560  sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestTimedOut ) );
561 
562  connect( this, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ),
563  sMainNAM, qOverload< QgsNetworkRequestParameters >( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
564 
565  connect( this, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ),
566  sMainNAM, qOverload< QgsNetworkReplyContent >( &QgsNetworkAccessManager::finished ) );
567 
569 
570 #ifndef QT_NO_SSL
571  connect( this, &QNetworkAccessManager::sslErrors,
572  sMainNAM, &QNetworkAccessManager::sslErrors,
573  connectionType );
574 
576 #endif
577 
579  }
580  else
581  {
582 #ifndef QT_NO_SSL
583  setSslErrorHandler( std::make_unique< QgsSslErrorHandler >() );
584 #endif
585  setAuthHandler( std::make_unique< QgsNetworkAuthenticationHandler>() );
586  }
587 #ifndef QT_NO_SSL
588  connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
589 #endif
590  connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
591  connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
592 
593  connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
594 
595  // check if proxy is enabled
596  QgsSettings settings;
597  QNetworkProxy proxy;
598  QStringList excludes;
599  QStringList noProxyURLs;
600 
601  bool proxyEnabled = settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool();
602  if ( proxyEnabled )
603  {
604  // This settings is keep for retrocompatibility, the returned proxy for these URL is the default one,
605  // meaning the system one
606  excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
607 
608  noProxyURLs = settings.value( QStringLiteral( "proxy/noProxyUrls" ), QStringList() ).toStringList();
609 
610  //read type, host, port, user, passw from settings
611  QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
612  int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
613 
614  QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
615  QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
616 
617  QString proxyTypeString = settings.value( QStringLiteral( "proxy/proxyType" ), "" ).toString();
618 
619  if ( proxyTypeString == QLatin1String( "DefaultProxy" ) )
620  {
621  mUseSystemProxy = true;
622  QNetworkProxyFactory::setUseSystemConfiguration( true );
623  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
624  if ( !proxies.isEmpty() )
625  {
626  proxy = proxies.first();
627  }
628  QgsDebugMsgLevel( QStringLiteral( "setting default proxy" ), 4 );
629  }
630  else
631  {
632  QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
633  if ( proxyTypeString == QLatin1String( "Socks5Proxy" ) )
634  {
635  proxyType = QNetworkProxy::Socks5Proxy;
636  }
637  else if ( proxyTypeString == QLatin1String( "HttpProxy" ) )
638  {
639  proxyType = QNetworkProxy::HttpProxy;
640  }
641  else if ( proxyTypeString == QLatin1String( "HttpCachingProxy" ) )
642  {
643  proxyType = QNetworkProxy::HttpCachingProxy;
644  }
645  else if ( proxyTypeString == QLatin1String( "FtpCachingProxy" ) )
646  {
647  proxyType = QNetworkProxy::FtpCachingProxy;
648  }
649  QgsDebugMsg( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
650  .arg( proxyType )
651  .arg( proxyHost ).arg( proxyPort )
652  .arg( proxyUser, proxyPassword )
653  );
654  proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
655  }
656  // Setup network proxy authentication configuration
657  QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
658  if ( !authcfg.isEmpty( ) )
659  {
660  QgsDebugMsg( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
661  // Never crash! Never.
663  QgsApplication::authManager()->updateNetworkProxy( proxy, authcfg );
664  }
665  }
666 
667  setFallbackProxyAndExcludes( proxy, excludes, noProxyURLs );
668 
669  QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
670  if ( !newcache )
671  newcache = new QgsNetworkDiskCache( this );
672 
673  QString cacheDirectory = settings.value( QStringLiteral( "cache/directory" ) ).toString();
674  if ( cacheDirectory.isEmpty() )
675  cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
676  qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 50 * 1024 * 1024 ).toLongLong();
677  newcache->setCacheDirectory( cacheDirectory );
678  newcache->setMaximumCacheSize( cacheSize );
679  QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 );
680  QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 );
681 
682  if ( cache() != newcache )
683  setCache( newcache );
684 }
685 
687 {
688  return settingsNetworkTimeout.value();
689 }
690 
692 {
693  settingsNetworkTimeout.setValue( time );
694 }
695 
696 QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
697 {
699  br.setAuthCfg( authCfg );
700  br.get( request, forceRefresh, feedback );
701  return br.reply();
702 }
703 
704 QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
705 {
707  br.setAuthCfg( authCfg );
708  br.post( request, data, forceRefresh, feedback );
709  return br.reply();
710 }
711 
712 
713 //
714 // QgsNetworkRequestParameters
715 //
716 
717 QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
718  : mOperation( operation )
719  , mRequest( request )
720  , mOriginatingThreadId( QStringLiteral( "0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
721  , mRequestId( requestId )
722  , mContent( content )
723  , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
724  , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
725 {
726 }
727 
728 
729 //
730 // QgsSslErrorHandler
731 //
732 
733 void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
734 {
735  Q_UNUSED( reply )
736  QgsDebugMsg( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
737 }
738 
739 //
740 // QgsNetworkAuthenticationHandler
741 //
742 
743 void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
744 {
745  Q_UNUSED( reply )
746  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() ) );
747 }
748 
750 {
751  Q_UNUSED( url )
752  QgsDebugMsg( QStringLiteral( "Network authentication required external browser to open URL %1, but no handler was in place" ).arg( url.toString() ) );
753 }
754 
756 {
757  QgsDebugMsg( QStringLiteral( "Network authentication required external browser closed, but no handler was in place" ) );
758 }
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition: qgis.cpp:290
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.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
ErrorCode post(QNetworkRequest &request, const QByteArray &data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made.
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.
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.
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 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.
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 *)
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.
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:1080
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1079
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38