diff -r 000000000000 -r dd21522fd290 codhandler/codeng/src/HttpLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/codhandler/codeng/src/HttpLoader.cpp Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,1973 @@ +/* +* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: +* Loading functions for COD Handler. +* +* +*/ + + +// INCLUDE FILES + +#include "HttpLoader.h" +#include "CodSaver.h" +#include "HttpTcpSession.h" +#include "HttpWapSession.h" +#include "CodLogger.h" +#include "CodPanic.h" +#include "CodError.h" +#include "CodUtil.h" +#include "Connection.h" +#include "Timeout.h" +#include "CodProgress.h" +#include "CodEngbase.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HeaderField.h" +#include "FileExt.h" +#include "CodData.h" + + + +_LIT8( KDRMOldContentType, "x-drm-old-content-type"); // old content type header to be added +_LIT8( KAcceptRangeHeader, "bytes"); + +// ================= CONSTANTS ======================= + +/// Maximum user name length we support. +LOCAL_D const TInt KCodMaxAuthUserName = 50; +/// Maximum password length we support. +LOCAL_D const TInt KCodMaxAuthPassword = 50; +/// WSP protocol name. +_LIT8( KCodWspProtocol, "WSP/WSP" ); +/// Accept-header value (application/vnd.wap.mms-message). +_LIT8( KCodAcceptMmsHeaderValue, "application/vnd.wap.mms-message" ); +/// Accept-header value (application/vnd.wap.sic). +_LIT8( KCodAcceptSiHeaderValue, "application/vnd.wap.sic" ); +/// "No activity" timeout for GET request (in microseconds), 60 sec - Updated to 60 secs as part of error fix JSIN-7JSE6H +/// Let's try to keep the same timeout for WCDMA & GPRS (???) +LOCAL_D const TInt KCodGetTimeout = 60000000; +/// "No activity" timeout for POST request (in microseconds), 10 sec. +LOCAL_D const TInt KCodPostTimeout = 10000000; +/// Install-notify timeout (in microseconds), 50 msec. +LOCAL_D const TInt KCodINTimeout = 50000; +/// Retry count for GET request timeout (1 attempt == no retry). +LOCAL_D const TInt KCodGetRetry = 1; +/// Retry count for POST request timeout (2 attempt == 1 retry). +LOCAL_D const TInt KCodPostRetry = 2; +/// Estimated data transfer for reponse headers. Needed for progress. +LOCAL_D const TInt KCodRespHdrsTraffic = 512; +/// characters which should be escaped from requested URL's +_LIT8( KUriEscChars, " "); + +// ================= MEMBER FUNCTIONS ======================= + +// --------------------------------------------------------- +// CHttpLoader::NewL() +// --------------------------------------------------------- +// +CHttpLoader* CHttpLoader::NewL + ( + CConnection& aConnection, + MCodLoadObserver* aCodLoadObserver, + TCodProgress* aProgress , + CCodEngBase* aCodEng + ) + { + CHttpLoader* loader = + new( ELeave ) CHttpLoader( aConnection, aCodLoadObserver, aProgress, aCodEng ); + CleanupStack::PushL( loader ); + loader->ConstructL(); + CleanupStack::Pop( loader ); + return loader; + } +// --------------------------------------------------------- +// CHttpLoader::~CHttpLoader() +// --------------------------------------------------------- +// +CHttpLoader::~CHttpLoader() + { + Cancel(); + iFeatMgr.Close(); + delete iTimeout; + delete iSess; + delete iNotifyBody; + delete iUri; + delete iINTimeout; + + if(iResponseHeaders != NULL) + { + iResponseHeaders->ResetAndDestroy(); + delete iResponseHeaders; + } + + delete iDownloadInfo; + + REComSession::FinalClose(); + __ASSERT_DEBUG( !iParentStatus, CodPanic( ECodRequestPending ) ); + CLOG(( EHttpLoad, 2, _L("*** CHttpLoader::~CHttpLoader") )); + } + +// --------------------------------------------------------- +// CHttpLoader::LoadL() +// --------------------------------------------------------- +// +void CHttpLoader::LoadL + ( + const TDesC8& aUrl, + MCodSaverFactory& aSaverFactory, + TRequestStatus* aStatus + ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::LoadL") )); + // Misuse asserts. + __ASSERT_ALWAYS( aStatus, CodPanic( ECodInvalidArguments ) ); + __ASSERT_ALWAYS( iState == EInit, CodPanic( ECodOffState ) ); + // Internal asserts. + __ASSERT_DEBUG( !iParentStatus, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( !iSaverFactory, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( !iSaver, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( !iUri, CodPanic( ECodInternal ) ); + + iUri = aUrl.AllocL(); + iMethod = HTTP::EGET; + iRetry = KCodGetRetry; + iSaverFactory = &aSaverFactory; + iResult = KErrNone; + iRedirect = EFalse; + + + iCodEng->LoadSubInfoFileL(iCodEng->Data().ActiveDownload() , iResponseHeaders ); + + TBuf8<128> contentType; + TRAPD( err, contentType = GetContentTypeL() ); + if(err == KErrNone) + { + iSaver = iSaverFactory->CreateSaverL( contentType ); + } + + iParentStatus = aStatus; + *iParentStatus = KRequestPending; + + if( iProgress && iSaver ) + { + iSaver->SetSourceUriL(iUri->Des() ); + TInt dlSize( 0 ); + //Current Track Downloaded Size + dlSize += iCodEng->Data()[iCodEng->Data().ActiveDownload()]->DownloadedSize(); + + if(dlSize >= 0) + { + iProgress->SetAmountL(dlSize); + } + } + + if( iProgress ) + { + iProgress->StartPhaseL(TCodProgress::ELoad); + } + + Continue( EStart ); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::LoadL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::NotifyL() +// --------------------------------------------------------- +// +void CHttpLoader::NotifyL +( const TDesC8& aUrl, const TDesC8& aNotifyBody, TRequestStatus* aStatus ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::NotifyL") )); + // Misuse asserts. + __ASSERT_ALWAYS( aStatus, CodPanic( ECodInvalidArguments ) ); + __ASSERT_ALWAYS( iState == EInit, CodPanic( ECodOffState ) ); + // Internal asserts. + __ASSERT_DEBUG( !iParentStatus, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( !iNotifyBody, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( !iUri, CodPanic( ECodInternal ) ); + + // Allocation of two members iUri+iNotifyBody is atomic. + HBufC8* notifyBody = aNotifyBody.AllocLC(); + iUri = aUrl.AllocL(); + iNotifyBody = notifyBody; + CleanupStack::Pop( notifyBody ); // now member. + iMethod = HTTP::EPOST; + iRetry = KCodPostRetry; + iParentStatus = aStatus; + *iParentStatus = KRequestPending; + iResult = KErrNone; + + Continue( EStart ); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::NotifyL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::DoCancel() +// --------------------------------------------------------- +// +void CHttpLoader::DoCancel() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::DoCancel iState(%d)"), iState )); + switch ( iState ) + { + case EStart: + { + // Already completed (by self-completion). + __ASSERT_DEBUG( iStatus != KRequestPending, \ + CodPanic( ECodInternal ) ); + SelfComplete( KErrCancel ); + break; + } + + case EOpen: + { + __ASSERT_DEBUG( iSess, CodPanic( ECodInternal ) ); + // This will complete our status. + iSess->Disconnect(); + break; + } + + case ERequest: + { + CompleteTransaction( KErrCancel ); + break; + } + + case EInit: + default: + { + // No requests should be outstanding in these states. + CLOG(( EHttpLoad, 0, _L("CHttpLoader::DoCancel: unexpected state") )); + CodPanic( ECodInternal ); + break; + } + } + + iResult = KErrCancel; + Done(); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::DoCancel") )); + } + + + +void CHttpLoader::Pause() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::Pause iState(%d)"), iState )); + switch ( iState ) + { + case ERequest: + { + CompleteTransaction( KErrCodHttpDownloadPaused ); + break; + } + } + //Done(); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::Pause") )); + } + +// --------------------------------------------------------- +// CHttpLoader::RunL() +// --------------------------------------------------------- +// +void CHttpLoader::RunL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::RunL iState(%d) iStatus(%d)"), \ + iState, iStatus.Int() )); + + User::LeaveIfError( iStatus.Int() ); // Handle errors in RunError. + switch ( iState ) + { + case EStart: + { + // Operation initiated - Open a session. + OpenSessionL(); + break; + } + + case EOpen: + { + // Session opened. Make the HTTP request. + RequestL(); + break; + } + + case ERequest: + { + // Request completed, we are done. + Done(); + break; + } + + case EInit: + default: + { + // No requests should be outstanding in these states. + CLOG(( ECodEng, 0, _L("CHttpLoader::RunL: unexpected state") )); + CodPanic( ECodInternal ); + break; + } + } + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::RunL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::RunError() +// --------------------------------------------------------- +// +TInt CHttpLoader::RunError( TInt aError ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::RunError aError(%d)"), aError )); + iResult = aError; + Done(); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::RunError") )); + return KErrNone; + } + +// --------------------------------------------------------- +// CHttpLoader::MHFRunL() +// --------------------------------------------------------- +// +void CHttpLoader::MHFRunL +( RHTTPTransaction DEBUG_ONLY( aTransaction ), const THTTPEvent& aEvent ) + { + CLOG(( EHttpLoad, 0, _L("-> CHttpLoader::MHFRunL event(%d)"), \ + aEvent.iStatus )); + __ASSERT_DEBUG( aTransaction == iTrans, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + + StartTimeout(); // There was activity -> restart timeout. + + switch ( aEvent.iStatus ) + { + case THTTPEvent::EGotResponseHeaders: + { + // Now we know that the request was processed by the server. + iSuppressErrors = EFalse; + if (iMethod == HTTP::EGET) + { + StoreResponseHeaderL(); + + //If DD contain TYPE defined more than once, + //TYPE defined in the ClntServ should match the Content-Type that we obtain in the header while content downloading + + const TDesC8& ContentType = GetContentTypeL( iTrans.Response().GetHeaderCollection() ); + const CCodData& CodData = iCodEng->Data(); + + for ( TInt i = 1; i < CodData[CodData.ActiveDownload()]->Types().MdcaCount(); i++ ) + { + const TDataType& type( CodData[CodData.ActiveDownload()]->Types().MdcaPoint( i ) ); + if(ContentType.Find (type.Des8()) != KErrNotFound ) + { + CodData[CodData.ActiveDownload()]->ReArrangeTypesL(i); + iCodEng->ContentTypeChanged(); + break; + } + } + + iCodEng->StoreSubInfoFileL(iResponseHeaders, iCodEng->Data().ActiveDownload() ); + } + HandleResponseHeadersL( iTrans.Response() ); + + if (iResult == KErrCodHttpBadUrl ) + { + User::Leave( KErrCodHttpBadUrl ); + } + else if ( iResult == KErrCodHttpBadResponse ) + { + User::Leave( KErrCodHttpBadResponse ); + } + else + { + // Increment progress a bit to make update frequent. + IncProgressL( KCodRespHdrsTraffic ); + } + break; + } + + case THTTPEvent::EGotResponseBodyData: + { + // Get body data and save (GET) or ignore (POST) + TInt err( KErrNone ); + TInt size; + MHTTPDataSupplier* body = iTrans.Response().Body(); + TPtrC8 bodyP; + // Caution: no leaving between body->GetNextDataPart and + // body->ReleaseData calls! Data must always be released. +#ifdef __TEST_COD_LOG + TBool lastChunk = // ('Log-only' variable.) +#endif /* def __TEST_COD_LOG */ + body->GetNextDataPart( bodyP ); // No leave... + size = bodyP.Size(); +#ifdef __TEST_COD_LOG + CDUMP(( EHttpLoad, 5, _S("Data: "), _S(" "), \ + bodyP.Ptr(), bodyP.Size() )); + if ( lastChunk ) + { + CLOG(( EHttpLoad, 5, _L(" (EOF)") )); + } + else + { + CLOG(( EHttpLoad, 5, _L(" (more data)") )); + } +#endif /* def __TEST_COD_LOG */ + if ( iMethod == HTTP::EGET ) + { +//TODO check if iSaver == NULL occures __ASSERT_DEBUG( iSaver, CodPanic( ECodInternal ) ); + if (iSaver) + { + err = iSaver->AppendData( bodyP ); + iCodEng->UpdateDownloadedSize( bodyP.Size()); + } + } + body->ReleaseData(); // ...until here. + User::LeaveIfError( err ); + IncProgressL( size ); + break; + } + + case THTTPEvent::EResponseComplete: + { + if(iSaver) + { + iSaver->OnComplete(); + } + + break; + } + + case THTTPEvent::EFailed: + { + // Safety code: we should already have an error code. + if ( iResult == KErrNone ) + { + iResult = KErrCodWapConnectionDropped; + } + // Fall through. + } + + case THTTPEvent::ESucceeded: + { + CompleteTransaction( iResult ); + break; + } + + case THTTPEvent::ERedirectRequiresConfirmation: + { + // 3xx redirect response received for POST. Redirect filter is + // cautious to redirect POST request and asks for confirmation. + // Confirmation means that we must resubmit the transaction. + iTrans.SubmitL(); + break; + } + + case THTTPEvent::ERedirectedPermanently: + { + RedirectedPermanentlyL(iTrans.Request().URI().UriDes()); + break; + } + + case THTTPEvent::ERedirectedTemporarily: + { + RedirectedTemporaryL(iTrans.Request().URI().UriDes()); + break; + } + + default: + { + if( aEvent.iStatus == KErrHttpRedirectUseProxy && UseProxyL() ) + { + // KErrHttpRedirectUseProxy is sent by redirect filter if the + // response is 305 Use Proxy. This is not really an error; + // error code is returned only to indicate that the redirect + // filter cannot handle this kind of redirect automatically; + // client action is required. + // + // If UseProxyL can successfully set the new proxy address, + // this is not an error. Otherwise it is. + ; + } + else if( aEvent.iStatus == KErrConnectionTerminated ) + { + // if we get KErrConnectionTerminated during period of time + // when install-notify timeout is active, that means that + // connection has closed prior to timer firing and we should + // not make content available to user, then handle the error + // normally after that + CLOG(( EHttpLoad, 2, _L("CHttpLoader::MHFRunL: aEvent.iStatus = KErrConnectionTerminated") )); + if( iINTimeout && iINTimeout->IsActive() ) + { + CLOG(( EHttpLoad, 2, _L(" :install-notify timeout active: set iSuppressError=EFalse") )); + iINTimeout->Cancel(); + iSuppressErrors = EFalse; + } + // finish handling error in MHFRunError + User::Leave( KErrConnectionTerminated ); + } + else if( aEvent.iStatus == KErrHttpPartialResponseReceived ) + { + //Partial response has been received and connection has been disconnected. This error will be + //propagated to the client only, if the HTTP:ENotifyOnDisconnect property is set with a value + //HTTP::EEnableDisconnectNotification + + //This error code was cancelling the pausable download. This error shud be ignored to keep the + //paused download. + //TSW Err ID : SXUU-77SRWL + + CLOG(( EHttpLoad, 2, _L("CHttpLoader::MHFRunL: aEvent.iStatus = KErrHttpPartialResponseReceived") )); + } + else + { + // Handle errors in MHFRunError. + User::LeaveIfError( aEvent.iStatus ); + } + break; + } + } + CLOG(( EHttpLoad, 0, _L("<- CHttpLoader::MHFRunL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::MHFRunError() +// --------------------------------------------------------- +// +TInt CHttpLoader::MHFRunError + ( + TInt aError, + RHTTPTransaction DEBUG_ONLY( aTransaction ), + const THTTPEvent& /*aEvent*/ + ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::MHFRunError (%d)"), aError )); + __ASSERT_DEBUG( aTransaction == iTrans, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + CompleteTransaction( aError ); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::MHFRunError") )); + return KErrNone; + } + +// --------------------------------------------------------- +// CHttpLoader::GetNextDataPart() +// --------------------------------------------------------- +// +TBool CHttpLoader::GetNextDataPart( TPtrC8& aDataPart ) + { + __ASSERT_DEBUG( iNotifyBody, CodPanic( ECodInternal ) ); + aDataPart.Set( iNotifyBody->Des() ); + return ETrue; + } + +// --------------------------------------------------------- +// CHttpLoader::ReleaseData() +// --------------------------------------------------------- +// +void CHttpLoader::ReleaseData() + { + // Do not delete iNotifyBody here. Reset() may be called and since we + // do support it, we may need to provide the data again. + // iNotifyBody will be deleted in Done(); that's a safe place. + } + +// --------------------------------------------------------- +// CHttpLoader::OverallDataSize() +// --------------------------------------------------------- +// +TInt CHttpLoader::OverallDataSize() + { + __ASSERT_DEBUG( iNotifyBody, CodPanic( ECodInternal ) ); + return iNotifyBody->Des().Size(); + } + +// --------------------------------------------------------- +// CHttpLoader::Reset() +// --------------------------------------------------------- +// +TInt CHttpLoader::Reset() + { + return KErrNone; + } + +// --------------------------------------------------------- +// CHttpLoader::GetCredentialsL() +// --------------------------------------------------------- +// +TBool CHttpLoader::GetCredentialsL + ( + const TUriC8& aURI, + RString aRealm, + RStringF /*aAuthenticationType*/, + RString& aUsername, + RString& aPassword + ) + { + TBool ret( EFalse ); + if ( iCodLoadObserver ) + { + // Unfortunately, everything has to be converted from 8 to 16 bit + // and vice versa. + HBufC* host = CodUtil::ConvertLC( aURI.UriDes() ); + HBufC* realm = CodUtil::ConvertLC( aRealm.DesC() ); + TBuf userName; + TBuf password; + ret = iCodLoadObserver->UserAuthL + ( *host, *realm, EFalse, userName, password ); + CleanupStack::PopAndDestroy( 2 ); // realm, host + HBufC8* userName8 = CodUtil::ConvertLC( userName ); + aUsername = iSess->Sess().StringPool().OpenStringL( *userName8 ); + CleanupStack::PopAndDestroy( userName8 ); + CleanupClosePushL( aUsername ); + HBufC8* password8 = CodUtil::ConvertLC( password ); + aPassword = iSess->Sess().StringPool().OpenStringL( *password8 ); + CleanupStack::PopAndDestroy( password8 ); + CleanupStack::Pop(); // closing aUsername + } + return ret; + } + +// --------------------------------------------------------- +// CHttpLoader::CHttpLoader() +// --------------------------------------------------------- +// +CHttpLoader::CHttpLoader + ( + CConnection& aConnection, + MCodLoadObserver* aCodLoadObserver, + TCodProgress* aProgress, + CCodEngBase* aCodEng + ) +: CActive( CActive::EPriorityStandard ), + iMethod( HTTP::EGET ), + iHttpVersion( HTTP::EHttp11 ), + iConn( aConnection ), + iCodLoadObserver( aCodLoadObserver ), + iState( EInit ), + iResult( KErrNone ), + iSuppressErrors( EFalse ), + iProxySet( EFalse ), + iProgress( aProgress ), + iRetry( KCodGetRetry ), + iCodEng(aCodEng), + iPausableDRM( ETrue ) + { + CActiveScheduler::Add( this ); + if (iCodLoadObserver) + { + iCodLoadObserver->SetConnError( KErrNone); + } + CLOG(( EHttpLoad, 2, _L("*** CHttpLoader::CHttpLoader") )); + } +// --------------------------------------------------------- +// CHttpLoader::ConstructL() +// --------------------------------------------------------- +// +void CHttpLoader::ConstructL() + { + iFeatMgr.OpenL(); + + iResponseHeaders = new (ELeave) CArrayPtrFlat(2); + + } + +// --------------------------------------------------------- +// CHttpLoader::OpenSessionL +// --------------------------------------------------------- +// +void CHttpLoader::OpenSessionL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::OpenSessionL") )); + + if( iSess ) + { + // Already opened. + // Synchronous state change - we are already under RunL. + CLOG(( EHttpLoad, 3, _L(" already open") )); + iState = EOpen; + RequestL(); + } + else + { + if ( iFeatMgr.FeatureSupported( KFeatureIdWsp ) ) + { + CLOG(( EHttpLoad, 4, _L(" KFeatureIdWsp supported") )); + TUint32 iap; + if ( !iConn.IsConnected( iap ) ) + { + CLOG(( EHttpLoad, 3, _L(" not connected, leaving") )); + User::Leave( KErrCodCannotConnect ); + } + HBufC8* gateway = CodUtil::WapGatewayL( iap ); // NULL if not WAP. + CleanupStack::PushL( gateway ); // Push NULL is OK. + if ( gateway ) + { + CLOG(( EHttpLoad, 4, _L(" AP has WAP gateway") )); + // We have a valid WAP gateway. Check WSP protocol + // availability. + CLOG(( EHttpLoad, 3, _L(" Protocols available:") )); + TInt i; + TPtrC8 protocol; + RPointerArray protocols; + RHTTPSession::ListAvailableProtocolsL( protocols ); + for( i = 0; i < protocols.Count(); i++ ) + { + protocol.Set( protocols[i]->Des() ); + CLOG(( EWapConn | ETcpConn, 3, _L8(" <%S>"), &protocol )); + if( !protocol.Compare( KCodWspProtocol ) ) + { + // WSP available. Use a WAP session. + iSess = CHttpWapSession::NewL( *gateway ); + break; + } + } + } + CleanupStack::PopAndDestroy( gateway ); // NULL is OK. + } + if( !iSess ) + { + // No valid WAP gateway or WSP not available - use TCP session. + iSess = CHttpTcpSession::NewL(); + } + + SetupSessionL(); + + iSess->ConnectL( &iStatus ); + iState = EOpen; + SetActive(); + } + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::OpenSessionL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::RequestL() +// --------------------------------------------------------- +// +void CHttpLoader::RequestL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::RequestL") )); + __ASSERT_DEBUG( iState == EOpen, CodPanic( ECodOffState ) ); + __ASSERT_DEBUG( iSess, CodPanic( ECodInternal ) ); + if( !iTimeout ) + { + iTimeout = CTimeout::NewL + ( CActive::EPriorityStandard, TCallBack( StaticTimeout, this ) ); + } + iHttpVersion = HTTP::EHttp11; + CreateTransactionL(); + SubmitTransactionL(); + // Wait it out. + // In ERequest state there is no other object that takes a request status + // to complete. Instead, events come via MHF... callbacks, and there is + // always a final event, when we complete our status manually. + iState = ERequest; + iStatus = KRequestPending; + SetActive(); + StartTimeout(); // After state change! Timeout unexpected in other states. + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::RequestL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::Done() +// --------------------------------------------------------- +// +void CHttpLoader::Done() + { + CLOG(( EHttpLoad, 2, \ + _L("-> CHttpLoader::Done iResult(%d) iSuppressErrors(%d)"), \ + iResult, iSuppressErrors )); + if( iTimeout ) + { + iTimeout->Cancel(); + } + iTrans.Close(); + if( iProxySet && iSess ) + { + // Remove proxy address property from session. + RHTTPConnectionInfo connInfo = iSess->Sess().ConnectionInfo(); + connInfo.RemoveProperty( StringF( HTTP::EUseProxy ) ); + connInfo.RemoveProperty( StringF( HTTP::EProxyAddress ) ); + iProxySet = EFalse; + } + if ( iSaver ) + { + iSaver->CloseStore(); + } + if( iINTimeout ) + { + iINTimeout->Cancel(); + } + iSaverFactory = NULL; + iSaver = NULL; + delete iNotifyBody; + iNotifyBody = NULL; + delete iUri; + iUri = NULL; + iMethod = HTTP::EGET; + if ( iSuppressErrors ) + { + iResult = KErrNone; + iSuppressErrors = EFalse; + } + // Notify parent. + __ASSERT_DEBUG( iParentStatus, CodPanic( ECodInternal ) ); + User::RequestComplete( iParentStatus, iResult ); + iParentStatus = NULL; + iState = EInit; + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::Done") )); + } + +// --------------------------------------------------------- +// CHttpLoader::Continue +// --------------------------------------------------------- +// +void CHttpLoader::Continue( TState aNextState ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::Continue nextState(%d)"), \ + aNextState )); + __ASSERT_DEBUG( !IsActive(), CodPanic( ECodInternal ) ); + + iState = aNextState; + TRequestStatus* ownStatus = &iStatus; + *ownStatus = KRequestPending; + SetActive(); + User::RequestComplete( ownStatus, KErrNone ); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::Continue") )); + } + +// --------------------------------------------------------- +// CHttpLoader::SelfComplete +// --------------------------------------------------------- +// +void CHttpLoader::SelfComplete( TInt aError ) + { + // This method safely handles the case when a request may be completed + // from more then one place. + // The main use is for loading (ERequest state), where + // there is no real external request made and the request can complete + // the following ways: + // - Transaction finished + // - Error (MHFRunError()) + // - Cancel + // - Timeout + CLOG(( EHttpLoad, 2, _L("CHttpLoader::SelfComplete(%d)"), aError )); + if ( iStatus == KRequestPending ) + { + // Request is pending, complete now. + CLOG(( EHttpLoad, 4, _L(" completing now") )); + TRequestStatus* ownStatus = &iStatus; + User::RequestComplete( ownStatus, aError ); + } + else + { + // Request already completed. + // - If this second completion is error, override status. + // - If this second completion is success, don't override - existing + // result may be error and we can't undo that. + CLOG(( EHttpLoad, 4, _L(" already completed") )); + if ( aError != KErrNone ) + { + iStatus = aError; + } + } + } +// ----------------------------------------------------------------------------- +// CHttpLoader::SetRequestHeaderL +// ?implementation_description +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CHttpLoader::SetRequestHeaderL( RStringPool& aStringPool, + RHTTPHeaders& aHeaders) + { + // Set default Accept header + SetHeaderL( aHeaders, HTTP::EAccept, HTTP::EAnyAny ); + + // Some wap gateways update their database of MMS capable devices + // *each time* the phone accesses the gateway. If these accept header is + // only */* in content download, then the database will be updated so that + // the terminal cannot receive MMS messages. As a result, MMS notifications + // will not be sent to the phone and the user never receives MMS messages. + // Overcome: add MMS and SI content types explicitly. + SetHeaderL( aHeaders, HTTP::EAccept, KCodAcceptMmsHeaderValue ); + SetHeaderL( aHeaders, HTTP::EAccept, KCodAcceptSiHeaderValue ); + + if ( iMethod == HTTP::EPOST ) + { + // Content type header and body for POST. + SetHeaderL( aHeaders, HTTP::EContentType, HTTP::ETextPlain ); + iTrans.Request().SetBody( *this ); + } + + // Find ETag in response header + RStringF etag = aStringPool.StringF(HTTP::EETag, RHTTPSession::GetTable()); + TInt fieldInd = FindHeaderField( iResponseHeaders, etag.DesC() ); + + if( fieldInd != KErrNotFound ) + // ETag is known. ETag identifies the content. Set If-Match to see + // that if it's changed, or a redirection goes to another url. + // Server will respond with 412 on error. + { + RStringF ifMatch = aStringPool.StringF(HTTP::EIfMatch, RHTTPSession::GetTable()); + aHeaders.RemoveField( ifMatch ); + aHeaders.SetRawFieldL( ifMatch, + *(*iResponseHeaders)[fieldInd]->FieldRawData(), + KHttpFieldSeparator ); + } + + SetRangeFieldL( aStringPool, aHeaders ); + } +// --------------------------------------------------------- +// CHttpLoader::CreateTransactionL() +// --------------------------------------------------------- +// +void CHttpLoader::CreateTransactionL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::CreateTransactionL") )); + __ASSERT_DEBUG( iUri, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iMethod == HTTP::EGET || iMethod == HTTP::EPOST, \ + CodPanic( ECodInternal ) ); + +#ifdef __TEST_COD_LOG + TPtrC8 uriDes( iUri->Des() ); + CLOG(( EHttpLoad, 4, _L8(" method<%S> URI<%S>"), \ + &StringF( iMethod ).DesC(), &uriDes )); +#endif /* def __TEST_COD_LOG */ + + // Create the transaction. + TUriParser8 uri; + User::LeaveIfError( uri.Parse( *iUri ) ); + + // escape the uri for characters defined in KUriEscChars + HBufC8* escUri = NULL; + TRAPD( err, escUri = EscapeUtils::SpecificEscapeEncodeL(*iUri, KUriEscChars) ); + if( err == KErrNone ) + { + // switch buffers to use the the escape-encoded version + delete iUri; + iUri = escUri; + User::LeaveIfError( uri.Parse( *iUri ) ); + } + else + { + // if encoding function leaves, perform cleanup for safety + // and attempt to continue with request + delete escUri; + } + iTrans = iSess->Sess().OpenTransactionL( uri, *this, StringF( iMethod ) ); + + // Set request headers. + RHTTPHeaders hdr = iTrans.Request().GetHeaderCollection(); + + RStringPool strPool = iSess->Sess().StringPool(); + SetRequestHeaderL(strPool, hdr); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::CreateTransactionL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::SubmitTransactionL() +// --------------------------------------------------------- +// +void CHttpLoader::SubmitTransactionL() + { +#ifdef __TEST_COD_LOG + CLOG(( EHttpLoad, 0, _L("-> CHttpLoader::SubmitTransactionL") )); + RHTTPRequest req = iTrans.Request(); + TPtrC8 method( req.Method().DesC() ); + TPtrC8 uri( req.URI().UriDes() ); + CLOG(( EHttpLoad, 0, _L8(" method<%S> URI<%S>"), &method, &uri )); + LogHeaders( req.GetHeaderCollection() ); +#endif /* def __TEST_COD_LOG */ +#ifdef _DEBUG + HBufC* method16 = CodUtil::ConvertLC( iTrans.Request().Method().DesC() ); + User::InfoPrint( *method16 ); + CleanupStack::PopAndDestroy( method16 ); +#endif + + if( !IsConnectionActive() ) + { + User::Leave( KErrCodHttpCommsFailed ); + } + CLOG(( EHttpLoad, 0, _L(" :calling RHttpTransaction.SubmitL()") )); + iTrans.SubmitL(); + if( iMethod == HTTP::EPOST ) // ie, install-notify + { + if( !iINTimeout ) + { + iINTimeout = CTimeout::NewL + ( CActive::EPriorityStandard, TCallBack( INStaticTimeout, this ) ); + } + CLOG(( EHttpLoad, 1, _L(" :starting iINTimeout with timeout of %d microsecs"), KCodINTimeout )); + TTimeIntervalMicroSeconds32 inTimeout = KCodINTimeout; + iINTimeout->After( inTimeout ); + } + CLOG(( EHttpLoad, 0, _L("<- CHttpLoader::SubmitTransactionL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::CompleteTransaction() +// --------------------------------------------------------- +// +void CHttpLoader::CompleteTransaction( TInt aError ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::CompleteTransaction(%d)"), \ + aError )); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + iTrans.Close(); + iResult = aError; + SelfComplete( iResult ); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::CompleteTransaction") )); + } + +// --------------------------------------------------------- +// CHttpLoader::RestartTransaction() +// --------------------------------------------------------- +// +void CHttpLoader::RestartTransaction() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::RestartTransaction"))); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + + iState = EStart; + CompleteTransaction(KErrNone); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::CompleteTransaction") )); + } + + +// --------------------------------------------------------- +// CHttpLoader::AcceptRangesSupported() +// --------------------------------------------------------- +// +TBool CHttpLoader::AcceptRangesSupported() + { + RStringF range = iSess->Sess().StringPool().StringF(HTTP::EAcceptRanges, RHTTPSession::GetTable()); + THTTPHdrVal value; + + TInt index = FindHeaderField(iResponseHeaders, range.DesC()); + + if( index != KErrNotFound ) + { + if( !(*iResponseHeaders)[index]->FieldRawData()->Compare( KAcceptRangeHeader() ) ) + { + return ETrue; + } + } + + return EFalse; + } + +// --------------------------------------------------------- +// CHttpLoader::HandleResponseHeadersL() +// --------------------------------------------------------- +// +void CHttpLoader::HandleResponseHeadersL( RHTTPResponse aResponse ) + { + CLOG(( EHttpLoad, 0, _L("-> CHttpLoader::HandleResponseHeadersL") )); + TInt httpCode = aResponse.StatusCode(); +#ifdef __TEST_COD_LOG + CLOG(( EHttpLoad, 0, _L8(" HttpStatus(%d) <%S>"), \ + httpCode, &(aResponse.StatusText().DesC()) )); + LogHeaders( aResponse.GetHeaderCollection() ); +#endif /* def __TEST_COD_LOG */ + + if ( HTTPStatus::IsInformational( httpCode ) ) + { + // 1xx + // Informational messages. Do nothing. + } + else if ( httpCode == HTTPStatus::EOk || + httpCode == HTTPStatus::ENonAuthoritativeInfo || + httpCode == HTTPStatus::EPartialContent + ) + { + // 200 OK + // 203 Non-Authoritative Information + iResult = KErrNone; + if ( iMethod == HTTP::EGET ) + { + // Successful GET. Get a saver. + __ASSERT_DEBUG( iSaverFactory, CodPanic( ECodInternal ) ); + + if(!iSaver) + { + iSaver = iSaverFactory->CreateSaverL( GetContentTypeL( aResponse.GetHeaderCollection() ) ); + iSaver->SetSourceUriL( GetSourceUriL( iTrans ) ); + } + if(httpCode != HTTPStatus::EPartialContent) + { + if( !iProgress->CurrentValue() ) + { + iSaver->ResetL(); + iProgress->SetAmountL(1024); + } + } + CheckRealDRMContentTypeL(); + iCodEng->SetPausable(AcceptRangesSupported() && iCodEng->Pausable()); + + // If we know the size, check it is valid and preallocate buffer + // for content. + if ( aResponse.HasBody() ) + { + TInt dataSize = aResponse.Body()->OverallDataSize(); + if ( dataSize >= 0 ) + { + // Content size is known. Check if fits. + User::LeaveIfError( iSaver->CheckMaxSize( dataSize ) ); + } + } + iCodEng->SetResumePDAvailable(); + } + else + { + // Successful POST. + // Do nothing. + } + } + else if ( HTTPStatus::IsSuccessful( httpCode ) ) + { + // 2xx + // Success codes without an usable body. + // For GET, it is a failure; for POST it's OK + iResult = (iMethod == HTTP::EGET) ? KErrCodHttpNoResponse : KErrNone; + } + // 3xx codes handled by redirect filter. + else if ( httpCode == HTTPStatus::EUnauthorized || + httpCode == HTTPStatus::EProxyAuthenticationRequired ) + { + // 401 Unauthorized + // 407 Proxy authentication required + iResult = KErrCodHttpAuthFailed; + } + else if ( httpCode == HTTPStatus::ENotFound || + httpCode == HTTPStatus::EGone ) + { + // 404 Not found + // 410 Gone + iResult = KErrCodHttpBadUrl; + + // Cancel the download if bad url. + // Set pausable status to false. + iCodEng->SetPausable( EFalse ); + } + + else if ( httpCode == HTTPStatus::ERequestedRangeNotSatisfiable ) + { + //416 Requested Range Not Satisfiable. Download has to be cancelled. + iResult = KErrCodHttpRequestedRangeNotSatisfiable; + } + else if( httpCode == HTTPStatus::EPreconditionFailed ) + { + // Re init the download + if(iSaver) + { + iSaver->ResetL(); + iProgress->SetAmountL(0); + } + CompleteTransaction( KErrCodHttpPreconditionFailed ); + } + else if ( HTTPStatus::IsClientError( httpCode ) ) + { + // 4xx + iResult = KErrCodHttpUnavailable; + } + else if ( httpCode == HTTPStatus::EHTTPVersionNotSupported ) + { + // 505 HTTP Version Not Supported + // Retry with lower HTTP version if we can. + iResult = VersionRetryL() ? KErrNone : KErrCodHttpUnavailable; + } + else if ( HTTPStatus::IsServerError( httpCode ) ) + { + // 5xx + // HTTP/1.0 servers may return other 5xx error codes for HTTP/1.1 + // requests. So the same treatment is given for all 5xx errors + // (version retry) - it's worth a try. + iResult = VersionRetryL() ? KErrNone : KErrCodHttpServerError; + } + else + { + // Everything else. + iResult = KErrCodHttpBadResponse; + } + CLOG(( EHttpLoad, 0, _L("<- CHttpLoader::HandleResponseHeadersL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::VersionRetryL() +// --------------------------------------------------------- +// +TBool CHttpLoader::VersionRetryL() + { + CLOG(( EHttpLoad, 2, \ + _L8("-> CHttpLoader::VersionRetryL iHttpVersion <%S>"), \ + &(StringF( iHttpVersion ).DesC() ) )); + + // We should be in ERequest state, with the request outstanding. + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iStatus == KRequestPending, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( IsActive(), CodPanic( ECodInternal ) ); + + TBool retryDone( EFalse ); + if ( iHttpVersion == HTTP::EHttp11 ) + { + // Currently using HTTP/1.1. Cancel transaction and resubmit it using + // HTTP/1.0. + iTrans.Cancel(); + iSess->Sess().ConnectionInfo().SetPropertyL + ( + StringF( HTTP::EHTTPVersion ), + THTTPHdrVal( StringF( HTTP::EHttp10 ) ) + ); + iHttpVersion = HTTP::EHttp10; + SubmitTransactionL(); + retryDone = ETrue; + } + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::VersionRetryL return(0x%x)"), \ + retryDone )); + return retryDone; + } + +// --------------------------------------------------------- +// CHttpLoader::UseProxyL() +// --------------------------------------------------------- +// +TBool CHttpLoader::UseProxyL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::UseProxyL iProxySet(0x%x)"), \ + iProxySet )); + + // We should be in ERequest state, with the request outstanding. + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iStatus == KRequestPending, CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( IsActive(), CodPanic( ECodInternal ) ); + __ASSERT_DEBUG( iTrans.Response().StatusCode() == HTTPStatus::EUseProxy, \ + CodPanic( ECodInvalidArguments ) ); + + // First check if we already have a proxy address set. If yes, do not set + // another. + if( !iProxySet ) + { + TBool proxySet( EFalse ); // Set by somebody else (?). + RStringF proxyUsage = StringF( HTTP::EProxyUsage ); + RStringF useProxy = StringF( HTTP::EUseProxy ); + RHTTPConnectionInfo connInfo = iSess->Sess().ConnectionInfo(); + THTTPHdrVal val; + if( connInfo.Property( proxyUsage, val ) ) + { + if( val.Type() != THTTPHdrVal::KStrFVal ) + { + User::Leave( KErrCodHttpBadResponse ); + } + if( val.StrF() == useProxy ) + { + proxySet = ETrue; + CLOG(( EHttpLoad, 2, _L(" proxy already set") )); + } + } + if( !proxySet ) + { + // No proxy is currently set. Get the proxy address to set, from + // the Location header field. + RHTTPHeaders headers = iTrans.Response().GetHeaderCollection(); + if( !headers.GetField( StringF( HTTP::ELocation ), 0, val ) ) + { + if( val.Type() != THTTPHdrVal::KStrFVal ) + { + User::Leave( KErrCodHttpBadResponse ); + } + // Cancel the transaction and resubmit after the proxy was set. + CLOG(( EHttpLoad, 2, _L8(" setting proxy <%S>"), \ + &(val.StrF().DesC()) )); + iTrans.Cancel(); + connInfo.SetPropertyL( StringF( HTTP::EProxyAddress ), val ); + connInfo.SetPropertyL( proxyUsage, THTTPHdrVal( useProxy ) ); + SubmitTransactionL(); + iProxySet = ETrue; + } + } + } + CLOG(( EHttpLoad, 2, \ + _L("<- CHttpLoader::UseProxyL return iProxySet(0x%x)"), iProxySet )); + return iProxySet; + } + +// --------------------------------------------------------- +// CHttpLoader::SetupSessionL() +// --------------------------------------------------------- +// +void CHttpLoader::SetupSessionL() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::SetupSessionL") )); + // Set our Socket Server handle and Connection as session properties. + iSess->Sess().ConnectionInfo().SetPropertyL + ( + StringF( HTTP::EHttpSocketServ ), + THTTPHdrVal( iConn.SockServ().Handle() ) + ); + iSess->Sess().ConnectionInfo().SetPropertyL + ( + StringF( HTTP::EHttpSocketConnection ), + THTTPHdrVal( REINTERPRET_CAST( TInt, &iConn.Conn() ) ) + ); + + // Set the disconnect notification + iSess->Sess().ConnectionInfo().SetPropertyL + ( + iSess->Sess().StringPool().StringF( HTTP::ENotifyOnDisconnect, RHTTPSession::GetTable() ), + iSess->Sess().StringPool().StringF( HTTP::EEnableDisconnectNotification, RHTTPSession::GetTable() ) + ); + + CLOG(( EHttpLoad, 4, _L(" Install UAProf filter...") )); + // Install UAProf filter. + CHttpUAProfFilterInterface::InstallFilterL( iSess->Sess() ); + + CLOG(( EHttpLoad, 4, _L(" Install Cookie filter...") )); + // Install cookie filter. + CHttpCookieFilter::InstallFilterL( iSess->Sess() ); + + CLOG(( EHttpLoad, 4, _L(" Install Auth filter...") )); + // Install authentication filter. + InstallAuthenticationL( iSess->Sess() ); + + CLOG(( EHttpLoad, 4, _L(" Install Deflate filter...") )); + // Install deflate Filter. + CHttpDeflateFilter::InstallFilterL( iSess->Sess() ); + + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::SetupSessionL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::StartTimeout() +// --------------------------------------------------------- +// +void CHttpLoader::StartTimeout() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::StartTimeout") )); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + __ASSERT_DEBUG( iTimeout, CodPanic( ECodInternal ) ); + TTimeIntervalMicroSeconds32 timeout = + iMethod == HTTP::EGET ? KCodGetTimeout : KCodPostTimeout; + iTimeout->Cancel(); // Cancel pending (if any). + iTimeout->After( timeout ); // Start over. + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::StartTimeout (%d microsecs)"), \ + timeout.Int() )); + } + +// --------------------------------------------------------- +// CHttpLoader::Timeout() +// --------------------------------------------------------- +// +void CHttpLoader::Timeout() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::Timeout iRetry(%d)"), iRetry )); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + if ( --iRetry > 0 ) + { + CompleteTransaction( KErrNone ); // Close (abandon) transaction. + iState = EOpen; // To RequestL() by self-complete. + } + else + { + if (iCodLoadObserver) + { + iCodLoadObserver->SetConnError( KErrTimedOut); + } + CompleteTransaction( KErrTimedOut ); + } + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::Timeout") )); + } + +// --------------------------------------------------------- +// CHttpLoader::INTimeout() +// --------------------------------------------------------- +// +void CHttpLoader::INTimeout() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::INTimeout") )); + __ASSERT_DEBUG( iState == ERequest, CodPanic( ECodOffState ) ); + iSuppressErrors = ETrue; // release content to user + if( !IsConnectionActive() ) + { + iSuppressErrors = EFalse; // do not release content + iResult = KErrCodHttpCommsFailed; // set error code + } + CLOG(( EHttpLoad, 2, _L(" :iSuppressErrors = %d"), iSuppressErrors )); + CLOG(( EHttpLoad, 2, _L(" :iResult = %d"), iResult )); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::INTimeout") )); + } + +// --------------------------------------------------------- +// CHttpLoader::IncProgressL() +// --------------------------------------------------------- +// +void CHttpLoader::IncProgressL( TInt aSize ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::IncProgressL(%d)"), aSize )); + if ( iProgress ) + { + iProgress->IncrementL( aSize ); + } + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::IncProgressL") )); + } + +// --------------------------------------------------------- +// CHttpLoader::SetHeaderL() +// --------------------------------------------------------- +// +void CHttpLoader::SetHeaderL +( RHTTPHeaders aHeaders, HTTP::TStrings aHdrField, const TDesC8& aHdrValue ) + { + RStringF valStr = iSess->Sess().StringPool().OpenFStringL( aHdrValue ); + CleanupClosePushL( valStr ); + SetHeaderL( aHeaders, aHdrField, valStr ); + CleanupStack::PopAndDestroy(); // close valStr + } + +// --------------------------------------------------------- +// CHttpLoader::SetHeaderL() +// --------------------------------------------------------- +// +void CHttpLoader::SetHeaderL +( RHTTPHeaders aHeaders, HTTP::TStrings aHdrField, HTTP::TStrings aHdrValue ) + { + SetHeaderL( aHeaders, aHdrField, StringF( aHdrValue ) ); + } + +// --------------------------------------------------------- +// CHttpLoader::SetHeaderL() +// --------------------------------------------------------- +// +void CHttpLoader::SetHeaderL +( RHTTPHeaders aHeaders, HTTP::TStrings aHdrField, const RStringF aHdrValue ) + { + CLOG(( EHttpLoad, 2, _L8("CHttpLoader::SetHeaderL <%S> <%S>"), \ + &StringF( aHdrField ).DesC(), &aHdrValue.DesC() )); + THTTPHdrVal val( aHdrValue ); + aHeaders.SetFieldL( StringF( aHdrField ), val ); + } + +// --------------------------------------------------------- +// CHttpLoader::GetContentTypeL() +// --------------------------------------------------------- +// +const TDesC8& CHttpLoader::GetContentTypeL( RHTTPHeaders aHeaders ) + { + THTTPHdrVal hdrVal; + User::LeaveIfError + ( aHeaders.GetField( StringF( HTTP::EContentType ), 0, hdrVal ) ); + if( hdrVal.Type() != THTTPHdrVal::KStrFVal ) + { + User::Leave( KErrCodHttpBadResponse ); + } + return hdrVal.StrF().DesC(); + } + +// --------------------------------------------------------- +// CHttpLoader::GetContentTypeL() +// --------------------------------------------------------- +// +const TDesC8& CHttpLoader::GetContentTypeL() + { + _LIT8(KContentType, "Content-Type"); + + THTTPHdrVal value; + + TInt index = FindHeaderField(iResponseHeaders, KContentType); + + if( index != KErrNotFound ) + { + HBufC8 *ptr = (*iResponseHeaders)[index]->FieldRawData(); + return *ptr; + } + + return KNullDesC8; + } + +// --------------------------------------------------------- +// CHttpLoader::GetSourceUriL() +// --------------------------------------------------------- +// +const TDesC8& CHttpLoader::GetSourceUriL( RHTTPTransaction aTransaction ) + { + // TODO if redirected URI exists, how do we get it? + // For the moment request URI is used. + return aTransaction.Request().URI().UriDes(); + } + +// --------------------------------------------------------- +// CHttpLoader::StaticTimeout() +// --------------------------------------------------------- +// +TInt CHttpLoader::StaticTimeout( TAny* aPtr ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::StaticTimeout") )); + STATIC_CAST( CHttpLoader*, aPtr )->Timeout(); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::StaticTimeout") )); + return EFalse; + } + +// --------------------------------------------------------- +// CHttpLoader::INStaticTimeout() +// --------------------------------------------------------- +// +TInt CHttpLoader::INStaticTimeout( TAny* aPtr ) + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::INStaticTimeout") )); + STATIC_CAST( CHttpLoader*, aPtr )->INTimeout(); + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::INStaticTimeout") )); + return EFalse; + } + + +// --------------------------------------------------------- +// CHttpLoader::IsConnectionActive() +// --------------------------------------------------------- +// +TBool CHttpLoader::IsConnectionActive() + { + CLOG(( EHttpLoad, 2, _L("-> CHttpLoader::IsConnectionActive") )); + + THTTPHdrVal val; + TBool active( ETrue ); + TBool hasProp = iSess->Sess().ConnectionInfo().Property + ( StringF( HTTP::EHttpSocketConnection ), val ); + if( hasProp && val.Type() == THTTPHdrVal::KTIntVal) + { + active = ( val.Int() != NULL ); + CLOG(( EHttpLoad, 0, _L(" :RConnection found = %d"), active )); + } + + if( active ) // confirm with second level check + { + TUint32 iap; + active = iConn.IsConnected( iap ); + CLOG(( EHttpLoad, 3, _L(" :CConnection.IsConnection() = %d"), active )); + } + CLOG(( EHttpLoad, 2, _L("<- CHttpLoader::IsConnectionActive") )); + return active; + } + +// ----------------------------------------------------------------------------- +// CHttpDownload::StoreResponseHeaderL +// ?implementation_description +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CHttpLoader::StoreResponseHeaderL() + { + + TPtrC8 rawData; + RStringPool strPool = iSess->Sess().StringPool(); + + RHTTPHeaders headers( iTrans.Response().GetHeaderCollection() ); + THTTPHdrFieldIter it = headers.Fields(); + + // forget the previous headers + iResponseHeaders->ResetAndDestroy(); + + while ( !it.AtEnd() ) + { + RStringTokenF fieldName = it(); + RStringF fieldNameStr = strPool.StringF (fieldName ); + + headers.GetRawField( fieldNameStr, rawData ); + + CHeaderField* field = CHeaderField::NewL( &fieldNameStr.DesC(), &rawData ); + CleanupStack::PushL( field ); + + iResponseHeaders->AppendL( field ); + + CleanupStack::Pop( field ); + + ++it; + } + + //ParseContentTypeL( strPool ); + + RStringF length = strPool.StringF(HTTP::EContentLength,RHTTPSession::GetTable()); + RStringF date = strPool.StringF(HTTP::EDate,RHTTPSession::GetTable()); + RStringF expires = strPool.StringF(HTTP::EExpires,RHTTPSession::GetTable()); + RStringF maxAge = strPool.StringF(HTTP::EMaxAge,RHTTPSession::GetTable()); + RStringF cacheControl = strPool.StringF(HTTP::ECacheControl,RHTTPSession::GetTable()); + RStringF acceptRanges = strPool.StringF(HTTP::EAcceptRanges,RHTTPSession::GetTable()); + + THTTPHdrVal value; + /* + if( !headers.GetField( length, 0, value ) ) + { + if( iStorage->Length() == KDefaultContentLength ) + // content size is + { + iStorage->SetLength( value ); + } + } + + CheckRealDRMContentTypeL(); + if( !iDrmContentLengthValid ) + // Content was original encoded -> we don't know the actual content size. + { + iStorage->SetLength( KDefaultContentLength ); + }*/ + + CheckRealDRMContentTypeL(); + + iMaxAge = 0; + TInt parts( 0 ); + // this leave is trapped because we can still go on + TRAPD( err, parts = headers.FieldPartsL( cacheControl ) ); + + if( !err ) + // try to find max-age in Cache-control field + { + for( TInt i = 0; i < parts; ++i ) + { + RStringF directive; + THTTPHdrVal hdrVal; + TInt err; + + // Get the cache-control from the headers + // initialise the fieldname + headers.GetField( cacheControl, i, hdrVal ); + + if((hdrVal.Type() == THTTPHdrVal::KStrVal) || (hdrVal.Type() == THTTPHdrVal::KStrFVal)) + { + RStringF cacheDir = hdrVal.StrF(); + + TInt endPos; + _LIT8(KFind, "="); + + endPos = cacheDir.DesC().Find( KFind ); + if( endPos != -1 ) + { + TRAP(err, directive = strPool.OpenFStringL(cacheDir.DesC().Left(endPos))); + if( !err ) + { + if( directive == maxAge ) + { + TInt valueInt( 0 ); + TLex8 value( cacheDir.DesC().Right(cacheDir.DesC().Length() - endPos - 1) ); + + value.Val( valueInt ); + iMaxAge = valueInt; + } + } + } + } + } + } + + if( !headers.GetField( expires, 0, value ) ) + { + iExpires = value; + } + else + { + iExpires.SetYear( 0 ); + } + + if( !headers.GetField( date, 0, value ) ) + { + iDate = value; + } + else + { + iDate.SetYear( 0 ); + } + } + + + +void CHttpLoader::LoadHeadersL( RFile& aFile) + { + TInt headers; + READ_INT_L( aFile, headers ); + + iResponseHeaders->ResetAndDestroy(); + + for( TInt i = 0; i < headers; ++i ) + { + CHeaderField* field = CHeaderField::NewL(); + CleanupStack::PushL( field ); + + field->LoadHeaderInfoL( aFile ); + iResponseHeaders->AppendL( field ); + + CleanupStack::Pop( field ); + } + } + +void CHttpLoader::SetRangeFieldL( RStringPool& aStringPool, + RHTTPHeaders& aHeaders ) + { + + if(!iSaver) + { + return; + } + + TInt size (iSaver->DownloadedFileSize()); + + if( size <= 0 ) + { + // no bytes have been downloaded yet + return; + } + + RStringF range = aStringPool.StringF(HTTP::ERange, RHTTPSession::GetTable()); + + aHeaders.RemoveField( range ); + + TBuf8<48> rawData; + + rawData.Format( _L8("bytes=%d-"), size); + + aHeaders.SetRawFieldL( range, rawData, KHttpFieldSeparator ); + } + +// ----------------------------------------------------------------------------- +// CHttpLoader::FindHeaderField +// ?implementation_description +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CHttpLoader::FindHeaderField( CArrayPtrFlat* aHeaders, + const TDesC8& aFieldName ) const + { + for( TInt index = 0; index < aHeaders->Count(); ++index ) + { + if( *(*aHeaders)[index]->FieldName() == aFieldName ) + { + return index; + } + } + + return KErrNotFound; + } + +void CHttpLoader::CheckRealDRMContentTypeL() + { + iPausableDRM = ETrue; + iDrmContentLengthValid = ETrue; + + TInt index = FindHeaderField( iResponseHeaders, KDRMOldContentType ); + if( index != KErrNotFound ) + // this is an old DRM protected content + // This transaction cannot be paused. + { + if( !(*iResponseHeaders)[index]->FieldRawData()->Compare( KDrmMessageMimeType() ) ) + { + iPausableDRM = EFalse; + } + } + + UpdatePausableL(); + } + +// ----------------------------------------------------------------------------- +// CHttpLoader::UpdatePausableL +// ?implementation_description +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CHttpLoader::UpdatePausableL() + { + TBool pausable( ETrue ); + + if( !iPausableDRM ) + { + pausable = EFalse; + } + + if( iMethod == EMethodPOST ) + { + pausable = EFalse; + } + + if( pausable != iCodEng->Pausable() ) + { + iCodEng->SetPausable( pausable ); + + //TODO : Shud the change in pause behavior shud be notified. ?? + // inform client about change + //TriggerEvent( iPausable ? EHttpDlPausable : EHttpDlNonPausable ); + + //TRAP_IGNORE(StoreDownloadInfoL() ); + iCodEng->StoreSubInfoFileL(iResponseHeaders, iCodEng->Data().ActiveDownload() ); + } + } + +// --------------------------------------------------------- +// CHttpLoader::LogHeaders() +// --------------------------------------------------------- +// +void CHttpLoader::LogHeaders( RHTTPHeaders LOG_ONLY( aHeaders ) ) + { +#ifdef __TEST_COD_LOG + _LIT(KDateFormat,"%D%M%Y%/0%1%/1%2%/2%3%/3 %:0%H%:1%T%:2%S.%C%:3"); + + CLOG(( EHttpLoad, 1, _L("Headers:") )); + + TInt i; + TInt fieldParts; + RStringPool strP = iTrans.Session().StringPool(); + THTTPHdrFieldIter it = aHeaders.Fields(); + + while ( !it.AtEnd() ) + { + RStringTokenF fieldName = it(); + RStringF fieldNameStr = strP.StringF (fieldName ); + THTTPHdrVal fieldVal; + fieldParts = 0; // For the case if next the call fails. + TRAP_IGNORE( fieldParts = aHeaders.FieldPartsL( fieldNameStr ) ); + for ( i = 0; i < fieldParts; i++ ) + { + if ( aHeaders.GetField( fieldNameStr, i, fieldVal ) == KErrNone ) + { + const TDesC8& fieldNameDesC = fieldNameStr.DesC(); + switch ( fieldVal.Type() ) + { + case THTTPHdrVal::KTIntVal: + { + CLOG(( EHttpLoad, 1, _L8(" <%S> (%d)"), \ + &fieldNameDesC, fieldVal.Int() )); + break; + } + + case THTTPHdrVal::KStrFVal: + { + RStringF fieldValStr = strP.StringF( fieldVal.StrF() ); + const TDesC8& fieldValDesC = fieldValStr.DesC(); + CLOG(( EHttpLoad, 1, _L8(" <%S> <%S>"), \ + &fieldNameDesC, &fieldValDesC )); + } + break; + + case THTTPHdrVal::KStrVal: + { + RString fieldValStr = strP.String( fieldVal.Str() ); + const TDesC8& fieldValDesC = fieldValStr.DesC(); + CLOG(( EHttpLoad, 1, _L8(" <%S> <%S>"), \ + &fieldNameDesC, &fieldValDesC )); + } + break; + + case THTTPHdrVal::KDateVal: + { + TDateTime date = fieldVal.DateTime(); + TBuf<40> dateTimeString; + TTime t( date ); + TRAP_IGNORE\ + ( t.FormatL( dateTimeString, KDateFormat ) ); + TBuf8<40> dateTimeString8; + dateTimeString8.Copy( dateTimeString ); + CLOG(( EHttpLoad, 1, _L8(" <%S> <%S>"), \ + &fieldNameDesC, &dateTimeString8 )); + } + break; + + default: + CLOG(( EHttpLoad, 1, \ + _L8(" <%S> unrecognised value type(%d)"), \ + &fieldNameDesC, fieldVal.Type() )); + break; + } + + // Display realm for WWW-Authenticate header. + RStringF wwwAuth = strP.StringF + ( HTTP::EWWWAuthenticate, RHTTPSession::GetTable() ); + if ( fieldNameStr == wwwAuth ) + { + // check the auth scheme is 'basic' + RStringF basic = strP.StringF + ( HTTP::EBasic, RHTTPSession::GetTable() ); + RStringF realm = strP.StringF + ( HTTP::ERealm, RHTTPSession::GetTable() ); + THTTPHdrVal realmVal; + if ( ( fieldVal.StrF() == basic ) && + ( !aHeaders.GetParam( wwwAuth, realm, realmVal ) ) ) + { + RString realmValStr = strP.String( realmVal.Str() ); + const TDesC8& realmValDesC = realmValStr.DesC(); + CLOG(( EHttpLoad, 1, _L8(" Realm<%S>"), \ + &realmValDesC )); + } + } + } + } + ++it; + } +#endif /* def __TEST_COD_LOG */ + } + + +// ----------------------------------------------------------------------------- +// CHttpLoader::RedirectedPermanentlyL +// ----------------------------------------------------------------------------- +// +void CHttpLoader::RedirectedPermanentlyL( const TDesC8& aNewUrl ) + { + CLOG(( EHttpLoad, 0, _L("CHttpLoader::RedirectedPermanentlyL => NewUrl(%S)"), \ + &aNewUrl )); + + // Use the redirected Url + ReallocateStringL( iUri, aNewUrl, KMaxUrlLength ); + + // There has already been a temporary redirection + // This permanent is not used on next submitted request + if (!iRedirect) + { + HBufC* newUrl = CodUtil::ConvertLC( aNewUrl ); + iCodEng->SetUrlL( *newUrl ); + + CleanupStack::PopAndDestroy( newUrl ); + } + } + +// ----------------------------------------------------------------------------- +// CHttpLoader::RedirectedTemporaryL +// ----------------------------------------------------------------------------- +// +void CHttpLoader::RedirectedTemporaryL( const TDesC8& aNewUrl ) + { + CLOG(( EHttpLoad, 0, _L("CHttpLoader::RedirectedTemporaryL => NewUrl(%S)"), \ + &aNewUrl )); + + // Make it TRUE so that any permanent redirections after + // this are not saved + iRedirect = ETrue; + ReallocateStringL( iUri, aNewUrl, KMaxUrlLength ); + } + +// ----------------------------------------------------------------------------- +// CHttpLoader::ResponseHeaders +// ----------------------------------------------------------------------------- +// +CArrayPtrFlat* CHttpLoader::ResponseHeaders() + { + return iResponseHeaders; + }