webengine/osswebengine/cache/src/HttpCacheHandler.cpp
changeset 0 dd21522fd290
child 8 7c90e6132015
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/cache/src/HttpCacheHandler.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,1287 @@
+/*
+* Copyright (c) 2006 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:  Implementation of CHttpCacheHandler
+*
+*/
+
+// INCLUDE FILES
+#include "HttpCacheHandler.h"
+#include "HttpCacheManager.h"
+#include "HttpCacheEntry.h"
+#include "HttpCacheLookupTable.h"
+#include "HttpCacheStreamHandler.h"
+#include "HttpCacheUtil.h"
+#include "HttpCacheEvictionHandler.h"
+#include "HttpCacheObserver.h"
+#include <http/RHTTPTransaction.h>
+#include <http/rhttpsession.h>
+#include <http/rhttpheaders.h>
+#include <HttpStringConstants.h>
+#include <httperr.h>
+#include <s32file.h>
+#include <BrCtlDefs.h>
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+void PanicCacheHandler(
+    TInt aError)
+    {
+    _LIT(KCachePanic, "cacheHandler Panic");
+
+    User::Panic( KCachePanic, aError );
+    }
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::CHttpCacheHandler
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CHttpCacheHandler::CHttpCacheHandler(
+    TInt aSize ) : iSize( aSize )
+    {
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::ConstructL(
+    const TDesC& aDirectory,
+    const TDesC& aIndexFile,
+    TInt aCriticalLevel)
+    {
+	User::LeaveIfError(iRfs.Connect());
+    //
+    iIndexFile = aIndexFile.AllocL();
+    //
+    iDirectory = aDirectory.AllocL();
+    //
+    iEvictionHandler = CHttpCacheEvictionHandler::NewL();
+    //
+    iStreamHandler = CHttpCacheStreamHandler::NewL( iDirectory->Des(), aCriticalLevel );
+    //
+    iLookupTable = CHttpCacheLookupTable::NewL( *iEvictionHandler, *iStreamHandler );
+    //1. Set up top-level cache directory if it doesn't exist. 
+    TEntry entry;
+    TInt err( KErrNone );
+    if (iRfs.Entry(iDirectory->Des(), entry) != KErrNone)
+        {
+        err = iRfs.MkDirAll(iDirectory->Des());
+        }
+
+    //2. Create subdirectories to store header/body files
+    if ( err == KErrNone || err == KErrAlreadyExists ) { //harmless errors
+    
+        __ASSERT_DEBUG ( (iDirectory->Des().LocateReverse( KPathDelimiter ) == (iDirectory->Des().Length() - 1)),
+                PanicCacheHandler( KErrCorrupt ) ); // We assume that iDirectory is terminated by a forward slash
+        
+        HBufC* subDir = HBufC::NewL(KMaxPath); // Base cache dir + subdir name + another delimiter
+        _LIT(KFormat,"%S%x%c");
+        for (TUint i = 0; i < KCacheSubdirCount; i++)
+            {
+            TPtrC ptr (iDirectory->Des());
+            subDir->Des().Format(KFormat, &ptr, i, KPathDelimiter); 
+            TInt err2 = iRfs.MkDir(subDir->Des()); 
+            __ASSERT_DEBUG ( (err2 == KErrNone || err2 == KErrAlreadyExists), PanicCacheHandler( err2 ) ); 
+            }
+        delete subDir;
+        //end cache dir + subdir creation
+    } else {
+        User::Leave(err);
+    }
+
+    OpenLookupTableL();
+    //
+    iHttpCacheObserver = CHttpCacheObserver::NewL(iDirectory, iIndexFile, this);
+    iHttpCacheObserver->StartObserver();
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CHttpCacheHandler* CHttpCacheHandler::NewL(
+    TInt aSize,
+    const TDesC& aDirectory,
+    const TDesC& aIndexFile,
+    TInt aCriticalLevel)
+    {
+    CHttpCacheHandler* self = new( ELeave ) CHttpCacheHandler( aSize );
+
+    CleanupStack::PushL( self );
+    self->ConstructL( aDirectory, aIndexFile, aCriticalLevel );
+    CleanupStack::Pop();
+
+    return self;
+    }
+
+// Destructor
+CHttpCacheHandler::~CHttpCacheHandler()
+    {
+   
+        TRAP_IGNORE( SaveLookupTableL() );
+   
+    //
+    delete iHttpCacheObserver;
+    //
+    if (iEvictionHandler)
+        {
+        iEvictionHandler->RemoveAll();
+        }
+
+    //
+    delete iLookupTable;
+    //
+    delete iEvictionHandler;
+    //
+    delete iStreamHandler;
+    //
+    delete iDirectory;
+    //
+    delete iIndexFile;
+    //
+    iRfs.Close();
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::RequestL
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::RequestL(
+    RHTTPTransaction& aTrans,
+    TBrCtlDefs::TBrCtlCacheMode aCacheMode,
+    THttpCacheEntry& aCacheEntry )
+    {
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "request item" ), aTrans.Request().URI().UriDes() );
+    //
+    TInt status( KErrNotFound );
+    CHttpCacheEntry* entry = NULL;
+    // 0. check if we need to check cache at all (protected vs no cache mode)
+    // 1. check if the url is in the cache
+    // 2. check if it is complete
+    // 3. see if it is useable
+
+    // use protected item on reload???
+    // currently not
+    // and do not use cache for post
+    if( aCacheMode != TBrCtlDefs::ECacheModeNoCache &&
+        HttpCacheUtil::MethodFromStr( aTrans.Request().Method(), aTrans.Session().StringPool() ) != EMethodPost )
+        {
+        // If the cacheMode is noCache then it must ignore the cached entry.
+        entry = iLookupTable->Find( aTrans.Request().URI().UriDes() );
+        // check if the trailing slash is missing
+        if( !entry )
+            {
+            TUriC8 uri = aTrans.Request().URI();
+
+            if( uri.Extract( EUriPath ).Length() == 0 )
+                {
+                CUri8* fixeduri = CUri8::NewLC( uri );
+                fixeduri->SetComponentL( _L8("/"), EUriPath );
+                //
+                entry = iLookupTable->Find( fixeduri->Uri().UriDes() );
+                //
+                CleanupStack::PopAndDestroy(); // fixeduri
+                }
+            }
+        //
+        if( entry && entry->State() == CHttpCacheEntry::ECacheComplete )
+            {
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "item is in the cache" ) );
+#endif            
+            //
+            status = CacheNeedsValidationL( *entry, aTrans, aCacheMode ) ? KErrNotReady : KErrNone;
+            // entry could be invalidated at this point. check for status to make sure
+            // the entry is still valid
+            }
+        // prepare stream for request
+        if( status == KErrNone )
+            {
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "prepare item for sending" ) );
+#endif
+            // attach entry to the stream
+            if( iStreamHandler->AttachL( *entry ) )
+                {
+                entry->SetState( CHttpCacheEntry::ECacheRequesting );
+                entry->Accessed();
+                }
+            else
+                {
+                // cleanup on the corrupt entry
+                HandleCorruptEntry( *entry );
+                entry = NULL;
+                // item is not in cache
+                status = KErrNotFound;
+                }
+            }
+        // cleanup
+        if( status == KErrNone && entry )
+            {
+            // save handler and entry so that
+            // on next call we don't have to start a lookup again
+            aCacheEntry.iCacheHandler = this;
+            aCacheEntry.iCacheEntry = entry;
+            }
+        else
+            {
+            // cleanup response headers
+            //
+            // response is not in the cache, so remove all the response header fields
+            // set by cache handler
+            RHTTPHeaders responseHeaders = aTrans.Response().GetHeaderCollection();
+            responseHeaders.RemoveAllFields();
+            }
+        }
+#ifdef __CACHELOG__
+    else
+        {
+        HttpCacheUtil::WriteLog( 0, _L( "reload: do not use cache" ) );
+        }
+    if( status != KErrNone && entry )
+        {
+        //
+        HttpCacheUtil::WriteLog( 0, _L( "item needs validation" ) );
+        }
+    else
+        {
+        //
+        HttpCacheUtil::WriteLog( 0, _L( "item is not in the cache" ) );
+        }
+#endif // __CACHELOG__
+
+    return status;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::RequestHeadersL
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::RequestHeadersL(
+    RHTTPTransaction& aTrans,
+    THttpCacheEntry& aCacheEntry )
+    {
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "request http headers" ), aTrans.Request().URI().UriDes() );
+    //
+    TInt status( KErrNotFound );
+    CHttpCacheEntry* entry = aCacheEntry.iCacheEntry;
+    //
+    if( entry && entry->State() == CHttpCacheEntry::ECacheRequesting )
+        {
+        // response headers should already have all the headers
+        // as RequestL call adds them all.
+        // no need to do much here
+        // get header from file
+        status = KErrNone;
+#ifdef __CACHELOG__
+        HttpCacheUtil::WriteLog( 0, _L( "sending http headers" ) );
+#endif        
+        }
+    return status;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::RequestNextChunkL
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* CHttpCacheHandler::RequestNextChunkL(
+    RHTTPTransaction& aTrans,
+    TBool& aLastChunk,
+    THttpCacheEntry& aCacheEntry )
+    {
+    (void)aTrans; //suppress compiler and PC-lint warnings
+    //
+    HBufC8* bodyStr = NULL;
+    CHttpCacheEntry* entry = aCacheEntry.iCacheEntry;
+    //
+    if( entry && entry->State() == CHttpCacheEntry::ECacheRequesting )
+        {
+        // get next chunk
+        bodyStr = iStreamHandler->NextChunkL( *entry, aLastChunk );
+        }
+    return bodyStr;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::RequestClosed
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::RequestClosed(
+    RHTTPTransaction* aTrans,
+    THttpCacheEntry& aCacheEntry )
+    {
+    if (aTrans)
+        HttpCacheUtil::WriteUrlToLog( 0, _L( "Request is closed" ), aTrans->Request().URI().UriDes() );
+    // fine
+    // make sure transaction is moved to the complete list
+    CHttpCacheEntry* entry = aCacheEntry.iCacheEntry;
+
+    if( entry )
+        {
+        // normal close on a request - when the content is loaded from the cache
+        if( entry->State() == CHttpCacheEntry::ECacheRequesting )
+            {
+            entry->SetState( CHttpCacheEntry::ECacheComplete );
+            iStreamHandler->Detach( *entry );
+            }
+        // transaction is closed without being completed
+        else if( entry->State() == CHttpCacheEntry::ECacheResponding ||
+            entry->State() == CHttpCacheEntry::ECacheDestroyed )
+            {
+            // remove uncompleted/destroyed entry
+            iStreamHandler->Detach( *entry );
+            HandleCorruptEntry( *entry );
+            entry = NULL;
+            aCacheEntry.iCacheEntry = NULL;
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "uncompleted entry" ) );
+#endif
+            }
+        else if( entry->State() == CHttpCacheEntry::ECacheComplete )
+            {
+            // normal close on the request - when the contet is saved to the cache
+            // ResponseComplete has already been called
+            // check if the stream is released
+            __ASSERT_DEBUG( !iStreamHandler->Find( *entry ) , PanicCacheHandler( KErrCorrupt ) );
+            }
+        else
+            {
+            __ASSERT_DEBUG( EFalse , PanicCacheHandler( KErrCorrupt ) );
+            }
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::AdjustResponseTime
+//
+// -----------------------------------------------------------------------------
+//
+
+void CHttpCacheHandler::AdjustResponseTime(RHTTPTransaction& aTrans)
+    {
+    RStringF fieldName;
+    THTTPHdrVal dateValue;
+    TDateTime date;
+    TTime now;
+    TInt err;
+    TTime serverResponseTime;
+    RHTTPHeaders respHeaders = aTrans.Response().GetHeaderCollection();
+    RStringPool strP = aTrans.Session().StringPool();
+
+    // Get the current time. All internet dates are GMT
+
+    now.UniversalTime();
+
+    // Get the date from the headers. Compare this with the response
+    // GMT time. If the server time is wrong then adjust the time here.
+
+    fieldName = strP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
+    err = respHeaders.GetField( fieldName, 0, dateValue );
+    if( err == KErrNotFound || dateValue.Type() != THTTPHdrVal::KDateVal )
+        {
+        serverResponseTime = 0;
+        }
+    else
+        {
+        serverResponseTime = TTime( dateValue.DateTime() );
+        }
+
+
+    // Implies some thing wrong with with origin server time.
+
+    if ( now < serverResponseTime )
+        {
+#ifdef __CACHELOG__
+        HttpCacheUtil::WriteLog( 0, _L( "Response time is less than GMT time" ) );
+#endif
+        // Remove the field first. It is must otherwise raw field
+        // data is not modified. The cache data is written from the
+        // raw field data.
+        if( respHeaders.RemoveField(fieldName) == KErrNone )
+            {
+            dateValue.SetDateTime( now.DateTime() );
+            TRAP_IGNORE( respHeaders.SetFieldL( fieldName, dateValue ) );
+            }
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::ReceivedResponseHeadersL
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::ReceivedResponseHeadersL(
+    RHTTPTransaction& aTrans,
+    THttpCacheEntry& aCacheEntry )
+    {
+#ifdef __CACHELOG__
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "received http headers" ), aTrans.Request().URI().UriDes() );
+#endif
+    //
+    TBool protectedEntry( EFalse );
+    // check if the item is cacheable
+    // no item should be bigger than the 1/3 of the cache size
+    if( HttpCacheUtil::IsCacheable( aTrans, ( iSize / 3 ), protectedEntry ) )
+        {
+        // check if the entry is already in the cache
+        CHttpCacheEntry* entry = iLookupTable->Find( aTrans.Request().URI().UriDes() );
+        if( entry )
+            {
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteUrlToLog( 0, _L( "item is already in the cache" ), entry->Url() );
+#endif            
+            //
+            if( entry->State() != CHttpCacheEntry::ECacheComplete )
+                {
+                // multiple incoming entries? doh.
+#ifdef __CACHELOG__
+                HttpCacheUtil::WriteLog( 0, _L( "MULTIPLE REQUEST!!!!!!!!!!!!!!!!!!!!!!!!!" ) );
+#endif                
+                // __ASSERT_DEBUG( EFalse, PanicCacheHandler( KErrCorrupt ) );
+                // ignore this one and the first will proceed.
+                entry = NULL;
+                }
+            }
+        else
+            {
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "create new cache item" ) );
+#endif
+            //Check adjustment of response time is required or not.
+            AdjustResponseTime( aTrans );
+            // hash it
+            entry = iLookupTable->InsertL( aTrans.Request().URI().UriDes() );
+            if( entry )
+                {
+                // protect this entry
+                if( protectedEntry )
+                    {
+#ifdef __CACHELOG__
+                    HttpCacheUtil::WriteLog( 0, _L( "this item is protected" ) );
+#endif
+                    //
+                    entry->SetProtected();
+                    RHTTPHeaders responseHeaders = aTrans.Response().GetHeaderCollection();
+                    RStringPool strP = aTrans.Session().StringPool();
+
+                    // double expiration time
+                    HttpCacheUtil::AdjustExpirationTimeL( responseHeaders, strP );
+                    }
+                }
+            else
+                {
+                // no luck with the lookuptable
+                __ASSERT_DEBUG( EFalse, PanicCacheHandler( KErrCorrupt ) );
+                }
+            }
+        // save headers
+        if( entry )
+            {
+            // attach it to the stream handler
+            if( iStreamHandler->AttachL( *entry ) )
+                {
+                entry->SetState( CHttpCacheEntry::ECacheResponding );
+                // 1. handle only 304 and 200
+                // 2. check if either the header or the body ( or both ) need to be updated
+                // 3. update the headers anyway in case of notmodified (304)
+                // 4. remove the old body in case of bodyupdate
+                TInt httpStatus( aTrans.Response().StatusCode() );
+#ifdef __CACHELOG__
+                HttpCacheUtil::WriteLog( 0, _L( "status code: " ), httpStatus );
+#endif
+                //
+                TBool ok( EFalse );
+                if( httpStatus == HTTPStatus::EOk )
+                    {
+                    ok = HandleResponseOkL( *entry, aTrans );
+                    }
+                else if( httpStatus == HTTPStatus::ENotModified )
+                    {
+                    ok = HandleResponseNotModifiedL( *entry, aTrans );
+                    }
+                //
+                // entry could be corrupted at this point
+                if( ok )
+                    {
+                    // save handler and entry so that
+                    // on next call we don't have to start a lookup again
+                    aCacheEntry.iCacheHandler = this;
+                    aCacheEntry.iCacheEntry = entry;
+                    }
+                else
+                    {
+                    iStreamHandler->Detach( *entry );
+                    HandleCorruptEntry( *entry );
+                    entry = NULL;
+                    }
+                }
+            else
+                {
+                HandleCorruptEntry( *entry );
+                entry = NULL;
+                }
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::ReceivedResponseBodyDataL
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::ReceivedResponseBodyDataL(
+    RHTTPTransaction& aTrans,
+    MHTTPDataSupplier& aBodyDataSupplier,
+    THttpCacheEntry& aCacheEntry )
+    {
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "received body" ), aTrans.Request().URI().UriDes() );
+    // 1. check if we are caching this resource
+    // 2. update the body data
+    CHttpCacheEntry* entry = aCacheEntry.iCacheEntry;
+
+    if( entry && entry->State() == CHttpCacheEntry::ECacheResponding )
+        {
+        HBufC8* bodyStr = HttpCacheUtil::BodyToBufferL( aBodyDataSupplier );
+        if( bodyStr )
+            {
+            // erase entry if we are unable to save it (low disk space)
+            if( !SaveBuffer( *entry, bodyStr->Des(), ETrue ) )
+                {
+                // detach it from the stream and erase it
+                iStreamHandler->Detach( *entry );
+                HandleCorruptEntry( *entry );
+#ifdef __CACHELOG__                
+                HttpCacheUtil::WriteLog( 0, _L( "body cannot be saved" ) );
+#endif                
+                entry = NULL;
+                // remove entry
+                aCacheEntry.iCacheEntry = NULL;
+                }
+#ifdef __CACHELOG__
+            else
+                {
+                HttpCacheUtil::WriteLog( 0, _L( "body is saved" ) );
+                }
+#endif // __CACHELOG__
+            //
+            delete bodyStr;
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::ResponseComplete
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::ResponseComplete(
+    RHTTPTransaction& aTrans,
+    THttpCacheEntry& aCacheEntry )
+    {
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "response complete" ), aTrans.Request().URI().UriDes() );
+    // 1. check if we are caching this resource
+    // 2. mark the entry as complete
+    CHttpCacheEntry* entry = aCacheEntry.iCacheEntry;
+
+    if( entry )
+        {
+        if( entry->State() == CHttpCacheEntry::ECacheResponding )
+            {
+            // flush the entry
+            if( !iStreamHandler->Flush( *entry ) )
+                {
+                iStreamHandler->Detach( *entry );
+                HandleCorruptEntry( *entry );
+#ifdef __CACHELOG__
+                HttpCacheUtil::WriteLog( 0, _L( "body cannot be saved" ) );
+#endif
+                entry = NULL;
+                // remove entry
+                aCacheEntry.iCacheEntry = NULL;
+                }
+            else
+                {
+                entry->SetState( CHttpCacheEntry::ECacheComplete );
+                iStreamHandler->Detach( *entry );
+                }
+            }
+        else if( entry->State() == CHttpCacheEntry::ECacheDestroyed )
+            {
+            iStreamHandler->Detach( *entry );
+            HandleCorruptEntry( *entry, EFalse );
+            aCacheEntry.iCacheEntry = NULL;
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// Removes all entries in the Cache lookup table, commits table to disk. 
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::RemoveAllL()
+    {
+#ifdef __CACHELOG__
+    HttpCacheUtil::WriteLog( 0, _L( "remove all items" ) );
+#endif
+    TInt numberOfBytes;
+    // clear all the inactive entries
+    numberOfBytes = iLookupTable->RemoveAll();
+    // and save it. user initiated. no need to do idle save
+    SaveLookupTableL();
+    return numberOfBytes;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::ListFiles
+// Adds all filenames known to this Cache to aFilenameList
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::ListFiles(RPointerArray<TDesC>& aFilenameList)
+    {
+    return iLookupTable->ListFiles(aFilenameList);
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::RemoveL
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::RemoveL(
+    const TDesC8& aUrl )
+    {
+    TInt status( KErrNotFound );
+    HttpCacheUtil::WriteUrlToLog( 0, _L( "remove item:" ), aUrl );
+    CHttpCacheEntry* entry = iLookupTable->Find( aUrl );
+
+    if( entry )
+        {
+        if( entry->State() == CHttpCacheEntry::ECacheComplete )
+            {
+            // delete
+            status = iLookupTable->Remove( aUrl );
+            }
+        else
+            {
+            // mark it as deleted and erase it when the
+            // trans is complete
+            entry->SetState( CHttpCacheEntry::ECacheDestroyed );
+            status = KErrNone;
+            }
+        }
+    return status;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::Find
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::Find(
+    const TDesC8& aUrl )
+    {
+    // find
+    CHttpCacheEntry* entry = iLookupTable->Find( aUrl );
+    return ( entry ? ( entry->State() == CHttpCacheEntry::ECacheComplete ) : EFalse );
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::SaveL
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::SaveL(
+    const TDesC8& aUrl,
+    const TDesC8& aHeader,
+    const TDesC8& aContent )
+    {
+    TBool saved( EFalse );
+    // check if entry exist. do not overwrite.
+    CHttpCacheEntry* entry = iLookupTable->Find( aUrl );
+    if( !entry )
+        {
+        entry = iLookupTable->InsertL( aUrl );
+        // prepare for saving
+        if( entry && iStreamHandler->AttachL( *entry ) )
+            {
+            // save header and body
+            saved = SaveBuffer( *entry, aHeader, EFalse ) && SaveBuffer( *entry, aContent, ETrue );
+            if( saved )
+                {
+                // flush
+                saved = iStreamHandler->Flush( *entry );
+                if( saved )
+                    {
+                    entry->SetState( CHttpCacheEntry::ECacheComplete );
+                    }
+                }
+            iStreamHandler->Detach( *entry );
+            }
+        // cleanup
+        if( !saved && entry )
+            {
+            HandleCorruptEntry( *entry );
+            }
+        }
+    return saved;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::AddHeaderL
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpCacheHandler::AddHeaderL(
+    const TDesC8& aUrl,
+    const TDesC8& aName,
+    const TDesC8& aValue )
+    {
+    TInt status( KErrNotFound );
+    //
+    CHttpCacheEntry* entry = iLookupTable->Find( aUrl );
+    if( entry )
+        {
+        TBool attached;
+        //
+        attached = iStreamHandler->AttachL( *entry );
+        // get headers
+        HBufC8* headersStr = iStreamHandler->HeadersL( *entry );
+        if( headersStr )
+            {
+            CleanupStack::PushL( headersStr );
+            // alter headers and save them
+            HBufC8* newHeaderStr = HttpCacheUtil::AddHeaderLC( aName, aValue, headersStr->Des() );
+            if( newHeaderStr )
+                {
+                // remove old headers first
+                iStreamHandler->RemoveHeaders( *entry );
+                // save new headers
+                if( !SaveBuffer( *entry, newHeaderStr->Des(), EFalse ) )
+                    {
+                    status = KErrDirFull;
+                    // failed. should we save the original headers?
+                    TBool saveOk( SaveBuffer( *entry, headersStr->Des(), EFalse ) );
+                    // original save should never fail
+                    __ASSERT_DEBUG( saveOk, PanicCacheHandler( KErrCorrupt ) );
+
+                    if( !saveOk )
+                        {
+                        // sorry, we made this entry corrupt. remove it
+                        iStreamHandler->Detach( *entry );
+                        HandleCorruptEntry( *entry );
+                        entry = NULL;
+                        }
+                    }
+                else
+                    {
+                    status = KErrNone;
+                    }
+                CleanupStack::PopAndDestroy(); // newHeaderStr
+                }
+            CleanupStack::PopAndDestroy(); // headersStr
+            }
+        // detach
+        if( attached )
+            {
+            iStreamHandler->Detach( *entry );
+            }
+        }
+    return status;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::CacheNeedsValidationL
+//
+// Note: This function check if cache needs validation
+//       Delete cache entry from this function is too early, should avoid it
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::CacheNeedsValidationL(
+    CHttpCacheEntry& aCacheEntry,
+    RHTTPTransaction& aTrans,
+    TBrCtlDefs::TBrCtlCacheMode aCacheMode )
+    {
+    // The entry isn't useable unless otherwise stated
+    TBool mustRevalidate( ETrue );
+
+    // If the cacheMode is PreferCache then it must use it even if it
+    // is expired, etc.  This is typically used during history navigation.
+    //
+    // Note: Cache also set the cacheMode to PreferCache after a resource has
+    //       been validated -- that is when it tries to refetch an entry from
+    //       cache after receiving a 304 response.
+    // get cached headers
+    if( iStreamHandler->AttachL( aCacheEntry ) )
+        {
+        HBufC8* headersStr = iStreamHandler->HeadersL( aCacheEntry );
+        CleanupStack::PushL( headersStr );
+        iStreamHandler->Detach( aCacheEntry );
+        // headersStr == NULL happens if you erase the cache directory
+        // using a file manager
+        if( headersStr )
+            {
+            // use response headers for retreiving cached headers
+            RHTTPHeaders responseHeaders = aTrans.Response().GetHeaderCollection();
+            RHTTPHeaders requestHeaders = aTrans.Request().GetHeaderCollection();
+            RStringPool strP = aTrans.Session().StringPool();
+            // convert the buffer to httpHeader
+            HttpCacheUtil::BufferToHeadersL( headersStr->Des(), responseHeaders, strP );
+
+            // check if we need to validate the cahce
+            if( aCacheMode == TBrCtlDefs::ECacheModeOnlyCache || aCacheMode == TBrCtlDefs::ECacheModeHistory  )
+                {
+                // no validation required
+#ifdef __CACHELOG__
+                HttpCacheUtil::WriteLog( 0, _L( "prefer cache mode. no need to revalidate" ), aCacheMode );
+#endif                
+                mustRevalidate = EFalse;
+                }
+            else
+                {
+                // Get the pragma no-cache header from the headers
+                // no-cache on request header means "do not use cache"
+                if( !HttpCacheUtil::PragmaNoCache( aTrans ) )
+                    {
+                    if( !HttpCacheUtil::CacheTimeIsFresh( requestHeaders, responseHeaders, strP  ) )
+                        {
+#ifdef __CACHELOG__
+                        HttpCacheUtil::WriteLog( 0, _L( "cache item is not fresh. needs revalidation" ) );
+#endif
+                        // MKLE-7PRD27: Avoid removing cache entry here 
+                        
+                        mustRevalidate = ETrue;
+                        // add headers like EIfModifiedSince, EETag, EIfNoneMatch
+                        HttpCacheUtil::AddValidationHeaders( responseHeaders, requestHeaders, strP );
+                        }
+                    else
+                        {
+#ifdef __CACHELOG__
+                        HttpCacheUtil::WriteLog( 0, _L( "cache item is fresh. needs no revalidation" ) );
+#endif                        
+                        //
+                        mustRevalidate = EFalse;
+                        }
+                    }
+                else
+                    {
+                    // needs validation
+#ifdef __CACHELOG__
+                    HttpCacheUtil::WriteLog( 0, _L( "no cache/no store header present. need revalidation" ) );
+#endif
+                    mustRevalidate = ETrue;
+                    }
+
+                }
+            }
+        CleanupStack::PopAndDestroy(); // headersStr
+        }
+    else
+        {
+        HandleCorruptEntry( aCacheEntry );
+        // needs validation
+        mustRevalidate = ETrue;
+        }
+    return mustRevalidate;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::CacheNeedsSpaceL
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::CacheNeedsSpaceL(
+    TInt aSize )
+    {
+    TBool ok( ETrue );
+#ifdef __CACHELOG__
+    HttpCacheUtil::WriteLog( 0, _L( "the cache is this big" ), iSize );
+    HttpCacheUtil::WriteLog( 0, _L( "we occupy" ), iStreamHandler->SavedContentSize()  );
+    HttpCacheUtil::WriteLog( 0, _L( "this item needs space:" ), aSize );
+#endif // __CACHELOG__
+
+    // check if we need extra space
+    if( iStreamHandler->SavedContentSize() + aSize > iSize )
+        {
+#ifdef __CACHELOG__
+        // items in the cache
+        TInt size( 0 );
+        HttpCacheUtil::WriteLog( 0, _L( "cached items" ) );
+        const CArrayPtrFlat<CHttpCacheEntry>& entries = iLookupTable->Entries();
+        for( TInt i = 0; i < entries.Count(); i++ )
+            {
+            CHttpCacheEntry* entry = entries.At( i );
+            if( entry && entry != (CHttpCacheEntry*)0xffffffff )
+                {
+                HttpCacheUtil::WriteUrlToLog( 0, entry->Url(), entry->Size() );
+                size+=entry->Size();
+                size+=entry->HeaderSize();
+                }
+            }
+        HttpCacheUtil::WriteLog( 0, _L( "occupy with headers:" ), size );
+#endif // __CACHELOG__
+        CArrayPtrFlat<CHttpCacheEntry>* evictedList = iEvictionHandler->EvictL( aSize );
+        if( evictedList && evictedList->Count() )
+            {
+            // destroy items
+            CHttpCacheEntry* entry;
+            for( TInt i = 0; i < evictedList->Count(); i++ )
+                {
+                //
+                entry = evictedList->At( i );
+                if( entry )
+                    {
+                    // destroy
+                    iLookupTable->Remove( entry->Url() );
+                    }
+                }
+            // __ASSERT_DEBUG( iStreamHandler->SavedContentSize() + aSize < iSize, PanicCacheHandler( KErrCorrupt ) );
+            // ok = ETrue if there is enough space
+            ok = ( iStreamHandler->SavedContentSize() + aSize < iSize );
+            }
+        else
+            {
+            // interesting...nobody can be evicted...
+            // they are all protected entries?
+            // or the incoming -not yet complete- items take the entire cache?
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "NO SPACE can be released!!!" ) );
+#endif            
+            ok = EFalse;
+            }
+        delete evictedList;
+        }
+    return ok;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::HandleResponseOkL
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::HandleResponseOkL(
+    CHttpCacheEntry& aEntry,
+    RHTTPTransaction& aTrans )
+    {
+    TBool saveOk( ETrue );
+    RHTTPHeaders responseHeader = aTrans.Response().GetHeaderCollection();
+    RStringPool strP = aTrans.Session().StringPool();
+    HBufC8* responseHeaderStr = HttpCacheUtil::HeadersToBufferLC( responseHeader, strP );
+    //
+    TBool update( ETrue );
+    // get cached headers to compare
+    HBufC8* cachedHeaderStr = iStreamHandler->HeadersL( aEntry );
+    // we've got some headers to update,
+    // check if we really need to update them
+    if( cachedHeaderStr )
+        {
+        CleanupStack::PushL( cachedHeaderStr );
+        //
+        update = HttpCacheUtil::CacheNeedsUpdateL( responseHeader, cachedHeaderStr->Des(), strP );
+
+        CleanupStack::PopAndDestroy(); // cachedHeaderStr
+        }
+    //
+    if( update )
+        {
+#ifdef __CACHELOG__
+        HttpCacheUtil::WriteLog( 0, _L( "udpate headers" ) );
+#endif
+        if( aEntry.HeaderSize() )
+            {
+            // remove it first
+            iStreamHandler->RemoveHeaders( aEntry );
+            }
+        // save
+        saveOk = SaveBuffer( aEntry, responseHeaderStr->Des() );
+
+        if( aEntry.Size() )
+            {
+#ifdef __CACHELOG__
+            HttpCacheUtil::WriteLog( 0, _L( "remove body" ) );
+#endif            
+            //
+            iStreamHandler->RemoveBodyData( aEntry );
+            }
+        }
+    else
+        {
+        // if neither the header nor the body need to be updated, then
+        // detach entry to protect from being updated
+#ifdef __CACHELOG__
+        HttpCacheUtil::WriteLog( 0, _L( "no udpate needed, ignore response" ) );
+#endif        
+        //
+        aEntry.SetState( CHttpCacheEntry::ECacheComplete );
+        iStreamHandler->Detach( aEntry );
+        // pretend that save was ok.
+        saveOk = ETrue;
+        }
+    // destroy corrupt entry by returning EFalse
+    CleanupStack::PopAndDestroy(); // responseHeaderStr
+    return saveOk;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::HandleResponseNotModifiedL
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::HandleResponseNotModifiedL(
+    CHttpCacheEntry& aEntry,
+    RHTTPTransaction& aTrans )
+    {
+    // oos? -out of space
+    TBool saveOk( ETrue );
+    RHTTPHeaders responseHeader = aTrans.Response().GetHeaderCollection();
+    RStringPool strP = aTrans.Session().StringPool();
+    HBufC8* responseHeaderStr = HttpCacheUtil::HeadersToBufferLC( responseHeader, strP );
+    // If a cache uses a received 304 response to update a cache entry,
+    // the cache MUST update the entry to reflect any new field values given in the response.
+    HBufC8* mergedHeadersStr = NULL;
+    HBufC8* cachedHeaderStr = iStreamHandler->HeadersL( aEntry );
+    CleanupStack::PushL( cachedHeaderStr );
+    // don't merge with empty headers
+    if( cachedHeaderStr )
+        {
+        mergedHeadersStr = HttpCacheUtil::MergeHeadersLC( cachedHeaderStr->Des(), responseHeader, strP );
+        CleanupStack::Pop(); // mergedHeadersStr
+        }
+    // don't update empty headers
+    if( mergedHeadersStr || responseHeaderStr )
+        {
+        // remove cached headers first
+        iStreamHandler->RemoveHeaders( aEntry );
+        // save merged headers  (reponse + cached)
+        if( mergedHeadersStr )
+            {
+            saveOk = SaveBuffer( aEntry, mergedHeadersStr->Des() );
+            }
+        else if( responseHeaderStr )
+            {
+            // save responseheader instead
+            saveOk = SaveBuffer( aEntry, responseHeaderStr->Des() );
+            }
+        // if save failed, let's see if we can save old
+        // headers
+        if( !saveOk && cachedHeaderStr )
+            {
+            saveOk = SaveBuffer( aEntry, cachedHeaderStr->Des() );
+            }
+        }
+    // do not remove the body as it was not modified
+    delete mergedHeadersStr;
+    CleanupStack::PopAndDestroy( 2 ); // cachedHeaderStr, responseHeaderStr
+    // check if save was ok.
+    // or nothing was not saved at all
+    if( saveOk )
+        {
+        // this item does not need update
+        aEntry.SetState( CHttpCacheEntry::ECacheComplete );
+        iStreamHandler->Detach( aEntry );
+        }
+    return saveOk;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::OpenLookupTableL
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::OpenLookupTableL()
+    {
+    OpenLookupTableL(iLookupTable);
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::OpenLookupTableL
+// Opens the index*.dat lookup table from file system. 
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::OpenLookupTableL(CHttpCacheLookupTable* aLookupTable)
+    {
+    // read entries from index.dat
+    RFileReadStream readStream;
+
+        iRfs.SetSessionPath( iDirectory->Des() );
+
+        TInt ret = KErrNone;
+        TInt tryCount = 0;
+        for (tryCount = 0; tryCount < 5; tryCount++) 
+            {
+            ret = readStream.Open( iRfs, iIndexFile->Des(), EFileRead | EFileShareAny );
+            if (ret == KErrInUse)
+                {
+                // When the cache is full, it takes 65 - 85 miliseconds to write the index.
+                // So wait 50 miliseconds and try again
+                User::After(50000);
+                }
+            else
+                {
+                break;
+                }
+            }
+        if( ret == KErrNone )
+            {
+            CleanupClosePushL( readStream );
+            aLookupTable->InternalizeL( readStream, iDirectory->Des() );
+            CleanupStack::PopAndDestroy(1); // readStream
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::SaveLookupTableL
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::SaveLookupTableL()
+    {
+#ifdef __CACHELOG__
+    HttpCacheUtil::WriteLog( 0, _L( "lookup table is saved" ) );
+#endif
+    // save entries to index.dat
+    RFileWriteStream writeStream;
+
+    // Don't get notified about own changes
+    iHttpCacheObserver->Cancel();
+    TInt ret = KErrNone;
+    TInt tryCount = 0;
+    for (tryCount = 0; tryCount < 5; tryCount++) 
+        {
+        ret = writeStream.Replace( iRfs, iIndexFile->Des(), EFileWrite );
+        if (ret == KErrInUse)
+            {
+            // When the cache is full, it takes 65 - 85 miliseconds to write the index.
+            // So wait 50 miliseconds and try again
+            User::After(50000);
+            }
+        else
+            {
+            break;
+            }
+        }
+    if( ret == KErrNone )
+        {
+        CleanupClosePushL( writeStream );
+        iLookupTable->ExternalizeL( writeStream );
+        writeStream.CommitL();
+        CleanupStack::PopAndDestroy(); // writeStream
+        }
+    iHttpCacheObserver->StartObserver();
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::HandleCorruptEntry
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::HandleCorruptEntry(
+    CHttpCacheEntry& aStrayEntry,
+    TBool aUpdate )
+    {
+    (void)aUpdate;//suppress compiler and PC-lint warnings
+#ifdef __CACHELOG__
+    HttpCacheUtil::WriteLog( 0, _L( "delete this stray entry" ) );
+#endif
+    // remove from the lookuptable
+    iLookupTable->EraseCorruptEntry( aStrayEntry.Url() );
+    }
+
+// -----------------------------------------------------------------------------
+// There used to be a CHttpCacheHandler::FixLookupTableL here. 
+// Go back in SVN to re-discover it :)
+//
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::SaveBuffer
+//
+// -----------------------------------------------------------------------------
+//
+TBool CHttpCacheHandler::SaveBuffer(
+    CHttpCacheEntry& aEntry,
+    const TDesC8& aBuffer,
+    TBool aBody )
+    {
+    TBool ok( EFalse );
+    TRAPD( err, ok = CacheNeedsSpaceL( aBuffer.Length() ) );
+    if( err == KErrNone && ok )
+        {
+        // safe save
+        ok = aBody ? iStreamHandler->SaveBodyData( aEntry, aBuffer ) : iStreamHandler->SaveHeaders( aEntry, aBuffer );
+        }
+#ifdef __CACHELOG__
+    else
+        {
+        // stop saving this entry
+        HttpCacheUtil::WriteUrlToLog( 0, _L( "item cannot be saved. remove it please" ), aEntry.Url() );
+        }
+#endif // __CACHELOG__
+    return ok;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::UpdateLookupTable
+//
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::UpdateLookupTable()
+    {
+    TRAP_IGNORE(UpdateLookupTableL());
+    iHttpCacheObserver->StartObserver();
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpCacheHandler::UpdateLookupTableL
+// Slow method due to much file-system interaction. Don't call it from performance critical code. 
+// -----------------------------------------------------------------------------
+//
+void CHttpCacheHandler::UpdateLookupTableL()
+    {
+    CHttpCacheEvictionHandler* evictionHandler = CHttpCacheEvictionHandler::NewL();
+    CleanupStack::PushL(evictionHandler);
+    CHttpCacheLookupTable* lookupTable = CHttpCacheLookupTable::NewL( *evictionHandler, *iStreamHandler );
+    CleanupStack::PushL(lookupTable);
+    OpenLookupTableL(lookupTable);
+    iLookupTable->MergeL(lookupTable, iRfs);
+	CleanupStack::PopAndDestroy(2); // lookupTable, evictionHandler
+    }
+//  End of File