Skip to content

Commit 1774e68

Browse files
committedDec 20, 2018
[FEATURE][API] New class for blocking (non-async) network requests
This new class, QgsBlockingNetworkRequest, is designed for performing SAFE blocking requests. It is thread safe and has full support for QGIS proxy and authentication settings. This class should be used whenever a blocking network request is required. Unlike implementations which rely on QApplication::processEvents() or creation of a QEventLoop, this class is completely thread safe and can be used on either the main thread or background threads without issue. Redirects are automatically handled by the class. After completion of a request, the reply content should be retrieved by calling getReplyContent(). This method returns a QgsNetworkReplyContent container, which is safe and cheap to copy and pass between threads without issue. The guts of this class have been copied from QgsWfsRequest (which has been using the same approach since 3.2)
1 parent e4959a6 commit 1774e68

File tree

5 files changed

+740
-0
lines changed

5 files changed

+740
-0
lines changed
 
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsblockingnetworkrequest.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
class QgsBlockingNetworkRequest : QObject
12+
{
13+
%Docstring
14+
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy
15+
and authentication settings.
16+
17+
This class should be used whenever a blocking network request is required. Unlike implementations
18+
which rely on QApplication.processEvents() or creation of a QEventLoop, this class is completely
19+
thread safe and can be used on either the main thread or background threads without issue.
20+
21+
Redirects are automatically handled by the class.
22+
23+
After completion of a request, the reply content should be retrieved by calling getReplyContent().
24+
This method returns a QgsNetworkReplyContent container, which is safe and cheap to copy and pass
25+
between threads without issue.
26+
27+
.. versionadded:: 3.6
28+
%End
29+
30+
%TypeHeaderCode
31+
#include "qgsblockingnetworkrequest.h"
32+
%End
33+
public:
34+
35+
enum ErrorCode
36+
{
37+
NoError,
38+
NetworkError,
39+
TimeoutError,
40+
ServerExceptionError,
41+
};
42+
43+
explicit QgsBlockingNetworkRequest();
44+
%Docstring
45+
Constructor for QgsBlockingNetworkRequest
46+
%End
47+
48+
~QgsBlockingNetworkRequest();
49+
50+
ErrorCode get( QNetworkRequest &request, bool forceRefresh = false );
51+
%Docstring
52+
Performs a "get" operation on the specified ``request``.
53+
54+
If ``forceRefresh`` is false then previously cached replies may be used for the request. If
55+
it is set to true then a new query is always performed.
56+
57+
If an authCfg() has been set, then any authentication configuration required will automatically be applied to
58+
``request``. There is no need to manually apply the authentication to the request prior to calling
59+
this method.
60+
61+
The method will return NoError if the get operation was successful. The contents of the reply can be retrieved
62+
by calling reply().
63+
64+
If an error was encountered then a specific ErrorCode will be returned, and a detailed error message
65+
can be retrieved by calling errorMessage().
66+
67+
.. seealso:: :py:func:`post`
68+
%End
69+
70+
ErrorCode post( QNetworkRequest &request, const QByteArray &data, bool forceRefresh = false );
71+
%Docstring
72+
Performs a "post" operation on the specified ``request``, using the given ``data``.
73+
74+
If ``forceRefresh`` is false then previously cached replies may be used for the request. If
75+
it is set to true then a new query is always performed.
76+
77+
If an authCfg() has been set, then any authentication configuration required will automatically be applied to
78+
``request``. There is no need to manually apply the authentication to the request prior to calling
79+
this method.
80+
81+
The method will return NoError if the get operation was successful. The contents of the reply can be retrieved
82+
by calling reply().
83+
84+
If an error was encountered then a specific ErrorCode will be returned, and a detailed error message
85+
can be retrieved by calling errorMessage().
86+
87+
.. seealso:: :py:func:`get`
88+
%End
89+
90+
QString errorMessage() const;
91+
%Docstring
92+
Returns the error message string, after a get() or post() request has been made.\
93+
%End
94+
95+
QgsNetworkReplyContent reply() const;
96+
%Docstring
97+
Returns the content of the network reply, after a get() or post() request has been made.
98+
%End
99+
100+
QString authCfg() const;
101+
%Docstring
102+
Returns the authentication config id which will be used during the request.
103+
104+
.. seealso:: :py:func:`setAuthCfg`
105+
%End
106+
107+
void setAuthCfg( const QString &authCfg );
108+
%Docstring
109+
Sets the authentication config id which should be used during the request.
110+
111+
.. seealso:: :py:func:`authCfg`
112+
%End
113+
114+
public slots:
115+
116+
void abort();
117+
%Docstring
118+
Aborts the network request immediately.
119+
%End
120+
121+
signals:
122+
123+
void downloadProgress( qint64, qint64 );
124+
%Docstring
125+
Emitted when when data arrives during a request.
126+
%End
127+
128+
void downloadFinished();
129+
%Docstring
130+
Emitted once a request has finished downloading.
131+
%End
132+
133+
};
134+
135+
136+
/************************************************************************
137+
* This file has been generated automatically from *
138+
* *
139+
* src/core/qgsblockingnetworkrequest.h *
140+
* *
141+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
142+
************************************************************************/

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@
313313
%Include auto_generated/qgsactionscoperegistry.sip
314314
%Include auto_generated/qgsanimatedicon.sip
315315
%Include auto_generated/qgsauxiliarystorage.sip
316+
%Include auto_generated/qgsblockingnetworkrequest.sip
316317
%Include auto_generated/qgsbrowsermodel.sip
317318
%Include auto_generated/qgsbrowserproxymodel.sip
318319
%Include auto_generated/qgscoordinatereferencesystem.sip

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ SET(QGIS_CORE_SRCS
152152
qgsattributeeditorelement.cpp
153153
qgsauxiliarystorage.cpp
154154
qgsbearingutils.cpp
155+
qgsblockingnetworkrequest.cpp
155156
qgsbrowsermodel.cpp
156157
qgsbrowserproxymodel.cpp
157158
qgscachedfeatureiterator.cpp
@@ -595,6 +596,7 @@ SET(QGIS_CORE_MOC_HDRS
595596
qgsactionscoperegistry.h
596597
qgsanimatedicon.h
597598
qgsauxiliarystorage.h
599+
qgsblockingnetworkrequest.h
598600
qgsbrowsermodel.h
599601
qgsbrowserproxymodel.h
600602
qgscoordinatereferencesystem.h
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
/***************************************************************************
2+
qgsblockingnetworkrequest.cpp
3+
-----------------------------
4+
begin : November 2018
5+
copyright : (C) 2018 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsblockingnetworkrequest.h"
17+
#include "qgslogger.h"
18+
#include "qgsapplication.h"
19+
#include "qgsnetworkaccessmanager.h"
20+
#include "qgsauthmanager.h"
21+
#include "qgsmessagelog.h"
22+
#include <QUrl>
23+
#include <QNetworkRequest>
24+
#include <QNetworkReply>
25+
#include <QMutex>
26+
#include <QWaitCondition>
27+
#include <QNetworkCacheMetaData>
28+
#include <QAuthenticator>
29+
30+
const qint64 READ_BUFFER_SIZE_HINT = 1024 * 1024;
31+
32+
QgsBlockingNetworkRequest::QgsBlockingNetworkRequest()
33+
{
34+
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::requestTimedOut, this, &QgsBlockingNetworkRequest::requestTimedOut );
35+
}
36+
37+
QgsBlockingNetworkRequest::~QgsBlockingNetworkRequest()
38+
{
39+
abort();
40+
}
41+
42+
void QgsBlockingNetworkRequest::requestTimedOut( QNetworkReply *reply )
43+
{
44+
if ( reply == mReply )
45+
mTimedout = true;
46+
}
47+
48+
QString QgsBlockingNetworkRequest::authCfg() const
49+
{
50+
return mAuthCfg;
51+
}
52+
53+
void QgsBlockingNetworkRequest::setAuthCfg( const QString &authCfg )
54+
{
55+
mAuthCfg = authCfg;
56+
}
57+
58+
QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::get( QNetworkRequest &request, bool forceRefresh )
59+
{
60+
return doRequest( Get, request, forceRefresh );
61+
}
62+
63+
QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::post( QNetworkRequest &request, const QByteArray &data, bool forceRefresh )
64+
{
65+
mPostData = data;
66+
return doRequest( Post, request, forceRefresh );
67+
}
68+
69+
QgsBlockingNetworkRequest::ErrorCode QgsBlockingNetworkRequest::doRequest( QgsBlockingNetworkRequest::Method method, QNetworkRequest &request, bool forceRefresh )
70+
{
71+
mMethod = method;
72+
73+
abort(); // cancel previous
74+
mIsAborted = false;
75+
mTimedout = false;
76+
mGotNonEmptyResponse = false;
77+
78+
mErrorMessage.clear();
79+
mErrorCode = NoError;
80+
mForceRefresh = forceRefresh;
81+
mReplyContent.clear();
82+
83+
if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
84+
{
85+
mErrorCode = NetworkError;
86+
mErrorMessage = errorMessageFailedAuth();
87+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
88+
return NetworkError;
89+
}
90+
91+
QgsDebugMsgLevel( QStringLiteral( "Calling: %1" ).arg( request.url().toString() ), 2 );
92+
93+
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
94+
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
95+
96+
QWaitCondition waitCondition;
97+
QMutex waitConditionMutex;
98+
bool threadFinished = false;
99+
bool success = false;
100+
101+
std::function<void()> downloaderFunction = [ this, request, &waitConditionMutex, &waitCondition, &threadFinished, &success ]()
102+
{
103+
if ( QThread::currentThread() != QgsApplication::instance()->thread() )
104+
QgsNetworkAccessManager::instance( Qt::DirectConnection );
105+
106+
success = true;
107+
108+
switch ( mMethod )
109+
{
110+
case Get:
111+
mReply = QgsNetworkAccessManager::instance()->get( request );
112+
break;
113+
114+
case Post:
115+
mReply = QgsNetworkAccessManager::instance()->post( request, mPostData );
116+
break;
117+
};
118+
119+
mReply->setReadBufferSize( READ_BUFFER_SIZE_HINT );
120+
121+
if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
122+
{
123+
mErrorCode = NetworkError;
124+
mErrorMessage = errorMessageFailedAuth();
125+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
126+
waitCondition.wakeAll();
127+
success = false;
128+
}
129+
else
130+
{
131+
// We are able to use direct connection here, because we
132+
// * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
133+
// * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
134+
connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
135+
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
136+
137+
auto resumeMainThread = [&waitConditionMutex, &waitCondition]()
138+
{
139+
waitConditionMutex.lock();
140+
waitCondition.wakeAll();
141+
waitConditionMutex.unlock();
142+
143+
waitConditionMutex.lock();
144+
waitCondition.wait( &waitConditionMutex );
145+
waitConditionMutex.unlock();
146+
};
147+
148+
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authenticationRequired, this, resumeMainThread, Qt::DirectConnection );
149+
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, this, resumeMainThread, Qt::DirectConnection );
150+
151+
#ifndef QT_NO_SSL
152+
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::sslErrors, this, resumeMainThread, Qt::DirectConnection );
153+
#endif
154+
QEventLoop loop;
155+
connect( this, &QgsBlockingNetworkRequest::downloadFinished, &loop, &QEventLoop::quit, Qt::DirectConnection );
156+
loop.exec();
157+
}
158+
waitConditionMutex.lock();
159+
threadFinished = true;
160+
waitCondition.wakeAll();
161+
waitConditionMutex.unlock();
162+
};
163+
164+
if ( QThread::currentThread() == QApplication::instance()->thread() )
165+
{
166+
std::unique_ptr<DownloaderThread> downloaderThread = qgis::make_unique<DownloaderThread>( downloaderFunction );
167+
downloaderThread->start();
168+
169+
while ( true )
170+
{
171+
waitConditionMutex.lock();
172+
if ( threadFinished )
173+
{
174+
waitConditionMutex.unlock();
175+
break;
176+
}
177+
waitCondition.wait( &waitConditionMutex );
178+
179+
// If the downloader thread wakes us (the main thread) up and is not yet finished
180+
// he needs the authentication to run.
181+
// The processEvents() call gives the auth manager the chance to show a dialog and
182+
// once done with that, we can wake the downloaderThread again and continue the download.
183+
if ( !threadFinished )
184+
{
185+
waitConditionMutex.unlock();
186+
187+
QgsApplication::instance()->processEvents();
188+
waitConditionMutex.lock();
189+
waitCondition.wakeAll();
190+
waitConditionMutex.unlock();
191+
}
192+
else
193+
{
194+
waitConditionMutex.unlock();
195+
}
196+
}
197+
// wait for thread to gracefully exit
198+
downloaderThread->wait();
199+
}
200+
else
201+
{
202+
downloaderFunction();
203+
}
204+
return mErrorCode;
205+
}
206+
207+
void QgsBlockingNetworkRequest::abort()
208+
{
209+
mIsAborted = true;
210+
if ( mReply )
211+
{
212+
mReply->deleteLater();
213+
mReply = nullptr;
214+
}
215+
}
216+
217+
void QgsBlockingNetworkRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
218+
{
219+
QgsDebugMsgLevel( QStringLiteral( "%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( "unknown number of" ) : QString::number( bytesTotal ) ), 2 );
220+
221+
if ( bytesReceived != 0 )
222+
mGotNonEmptyResponse = true;
223+
224+
if ( !mIsAborted && mReply )
225+
{
226+
if ( mReply->error() == QNetworkReply::NoError )
227+
{
228+
QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
229+
if ( !redirect.isNull() )
230+
{
231+
// We don't want to emit downloadProgress() for a redirect
232+
return;
233+
}
234+
}
235+
}
236+
237+
emit downloadProgress( bytesReceived, bytesTotal );
238+
}
239+
240+
void QgsBlockingNetworkRequest::replyFinished()
241+
{
242+
if ( !mIsAborted && mReply )
243+
{
244+
if ( mReply->error() == QNetworkReply::NoError )
245+
{
246+
QgsDebugMsgLevel( QStringLiteral( "reply OK" ), 2 );
247+
QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
248+
if ( !redirect.isNull() )
249+
{
250+
QgsDebugMsgLevel( QStringLiteral( "Request redirected." ), 2 );
251+
252+
const QUrl &toUrl = redirect.toUrl();
253+
mReply->request();
254+
if ( toUrl == mReply->url() )
255+
{
256+
mErrorMessage = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() );
257+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
258+
mReplyContent.clear();
259+
}
260+
else
261+
{
262+
QNetworkRequest request( toUrl );
263+
264+
if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
265+
{
266+
mReplyContent.clear();
267+
mErrorMessage = errorMessageFailedAuth();
268+
mErrorCode = NetworkError;
269+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
270+
emit downloadFinished();
271+
return;
272+
}
273+
274+
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
275+
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
276+
277+
mReply->deleteLater();
278+
mReply = nullptr;
279+
280+
QgsDebugMsgLevel( QStringLiteral( "redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 2 );
281+
switch ( mMethod )
282+
{
283+
case Get:
284+
mReply = QgsNetworkAccessManager::instance()->get( request );
285+
break;
286+
287+
case Post:
288+
mReply = QgsNetworkAccessManager::instance()->post( request, mPostData );
289+
break;
290+
};
291+
292+
mReply->setReadBufferSize( READ_BUFFER_SIZE_HINT );
293+
294+
if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
295+
{
296+
mReplyContent.clear();
297+
mErrorMessage = errorMessageFailedAuth();
298+
mErrorCode = NetworkError;
299+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
300+
emit downloadFinished();
301+
return;
302+
}
303+
304+
connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
305+
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
306+
return;
307+
}
308+
}
309+
else
310+
{
311+
const QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
312+
313+
if ( nam->cache() )
314+
{
315+
QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );
316+
317+
QNetworkCacheMetaData::RawHeaderList hl;
318+
Q_FOREACH ( const QNetworkCacheMetaData::RawHeader &h, cmd.rawHeaders() )
319+
{
320+
if ( h.first != "Cache-Control" )
321+
hl.append( h );
322+
}
323+
cmd.setRawHeaders( hl );
324+
325+
QgsDebugMsgLevel( QStringLiteral( "expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
326+
if ( cmd.expirationDate().isNull() )
327+
{
328+
cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( mExpirationSec ) );
329+
}
330+
331+
nam->cache()->updateMetaData( cmd );
332+
}
333+
else
334+
{
335+
QgsDebugMsgLevel( QStringLiteral( "No cache!" ), 2 );
336+
}
337+
338+
#ifdef QGISDEBUG
339+
bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
340+
QgsDebugMsgLevel( QStringLiteral( "Reply was cached: %1" ).arg( fromCache ), 2 );
341+
#endif
342+
343+
mReplyContent = QgsNetworkReplyContent( mReply );
344+
if ( mReplyContent.content().isEmpty() && !mGotNonEmptyResponse )
345+
{
346+
mErrorMessage = tr( "empty response: %1" ).arg( mReply->errorString() );
347+
mErrorCode = ServerExceptionError;
348+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
349+
}
350+
}
351+
}
352+
else
353+
{
354+
mErrorMessage = mReply->errorString();
355+
mErrorCode = ServerExceptionError;
356+
QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
357+
mReplyContent.clear();
358+
}
359+
}
360+
if ( mTimedout )
361+
mErrorCode = TimeoutError;
362+
363+
if ( mReply )
364+
{
365+
mReply->deleteLater();
366+
mReply = nullptr;
367+
}
368+
369+
emit downloadFinished();
370+
}
371+
372+
QString QgsBlockingNetworkRequest::errorMessageFailedAuth()
373+
{
374+
return tr( "network request update failed for authentication config" );
375+
}

‎src/core/qgsblockingnetworkrequest.h

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/***************************************************************************
2+
qgsblockingnetworkrequest.h
3+
---------------------------
4+
begin : November 2018
5+
copyright : (C) 2018 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#ifndef QGSBLOCKINGNETWORKREQUEST_H
16+
#define QGSBLOCKINGNETWORKREQUEST_H
17+
18+
#include "qgis_core.h"
19+
#include "qgsnetworkreply.h"
20+
#include <QThread>
21+
#include <QObject>
22+
#include <functional>
23+
24+
class QNetworkRequest;
25+
class QNetworkReply;
26+
27+
/**
28+
* A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy
29+
* and authentication settings.
30+
*
31+
* This class should be used whenever a blocking network request is required. Unlike implementations
32+
* which rely on QApplication::processEvents() or creation of a QEventLoop, this class is completely
33+
* thread safe and can be used on either the main thread or background threads without issue.
34+
*
35+
* Redirects are automatically handled by the class.
36+
*
37+
* After completion of a request, the reply content should be retrieved by calling getReplyContent().
38+
* This method returns a QgsNetworkReplyContent container, which is safe and cheap to copy and pass
39+
* between threads without issue.
40+
*
41+
* \ingroup core
42+
* \since QGIS 3.6
43+
*/
44+
class CORE_EXPORT QgsBlockingNetworkRequest : public QObject
45+
{
46+
Q_OBJECT
47+
public:
48+
49+
//! Error codes
50+
enum ErrorCode
51+
{
52+
NoError, //!< No error was encountered
53+
NetworkError, //!< A network error occurred
54+
TimeoutError, //!< Timeout was reached before a reply was received
55+
ServerExceptionError, //!< An exception was raised by the server
56+
};
57+
58+
//! Constructor for QgsBlockingNetworkRequest
59+
explicit QgsBlockingNetworkRequest();
60+
61+
~QgsBlockingNetworkRequest() override;
62+
63+
/**
64+
* Performs a "get" operation on the specified \a request.
65+
*
66+
* If \a forceRefresh is false then previously cached replies may be used for the request. If
67+
* it is set to true then a new query is always performed.
68+
*
69+
* If an authCfg() has been set, then any authentication configuration required will automatically be applied to
70+
* \a request. There is no need to manually apply the authentication to the request prior to calling
71+
* this method.
72+
*
73+
* The method will return NoError if the get operation was successful. The contents of the reply can be retrieved
74+
* by calling reply().
75+
*
76+
* If an error was encountered then a specific ErrorCode will be returned, and a detailed error message
77+
* can be retrieved by calling errorMessage().
78+
*
79+
* \see post()
80+
*/
81+
ErrorCode get( QNetworkRequest &request, bool forceRefresh = false );
82+
83+
/**
84+
* Performs a "post" operation on the specified \a request, using the given \a data.
85+
*
86+
* If \a forceRefresh is false then previously cached replies may be used for the request. If
87+
* it is set to true then a new query is always performed.
88+
*
89+
* If an authCfg() has been set, then any authentication configuration required will automatically be applied to
90+
* \a request. There is no need to manually apply the authentication to the request prior to calling
91+
* this method.
92+
*
93+
* The method will return NoError if the get operation was successful. The contents of the reply can be retrieved
94+
* by calling reply().
95+
*
96+
* If an error was encountered then a specific ErrorCode will be returned, and a detailed error message
97+
* can be retrieved by calling errorMessage().
98+
*
99+
* \see get()
100+
*/
101+
ErrorCode post( QNetworkRequest &request, const QByteArray &data, bool forceRefresh = false );
102+
103+
/**
104+
* Returns the error message string, after a get() or post() request has been made.\
105+
*/
106+
QString errorMessage() const { return mErrorMessage; }
107+
108+
/**
109+
* Returns the content of the network reply, after a get() or post() request has been made.
110+
*/
111+
QgsNetworkReplyContent reply() const { return mReplyContent; }
112+
113+
/**
114+
* Returns the authentication config id which will be used during the request.
115+
* \see setAuthCfg()
116+
*/
117+
QString authCfg() const;
118+
119+
/**
120+
* Sets the authentication config id which should be used during the request.
121+
* \see authCfg()
122+
*/
123+
void setAuthCfg( const QString &authCfg );
124+
125+
public slots:
126+
127+
/**
128+
* Aborts the network request immediately.
129+
*/
130+
void abort();
131+
132+
signals:
133+
134+
/**
135+
* Emitted when when data arrives during a request.
136+
*/
137+
void downloadProgress( qint64, qint64 );
138+
139+
/**
140+
* Emitted once a request has finished downloading.
141+
*/
142+
void downloadFinished();
143+
144+
private slots:
145+
void replyProgress( qint64, qint64 );
146+
void replyFinished();
147+
void requestTimedOut( QNetworkReply *reply );
148+
149+
private :
150+
151+
enum Method
152+
{
153+
Get,
154+
Post
155+
};
156+
157+
//! The reply to the request
158+
QNetworkReply *mReply = nullptr;
159+
160+
Method mMethod = Get;
161+
QByteArray mPostData;
162+
163+
//! Authentication configuration ID
164+
QString mAuthCfg;
165+
166+
//! The error message associated with the last error.
167+
QString mErrorMessage;
168+
169+
//! Error code
170+
ErrorCode mErrorCode = NoError;
171+
172+
QgsNetworkReplyContent mReplyContent;
173+
174+
//! Whether the request is aborted.
175+
bool mIsAborted = false;
176+
177+
//! Whether to force refresh (i.e. issue a network request and not use cache)
178+
bool mForceRefresh = false;
179+
180+
//! Whether the request has timed-out
181+
bool mTimedout = false;
182+
183+
//! Whether we already received bytes
184+
bool mGotNonEmptyResponse = false;
185+
186+
int mExpirationSec = 30;
187+
188+
ErrorCode doRequest( Method method, QNetworkRequest &request, bool forceRefresh );
189+
190+
QString errorMessageFailedAuth();
191+
192+
};
193+
194+
///@cond PRIVATE
195+
#ifndef SIP_RUN
196+
197+
class DownloaderThread : public QThread
198+
{
199+
Q_OBJECT
200+
201+
public:
202+
DownloaderThread( const std::function<void()> &function, QObject *parent = nullptr )
203+
: QThread( parent )
204+
, mFunction( function )
205+
{
206+
}
207+
208+
void run() override
209+
{
210+
mFunction();
211+
}
212+
213+
private:
214+
std::function<void()> mFunction;
215+
};
216+
217+
#endif
218+
///@endcond
219+
220+
#endif // QGSBLOCKINGNETWORKREQUEST_H

0 commit comments

Comments
 (0)
Please sign in to comment.