QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 constExcludeList = nam->excludeList();
81  for ( const QString &exclude : constExcludeList )
82  {
83  if ( !exclude.trimmed().isEmpty() && url.startsWith( exclude ) )
84  {
85  QgsDebugMsgLevel( QStringLiteral( "using default proxy for %1 [exclude %2]" ).arg( url, exclude ), 4 );
86  return QList<QNetworkProxy>() << QNetworkProxy();
87  }
88  }
89 
90  if ( nam->useSystemProxy() )
91  {
92  QgsDebugMsgLevel( QStringLiteral( "requesting system proxy for query %1" ).arg( url ), 4 );
93  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery( query );
94  if ( !proxies.isEmpty() )
95  {
96  QgsDebugMsgLevel( QStringLiteral( "using system proxy %1:%2 for query" )
97  .arg( proxies.first().hostName() ).arg( proxies.first().port() ), 4 );
98  return proxies;
99  }
100  }
101 
102  QgsDebugMsgLevel( QStringLiteral( "using fallback proxy for %1" ).arg( url ), 4 );
103  return QList<QNetworkProxy>() << nam->fallbackProxy();
104  }
105 };
107 
108 //
109 // Static calls to enforce singleton behavior
110 //
111 QgsNetworkAccessManager *QgsNetworkAccessManager::instance( Qt::ConnectionType connectionType )
112 {
113  static QThreadStorage<QgsNetworkAccessManager> sInstances;
114  QgsNetworkAccessManager *nam = &sInstances.localData();
115 
116  if ( nam->thread() == qApp->thread() )
117  sMainNAM = nam;
118 
119  if ( !nam->mInitialized )
120  nam->setupDefaultProxyAndCache( connectionType );
121 
122  return nam;
123 }
124 
126  : QNetworkAccessManager( parent )
127 {
128  setProxyFactory( new QgsNetworkProxyFactory() );
129 }
130 
131 void QgsNetworkAccessManager::setSslErrorHandler( std::unique_ptr<QgsSslErrorHandler> handler )
132 {
133  Q_ASSERT( sMainNAM == this );
134  mSslErrorHandler = std::move( handler );
135 }
136 
137 void QgsNetworkAccessManager::setAuthHandler( std::unique_ptr<QgsNetworkAuthenticationHandler> handler )
138 {
139  Q_ASSERT( sMainNAM == this );
140  mAuthHandler = std::move( handler );
141 }
142 
143 void QgsNetworkAccessManager::insertProxyFactory( QNetworkProxyFactory *factory )
144 {
145  mProxyFactories.insert( 0, factory );
146 }
147 
148 void QgsNetworkAccessManager::removeProxyFactory( QNetworkProxyFactory *factory )
149 {
150  mProxyFactories.removeAll( factory );
151 }
152 
153 const QList<QNetworkProxyFactory *> QgsNetworkAccessManager::proxyFactories() const
154 {
155  return mProxyFactories;
156 }
157 
159 {
160  return mExcludedURLs;
161 }
162 
163 const QNetworkProxy &QgsNetworkAccessManager::fallbackProxy() const
164 {
165  return mFallbackProxy;
166 }
167 
168 void QgsNetworkAccessManager::setFallbackProxyAndExcludes( const QNetworkProxy &proxy, const QStringList &excludes )
169 {
170  QgsDebugMsgLevel( QStringLiteral( "proxy settings: (type:%1 host: %2:%3, user:%4, password:%5" )
171  .arg( proxy.type() == QNetworkProxy::DefaultProxy ? QStringLiteral( "DefaultProxy" ) :
172  proxy.type() == QNetworkProxy::Socks5Proxy ? QStringLiteral( "Socks5Proxy" ) :
173  proxy.type() == QNetworkProxy::NoProxy ? QStringLiteral( "NoProxy" ) :
174  proxy.type() == QNetworkProxy::HttpProxy ? QStringLiteral( "HttpProxy" ) :
175  proxy.type() == QNetworkProxy::HttpCachingProxy ? QStringLiteral( "HttpCachingProxy" ) :
176  proxy.type() == QNetworkProxy::FtpCachingProxy ? QStringLiteral( "FtpCachingProxy" ) :
177  QStringLiteral( "Undefined" ),
178  proxy.hostName() )
179  .arg( proxy.port() )
180  .arg( proxy.user(),
181  proxy.password().isEmpty() ? QStringLiteral( "not set" ) : QStringLiteral( "set" ) ), 4 );
182 
183  mFallbackProxy = proxy;
184  mExcludedURLs = excludes;
185  // remove empty records from excludes list -- these would otherwise match ANY url, so the proxy would always be skipped!
186  mExcludedURLs.erase( std::remove_if( mExcludedURLs.begin(), mExcludedURLs.end(), // clazy:exclude=detaching-member
187  []( const QString & url )
188  {
189  return url.trimmed().isEmpty();
190  } ), mExcludedURLs.end() ); // clazy:exclude=detaching-member
191 
192 }
193 
194 QNetworkReply *QgsNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData )
195 {
196  QgsSettings s;
197 
198  QNetworkRequest *pReq( const_cast< QNetworkRequest * >( &req ) ); // hack user agent
199 
200  QString userAgent = s.value( QStringLiteral( "/qgis/networkAndProxy/userAgent" ), "Mozilla/5.0" ).toString();
201  if ( !userAgent.isEmpty() )
202  userAgent += ' ';
203  userAgent += QStringLiteral( "QGIS/%1" ).arg( Qgis::QGIS_VERSION );
204  pReq->setRawHeader( "User-Agent", userAgent.toUtf8() );
205 
206 #ifndef QT_NO_SSL
207  bool ishttps = pReq->url().scheme().compare( QLatin1String( "https" ), Qt::CaseInsensitive ) == 0;
208  if ( ishttps && !QgsApplication::authManager()->isDisabled() )
209  {
210  QgsDebugMsgLevel( QStringLiteral( "Adding trusted CA certs to request" ), 3 );
211  QSslConfiguration sslconfig( pReq->sslConfiguration() );
212  // Merge trusted CAs with any additional CAs added by the authentication methods
213  sslconfig.setCaCertificates( QgsAuthCertUtils::casMerge( QgsApplication::authManager()->trustedCaCertsCache(), sslconfig.caCertificates( ) ) );
214  // check for SSL cert custom config
215  QString hostport( QStringLiteral( "%1:%2" )
216  .arg( pReq->url().host().trimmed() )
217  .arg( pReq->url().port() != -1 ? pReq->url().port() : 443 ) );
219  if ( !servconfig.isNull() )
220  {
221  QgsDebugMsg( QStringLiteral( "Adding SSL custom config to request for %1" ).arg( hostport ) );
222  sslconfig.setProtocol( servconfig.sslProtocol() );
223  sslconfig.setPeerVerifyMode( servconfig.sslPeerVerifyMode() );
224  sslconfig.setPeerVerifyDepth( servconfig.sslPeerVerifyDepth() );
225  }
226 
227  pReq->setSslConfiguration( sslconfig );
228  }
229 #endif
230 
231  static QAtomicInt sRequestId = 0;
232  const int requestId = ++sRequestId;
233  QByteArray content;
234  if ( QBuffer *buffer = qobject_cast<QBuffer *>( outgoingData ) )
235  {
236  content = buffer->buffer();
237  }
238 
239  emit requestAboutToBeCreated( QgsNetworkRequestParameters( op, req, requestId, content ) );
241  emit requestAboutToBeCreated( op, req, outgoingData );
243  QNetworkReply *reply = QNetworkAccessManager::createRequest( op, req, outgoingData );
244  reply->setProperty( "requestId", requestId );
245 
247  emit requestCreated( reply );
249 
250  connect( reply, &QNetworkReply::downloadProgress, this, &QgsNetworkAccessManager::onReplyDownloadProgress );
251 #ifndef QT_NO_SSL
252  connect( reply, &QNetworkReply::sslErrors, this, &QgsNetworkAccessManager::onReplySslErrors );
253 #endif
254 
255  // The timer will call abortRequest slot to abort the connection if needed.
256  // The timer is stopped by the finished signal and is restarted on downloadProgress and
257  // uploadProgress.
258  QTimer *timer = new QTimer( reply );
259  timer->setObjectName( QStringLiteral( "timeoutTimer" ) );
260  connect( timer, &QTimer::timeout, this, &QgsNetworkAccessManager::abortRequest );
261  timer->setSingleShot( true );
262  timer->start( timeout() );
263 
264  connect( reply, &QNetworkReply::downloadProgress, timer, [timer] { timer->start(); } );
265  connect( reply, &QNetworkReply::uploadProgress, timer, [timer] { timer->start(); } );
266  connect( reply, &QNetworkReply::finished, timer, &QTimer::stop );
267  QgsDebugMsgLevel( QStringLiteral( "Created [reply:%1]" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ), 3 );
268 
269  return reply;
270 }
271 
272 #ifndef QT_NO_SSL
273 void QgsNetworkAccessManager::unlockAfterSslErrorHandled()
274 {
275  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
276  mSslErrorWaitCondition.wakeOne();
277 }
278 #endif
279 
280 void QgsNetworkAccessManager::abortRequest()
281 {
282  QTimer *timer = qobject_cast<QTimer *>( sender() );
283  Q_ASSERT( timer );
284 
285  QNetworkReply *reply = qobject_cast<QNetworkReply *>( timer->parent() );
286  Q_ASSERT( reply );
287 
288  reply->abort();
289  QgsDebugMsgLevel( QStringLiteral( "Abort [reply:%1] %2" ).arg( reinterpret_cast< qint64 >( reply ), 0, 16 ).arg( reply->url().toString() ), 3 );
290  QgsMessageLog::logMessage( tr( "Network request %1 timed out" ).arg( reply->url().toString() ), tr( "Network" ) );
291  // Notify the application
292  emit requestTimedOut( QgsNetworkRequestParameters( reply->operation(), reply->request(), getRequestId( reply ) ) );
293  emit requestTimedOut( reply );
294 }
295 
296 void QgsNetworkAccessManager::onReplyFinished( QNetworkReply *reply )
297 {
298  emit finished( QgsNetworkReplyContent( reply ) );
299 }
300 
301 void QgsNetworkAccessManager::onReplyDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
302 {
303  if ( QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() ) )
304  {
305  emit downloadProgress( getRequestId( reply ), bytesReceived, bytesTotal );
306  }
307 }
308 
309 #ifndef QT_NO_SSL
310 void QgsNetworkAccessManager::onReplySslErrors( const QList<QSslError> &errors )
311 {
312  QNetworkReply *reply = qobject_cast< QNetworkReply *>( sender() );
313  Q_ASSERT( reply );
314  Q_ASSERT( reply->manager() == this );
315 
316  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst SSL error is handled" ) );
317  pauseTimeout( reply );
318 
319  emit requestEncounteredSslErrors( getRequestId( reply ), errors );
320 
321  // in main thread this will trigger SSL error handler immediately and return once the errors are handled,
322  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
323  emit sslErrorsOccurred( reply, errors );
324  if ( this != sMainNAM )
325  {
326  // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
327  // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
328  mSslErrorHandlerMutex.lock();
329  mSslErrorWaitCondition.wait( &mSslErrorHandlerMutex );
330  mSslErrorHandlerMutex.unlock();
331  afterSslErrorHandled( reply );
332  }
333 }
334 
335 void QgsNetworkAccessManager::afterSslErrorHandled( QNetworkReply *reply )
336 {
337  if ( reply->manager() == this )
338  {
339  restartTimeout( reply );
340  emit sslErrorsHandled( reply );
341  }
342  else if ( this == sMainNAM )
343  {
344  // notify other threads to allow them to handle the reply
345  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterSslErrorHandled(); // safe to call directly - the other thread will be stuck waiting for us
346  }
347 }
348 
349 void QgsNetworkAccessManager::unlockAfterAuthRequestHandled()
350 {
351  Q_ASSERT( QThread::currentThread() == QApplication::instance()->thread() );
352  mAuthRequestWaitCondition.wakeOne();
353 }
354 
355 void QgsNetworkAccessManager::afterAuthRequestHandled( QNetworkReply *reply )
356 {
357  if ( reply->manager() == this )
358  {
359  restartTimeout( reply );
360  emit authRequestHandled( reply );
361  }
362  else if ( this == sMainNAM )
363  {
364  // notify other threads to allow them to handle the reply
365  qobject_cast< QgsNetworkAccessManager *>( reply->manager() )->unlockAfterAuthRequestHandled(); // safe to call directly - the other thread will be stuck waiting for us
366  }
367 }
368 
369 void QgsNetworkAccessManager::pauseTimeout( QNetworkReply *reply )
370 {
371  Q_ASSERT( reply->manager() == this );
372 
373  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
374  if ( timer && timer->isActive() )
375  {
376  timer->stop();
377  }
378 }
379 
380 void QgsNetworkAccessManager::restartTimeout( QNetworkReply *reply )
381 {
382  Q_ASSERT( reply->manager() == this );
383  // restart reply timeout
384  QTimer *timer = reply->findChild<QTimer *>( QStringLiteral( "timeoutTimer" ) );
385  if ( timer )
386  {
387  Q_ASSERT( !timer->isActive() );
388  QgsDebugMsg( QStringLiteral( "Restarting network reply timeout" ) );
389  timer->setSingleShot( true );
390  timer->start( timeout() );
391  }
392 }
393 
394 int QgsNetworkAccessManager::getRequestId( QNetworkReply *reply )
395 {
396  return reply->property( "requestId" ).toInt();
397 }
398 
399 void QgsNetworkAccessManager::handleSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
400 {
401  mSslErrorHandler->handleSslErrors( reply, errors );
402  afterSslErrorHandled( reply );
403 }
404 
405 #endif
406 
407 void QgsNetworkAccessManager::onAuthRequired( QNetworkReply *reply, QAuthenticator *auth )
408 {
409  Q_ASSERT( reply );
410  Q_ASSERT( reply->manager() == this );
411 
412  QgsDebugMsg( QStringLiteral( "Stopping network reply timeout whilst auth request is handled" ) );
413  pauseTimeout( reply );
414 
415  emit requestRequiresAuth( getRequestId( reply ), auth->realm() );
416 
417  // in main thread this will trigger auth handler immediately and return once the request is satisfied,
418  // while in worker thread the signal will be queued (and return immediately) -- hence the need to lock the thread in the next block
419  emit authRequestOccurred( reply, auth );
420 
421  if ( this != sMainNAM )
422  {
423  // lock thread and wait till error is handled. If we return from this slot now, then the reply will resume
424  // without actually giving the main thread the chance to act on the ssl error and possibly ignore it.
425  mAuthRequestHandlerMutex.lock();
426  mAuthRequestWaitCondition.wait( &mAuthRequestHandlerMutex );
427  mAuthRequestHandlerMutex.unlock();
428  afterAuthRequestHandled( reply );
429  }
430 }
431 
432 void QgsNetworkAccessManager::handleAuthRequest( QNetworkReply *reply, QAuthenticator *auth )
433 {
434  mAuthHandler->handleAuthRequest( reply, auth );
435 
436  emit requestAuthDetailsAdded( getRequestId( reply ), auth->realm(), auth->user(), auth->password() );
437 
438  afterAuthRequestHandled( reply );
439 }
440 
441 QString QgsNetworkAccessManager::cacheLoadControlName( QNetworkRequest::CacheLoadControl control )
442 {
443  switch ( control )
444  {
445  case QNetworkRequest::AlwaysNetwork:
446  return QStringLiteral( "AlwaysNetwork" );
447  case QNetworkRequest::PreferNetwork:
448  return QStringLiteral( "PreferNetwork" );
449  case QNetworkRequest::PreferCache:
450  return QStringLiteral( "PreferCache" );
451  case QNetworkRequest::AlwaysCache:
452  return QStringLiteral( "AlwaysCache" );
453  }
454  return QStringLiteral( "PreferNetwork" );
455 }
456 
457 QNetworkRequest::CacheLoadControl QgsNetworkAccessManager::cacheLoadControlFromName( const QString &name )
458 {
459  if ( name == QLatin1String( "AlwaysNetwork" ) )
460  {
461  return QNetworkRequest::AlwaysNetwork;
462  }
463  else if ( name == QLatin1String( "PreferNetwork" ) )
464  {
465  return QNetworkRequest::PreferNetwork;
466  }
467  else if ( name == QLatin1String( "PreferCache" ) )
468  {
469  return QNetworkRequest::PreferCache;
470  }
471  else if ( name == QLatin1String( "AlwaysCache" ) )
472  {
473  return QNetworkRequest::AlwaysCache;
474  }
475  return QNetworkRequest::PreferNetwork;
476 }
477 
478 void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType connectionType )
479 {
480  mInitialized = true;
481  mUseSystemProxy = false;
482 
483  Q_ASSERT( sMainNAM );
484 
485  if ( sMainNAM != this )
486  {
487  connect( this, &QNetworkAccessManager::proxyAuthenticationRequired,
488  sMainNAM, &QNetworkAccessManager::proxyAuthenticationRequired,
489  connectionType );
490 
491  connect( this, qgis::overload< QNetworkReply *>::of( &QgsNetworkAccessManager::requestTimedOut ),
492  sMainNAM, qgis::overload< QNetworkReply *>::of( &QgsNetworkAccessManager::requestTimedOut ) );
493 
494  connect( this, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ),
495  sMainNAM, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestTimedOut ) );
496 
497  connect( this, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestAboutToBeCreated ),
498  sMainNAM, qgis::overload< QgsNetworkRequestParameters >::of( &QgsNetworkAccessManager::requestAboutToBeCreated ) );
499 
500  connect( this, qgis::overload< QgsNetworkReplyContent >::of( &QgsNetworkAccessManager::finished ),
501  sMainNAM, qgis::overload< QgsNetworkReplyContent >::of( &QgsNetworkAccessManager::finished ) );
502 
504 
505 #ifndef QT_NO_SSL
506  connect( this, &QNetworkAccessManager::sslErrors,
507  sMainNAM, &QNetworkAccessManager::sslErrors,
508  connectionType );
509 
511 #endif
512 
514  }
515  else
516  {
517 #ifndef QT_NO_SSL
518  setSslErrorHandler( qgis::make_unique< QgsSslErrorHandler >() );
519 #endif
520  setAuthHandler( qgis::make_unique< QgsNetworkAuthenticationHandler>() );
521  }
522 #ifndef QT_NO_SSL
523  connect( this, &QgsNetworkAccessManager::sslErrorsOccurred, sMainNAM, &QgsNetworkAccessManager::handleSslErrors );
524 #endif
525  connect( this, &QNetworkAccessManager::authenticationRequired, this, &QgsNetworkAccessManager::onAuthRequired );
526  connect( this, &QgsNetworkAccessManager::authRequestOccurred, sMainNAM, &QgsNetworkAccessManager::handleAuthRequest );
527 
528  connect( this, &QNetworkAccessManager::finished, this, &QgsNetworkAccessManager::onReplyFinished );
529 
530  // check if proxy is enabled
531  QgsSettings settings;
532  QNetworkProxy proxy;
533  QStringList excludes;
534 
535  bool proxyEnabled = settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool();
536  if ( proxyEnabled )
537  {
538  excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), QStringList() ).toStringList();
539 
540  //read type, host, port, user, passw from settings
541  QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
542  int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
543 
544  QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
545  QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
546 
547  QString proxyTypeString = settings.value( QStringLiteral( "proxy/proxyType" ), "" ).toString();
548 
549  if ( proxyTypeString == QLatin1String( "DefaultProxy" ) )
550  {
551  mUseSystemProxy = true;
552  QNetworkProxyFactory::setUseSystemConfiguration( true );
553  QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery();
554  if ( !proxies.isEmpty() )
555  {
556  proxy = proxies.first();
557  }
558  QgsDebugMsgLevel( QStringLiteral( "setting default proxy" ), 4 );
559  }
560  else
561  {
562  QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
563  if ( proxyTypeString == QLatin1String( "Socks5Proxy" ) )
564  {
565  proxyType = QNetworkProxy::Socks5Proxy;
566  }
567  else if ( proxyTypeString == QLatin1String( "HttpProxy" ) )
568  {
569  proxyType = QNetworkProxy::HttpProxy;
570  }
571  else if ( proxyTypeString == QLatin1String( "HttpCachingProxy" ) )
572  {
573  proxyType = QNetworkProxy::HttpCachingProxy;
574  }
575  else if ( proxyTypeString == QLatin1String( "FtpCachingProxy" ) )
576  {
577  proxyType = QNetworkProxy::FtpCachingProxy;
578  }
579  QgsDebugMsg( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
580  .arg( proxyType )
581  .arg( proxyHost ).arg( proxyPort )
582  .arg( proxyUser, proxyPassword )
583  );
584  proxy = QNetworkProxy( proxyType, proxyHost, proxyPort, proxyUser, proxyPassword );
585  }
586  // Setup network proxy authentication configuration
587  QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
588  if ( !authcfg.isEmpty( ) )
589  {
590  QgsDebugMsg( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
591  // Never crash! Never.
593  QgsApplication::authManager()->updateNetworkProxy( proxy, authcfg );
594  }
595  }
596 
597  setFallbackProxyAndExcludes( proxy, excludes );
598 
599  QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );
600  if ( !newcache )
601  newcache = new QgsNetworkDiskCache( this );
602 
603  QString cacheDirectory = settings.value( QStringLiteral( "cache/directory" ) ).toString();
604  if ( cacheDirectory.isEmpty() )
605  cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation );
606  qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 50 * 1024 * 1024 ).toLongLong();
607  newcache->setCacheDirectory( cacheDirectory );
608  newcache->setMaximumCacheSize( cacheSize );
609  QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 );
610  QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 );
611 
612  if ( cache() != newcache )
613  setCache( newcache );
614 }
615 
617 {
618  return QgsSettings().value( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), 60000 ).toInt();
619 }
620 
622 {
623  QgsSettings().setValue( QStringLiteral( "/qgis/networkAndProxy/networkTimeout" ), time );
624 }
625 
626 QgsNetworkReplyContent QgsNetworkAccessManager::blockingGet( QNetworkRequest &request, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
627 {
629  br.setAuthCfg( authCfg );
630  br.get( request, forceRefresh, feedback );
631  return br.reply();
632 }
633 
634 QgsNetworkReplyContent QgsNetworkAccessManager::blockingPost( QNetworkRequest &request, const QByteArray &data, const QString &authCfg, bool forceRefresh, QgsFeedback *feedback )
635 {
637  br.setAuthCfg( authCfg );
638  br.post( request, data, forceRefresh, feedback );
639  return br.reply();
640 }
641 
642 
643 //
644 // QgsNetworkRequestParameters
645 //
646 
647 QgsNetworkRequestParameters::QgsNetworkRequestParameters( QNetworkAccessManager::Operation operation, const QNetworkRequest &request, int requestId, const QByteArray &content )
648  : mOperation( operation )
649  , mRequest( request )
650  , mOriginatingThreadId( QStringLiteral( "0x%2" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
651  , mRequestId( requestId )
652  , mContent( content )
653  , mInitiatorClass( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorClass ) ).toString() )
654  , mInitiatorRequestId( request.attribute( static_cast< QNetworkRequest::Attribute >( QgsNetworkRequestParameters::AttributeInitiatorRequestId ) ) )
655 {
656 }
657 
658 
659 //
660 // QgsSslErrorHandler
661 //
662 
663 void QgsSslErrorHandler::handleSslErrors( QNetworkReply *reply, const QList<QSslError> & )
664 {
665  Q_UNUSED( reply )
666  QgsDebugMsg( QStringLiteral( "SSL errors occurred accessing URL:\n%1" ).arg( reply->request().url().toString() ) );
667 }
668 
669 //
670 // QgsNetworkAuthenticationHandler
671 //
672 
673 void QgsNetworkAuthenticationHandler::handleAuthRequest( QNetworkReply *reply, QAuthenticator * )
674 {
675  Q_UNUSED( reply )
676  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() ) );
677 }
virtual void handleSslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
Called whenever SSL errors are encountered during a network reply.
bool isNull() const
Whether configuration is null (missing components)
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...
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:51
static QString cacheLoadControlName(QNetworkRequest::CacheLoadControl control)
Returns the name for QNetworkRequest::CacheLoadControl.
int sslPeerVerifyDepth() const
Number or SSL client&#39;s peer to verify in connections.
Q_DECL_DEPRECATED void requestCreated(QNetworkReply *)
void requestTimedOut(QgsNetworkRequestParameters request)
Emitted when a network request has timed out.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkProxy with an authentication config.
void setCacheDirectory(const QString &cacheDir)
QgsNetworkRequestParameters()=default
Default constructor.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:624
Configuration container for SSL server connection exceptions or overrides.
QStringList excludeList() const
Returns the proxy exclude list.
Encapsulates parameters and properties of a network request.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
QNetworkReply * createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
static QNetworkRequest::CacheLoadControl cacheLoadControlFromName(const QString &name)
Returns QNetworkRequest::CacheLoadControl from a name.
QgsNetworkAccessManager(QObject *parent=nullptr)
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client&#39;s peer verify mode to use in connections.
virtual void handleAuthRequest(QNetworkReply *reply, QAuthenticator *auth)
Called whenever network authentication requests are encountered during a network reply.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
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.
void finished(QgsNetworkReplyContent reply)
Emitted whenever a pending network reply is finished.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
QString cacheDirectory() const
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made...
void removeProxyFactory(QNetworkProxyFactory *factory)
Removes a factory from the proxy factories list.
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port) ...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
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, const QNetworkRequest &, QIODevice *)
void insertProxyFactory(QNetworkProxyFactory *factory)
Inserts a factory into the proxy factories list.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:625
static QgsAuthManager * authManager()
Returns the application&#39;s authentication manager instance.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void requestAuthDetailsAdded(int requestId, const QString &realm, const QString &user, const QString &password)
Emitted when network authentication details have been added to a request.
void setupDefaultProxyAndCache(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Setup the QgsNetworkAccessManager (NAM) according to the user&#39;s settings.
void requestEncounteredSslErrors(int requestId, const QList< QSslError > &errors)
Emitted when a network request encounters SSL errors.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
qint64 maximumCacheSize() const
static int timeout()
Returns the network timeout length, in milliseconds.
const QNetworkProxy & fallbackProxy() const
Returns the fallback proxy used by the manager.
void downloadProgress(int requestId, qint64 bytesReceived, qint64 bytesTotal)
Emitted when a network reply receives a progress report.
bool useSystemProxy() const
Returns whether the system proxy should be used.
void setAuthHandler(std::unique_ptr< QgsNetworkAuthenticationHandler > handler)
Sets the application network authentication handler, which is used to respond to network authenticati...
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...
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...
network access manager for QGISThis class implements the QGIS network access manager.
void setFallbackProxyAndExcludes(const QNetworkProxy &proxy, const QStringList &excludes)
Sets the fallback proxy and URLs which shouldn&#39;t use it.
void setMaximumCacheSize(qint64 size)
Wrapper implementation of QNetworkDiskCache with all methods guarded by a mutex soly for internal use...
void requestRequiresAuth(int requestId, const QString &realm)
Emitted when a network request prompts an authentication request.
static void setTimeout(int time)
Sets the maximum timeout time for network requests, in milliseconds.
const QList< QNetworkProxyFactory * > proxyFactories() const
Returns a list of proxy factories used by the manager.