--- /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