upnp/upnpstack/upnphttptransfer/src/httpdownloadworker.cpp
changeset 0 f5a58ecadc66
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upnp/upnpstack/upnphttptransfer/src/httpdownloadworker.cpp	Tue Feb 02 01:12:20 2010 +0200
@@ -0,0 +1,646 @@
+/** @file
+* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies  this distribution, and is available 
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Handles download of one file at a time
+*
+*/
+
+
+// System include files
+#include <sysutil.h>
+#ifndef __SERIES60_31__
+#include <driveinfo.h>
+#endif
+
+// User include files
+#include "httpfile.h"
+#include "httpdownloadworker.h"
+#include "httpheader.h"
+#include "httpworkerobserver.h"
+#include "upnpcompvariant.hrh"
+
+// Constants
+_LIT8( KModuleName, "HttpTransfer");
+const TUint KDefaultBufferSize = 64000;
+const TUint32 KMaxBufferSize   = 0x100000; //1MB
+
+
+// ======== MEMBER FUNCTIONS ========
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::NewL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+CHttpDownloadWorker* CHttpDownloadWorker::NewL(
+                                           MHttpTransferObserver& aObserver,
+                                           TUint aIAPId,
+                                           TUint aBufferSize,
+                                           MHttpWorkerObserver& aCallback )
+    {
+    CHttpDownloadWorker* self = CHttpDownloadWorker::NewLC( aObserver,
+                                                            aIAPId,
+                                                            aBufferSize,
+                                                            aCallback );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::NewLC()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+CHttpDownloadWorker* CHttpDownloadWorker::NewLC(
+                                           MHttpTransferObserver& aObserver,
+                                           TUint aIAPId,
+                                           TUint aBufferSize,
+                                           MHttpWorkerObserver& aCallback )
+    {
+    CHttpDownloadWorker* self = new( ELeave ) CHttpDownloadWorker(
+                                                               aIAPId,
+                                                               aBufferSize,
+                                                               aObserver,
+                                                               aCallback );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    return self;
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::CHttpDownloadWorker()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+CHttpDownloadWorker::CHttpDownloadWorker( TUint /*aIAPId*/,
+                                          TUint aBufferSize,
+                                          MHttpTransferObserver& aObserver,
+                                          MHttpWorkerObserver& aCallback )
+    {
+    iProcessState = EHttpTransactionIdle;
+    iFileSizeFromHeader = 0;
+    iBytesTransferred = 0;
+    iObserver = &aObserver;
+    iCallback = &aCallback;
+
+    if ( aBufferSize <= 0 )
+        {
+        iBufferSize = KDefaultBufferSize;
+        }
+    else if ( aBufferSize > KMaxBufferSize )
+        {
+        iBufferSize = KMaxBufferSize;
+        }
+    else
+        {
+        iBufferSize = aBufferSize;
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::ConstructL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::ConstructL()
+    {
+    CHttpTransferWorker::ConstructL();
+    ConnectL();
+    iSessionTimer = CHttpNotifyTimer::NewL( this );
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::~CHttpDownloadWorker()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+CHttpDownloadWorker::~CHttpDownloadWorker()
+    {
+    delete iResponseBody;
+    delete iSessionTimer;
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::CheckDiskSpaceL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::CheckDiskSpaceL( TInt aSize )
+    {
+    TInt driveNum;
+    TDriveInfo aninfo;
+    if ( iProcessedFile->Path() )
+        {
+        TParse p;
+        p.Set( *(iProcessedFile->Path() ), NULL, NULL );
+        TPtrC pointer = p.Drive();
+        TLex lineParser( pointer.Ptr() );
+        TChar ch = lineParser.Get();
+
+        //sets the default drive or the fulnd value
+        User::LeaveIfError( iFsSession.CharToDrive( ch, driveNum ) );
+        }
+    else
+        {
+        TDriveInfo driveInfo;
+        User::LeaveIfError( iFile.Drive( driveNum, driveInfo ) );
+        }
+
+    User::LeaveIfError( iFsSession.Drive( aninfo, driveNum ) );
+
+    // get the drive status from DriveInfo in order to distinguish memory card
+    // and hard disk from each other in multiple drive environment.
+#ifndef __SERIES60_31__
+    TUint driveStatus = 0;
+    User::LeaveIfError( DriveInfo::GetDriveStatus(
+        iFsSession, driveNum, driveStatus ) );
+#endif
+
+    // The case for default device memory (C-drive).
+    if( aninfo.iType == EMediaNANDFlash )
+        {
+        if( SysUtil::FFSSpaceBelowCriticalLevelL
+                                        ( &iFsSession, aSize ) )
+             {
+             User::Leave( KErrDiskFull );
+             }
+        }
+#ifndef __SERIES60_31__
+    // Internal FLASH memory
+    // Multiple drive checking rules are applied here
+    // note that in case of S60 3.1 this case is skipped
+    else if( aninfo.iType == EMediaHardDisk &&
+             aninfo.iDriveAtt & KDriveAttRemovable &&
+             driveStatus & DriveInfo::EDriveInternal )
+        {
+        if( SysUtil::MMCSpaceBelowCriticalLevelL( &iFsSession, aSize ) )
+            {
+            User::Leave( KErrDiskFull );
+            }
+        }
+#endif
+    // Memory card (or practically any other memory type)
+    // This covers also emulator's EMediaRam.
+    else
+        {
+        if( SysUtil::DiskSpaceBelowCriticalLevelL
+                                        ( &iFsSession, aSize, driveNum ) )
+            {
+            User::Leave( KErrDiskFull );
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::WriteTailIntoFileL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::WriteTailIntoFileL()
+    {
+    if( iResponseBody )
+        {
+        if( iResponseBody->Length() > 0 )
+            {
+            CheckDiskSpaceL( iResponseBody->Length() );
+            TPtr8 ptrResponseBody = iResponseBody->Des();
+#ifndef RD_UPNP_REMOTE_ACCESS
+            TInt size( 0 );
+            iFile.Size( size );
+            if( size >= 0 )
+                {
+                //transfer succeed, total size can be set to file size
+                //it concerns chunked encoding or HTTP1.0 
+                if ( iFileSizeFromHeader == 0 )
+                    {
+                    iFileSizeFromHeader = size;
+                    }
+                
+#endif //RD_UPNP_REMOTE_ACCESS
+                // Write the data on the disk
+                User::LeaveIfError( iFile.Write( ptrResponseBody ) );
+                
+                delete iResponseBody;
+                iResponseBody = NULL;
+
+                // if the tracking is on send one more progress info
+#ifdef RD_UPNP_REMOTE_ACCESS
+                if ( iProcessedFile->TrackingOn() )
+#else // RD_UPNP_REMOTE_ACCESS
+                if ( iProcessedFile->TrackingOn()  )
+#endif // RD_UPNP_REMOTE_ACCESS
+                    {
+                    iObserver->TransferProgress( iProcessedFile->Key(),
+                                                 iBytesTransferred,
+                                                 iFileSizeFromHeader );
+                    }
+#ifndef RD_UPNP_REMOTE_ACCESS
+                }
+#endif //RD_UPNP_REMOTE_ACCESS
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::HandleResponseDataL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::HandleResponseDataL(
+                                              RHTTPTransaction aTransaction )
+    {
+    MHTTPDataSupplier* pRespBody = aTransaction.Response().Body();
+
+    if( pRespBody )
+        {
+        TPtrC8 dataChunk;
+        pRespBody->GetNextDataPart( dataChunk );
+
+        iSessionTimer->Cancel();
+        iSessionTimer->AfterSeconds( KSessionTimeout );
+
+        // increase the progress counter
+        iBytesTransferred += dataChunk.Length();
+
+        // Allocate memory for the data
+        if( !iResponseBody )
+            {
+            // this is the first call so create the buffer to retain
+            // response data
+            iResponseBody = HBufC8::NewL( iBufferSize );
+            }
+
+        if ( iResponseBody->Length() + dataChunk.Length() > iBufferSize  )
+            {
+            iResponseBody = iResponseBody->ReAllocL(
+                                                iResponseBody->Length() +
+                                                dataChunk.Length() );
+            }
+
+        // Append the new data to the buffer
+        TPtr8 ptrResponseBody = iResponseBody->Des();
+        ptrResponseBody.Append( dataChunk );
+        pRespBody->ReleaseData();
+        // If buffer is full write data to memory card ( to file )
+        if( iResponseBody->Length() > iBufferSize )
+            {
+#ifndef RD_UPNP_REMOTE_ACCESS
+            TInt size( 0 );
+            // if the file handle was not valid
+            User::LeaveIfError( iFile.Size( size ) );
+            if( size >= 0 )
+                {
+#endif // RD_UPNP_REMOTE_ACCESS
+                CheckDiskSpaceL( ptrResponseBody.Length() );
+                User::LeaveIfError( iFile.Write( ptrResponseBody ) );
+                delete iResponseBody;
+                iResponseBody = NULL;
+#ifndef RD_UPNP_REMOTE_ACCESS
+                }                      
+#endif // RD_UPNP_REMOTE_ACCESS
+
+            // Inform observer about the progress if progress tracking is on
+#ifdef RD_UPNP_REMOTE_ACCESS
+            if ( iProcessedFile->TrackingOn() )
+#else // RD_UPNP_REMOTE_ACCESS
+            if ( iProcessedFile->TrackingOn()  )
+#endif // RD_UPNP_REMOTE_ACCESS
+                {
+                iObserver->TransferProgress( iProcessedFile->Key(),
+                                             iBytesTransferred,
+                                             iFileSizeFromHeader );
+                }
+            }
+        }
+
+    else
+        {
+        aTransaction.Close();
+
+        // Remove remains of the file from the file system
+        delete iProcessedFile;
+        iProcessedFile = NULL;
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::HTTPGetL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::HttpGetL()
+    {
+    // if URI is null -> leave
+    if ( !iProcessedFile->Uri() )
+        {
+        User::Leave( KErrArgument );
+        }
+
+    TUriParser8 uri;
+    User::LeaveIfError( uri.Parse( *( iProcessedFile->Uri() ) ) );
+
+    RStringF method;
+    method = iStringPool.StringF( HTTP::EGET, RHTTPSession::GetTable() );
+
+    iHttpTransaction = iSession.OpenTransactionL( uri, *this, method );
+    RHTTPHeaders hdr = iHttpTransaction.Request().GetHeaderCollection();
+
+    SetHeaderL( hdr, HTTP::EUserAgent, KModuleName );
+
+    //Set the property of upnphttptransfer to ENotifyOnDisconnect
+    //Set the property of HTTP Transaction to EEnableDisconnectNotification
+    //The MHFRunL can get the really http error.
+    iHttpTransaction.PropertySet().SetPropertyL(
+        iSession.StringPool().StringF(
+            HTTP::ENotifyOnDisconnect,RHTTPSession::GetTable()),
+            iSession.StringPool().StringF(
+                HTTP::EEnableDisconnectNotification,
+                RHTTPSession::GetTable() ));
+
+    // Sets extra headers
+    RPointerArray<CHttpHeader> headerArray = iProcessedFile->Headers();
+    for ( TInt i = 0; i < headerArray.Count(); i++ )
+        {
+        SetHeaderL( hdr,
+                    headerArray[i]->FieldName(),
+                    headerArray[i]->FieldValue() );
+        }
+
+    // Now will get events in MHFRunL
+    iHttpTransaction.SubmitL();
+
+    //set timer
+    iSessionTimer->Cancel();
+    iSessionTimer->AfterSeconds( KSessionTimeout );
+
+    method.Close();
+
+    // Change state of the state machine
+    iProcessState = EHttpGetSent;
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::FinishDownload()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::FinishDownload( RHTTPTransaction aTransaction )
+    {
+    // close transaction
+    aTransaction.Close();
+
+    delete iResponseBody;
+    iResponseBody = NULL;
+    
+    // change state
+    iProcessState = EHttpTransactionIdle;
+    iProcessedFile->CloseFile(); 
+    iFile.Close();
+
+    // inform observers
+    iObserver->TransferCompleted( iProcessedFile->Key(), KErrNone );
+
+    delete iProcessedFile;
+    iProcessedFile = NULL;
+    
+    // inform the worker observer
+    iCallback->WorkerCompleted();
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::CompleteAndNotify()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::CompleteAndNotifyL( TInt aError )
+    {
+    iSessionTimer->Cancel();
+    iHttpTransaction.Cancel();
+    iHttpTransaction.Close();
+    iObserver->TransferCompleted( iProcessedFile->Key(), aError );
+
+    delete iResponseBody;
+    iResponseBody = NULL;
+    iProcessState = EHttpTransactionIdle;
+    iCallback->WorkerCompleted();
+
+    iFile.Close();
+    iProcessedFile->DeleteFileFromFileSystemL();
+    delete iProcessedFile;
+    iProcessedFile = NULL;
+    }
+
+
+// from base class CHttpTransferWorker
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::CancelTransfer()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::CancelTransfer()
+    {
+    iSessionTimer->Cancel();
+    // if transfer has not been started yet
+    if ( iProcessState == EHttpWaitingForStart )
+        {
+        delete iProcessedFile;
+        iProcessedFile = NULL;
+
+        iProcessState = EHttpTransactionIdle;
+        iCallback->WorkerCompleted();
+        }
+    // if process is finished do nothing
+    else if ( iProcessState != EHttpTransactionIdle )
+        {
+        iHttpTransaction.Cancel();
+        iHttpTransaction.Close();
+
+        delete iResponseBody;
+        iResponseBody = NULL;
+        iProcessState = EHttpTransactionIdle;
+        iCallback->WorkerCompleted();
+
+        iFile.Close();
+        TRAP_IGNORE( iProcessedFile->DeleteFileFromFileSystemL() )
+        delete iProcessedFile;
+        iProcessedFile = NULL;
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::StartProcessL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::StartProcessL()
+    {
+    CHttpTransferWorker::StartProcessL();
+
+    iProcessedFile->CreateFileInFileSystemL();
+
+    // File creation succeeded -> handle available
+    User::LeaveIfError( iFile.Duplicate( iProcessedFile->FileHandle() ) );
+
+    // Perform HTTP GET
+    HttpGetL();
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::MHFRunL()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::MHFRunL( RHTTPTransaction aTransaction,
+                                   const THTTPEvent& aEvent )
+    {
+    //set timer
+    iSessionTimer->Cancel();
+
+    // event handler
+    switch (aEvent.iStatus)
+        {
+        // Response headers received
+        case THTTPEvent::EGotResponseHeaders:
+            {
+            // accept all 'Successful' range of codes 2xx
+            TInt status = aTransaction.Response().StatusCode();
+            if ( !( ( status >= HTTPStatus::EOk ) &&
+                    ( status <  HTTPStatus::EMultipleChoices ) ) )
+                {
+                // calls MHFRunError
+                User::Leave( aTransaction.Response().StatusCode() );
+                }
+
+            else if ( iProcessState == EHttpGetSent )
+                {
+                RHTTPHeaders header =
+                           aTransaction.Response().GetHeaderCollection();
+                RStringF field;
+                field = iStringPool.StringF( HTTP::EContentLength,
+                                             RHTTPSession::GetTable() );
+                THTTPHdrVal value;
+
+                // Get value of the content-length field.
+                // Value is represented in bytes.
+                if ( header.GetField( field, 0, value ) == KErrNone )
+                    {
+                    // Set the value to filesize
+                    iFileSizeFromHeader = value.Int();
+                    }
+                else
+                    {
+                    // If the Content-Length is not defined
+                    // in case chunked encoding or HTTP1.0 
+                    // Set the iFileSizeFromHeader to zero
+                    iFileSizeFromHeader = 0;
+                    }
+
+                iBytesTransferred = 0;
+
+                iProcessState = EHttpGetResponseReceived;
+                }
+            iSessionTimer->AfterSeconds( KSessionTimeout );
+            break;
+            }
+
+        // Response body data received
+        // This is only entered when state is EHttpGetResponseReceived
+        //   --> no need to check state
+        case THTTPEvent::EGotResponseBodyData:
+            {
+            // handle body data
+            // Informs the observer as well
+            HandleResponseDataL( aTransaction );
+
+            break;
+            }
+
+        // Response completed
+        case THTTPEvent::EResponseComplete:
+            {
+            // Get response was completed
+            if ( iProcessState == EHttpGetResponseReceived )
+                {
+                // Write last part of the buffer to file ( if exists )
+                WriteTailIntoFileL();
+                }
+
+            break;
+            }
+
+        // Transaction succeeded
+        case THTTPEvent::ESucceeded:
+            {
+            if ( iProcessState == EHttpGetResponseReceived )
+                {
+                // Finish download
+                FinishDownload( aTransaction );
+                }
+
+            break;
+            }
+
+        // Transaction failed
+        case THTTPEvent::EFailed:
+            {
+            CompleteAndNotifyL( KErrGeneral );
+            break;
+            }
+
+        default:
+            {
+            if ( aEvent.iStatus < 0 )
+                {
+                CompleteAndNotifyL( aEvent.iStatus );
+                }
+            else
+                {
+                CompleteAndNotifyL( KErrGeneral );
+                }
+            break;
+            }
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CHttpDownloadWorker::MHFRunError()
+// (See comments in header file)
+// --------------------------------------------------------------------------
+//
+TInt CHttpDownloadWorker::MHFRunError( TInt aError,
+                                       RHTTPTransaction /*aTransaction*/,
+                                       const THTTPEvent& /*aEvent*/ )
+    {
+    // if the HTTP server responds with HTTP error code the aError variable
+    // contains the code which is above zero, changed to KErrGeneral
+    if ( aError > 0 )
+        {
+        aError = KErrGeneral;
+        }
+
+    TRAP_IGNORE( CompleteAndNotifyL( aError ) )
+
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpDownloadWorker::TimerEventL
+// Disconnect connection
+// -----------------------------------------------------------------------------
+//
+void CHttpDownloadWorker::TimerEventL( CHttpNotifyTimer* /*aTimer*/ )
+    {
+    CompleteAndNotifyL( KErrTimedOut );
+    }
+// end of file