QGIS API Documentation  3.2.0-Bonn (bc43194)
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 <QNetworkReply>
24 #include <QTextCodec>
25 
27 {
28  if ( mReply && mReply->isRunning() )
29  {
30  //cancel running request
31  mReply->abort();
32  }
33  if ( mReply )
34  {
35  mReply->deleteLater();
36  }
37 }
38 
40 {
41  fetchContent( QNetworkRequest( url ) );
42 }
43 
44 void QgsNetworkContentFetcher::fetchContent( const QNetworkRequest &request )
45 {
46  mContentLoaded = false;
47  mIsCanceled = false;
48 
49  if ( mReply )
50  {
51  //cancel any in progress requests
52  mReply->abort();
53  mReply->deleteLater();
54  mReply = nullptr;
55  }
56 
57  mReply = QgsNetworkAccessManager::instance()->get( request );
58  connect( mReply, &QNetworkReply::finished, this, [ = ] { contentLoaded(); } );
59  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsNetworkContentFetcher::downloadProgress );
60 }
61 
63 {
64  if ( !mContentLoaded )
65  {
66  return nullptr;
67  }
68 
69  return mReply;
70 }
71 
73 {
74  if ( !mContentLoaded || !mReply )
75  {
76  return QString();
77  }
78 
79  QByteArray array = mReply->readAll();
80 
81  //correctly encode reply as unicode
82  QTextCodec *codec = codecForHtml( array );
83  return codec->toUnicode( array );
84 }
85 
87 {
88  mIsCanceled = true;
89 
90  if ( mReply )
91  {
92  //cancel any in progress requests
93  mReply->abort();
94  mReply->deleteLater();
95  mReply = nullptr;
96  }
97 }
98 
99 QTextCodec *QgsNetworkContentFetcher::codecForHtml( QByteArray &array ) const
100 {
101  //QTextCodec::codecForHtml fails to detect "<meta charset="utf-8"/>" type tags
102  //see https://bugreports.qt.io/browse/QTBUG-41011
103  //so test for that ourselves
104 
105  //basic check
106  QTextCodec *codec = QTextCodec::codecForUtfText( array, nullptr );
107  if ( codec )
108  {
109  return codec;
110  }
111 
112  //check for meta charset tag
113  QByteArray header = array.left( 1024 ).toLower();
114  int pos = header.indexOf( "meta charset=" );
115  if ( pos != -1 )
116  {
117  pos += int( strlen( "meta charset=" ) ) + 1;
118  int pos2 = header.indexOf( '\"', pos );
119  QByteArray cs = header.mid( pos, pos2 - pos );
120  codec = QTextCodec::codecForName( cs );
121  if ( codec )
122  {
123  return codec;
124  }
125  }
126 
127  //fallback to QTextCodec::codecForHtml
128  codec = QTextCodec::codecForHtml( array, codec );
129  if ( codec )
130  {
131  return codec;
132  }
133 
134  //no luck, default to utf-8
135  return QTextCodec::codecForName( "UTF-8" );
136 }
137 
138 void QgsNetworkContentFetcher::contentLoaded( bool ok )
139 {
140  Q_UNUSED( ok );
141 
142  if ( mIsCanceled )
143  {
144  emit finished();
145  return;
146  }
147 
148  if ( mReply->error() != QNetworkReply::NoError )
149  {
150  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), mReply->errorString() ) );
151  mContentLoaded = true;
152  emit finished();
153  return;
154  }
155 
156  QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
157  if ( redirect.isNull() )
158  {
159  //no error or redirect, got target
160  QVariant status = mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
161  if ( !status.isNull() && status.toInt() >= 400 )
162  {
163  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), status.toString() ) );
164  }
165  mContentLoaded = true;
166  emit finished();
167  return;
168  }
169 
170  //redirect, so fetch redirect target
171  mReply->deleteLater();
172  fetchContent( redirect.toUrl() );
173 }
174 
175 
176 
177 
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Emitted when data is received.
QNetworkReply * reply()
Returns a reference to the network reply.
QString contentAsString() const
Returns the fetched content as a string.
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).
void finished()
Emitted when content has loaded.
void cancel()
Cancels any ongoing request.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
void fetchContent(const QUrl &url)
Fetches content from a remote URL and handles redirects.