26using namespace Qt::StringLiterals;
30 , mFeedback( feedback )
31 , mThread( QThread::currentThread() )
33 CPLHTTPPushFetchCallback( QgsCPLHTTPFetchOverrider::callback,
this );
38 CPLHTTPPopFetchCallback();
42CPLHTTPResult *QgsCPLHTTPFetchOverrider::callback(
43 const char *pszURL, CSLConstList papszOptions, GDALProgressFunc ,
void * , CPLHTTPFetchWriteFunc pfnWrite,
void *pWriteArg,
void *pUserData
48 auto psResult =
static_cast<CPLHTTPResult *
>( CPLCalloc(
sizeof( CPLHTTPResult ), 1 ) );
49 if ( CSLFetchNameValue( papszOptions,
"CLOSE_PERSISTENT" ) )
58 for (
const char *pszOption : {
"FORM_FILE_PATH",
"FORM_ITEM_COUNT" } )
60 if ( CSLFetchNameValue( papszOptions, pszOption ) )
67 if ( pThis->mFeedback && pThis->mFeedback->
isCanceled() )
69 psResult->nStatus = 1;
70 psResult->pszErrBuf = CPLStrdup(
"download interrupted by user" );
74 QgsBlockingNetworkRequest blockingRequest;
77 QNetworkRequest request( QString::fromUtf8( pszURL ) );
78 for (
const auto &keyValue : pThis->mAttributes )
80 request.setAttribute( keyValue.first, keyValue.second );
84 const char *pszHeaders = CSLFetchNameValue( papszOptions,
"HEADERS" );
87 char **papszTokensHeaders = CSLTokenizeString2( pszHeaders,
"\r\n", 0 );
88 for (
int i = 0; papszTokensHeaders[i] !=
nullptr; ++i )
90 char *pszKey =
nullptr;
91 const char *pszValue = CPLParseNameValue( papszTokensHeaders[i], &pszKey );
92 if ( pszKey && pszValue )
94 request.setRawHeader( QByteArray::fromStdString( pszKey ), QByteArray::fromStdString( pszValue ) );
98 CSLDestroy( papszTokensHeaders );
101 constexpr bool forceRefresh =
true;
102 const char *pszCustomRequest = CSLFetchNameValue( papszOptions,
"CUSTOMREQUEST" );
103 const char *pszPostFields = CSLFetchNameValue( papszOptions,
"POSTFIELDS" );
107 if ( !pszCustomRequest || EQUAL( pszCustomRequest,
"POST" ) )
109 errCode = blockingRequest.
post( request, QByteArray::fromStdString( pszPostFields ), forceRefresh, pThis->mFeedback );
111 else if ( EQUAL( pszCustomRequest,
"PUT" ) )
113 errCode = blockingRequest.
put( request, QByteArray::fromStdString( pszPostFields ), pThis->mFeedback );
117 QgsDebugError( u
"Invalid CUSTOMREQUEST = %1 when POSTFIELDS is defined"_s.arg( pszCustomRequest ) );
123 if ( !pszCustomRequest || EQUAL( pszCustomRequest,
"GET" ) )
127 else if ( EQUAL( pszCustomRequest,
"HEAD" ) )
129 errCode = blockingRequest.
head( request, forceRefresh, pThis->mFeedback );
131 else if ( EQUAL( pszCustomRequest,
"DELETE" ) )
133 errCode = blockingRequest.
deleteResource( request, pThis->mFeedback );
137 QgsDebugError( u
"Invalid CUSTOMREQUEST = %1 when POSTFIELDS is not defined"_s.arg( pszCustomRequest ) );
143 psResult->nStatus = 1;
144 psResult->pszErrBuf = CPLStrdup( blockingRequest.
errorMessage().toUtf8() );
148 const QgsNetworkReplyContent reply( blockingRequest.
reply() );
151 for (
const auto &pair : reply.rawHeaderPairs() )
153 if ( EQUAL( pair.first.toStdString().c_str(),
"Content-Type" ) )
155 CPLFree( psResult->pszContentType );
156 psResult->pszContentType = CPLStrdup( pair.second.toStdString().c_str() );
158 psResult->papszHeaders = CSLAddNameValue( psResult->papszHeaders, pair.first.toStdString().c_str(), pair.second.toStdString().c_str() );
162 QByteArray content( reply.content() );
168 if (
static_cast<int>( pfnWrite( content.data(), 1, content.size(), pWriteArg ) ) != content.size() )
170 psResult->nStatus = 1;
171 psResult->pszErrBuf = CPLStrdup(
"download interrupted by user" );
177 psResult->nDataLen =
static_cast<int>( content.size() );
178 psResult->pabyData =
static_cast<GByte *
>( CPLMalloc( psResult->nDataLen + 1 ) );
179 memcpy( psResult->pabyData, content.constData(), psResult->nDataLen );
180 psResult->pabyData[psResult->nDataLen] = 0;
188 mAttributes[code] = value;
193 mFeedback = feedback;
ErrorCode put(QNetworkRequest &request, QIODevice *data, QgsFeedback *feedback=nullptr)
Performs a "put" operation on the specified request, using the given data.
ErrorCode head(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "head" operation on the specified request.
ErrorCode post(QNetworkRequest &request, QIODevice *data, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "post" operation on the specified request, using the given data.
ErrorCode deleteResource(QNetworkRequest &request, QgsFeedback *feedback=nullptr)
Performs a "delete" operation on the specified request.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
@ EmptyResponseIsValid
Do not generate an error if getting an empty response (e.g. HTTP 204).
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Utility class to redirect GDAL's CPL HTTP calls through QgsBlockingNetworkRequest.
QgsCPLHTTPFetchOverrider(const QString &authCfg=QString(), QgsFeedback *feedback=nullptr)
Installs the redirection for the current thread.
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
Define attribute that must be forwarded to the actual QNetworkRequest.
QThread * thread() const
Returns the thread associated with the overrider.
void setFeedback(QgsFeedback *feedback)
Sets the feedback cancellation object for the redirection.
~QgsCPLHTTPFetchOverrider()
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
#define QgsDebugError(str)