VirtualBox

Changeset 4432 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Aug 30, 2007 8:28:11 AM (17 years ago)
Author:
vboxsync
Message:

2142: GUI should download Additions:

Downloader widget rewritten to make no use the QT networking class.
Free HappyHttp cross-platform networking framework implementation used (developed by Ben Campbell).

Location:
trunk/src/VBox/Frontends/VirtualBox
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk

    r4398 r4432  
    138138        src/VBoxDownloaderWgt.cpp \
    139139        src/VBoxVMListBox.cpp \
    140         src/VBoxFrameBuffer.cpp
     140        src/VBoxFrameBuffer.cpp \
     141        src/HappyHttp.cpp
    141142
    142143VirtualBox_SOURCES.win += \
     
    154155        src/darwin/VBoxAquaStyle.cpp \
    155156        src/darwin/VBoxUtils-darwin.cpp
     157
     158src/HappyHttp.cpp_CXXFLAGS += -fexceptions
     159src/VBoxDownloaderWgt.cpp_CXXFLAGS += -fexceptions
    156160
    157161## @todo how to detect what tool is used?
  • trunk/src/VBox/Frontends/VirtualBox/include/VBoxDownloaderWgt.h

    r4071 r4432  
    2020#define __VBoxDownloaderWgt_h__
    2121
     22#include "HappyHttp.h"
    2223#include "qwidget.h"
    2324#include "qurl.h"
     25#include "qmutex.h"
    2426class QStatusBar;
    2527class QAction;
    26 class QHttpResponseHeader;
    27 class QHttp;
    2828class QProgressBar;
    2929class QToolButton;
     30class QThread;
     31class QTimer;
     32typedef happyhttp::Connection HConnect;
    3033
    3134/** class VBoxDownloaderWgt
     
    4750    void languageChange();
    4851
     52    bool isCheckingPresence() { return mIsChecking; }
     53
    4954private slots:
    50 
    51     /* This slot is used to handle the progress of the file-downloading
    52      * procedure. It also checks the downloading status for the file
    53      * presence verifying purposes. */
    54     void processProgress (int aRead, int aTotal);
    55 
    56     /* This slot is used to handle the finish signal of every operation's
    57      * response. It is used to display the errors occurred during the download
    58      * operation and for the received-buffer serialization procedure. */
    59     void processFinished (int, bool aError);
    60 
    61     /* This slot is used to handle the header responses about the
    62      * requested operations. Watches for the header's status-code. */
    63     void processResponse (const QHttpResponseHeader &aHeader);
    6455
    6556    /* This slot is used to control the connection timeout. */
     
    7566
    7667private:
     68
     69    /* Used to process all the widget events */
     70    bool event (QEvent *aEvent);
    7771
    7872    /* This function is used to make a request to get a file */
     
    9084    void abortDownload (const QString &aReason = QString::null);
    9185
    92     QStatusBar *mStatusBar;
     86    void abortConnection();
     87
    9388    QUrl mUrl;
    9489    QString mTarget;
    95     QHttp *mHttp;
    96     bool mIsChecking;
     90    QStatusBar *mStatusBar;
     91    QAction *mAction;
    9792    QProgressBar *mProgressBar;
    9893    QToolButton *mCancelButton;
    99     QAction *mAction;
    100     int mStatus;
    101     bool mConnectDone;
     94    bool mIsChecking;
    10295    bool mSuicide;
     96    HConnect *mConn;
     97    QThread *mRequestThread;
     98    QMutex mMutex;
     99    QByteArray mDataArray;
     100    QDataStream mDataStream;
     101    QTimer *mTimeout;
    103102};
    104103
  • trunk/src/VBox/Frontends/VirtualBox/src/VBoxDownloaderWgt.cpp

    r4397 r4432  
    2626#include "qtoolbutton.h"
    2727#include "qlayout.h"
    28 #include "qhttp.h"
    2928#include "qstatusbar.h"
    3029#include "qdir.h"
    3130#include "qtimer.h"
    3231
     32/* These notifications are used to notify the GUI thread about different
     33 * downloading events: Downloading Started, Downloading in Progress,
     34 * Downloading Finished. */
     35enum
     36{
     37    StartDownloadEventType = QEvent::User + 100,
     38    ProcessDownloadEventType,
     39    FinishDownloadEventType,
     40    ErrorDownloadEventType
     41};
     42
     43class StartDownloadEvent : public QEvent
     44{
     45public:
     46    StartDownloadEvent (int aStatus, long aSize)
     47        : QEvent ((QEvent::Type) StartDownloadEventType)
     48        , mStatus (aStatus), mSize (aSize) {}
     49
     50    int  mStatus;
     51    long mSize;
     52};
     53
     54class ProcessDownloadEvent : public QEvent
     55{
     56public:
     57    ProcessDownloadEvent (const char *aData, ulong aSize)
     58        : QEvent ((QEvent::Type) ProcessDownloadEventType)
     59        , mData (QByteArray().duplicate (aData, aSize)) {}
     60
     61    QByteArray mData;
     62};
     63
     64class FinishDownloadEvent : public QEvent
     65{
     66public:
     67    FinishDownloadEvent()
     68        : QEvent ((QEvent::Type) FinishDownloadEventType) {}
     69};
     70
     71class ErrorDownloadEvent : public QEvent
     72{
     73public:
     74    ErrorDownloadEvent (const QString &aInfo)
     75        : QEvent ((QEvent::Type) ErrorDownloadEventType)
     76        , mInfo (aInfo) {}
     77
     78    QString mInfo;
     79};
     80
     81/* This callback is used to handle the file-downloading procedure
     82 * beginning. It checks the downloading status for the file
     83 * presence verifying purposes. */
     84void OnBegin (const happyhttp::Response *aResponse, void *aUserdata)
     85{
     86    VBoxDownloaderWgt *loader = static_cast<VBoxDownloaderWgt*> (aUserdata);
     87    if (loader->isCheckingPresence())
     88    {
     89        int status = aResponse->getstatus();
     90        QString contentLength = aResponse->getheader ("Content-length");
     91
     92        QApplication::postEvent (loader,
     93            new StartDownloadEvent (status, contentLength.toLong()));
     94    }
     95}
     96
     97/* This callback is used to handle the progress of the file-downloading
     98 * procedure. It also checks the downloading status for the file
     99 * presence verifying purposes. */
     100void OnData (const happyhttp::Response*, void *aUserdata,
     101             const unsigned char *aData, int aSize)
     102{
     103    VBoxDownloaderWgt *loader = static_cast<VBoxDownloaderWgt*> (aUserdata);
     104    if (!loader->isCheckingPresence())
     105    {
     106        QApplication::postEvent (loader,
     107            new ProcessDownloadEvent ((const char*)aData, aSize));
     108    }
     109}
     110
     111/* This callback is used to handle the finish signal of every operation's
     112 * response. It is used to display the errors occurred during the download
     113 * operation and for the received-buffer serialization procedure. */
     114void OnComplete (const happyhttp::Response*, void *aUserdata)
     115{
     116    VBoxDownloaderWgt *loader = static_cast<VBoxDownloaderWgt*> (aUserdata);
     117    if (!loader->isCheckingPresence())
     118        QApplication::postEvent (loader,
     119            new FinishDownloadEvent());
     120}
     121
    33122VBoxDownloaderWgt::VBoxDownloaderWgt (QStatusBar *aStatusBar, QAction *aAction,
    34123                                      const QString &aUrl, const QString &aTarget)
    35124    : QWidget (0, "VBoxDownloaderWgt")
    36     , mStatusBar (aStatusBar)
    37125    , mUrl (aUrl), mTarget (aTarget)
    38     , mHttp (0), mIsChecking (true)
     126    , mStatusBar (aStatusBar), mAction (aAction)
    39127    , mProgressBar (0), mCancelButton (0)
    40     , mAction (aAction), mStatus (0)
    41     , mConnectDone (false), mSuicide (false)
     128    , mIsChecking (true), mSuicide (false)
     129    , mConn (new HConnect (mUrl.host(), 80))
     130    , mRequestThread (0)
     131    , mDataStream (mDataArray, IO_WriteOnly)
     132    , mTimeout (new QTimer (this))
    42133{
    43134    /* Disable the associated action */
    44135    mAction->setEnabled (false);
     136    connect (mTimeout, SIGNAL (timeout()),
     137             this, SLOT (processTimeout()));
    45138
    46139    /* Drawing itself */
     
    55148    mCancelButton->setAutoRaise (true);
    56149    mCancelButton->setFocusPolicy (TabFocus);
    57     QSpacerItem *spacer = new QSpacerItem (0, 0, QSizePolicy::Expanding,
    58                                                  QSizePolicy::Fixed);
     150    connect (mCancelButton, SIGNAL (clicked()),
     151             this, SLOT (processAbort()));
    59152
    60153    QHBoxLayout *mainLayout = new QHBoxLayout (this);
    61154    mainLayout->addWidget (mProgressBar);
    62155    mainLayout->addWidget (mCancelButton);
    63     mainLayout->addItem (spacer);
     156    mainLayout->addItem (new QSpacerItem (0, 0, QSizePolicy::Expanding,
     157                                                QSizePolicy::Fixed));
    64158
    65159    /* Select the product version */
    66160    QString version = vboxGlobal().virtualBox().GetVersion();
    67161
    68 #if !defined(VBOX_WITHOUT_QHTTP)
    69     /* Initialize url operator */
    70     mHttp = new QHttp (this, "mHttp");
    71     mHttp->setHost (mUrl.host());
    72 
    73     /* Setup connections */
    74     connect (mHttp, SIGNAL (dataReadProgress (int, int)),
    75              this, SLOT (processProgress (int, int)));
    76     connect (mHttp, SIGNAL (requestFinished (int, bool)),
    77              this, SLOT (processFinished (int, bool)));
    78     connect (mHttp, SIGNAL (responseHeaderReceived (const QHttpResponseHeader&)),
    79              this, SLOT (processResponse (const QHttpResponseHeader&)));
    80 #endif /* !VBOX_WITHOUT_QHTTP */
    81     connect (mCancelButton, SIGNAL (clicked()),
    82              this, SLOT (processAbort()));
     162    /* Prepare the connection */
     163    mConn->setcallbacks (OnBegin, OnData, OnComplete, this);
    83164
    84165    languageChange();
     
    101182}
    102183
    103 /* This slot is used to handle the progress of the file-downloading
    104  * procedure. It also checks the downloading status for the file
    105  * presence verifying purposes. */
    106 void VBoxDownloaderWgt::processProgress (int aRead, int aTotal)
    107 {
    108     mConnectDone = true;
    109     if (aTotal != -1)
    110     {
    111         if (mIsChecking)
    112         {
    113 #if !defined(VBOX_WITHOUT_QHTTP)
    114             mHttp->abort();
    115 #endif
    116             if (mStatus == 404)
    117                 abortDownload (tr ("Could not locate the file on "
    118                                    "the server (response: %1).")
    119                                .arg (mStatus));
    120             else
    121                 processFile (aTotal);
    122         }
    123         else
    124             mProgressBar->setProgress (aRead, aTotal);
    125     }
    126     else
    127         abortDownload (tr ("Could not determine the file size."));
    128 }
    129 
    130 /* This slot is used to handle the finish signal of every operation's
    131  * response. It is used to display the errors occurred during the download
    132  * operation and for the received-buffer serialization procedure. */
    133 void VBoxDownloaderWgt::processFinished (int, bool aError)
    134 {
    135 #if !defined(VBOX_WITHOUT_QHTTP)
    136     if (aError && mHttp->error() != QHttp::Aborted)
    137     {
    138         mConnectDone = true;
    139         QString reason = mIsChecking ?
    140             tr ("Could not connect to the server (%1).") :
    141             tr ("Could not download the file (%1).");
    142         abortDownload (reason.arg (mHttp->errorString()));
    143     }
    144     else if (!aError && !mIsChecking)
    145     {
    146         mHttp->abort();
    147         /* Serialize the incoming buffer into the .iso image. */
    148         while (true)
    149         {
    150             QFile file (mTarget);
    151             if (file.open (IO_WriteOnly))
    152             {
    153                 file.writeBlock (mHttp->readAll());
    154                 file.close();
    155                 /// @todo the below action is not part of the generic
    156                 //  VBoxDownloaderWgt functionality, so this class should just
    157                 //  emit a signal when it is done saving the downloaded file
    158                 //  (succeeded or failed).
    159                 int rc = vboxProblem().confirmMountAdditions (mUrl.toString(),
    160                                            QDir::convertSeparators (mTarget));
    161                 if (rc == QIMessageBox::Yes)
    162                     vboxGlobal().consoleWnd().installGuestAdditionsFrom (mTarget);
    163                 QTimer::singleShot (0, this, SLOT (suicide()));
    164                 return;
    165             }
    166             else
    167             {
    168                 vboxProblem().message (mStatusBar->topLevelWidget(),
    169                     VBoxProblemReporter::Error,
    170                     tr ("<p>Failed to save the downloaded file as "
    171                         "<nobr><b>%1</b>.</nobr></p>")
    172                     .arg (QDir::convertSeparators (mTarget)));
    173             }
    174 
    175             /// @todo read the todo above (probably should just parametrize
    176             /// the title)
    177             QString target = vboxGlobal().getExistingDirectory (
    178                 QFileInfo (mTarget).dirPath(), this, "selectSaveDir",
    179                 tr ("Select folder to save Guest Additions image to"), true);
    180             if (target.isNull())
    181             {
    182                 QTimer::singleShot (0, this, SLOT (suicide()));
    183                 return;
    184             }
    185             else
    186                 mTarget = QDir (target).absFilePath (QFileInfo (mTarget).fileName());
    187         };
    188     }
    189 #else  /* VBOX_WITHOUT_QHTTP */
    190     NOREF (aError);
    191 #endif /* VBOX_WITHOUT_QHTTP */
    192 }
    193 
    194 /* This slot is used to handle the header responses about the
    195  * requested operations. Watches for the header's status-code. */
    196 void VBoxDownloaderWgt::processResponse (const QHttpResponseHeader &aHeader)
    197 {
    198 #if !defined(VBOX_WITHOUT_QHTTP)
    199     mStatus = aHeader.statusCode();
    200 #endif
    201 }
    202 
    203184/* This slot is used to control the connection timeout. */
    204185void VBoxDownloaderWgt::processTimeout()
    205186{
    206     if (mConnectDone)
    207         return;
    208 #if !defined(VBOX_WITHOUT_QHTTP)
    209     mHttp->abort();
    210187    abortDownload (tr ("Connection timed out."));
    211 #endif
    212188}
    213189
     
    215191void VBoxDownloaderWgt::processAbort()
    216192{
    217 #if !defined(VBOX_WITHOUT_QHTTP)
    218     mConnectDone = true;
    219     mHttp->abort();
    220193    abortDownload (tr ("The download process has been cancelled "
    221194                       "by the user."));
    222 #endif
    223195}
    224196
     
    228200void VBoxDownloaderWgt::suicide()
    229201{
     202    delete mRequestThread;
     203    delete mConn;
     204
    230205    mAction->setEnabled (true);
    231206    mStatusBar->removeWidget (this);
     
    233208}
    234209
     210/* Used to process all the widget events */
     211bool VBoxDownloaderWgt::event (QEvent *aEvent)
     212{
     213    switch (aEvent->type())
     214    {
     215        case StartDownloadEventType:
     216        {
     217            StartDownloadEvent *e = static_cast<StartDownloadEvent*> (aEvent);
     218
     219            mTimeout->stop();
     220            if (e->mStatus == 404)
     221                abortDownload (tr ("Could not locate the file on "
     222                    "the server (response: %1).").arg (e->mStatus));
     223            else
     224                processFile (e->mSize);
     225
     226            return true;
     227        }
     228        case ProcessDownloadEventType:
     229        {
     230            ProcessDownloadEvent *e = static_cast<ProcessDownloadEvent*> (aEvent);
     231
     232            mTimeout->start (20000, true);
     233            mProgressBar->setProgress (mProgressBar->progress() + e->mData.size());
     234            mDataStream.writeRawBytes (e->mData.data(), e->mData.size());
     235
     236            return true;
     237        }
     238        case FinishDownloadEventType:
     239        {
     240            mTimeout->stop();
     241
     242            /* Serialize the incoming buffer into the .iso image. */
     243            while (true)
     244            {
     245                QFile file (mTarget);
     246                if (file.open (IO_WriteOnly))
     247                {
     248                    file.writeBlock (mDataArray);
     249                    file.close();
     250                    /// @todo the below action is not part of the generic
     251                    //  VBoxDownloaderWgt functionality, so this class should just
     252                    //  emit a signal when it is done saving the downloaded file
     253                    //  (succeeded or failed).
     254                    int rc = vboxProblem().confirmMountAdditions (mUrl.toString(),
     255                                               QDir::convertSeparators (mTarget));
     256                    if (rc == QIMessageBox::Yes)
     257                        vboxGlobal().consoleWnd().installGuestAdditionsFrom (mTarget);
     258                    QTimer::singleShot (0, this, SLOT (suicide()));
     259                    break;
     260                }
     261                else
     262                {
     263                    vboxProblem().message (mStatusBar->topLevelWidget(),
     264                        VBoxProblemReporter::Error,
     265                        tr ("<p>Failed to save the downloaded file as "
     266                            "<nobr><b>%1</b>.</nobr></p>")
     267                        .arg (QDir::convertSeparators (mTarget)));
     268                }
     269
     270                /// @todo read the todo above (probably should just parametrize
     271                /// the title)
     272                QString target = vboxGlobal().getExistingDirectory (
     273                    QFileInfo (mTarget).dirPath(), this, "selectSaveDir",
     274                    tr ("Select folder to save Guest Additions image to"), true);
     275                if (target.isNull())
     276                    QTimer::singleShot (0, this, SLOT (suicide()));
     277                else
     278                    mTarget = QDir (target).absFilePath (QFileInfo (mTarget).fileName());
     279            }
     280
     281            return true;
     282        }
     283        case ErrorDownloadEventType:
     284        {
     285            ErrorDownloadEvent *e = static_cast<ErrorDownloadEvent*> (aEvent);
     286
     287            abortDownload (e->mInfo);
     288
     289            return true;
     290        }
     291        default:
     292            break;
     293    }
     294
     295    return QWidget::event (aEvent);
     296}
     297
    235298/* This function is used to make a request to get a file */
    236299void VBoxDownloaderWgt::getFile()
    237300{
    238     mConnectDone = false;
    239 #if !defined(VBOX_WITHOUT_QHTTP)
    240     mHttp->get (mUrl.path());
    241 #endif
    242     QTimer::singleShot (5000, this, SLOT (processTimeout()));
     301    /* Http request thread class */
     302    class Thread : public QThread
     303    {
     304    public:
     305
     306        Thread (VBoxDownloaderWgt *aParent, HConnect *aConn,
     307                const QString &aPath, QMutex *aMutex)
     308            : mParent (aParent), mConn (aConn)
     309            , mPath (aPath), mMutex (aMutex) {}
     310
     311        virtual void run()
     312        {
     313            try
     314            {
     315                mConn->request ("GET", mPath);
     316                while (mConn->outstanding())
     317                {
     318                    QMutexLocker locker (mMutex);
     319                    mConn->pump();
     320                }
     321            }
     322            catch (happyhttp::Wobbly &ex)
     323            {
     324                QApplication::postEvent (mParent,
     325                    new ErrorDownloadEvent (ex.what()));
     326            }
     327        }
     328
     329    private:
     330
     331        VBoxDownloaderWgt *mParent;
     332        HConnect *mConn;
     333        QString mPath;
     334        QMutex *mMutex;
     335    };
     336
     337    if (!mRequestThread)
     338        mRequestThread = new Thread (this, mConn, mUrl.path(), &mMutex);
     339    mRequestThread->start();
     340    mTimeout->start (20000, true);
    243341}
    244342
     
    248346void VBoxDownloaderWgt::processFile (int aSize)
    249347{
     348    abortConnection();
     349
    250350    /// @todo the below action is not part of the generic
    251351    //  VBoxDownloaderWgt functionality, so this class should just
     
    253353
    254354    /* Ask user about GA image downloading */
    255     int rc = vboxProblem().
    256         confirmDownloadAdditions (mUrl.toString(), aSize);
     355    int rc = vboxProblem().confirmDownloadAdditions (mUrl.toString(), aSize);
    257356    if (rc == QIMessageBox::Yes)
    258357    {
    259358        mIsChecking = false;
     359        mProgressBar->setTotalSteps (aSize);
    260360        getFile();
    261361    }
     
    264364}
    265365
    266 /* This wrapper displays an error message box (unless @aReason is
     366/* This wrapper displays an error message box (unless aReason is
    267367 * QString::null) with the cause of the download procedure
    268368 * termination. After the message box is dismissed, the downloader signals
     
    270370void VBoxDownloaderWgt::abortDownload (const QString &aReason)
    271371{
     372    abortConnection();
     373
    272374    /* Protect against double kill request. */
    273375    if (mSuicide)
     
    286388}
    287389
     390void VBoxDownloaderWgt::abortConnection()
     391{
     392    mMutex.lock();
     393    mConn->close();
     394    mMutex.unlock();
     395}
     396
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette