diff -r 000000000000 -r f5a58ecadc66 upnp/upnpstack/upnphttptransfer/src/httpdownloadworker.cpp --- /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 +#ifndef __SERIES60_31__ +#include +#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 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