Beispiel #1
0
bool QgsWfsRequest::sendPOST( const QUrl &url, const QString &contentTypeHeader, const QByteArray &data )
{
  abort(); // cancel previous
  mIsAborted = false;
  mTimedout = false;
  mGotNonEmptyResponse = false;

  mErrorMessage.clear();
  mErrorCode = QgsWfsRequest::NoError;
  mForceRefresh = true;
  mResponse.clear();

  if ( url.toEncoded().contains( "fake_qgis_http_endpoint" ) )
  {
    // Hack for testing purposes
    QUrl modifiedUrl( url );
    modifiedUrl.addQueryItem( QStringLiteral( "POSTDATA" ), QString::fromUtf8( data ) );
    return sendGET( modifiedUrl, true, true, false );
  }

  QNetworkRequest request( url );
  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsWfsRequest" ) );
  if ( !mUri.auth().setAuthorization( request ) )
  {
    mErrorCode = QgsWfsRequest::NetworkError;
    mErrorMessage = errorMessageFailedAuth();
    QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
    return false;
  }
  request.setHeader( QNetworkRequest::ContentTypeHeader, contentTypeHeader );

  mReply = QgsNetworkAccessManager::instance()->post( request, data );
  mReply->setReadBufferSize( READ_BUFFER_SIZE_HINT );
  if ( !mUri.auth().setAuthorizationReply( mReply ) )
  {
    mErrorCode = QgsWfsRequest::NetworkError;
    mErrorMessage = errorMessageFailedAuth();
    QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
    return false;
  }
  connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished );
  connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress );

  QEventLoop loop;
  connect( this, &QgsWfsRequest::downloadFinished, &loop, &QEventLoop::quit );
  loop.exec( QEventLoop::ExcludeUserInputEvents );

  return mErrorMessage.isEmpty();
}
Beispiel #2
0
bool QgsWFSRequest::sendPOST( const QUrl& url, const QString& contentTypeHeader, const QByteArray& data )
{
    abort(); // cancel previous
    mIsAborted = false;
    mTimedout = false;
    mGotNonEmptyResponse = false;

    mErrorMessage.clear();
    mErrorCode = QgsWFSRequest::NoError;
    mForceRefresh = true;
    mResponse.clear();

    if ( url.toEncoded().contains( "fake_qgis_http_endpoint" ) )
    {
        // Hack for testing purposes
        QUrl modifiedUrl( url );
        modifiedUrl.addQueryItem( "POSTDATA", QString::fromUtf8( data ) );
        return sendGET( modifiedUrl, true, true, false );
    }

    QNetworkRequest request( url );
    if ( !mUri.auth().setAuthorization( request ) )
    {
        mErrorCode = QgsWFSRequest::NetworkError;
        mErrorMessage = errorMessageFailedAuth();
        QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
        return false;
    }
    request.setHeader( QNetworkRequest::ContentTypeHeader, contentTypeHeader );

    mReply = QgsNetworkAccessManager::instance()->post( request, data );
    connect( mReply, SIGNAL( finished() ), this, SLOT( replyFinished() ) );
    connect( mReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( replyProgress( qint64, qint64 ) ) );

    QEventLoop loop;
    connect( this, SIGNAL( downloadFinished() ), &loop, SLOT( quit() ) );
    loop.exec( QEventLoop::ExcludeUserInputEvents );

    return mErrorMessage.isEmpty();
}
Beispiel #3
0
bool QgsWFSRequest::sendGET( const QUrl& url, bool synchronous, bool forceRefresh, bool cache )
{
    abort(); // cancel previous
    mIsAborted = false;
    mTimedout = false;
    mGotNonEmptyResponse = false;

    mErrorMessage.clear();
    mErrorCode = QgsWFSRequest::NoError;
    mForceRefresh = forceRefresh;
    mResponse.clear();

    QUrl modifiedUrl( url );
    if ( modifiedUrl.toString().contains( "fake_qgis_http_endpoint" ) )
    {
        // Just for testing with local files instead of http:// ressources
        QString modifiedUrlString = modifiedUrl.toString();
        // Qt5 does URL encoding from some reason (of the FILTER parameter for example)
        modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
        QgsDebugMsg( QString( "Get %1" ).arg( modifiedUrlString ) );
        modifiedUrlString = modifiedUrlString.mid( QString( "http://" ).size() );
        QString args = modifiedUrlString.mid( modifiedUrlString.indexOf( '?' ) );
        if ( modifiedUrlString.size() > 256 )
        {
            args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
        }
        else
        {
            args.replace( "?", "_" );
            args.replace( "&", "_" );
            args.replace( "<", "_" );
            args.replace( ">", "_" );
            args.replace( "'", "_" );
            args.replace( "\"", "_" );
            args.replace( " ", "_" );
            args.replace( ":", "_" );
            args.replace( "/", "_" );
            args.replace( "\n", "_" );
        }
#ifdef Q_OS_WIN
        // Passing "urls" like "http://c:/path" to QUrl 'eats' the : after c,
        // so we must restore it
        if ( modifiedUrlString[1] == '/' )
        {
            modifiedUrlString = modifiedUrlString[0] + ":/" + modifiedUrlString.mid( 2 );
        }
#endif
        modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf( '?' ) ) + args;
        QgsDebugMsg( QString( "Get %1 (after laundering)" ).arg( modifiedUrlString ) );
        modifiedUrl = QUrl::fromLocalFile( modifiedUrlString );
    }

    QNetworkRequest request( modifiedUrl );
    if ( !mUri.auth().setAuthorization( request ) )
    {
        mErrorCode = QgsWFSRequest::NetworkError;
        mErrorMessage = errorMessageFailedAuth();
        QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
        return false;
    }

    if ( cache )
    {
        request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
        request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
    }

    mReply = QgsNetworkAccessManager::instance()->get( request );
    connect( mReply, SIGNAL( finished() ), this, SLOT( replyFinished() ) );
    connect( mReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( replyProgress( qint64, qint64 ) ) );

    if ( !synchronous )
        return true;

    QEventLoop loop;
    connect( this, SIGNAL( downloadFinished() ), &loop, SLOT( quit() ) );
    loop.exec( QEventLoop::ExcludeUserInputEvents );

    return mErrorMessage.isEmpty();
}
Beispiel #4
0
void QgsWFSRequest::replyFinished()
{
    if ( !mIsAborted && mReply )
    {
        if ( mReply->error() == QNetworkReply::NoError )
        {
            QgsDebugMsg( "reply ok" );
            QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
            if ( !redirect.isNull() )
            {
                QgsDebugMsg( "Request redirected." );

                const QUrl& toUrl = redirect.toUrl();
                mReply->request();
                if ( toUrl == mReply->url() )
                {
                    mErrorMessage = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() );
                    QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
                    mResponse.clear();
                }
                else
                {
                    QNetworkRequest request( toUrl );
                    if ( !mUri.auth().setAuthorization( request ) )
                    {
                        mResponse.clear();
                        mErrorMessage = errorMessageFailedAuth();
                        mErrorCode = QgsWFSRequest::NetworkError;
                        QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
                        emit downloadFinished();
                        return;
                    }
                    request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
                    request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );

                    mReply->deleteLater();
                    mReply = nullptr;

                    QgsDebugMsg( QString( "redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ) );
                    mReply = QgsNetworkAccessManager::instance()->get( request );
                    connect( mReply, SIGNAL( finished() ), this, SLOT( replyFinished() ) );
                    connect( mReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( replyProgress( qint64, qint64 ) ) );
                    return;
                }
            }
            else
            {
                const QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();

                if ( nam->cache() )
                {
                    QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );

                    QNetworkCacheMetaData::RawHeaderList hl;
                    Q_FOREACH ( const QNetworkCacheMetaData::RawHeader &h, cmd.rawHeaders() )
                    {
                        if ( h.first != "Cache-Control" )
                            hl.append( h );
                    }
                    cmd.setRawHeaders( hl );

                    QgsDebugMsg( QString( "expirationDate:%1" ).arg( cmd.expirationDate().toString() ) );
                    if ( cmd.expirationDate().isNull() )
                    {
                        cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( defaultExpirationInSec() ) );
                    }

                    nam->cache()->updateMetaData( cmd );
                }
                else
                {
                    QgsDebugMsg( "No cache!" );
                }

#ifdef QGISDEBUG
                bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
                QgsDebugMsg( QString( "Reply was cached: %1" ).arg( fromCache ) );
#endif

                mResponse = mReply->readAll();

                if ( mResponse.isEmpty() && !mGotNonEmptyResponse )
                {
                    mErrorMessage = tr( "empty response: %1" ).arg( mReply->errorString() );
                    mErrorCode = QgsWFSRequest::ServerExceptionError;
                    QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
                }
            }
Beispiel #5
0
bool QgsWfsRequest::sendGET( const QUrl &url, bool synchronous, bool forceRefresh, bool cache )
{
  abort(); // cancel previous
  mIsAborted = false;
  mTimedout = false;
  mGotNonEmptyResponse = false;

  mErrorMessage.clear();
  mErrorCode = QgsWfsRequest::NoError;
  mForceRefresh = forceRefresh;
  mResponse.clear();

  QUrl modifiedUrl( url );

  // Specific code for testing
  if ( modifiedUrl.toString().contains( QLatin1String( "fake_qgis_http_endpoint" ) ) )
  {
    // Just for testing with local files instead of http:// resources
    QString modifiedUrlString = modifiedUrl.toString();
    // Qt5 does URL encoding from some reason (of the FILTER parameter for example)
    modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
    QgsDebugMsgLevel( QStringLiteral( "Get %1" ).arg( modifiedUrlString ), 4 );
    modifiedUrlString = modifiedUrlString.mid( QStringLiteral( "http://" ).size() );
    QString args = modifiedUrlString.mid( modifiedUrlString.indexOf( '?' ) );
    if ( modifiedUrlString.size() > 256 )
    {
      args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
    }
    else
    {
      args.replace( QLatin1String( "?" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "&" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "<" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( ">" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "'" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "\"" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( " " ), QLatin1String( "_" ) );
      args.replace( QLatin1String( ":" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "/" ), QLatin1String( "_" ) );
      args.replace( QLatin1String( "\n" ), QLatin1String( "_" ) );
    }
#ifdef Q_OS_WIN
    // Passing "urls" like "http://c:/path" to QUrl 'eats' the : after c,
    // so we must restore it
    if ( modifiedUrlString[1] == '/' )
    {
      modifiedUrlString = modifiedUrlString[0] + ":/" + modifiedUrlString.mid( 2 );
    }
#endif
    modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf( '?' ) ) + args;
    QgsDebugMsgLevel( QStringLiteral( "Get %1 (after laundering)" ).arg( modifiedUrlString ), 4 );
    modifiedUrl = QUrl::fromLocalFile( modifiedUrlString );
  }

  QgsDebugMsgLevel( QStringLiteral( "Calling: %1" ).arg( modifiedUrl.toDisplayString( ) ), 4 );

  QNetworkRequest request( modifiedUrl );
  if ( !mUri.auth().setAuthorization( request ) )
  {
    mErrorCode = QgsWfsRequest::NetworkError;
    mErrorMessage = errorMessageFailedAuth();
    QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
    return false;
  }

  if ( cache )
  {
    request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, forceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
    request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
  }

  QWaitCondition waitCondition;
  QMutex waitConditionMutex;
  bool threadFinished = false;
  bool success = false;

  std::function<void()> downloaderFunction = [ this, request, synchronous, &waitConditionMutex, &waitCondition, &threadFinished, &success ]()
  {
    if ( QThread::currentThread() != QgsApplication::instance()->thread() )
      QgsNetworkAccessManager::instance( Qt::DirectConnection );

    success = true;
    mReply = QgsNetworkAccessManager::instance()->get( request );

    mReply->setReadBufferSize( READ_BUFFER_SIZE_HINT );
    if ( !mUri.auth().setAuthorizationReply( mReply ) )
    {
      mErrorCode = QgsWfsRequest::NetworkError;
      mErrorMessage = errorMessageFailedAuth();
      QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
      waitCondition.wakeAll();
      success = false;
    }
    else
    {
      // We are able to use direct connection here, because we
      // * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
      // * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
      connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished, Qt::DirectConnection );
      connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress, Qt::DirectConnection );

      if ( synchronous )
      {
        auto resumeMainThread = [&waitConditionMutex, &waitCondition]()
        {
          waitConditionMutex.lock();
          waitCondition.wakeAll();
          waitConditionMutex.unlock();

          waitConditionMutex.lock();
          waitCondition.wait( &waitConditionMutex );
          waitConditionMutex.unlock();
        };

        connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authenticationRequired, this, resumeMainThread, Qt::DirectConnection );
        connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, this, resumeMainThread, Qt::DirectConnection );

#ifndef QT_NO_SSL
        connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::sslErrors, this, resumeMainThread, Qt::DirectConnection );
#endif
        QEventLoop loop;
        connect( this, &QgsWfsRequest::downloadFinished, &loop, &QEventLoop::quit, Qt::DirectConnection );
        loop.exec();
      }
    }
    waitConditionMutex.lock();
    threadFinished = true;
    waitCondition.wakeAll();
    waitConditionMutex.unlock();
  };

  if ( synchronous && QThread::currentThread() == QApplication::instance()->thread() )
  {
    std::unique_ptr<DownloaderThread> downloaderThread = qgis::make_unique<DownloaderThread>( downloaderFunction );
    downloaderThread->start();

    while ( true )
    {
      waitConditionMutex.lock();
      if ( threadFinished )
      {
        waitConditionMutex.unlock();
        break;
      }
      waitCondition.wait( &waitConditionMutex );

      // If the downloader thread wakes us (the main thread) up and is not yet finished
      // he needs the authentication to run.
      // The processEvents() call gives the auth manager the chance to show a dialog and
      // once done with that, we can wake the downloaderThread again and continue the download.
      if ( !threadFinished )
      {
        waitConditionMutex.unlock();

        QgsApplication::instance()->processEvents();
        waitConditionMutex.lock();
        waitCondition.wakeAll();
        waitConditionMutex.unlock();
      }
      else
      {
        waitConditionMutex.unlock();
      }
    }
    // wait for thread to gracefully exit
    downloaderThread->wait();
  }
  else
  {
    downloaderFunction();
  }
  return success && mErrorMessage.isEmpty();
}
Beispiel #6
0
void QgsWfsRequest::replyFinished()
{
  if ( !mIsAborted && mReply )
  {
    if ( mReply->error() == QNetworkReply::NoError )
    {
      QgsDebugMsgLevel( QStringLiteral( "reply OK" ), 4 );
      QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
      if ( !redirect.isNull() )
      {
        QgsDebugMsgLevel( QStringLiteral( "Request redirected." ), 4 );

        const QUrl &toUrl = redirect.toUrl();
        mReply->request();
        if ( toUrl == mReply->url() )
        {
          mErrorMessage = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() );
          QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
          mResponse.clear();
        }
        else
        {
          QNetworkRequest request( toUrl );
          QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsWfsRequest" ) );
          if ( !mUri.auth().setAuthorization( request ) )
          {
            mResponse.clear();
            mErrorMessage = errorMessageFailedAuth();
            mErrorCode = QgsWfsRequest::NetworkError;
            QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
            emit downloadFinished();
            return;
          }
          request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
          request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );

          mReply->deleteLater();
          mReply = nullptr;

          QgsDebugMsgLevel( QStringLiteral( "redirected: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 4 );
          mReply = QgsNetworkAccessManager::instance()->get( request );
          mReply->setReadBufferSize( READ_BUFFER_SIZE_HINT );
          if ( !mUri.auth().setAuthorizationReply( mReply ) )
          {
            mResponse.clear();
            mErrorMessage = errorMessageFailedAuth();
            mErrorCode = QgsWfsRequest::NetworkError;
            QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
            emit downloadFinished();
            return;
          }
          connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished, Qt::DirectConnection );
          connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress, Qt::DirectConnection );
          return;
        }
      }
      else
      {
        const QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();

        if ( nam->cache() )
        {
          QNetworkCacheMetaData cmd = nam->cache()->metaData( mReply->request().url() );

          QNetworkCacheMetaData::RawHeaderList hl;
          const auto constRawHeaders = cmd.rawHeaders();
          for ( const QNetworkCacheMetaData::RawHeader &h : constRawHeaders )
          {
            if ( h.first != "Cache-Control" )
              hl.append( h );
          }
          cmd.setRawHeaders( hl );

          QgsDebugMsgLevel( QStringLiteral( "expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 4 );
          if ( cmd.expirationDate().isNull() )
          {
            cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( defaultExpirationInSec() ) );
          }

          nam->cache()->updateMetaData( cmd );
        }
        else
        {
          QgsDebugMsgLevel( QStringLiteral( "No cache!" ), 4 );
        }

#ifdef QGISDEBUG
        bool fromCache = mReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ).toBool();
        QgsDebugMsgLevel( QStringLiteral( "Reply was cached: %1" ).arg( fromCache ), 4 );
#endif

        mResponse = mReply->readAll();

        if ( mResponse.isEmpty() && !mGotNonEmptyResponse )
        {
          mErrorMessage = tr( "empty response: %1" ).arg( mReply->errorString() );
          mErrorCode = QgsWfsRequest::ServerExceptionError;
          QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
        }
      }
    }
    else
    {
      mErrorMessage = errorMessageWithReason( mReply->errorString() );
      mErrorCode = QgsWfsRequest::ServerExceptionError;
      QgsMessageLog::logMessage( mErrorMessage, tr( "WFS" ) );
      mResponse.clear();
    }
  }
  if ( mTimedout )
    mErrorCode = QgsWfsRequest::TimeoutError;

  if ( mReply )
  {
    mReply->deleteLater();
    mReply = nullptr;
  }

  emit downloadFinished();
}