QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsnetworkcontentfetcher.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnetworkcontentfetcher.cpp
3  -------------------
4  begin : July, 2014
5  copyright : (C) 2014 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7 
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
21 #include "qgsmessagelog.h"
22 #include "qgsapplication.h"
23 #include "qgsauthmanager.h"
24 #include <QNetworkReply>
25 #include <QTextCodec>
26 
28 {
29  if ( mReply && mReply->isRunning() )
30  {
31  //cancel running request
32  mReply->abort();
33  }
34  delete mReply;
35 }
36 
37 void QgsNetworkContentFetcher::fetchContent( const QUrl &url, const QString &authcfg )
38 {
39  QNetworkRequest req( url );
40  QgsSetRequestInitiatorClass( req, QStringLiteral( "QgsNetworkContentFetcher" ) );
41 
42  fetchContent( req, authcfg );
43 }
44 
45 void QgsNetworkContentFetcher::fetchContent( const QNetworkRequest &r, const QString &authcfg )
46 {
47  QNetworkRequest request( r );
48 
49  mAuthCfg = authcfg;
50  if ( !mAuthCfg.isEmpty() )
51  {
52  QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
53  }
54 
55  mContentLoaded = false;
56  mIsCanceled = false;
57 
58  if ( mReply )
59  {
60  //cancel any in progress requests
61  mReply->abort();
62  mReply->deleteLater();
63  mReply = nullptr;
64  }
65 
66  mReply = QgsNetworkAccessManager::instance()->get( request );
67  if ( !mAuthCfg.isEmpty() )
68  {
69  QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg );
70  }
71  mReply->setParent( nullptr ); // we don't want thread locale QgsNetworkAccessManagers to delete the reply - we want ownership of it to belong to this object
72  connect( mReply, &QNetworkReply::finished, this, [ = ] { contentLoaded(); } );
73  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsNetworkContentFetcher::downloadProgress );
74 }
75 
77 {
78  if ( !mContentLoaded )
79  {
80  return nullptr;
81  }
82 
83  return mReply;
84 }
85 
87 {
88  if ( !mContentLoaded || !mReply )
89  {
90  return QString();
91  }
92 
93  QByteArray array = mReply->readAll();
94 
95  //correctly encode reply as unicode
96  QTextCodec *codec = codecForHtml( array );
97  return codec->toUnicode( array );
98 }
99 
101 {
102  mIsCanceled = true;
103 
104  if ( mReply )
105  {
106  //cancel any in progress requests
107  mReply->abort();
108  mReply->deleteLater();
109  mReply = nullptr;
110  }
111 }
112 
114 {
115  return mIsCanceled;
116 }
117 
118 QTextCodec *QgsNetworkContentFetcher::codecForHtml( QByteArray &array ) const
119 {
120  //QTextCodec::codecForHtml fails to detect "<meta charset="utf-8"/>" type tags
121  //see https://bugreports.qt.io/browse/QTBUG-41011
122  //so test for that ourselves
123 
124  //basic check
125  QTextCodec *codec = QTextCodec::codecForUtfText( array, nullptr );
126  if ( codec )
127  {
128  return codec;
129  }
130 
131  //check for meta charset tag
132  QByteArray header = array.left( 1024 ).toLower();
133  int pos = header.indexOf( "meta charset=" );
134  if ( pos != -1 )
135  {
136  pos += int( strlen( "meta charset=" ) ) + 1;
137  int pos2 = header.indexOf( '\"', pos );
138  QByteArray cs = header.mid( pos, pos2 - pos );
139  codec = QTextCodec::codecForName( cs );
140  if ( codec )
141  {
142  return codec;
143  }
144  }
145 
146  //fallback to QTextCodec::codecForHtml
147  codec = QTextCodec::codecForHtml( array, codec );
148  if ( codec )
149  {
150  return codec;
151  }
152 
153  //no luck, default to utf-8
154  return QTextCodec::codecForName( "UTF-8" );
155 }
156 
157 void QgsNetworkContentFetcher::contentLoaded( bool ok )
158 {
159  Q_UNUSED( ok )
160 
161  if ( mIsCanceled )
162  {
163  emit finished();
164  return;
165  }
166 
167  if ( mReply->error() != QNetworkReply::NoError )
168  {
169  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), mReply->errorString() ) );
170  mContentLoaded = true;
171  emit finished();
172  return;
173  }
174 
175  QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
176  if ( redirect.isNull() )
177  {
178  //no error or redirect, got target
179  QVariant status = mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
180  if ( !status.isNull() && status.toInt() >= 400 )
181  {
182  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), status.toString() ) );
183  }
184  mContentLoaded = true;
185  emit finished();
186  return;
187  }
188 
189  //redirect, so fetch redirect target
190  mReply->deleteLater();
191  fetchContent( redirect.toUrl(), mAuthCfg );
192 }
193 
194 
195 
196 
qgsauthmanager.h
QgsAuthManager::updateNetworkReply
bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkReply with an authentication config (used to skip known SSL errors,...
Definition: qgsauthmanager.cpp:1483
QgsSetRequestInitiatorClass
#define QgsSetRequestInitiatorClass(request, _class)
Definition: qgsnetworkaccessmanager.h:41
QgsApplication::authManager
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
Definition: qgsapplication.cpp:1282
qgsapplication.h
QgsNetworkContentFetcher::wasCanceled
bool wasCanceled() const
Returns true if the fetching was canceled.
Definition: qgsnetworkcontentfetcher.cpp:113
QgsNetworkContentFetcher::fetchContent
void fetchContent(const QUrl &url, const QString &authcfg=QString())
Fetches content from a remote URL and handles redirects.
Definition: qgsnetworkcontentfetcher.cpp:37
qgsnetworkaccessmanager.h
QgsNetworkContentFetcher::reply
QNetworkReply * reply()
Returns a reference to the network reply.
Definition: qgsnetworkcontentfetcher.cpp:76
QgsNetworkContentFetcher::cancel
void cancel()
Cancels any ongoing request.
Definition: qgsnetworkcontentfetcher.cpp:100
QgsNetworkContentFetcher::downloadProgress
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when data is received.
QgsNetworkContentFetcher::~QgsNetworkContentFetcher
~QgsNetworkContentFetcher() override
Definition: qgsnetworkcontentfetcher.cpp:27
QgsNetworkAccessManager::instance
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Definition: qgsnetworkaccessmanager.cpp:121
QgsAuthManager::updateNetworkRequest
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
Definition: qgsauthmanager.cpp:1458
QgsNetworkContentFetcher::contentAsString
QString contentAsString() const
Returns the fetched content as a string.
Definition: qgsnetworkcontentfetcher.cpp:86
QgsNetworkContentFetcher::finished
void finished()
Emitted when content has loaded.
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
qgsnetworkcontentfetcher.h
qgsmessagelog.h