Commit 6e4675c4 authored by Tyler Wilson's avatar Tyler Wilson
Browse files

Added a timeout event handler to edrget (ref#2289)

git-svn-id: http://subversion.wr.usgs.gov/repos/prog/isis3/trunk@6369 41f8697f-d340-4b68-9986-7bafba869bb8
parent 03f50b89
Loading
Loading
Loading
Loading
+54 −6
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
#include <iostream>

#include <QtCore>
#include <QDir>

#include "httpget.h"
#include "ftpget.h"
@@ -17,13 +18,24 @@ using namespace std;
void IsisMain() {

  // Get the file name from the GUI
  int timeOut = 60000;
  QString timeOutStr;
  UserInterface &ui = Application::GetUserInterface();
  QString guiURL = ui.GetString("URL");
  QString guiPath;

  if (ui.WasEntered("TOPATH")) {
    guiPath = ui.GetString("TOPATH");
  }


  if (ui.WasEntered("TIMEOUT")) {
        timeOut = ui.GetInteger("TIMEOUT");
  }


  QUrl qurl(guiURL);

  //test if scheme is ftp and set port
  if (qurl.scheme().toLower() == "ftp") {
    qurl.setPort(21);
@@ -33,14 +45,34 @@ void IsisMain() {
      if (ui.WasEntered("TOPATH") ) {
        parameters += " TOPATH=" + guiPath;
      }




      //////////////////////////////////////////////////////////////////////////////////
      // tjw(ref#2259):  The line below starts a child process that launches
      // $ISISROOT/bin/edrget .  This was done because QMainWindow::instance()->exec()
      // which starts the event processing loop has already been called, and
      // cannot be called again to catch events from the FtpGet/HttpGet objects.
      // Launching a child process appears to have been a quick and dirty way
      // to get around this.  This code should be refactored under a future ticket.
      // There is a lot of code duplication.
      //////////////////////////////////////////////////////////////////////////////////


      ProgramLauncher::RunIsisProgram("edrget", parameters);
  }

    else {

      FtpGet getter;
      QObject::connect(&getter, SIGNAL(done()), QCoreApplication::instance(), SLOT(quit()));

      //a false getFile return means no error and we sould execute the get.
      if(!getter.getFile(qurl, guiPath))  QCoreApplication::instance()->exec();

      //Starts the main event-processing loop for the application.  Since IsisMain already
      //started an event-processing loop, a child process was launched above.
      if(!getter.getFile(qurl, guiPath,timeOut))  QCoreApplication::instance()->exec();
      //if error occurred throw could not acquire
      if (getter.error() ) {
        QString localFileName;
@@ -49,8 +81,11 @@ void IsisMain() {
          localFileName += "/";
        }
        localFileName +=  QFileInfo(qurl.path()).fileName();               
        QString localFileNameStr(localFileName);
        QFile::remove(localFileNameStr);
        QFile::remove(localFileName);



        //tested
        QString msg = "Could not acquire [" + guiURL + "]";
        throw IException(IException::User, msg, _FILEINFO_);
      }
@@ -65,13 +100,24 @@ void IsisMain() {
      if (ui.WasEntered("TOPATH") ) {
        parameters += " TOPATH=" + guiPath;
      }


      //tjw
      //if (ui.WasEntered("TIMEOUT") ){
      //
      //    parameters += " TIMEOUT="+timeOutStr.setNum(timeOut);

      //}



      ProgramLauncher::RunIsisProgram("edrget", parameters);
    }
    else {
      HttpGet getter;
      QObject::connect(&getter, SIGNAL(done()), QCoreApplication::instance(), SLOT(quit()));
      //a false getFile return means no error and we sould execute the get.
      if(!getter.getFile(qurl, guiPath)) QCoreApplication::instance()->exec();
      if (!getter.getFile(qurl, guiPath,timeOut)) QCoreApplication::instance()->exec();
      //if error occurred then throw could not acquire
      if (getter.error() ) {
        QString localFileName;
@@ -79,8 +125,10 @@ void IsisMain() {
          localFileName += guiPath;
          localFileName += "/";
        }
        QString localFileNameStr(localFileName);
        QFile::remove(localFileNameStr);
        //tjw
        localFileName +=  QFileInfo(qurl.path()).fileName();
        //tested
        QFile::remove(localFileName);
        QString msg = "Could not acquire [" + guiURL + "]";
        throw IException(IException::User, msg, _FILEINFO_);
      }
+26 −2
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>

<application name="edrget" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://isis.astrogeology.usgs.gov/Schemas/Application/application.xsd">
<application name="edrget" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://isis.astrogeology.usgs.gov/Schemas/Application/application.xsd">
  <brief>
    Download network file using FTP or HTTP protocol.
  </brief>
@@ -32,9 +34,14 @@
    <change name="Steven Lambright" date="2007-08-14">
        Added an application test
    </change>
    <change name="Tyler Wilson" date ="2015-09-14">
        Added a timeout event handler to gracefully exit the application in the event
        the ftp server response time becomes too long.
    </change>
  </history>



  <groups>
    <group name="Files">
      <parameter name="URL">
@@ -51,7 +58,6 @@
          </p>
        </description>      
      </parameter>      

      <parameter name="TOPATH">
        <type>string</type>
        <brief>The path to the output file </brief>
@@ -65,6 +71,24 @@
      </parameter>

    </group>

    <group name = "Timeout (ms)">
    <parameter name ="TIMEOUT">
        <type>integer</type>
        <brief>Timeout (in milliseconds) for file requests.</brief>
        <description>
            If the application does not hear from the server in the
            allotted amount of time, it quits and displays an error.
        </description>
        <internalDefault>
            60000
        </internalDefault>
     </parameter>
    </group>


  </groups>



</application>
+137 −27
Original line number Diff line number Diff line
#include <QtCore>
#include <QtNetwork>
#include <iostream>

#include  "Application.h"
#include "ftpget.h"
#include "IString.h"
#include "IException.h"
#include "Progress.h"
#include  "Application.h"


#include <iostream>
#include <QtCore>
#include <QtNetwork>


using namespace std;

namespace Isis {

  FtpGet::FtpGet(QObject *parent) : QObject(parent) {
    //conncet the Qftp done signal to the ftpDone function



    //connect the Qftp done signal to the ftpDone function
    connect(&p_ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool) ) );
    //connect the Qftp progress signal to the ftpProgress function(ISIS progress)

    //tjw:  connect the QFtp progress signal to the ftpProgress function(ISIS progress)
    connect(&p_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
            this, SLOT(ftpProgress(qint64, qint64)));

    //tjw:  A timer for detecting network timeouts and exiting the application gracefully
    connect(&p_timer, SIGNAL(timeout()),this,SLOT(ftpTimeout() ) );


  }
  //**************************************
  // getFile function will check URL, if URL is good function will connect,
  // login, and get the file.  This function returns P_error.
  bool FtpGet::getFile(const QUrl &url, QString topath) {

  //*************************************************************************
  // getFile function will check URL, if URL is good, getFile will connect,
  // login, and get the file.  This function returns p_error.
  //*************************************************************************

  bool FtpGet::getFile(const QUrl &url, QString topath, int timeout) {


      //tjw:
      p_timeOut = timeout;




    //next four if check the URL and return true is there is error.
    if (!url.isValid() ) {
      string msg = "invalid URL";
//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
    }
    if(url.scheme().toLower() != "ftp") {
      string msg = "URL must start with 'ftp:'";
//       iException::Message(iException::User, msg, _FILEINFO_);

     //tested
     QString msg = QString("Invalid URL");
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

      p_error = true;
      return p_error;
    }

    //Dead code:  This condition is already checked before the function is hit
    //if (url.scheme().toLower() != "ftp") {
    //  QString msg = QString("URL must start with 'ftp:'");
    //  p_progress.SetText(msg);

    //  if (!Application::GetUserInterface().IsInteractive() )
    //     cout << msg.toStdString() << endl;

    //  p_error =  true;
    //  return p_error;
    //}
    //tested
    if (url.path().isEmpty() ) {
      string msg = "URL has no path";
//       iException::Message(iException::User, msg, _FILEINFO_);
      QString msg = QString("URL has no path");
      p_progress.SetText(msg);

      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

      p_error =  true;
      return p_error;
    }
@@ -48,18 +86,26 @@ namespace Isis {
      localFileName += topath;
      localFileName += "/";
    }
    //tested
    localFileName +=  QFileInfo(url.path()).fileName();
    if (localFileName.isEmpty() ) {
      string msg = "URL has no filename";
//       iException::Message(iException::User, msg, _FILEINFO_);
      QString msg = QString("URL has no filename");
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

      p_error = true;
      return p_error;
    }
    // check local file.
    p_file.setFileName(localFileName);
    if (!p_file.open(QIODevice::WriteOnly) ) {
      string msg = "Cannot open output file";
//       iException::Message(iException::User, msg, _FILEINFO_);
      QString msg = QString("Cannot open output file");
      p_progress.SetText(msg);

      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

      p_error =  true;
      return p_error;
    }
@@ -67,35 +113,92 @@ namespace Isis {
    p_ftp.connectToHost(url.host(), url.port() );
    p_ftp.login();
    p_ftp.get(url.path(), &p_file);

    p_lastDone = -1;
    p_error = false;
    return p_error;
  }


  void FtpGet::ftpDone(bool error) {

    if (error) {
      p_error = true;
      QString msg = p_ftp.errorString();
      msg.remove("\n");
//       iException::Message(iException::User, msg, _FILEINFO_);

    }
    else {
      p_error = false;
    }
    if (!p_error) {
      p_file.close();

      // this was added because final size may not match progress size so
      // you do not get 100% processed
      if (!Application::GetUserInterface().IsInteractive() ) {
        cout << "100% Processed" << endl;
      }
    }
    emit done();
    return;

  }



  //tjw:  Timeout event handler monitors the ftp connection and gracefully
  //closes and exits the application if a timeout occurs.
  void FtpGet::ftpTimeout() {

      bool fileExists = false;
      bool fileRemoved = false;
      QString fileRemovedQStr;

      QString timeoutSecs = QString("Timeout error:  An ftp get request exceeded ") +
              QString::number(p_timeOut)+ QString(" ms.");

      p_progress.SetText(timeoutSecs);

      if (!Application::GetUserInterface().IsInteractive() )
        cout << timeoutSecs.toStdString() << endl;


      p_ftp.abort();
      p_ftp.close();

      fileExists = p_file.exists();

      if (fileExists) {
          fileRemoved = p_file.remove();
      }


      if (!fileExists || fileRemoved)
          fileRemovedQStr = p_file.fileName() + QString(" successfully deleted.");



      if (!Application::GetUserInterface().IsInteractive() )
        cout << fileRemovedQStr.toStdString() << endl;



      emit done();
      return;
  }
  // ftpProgress uses the ISIS progress class to track progress



  // tjw:  ftpProgress uses the ISIS progress class to track progress
  void FtpGet::ftpProgress(qint64 done, qint64 total) {

    p_timer.start(p_timeOut);

    //double percentDone = 0;
    //percentDone = 100*((double)done/total);
    //cout << percentDone << endl;

    if (total == 0) return;
    if (total == -1) return;
    if (p_error) return;
@@ -104,10 +207,17 @@ namespace Isis {
      p_progress.SetMaximumSteps(total);
      p_progress.CheckStatus();
      p_lastDone = 1;

    }

    while (p_lastDone <= done) {

      p_progress.CheckStatus();
      p_lastDone++;

    }


   }

}
+15 −1
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@

#include <QFile>
#include <QFtp>
#include <QTimer>
#include "Progress.h"

class QUrl;
@@ -18,7 +19,7 @@ namespace Isis {
    public:
      FtpGet(QObject *parent = 0);

      bool getFile(const QUrl &url, QString topath);
      bool getFile(const QUrl &url, QString topath,int timeout);

      bool error() const {
        return p_error;
@@ -29,17 +30,30 @@ namespace Isis {
    signals:
      void done();

      //tjw
      void dataTransferProgress(qint64, qint64);


    private slots:
      void ftpDone(bool error);
      void ftpProgress(qint64 done, qint64 total);

      //tjw
      void ftpTimeout();


    private:

      QFtp p_ftp;
      QFile p_file;
      bool p_error;
      int p_lastDone;
      Progress p_progress;


      int p_timeOut;
      QTimer p_timer;

  };
}
#endif
+115 −15
Original line number Diff line number Diff line
#include <QtCore>
#include <QtNetwork>
#include <iostream>

#include "Application.h"
#include "httpget.h"
#include "IString.h"
#include "IException.h"
#include "IString.h"
#include "Progress.h"


#include <iostream>
#include <QtCore>
#include <QtNetwork>




using namespace std;

namespace Isis {

  HttpGet::HttpGet(QObject *parent) : QObject(parent) {


    //connect the QHttp done signal to the httpDone function
    connect(&p_http, SIGNAL(done(bool)), this, SLOT(httpDone(bool)));
    //connect the QHttp progress signal to the httpProgress function(Isis progress)
    connect(&p_http, SIGNAL(dataReadProgress(int, int)),
            this, SLOT(httpProgress(int, int)));

    //tjw:  A timer for detecting network timeouts and exiting
    //      the application gracefully
    connect(&p_timer, SIGNAL(timeout()),this,SLOT(httpTimeout() ) );

  }
  //**********************************************
  //****************************************************************************
  // getFile function will check URL, if URL is good the function will connect,
  // login, and get the file.  This function returns p_error
  bool HttpGet::getFile(const QUrl &url, QString topath) {
  //****************************************************************************

  bool HttpGet::getFile(const QUrl &url, QString topath,int timeout) {

      p_timeOut = timeout;

    //tested
    // The next four ifs will check the URL and return error is bad
    if (!url.isValid() ) {
      QString msg = "invalid URL";
      QString msg = "Invalid URL";
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
    }
    if(url.scheme().toLower() != "http") {
      QString msg = "URL must start with 'http:'";

    //Dead code:  this condition is already checked before this function is entered
    //if (url.scheme().toLower() != "http") {
    //  QString msg = "URL must start with 'http:'";
    //  p_progress.SetText(msg);
    //  if (!Application::GetUserInterface().IsInteractive() )
    //     cout << msg.toStdString() << endl;

//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
    }
    //p_error = true;
    //  return p_error;
    //}

    //tested
    if (url.path().isEmpty() ) {
      QString msg = "URL has no path";
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
@@ -49,8 +82,13 @@ namespace Isis {
      localFileName += "/";
    }
    localFileName +=  QFileInfo(url.path()).fileName();
    //tested
    if (localFileName.isEmpty() ) {
      QString msg = "URL has no filename";
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
@@ -59,6 +97,10 @@ namespace Isis {
    p_file.setFileName(localFileName);
    if (!p_file.open(QIODevice::WriteOnly) ) {
      QString msg = "Cannot open output file";
      p_progress.SetText(msg);
      if (!Application::GetUserInterface().IsInteractive() )
         cout << msg.toStdString() << endl;

//       iException::Message(iException::User, msg, _FILEINFO_);
      p_error = true;
      return p_error;
@@ -71,6 +113,48 @@ namespace Isis {
    return p_error;
  }


  //tjw:  Timeout event handler monitors the http connection and gracefully
  //closes and exits the application if a timeout occurs.
  void HttpGet::httpTimeout() {

      bool fileExists = false;
      bool fileRemoved = false;
      QString fileRemovedQStr;


      QString timeoutSecs = QString("Timeout error:  An http get request exceeded ") +
              QString::number(p_timeOut)+ QString(" ms.");

      p_progress.SetText(timeoutSecs);

      if (!Application::GetUserInterface().IsInteractive() )
        cout << timeoutSecs.toStdString() << endl;

      p_http.close();

      fileExists = p_file.exists();

      if (fileExists) {
          fileRemoved = p_file.remove();
      }


      if (!fileExists || fileRemoved)
          fileRemovedQStr = p_file.fileName() + QString(" successfully deleted.");


      p_progress.SetText(fileRemovedQStr);

      if (!Application::GetUserInterface().IsInteractive() )
        cout << fileRemovedQStr.toStdString() << endl;


      emit done();
      return;
  }


  void HttpGet::httpDone(bool error) {
    map <int, QString> errLUT;
    errLUT [204] = "No content";
@@ -89,7 +173,8 @@ namespace Isis {
      QString msg = p_http.errorString();
//       iException::Message(iException::User, msg, _FILEINFO_);
    }
    else if(p_http.lastResponse().statusCode()  != 200 && p_http.lastResponse().statusCode()  != 0) {
    else if (p_http.lastResponse().statusCode()  != 200 &&
             p_http.lastResponse().statusCode()  != 0) {
      p_error = true;
      QString msg = "error code: [" + errLUT[p_http.lastResponse().statusCode()] + "]";
//       iException::Message(iException::User, msg, _FILEINFO_);
@@ -97,12 +182,27 @@ namespace Isis {
    else {
      p_error = false;
    }
    if(!p_error) p_file.close();
    if (!p_error) {

      if (!Application::GetUserInterface().IsInteractive() ) {
          cout << "100% Processed" << endl;
      }
      p_file.close();

     }

    emit done();
    return;
  }
  // This function setsup and useses Isis progress classs to track progress.
  void HttpGet::httpProgress(int done, int total) {

      p_timer.start(p_timeOut);
      //double percentDone = 0;
      //percentDone = 100*((double)done/total);



    if (total == 0) return;
    if (p_error) return;
    if (p_lastDone < 0) {
Loading