httpfilters/deflatefilter/src/DeflateFilter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 15 Mar 2010 12:42:40 +0200
branchRCL_3
changeset 6 fa2fd8b2d6cc
parent 0 b16258d2340f
permissions -rw-r--r--
Revision: 201009 Kit: 201010

/*
* 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 "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: 
*  Deflate decompression filter for HTTP protocol stack.
*
*
*/


#include <http/rhttptransaction.h>
#include <http/rhttpheaders.h>
#include <http/rhttpresponse.h>
#include <httpstringconstants.h>
#include "DeflateFilter.h"
#include <bautils.h>
#include <eikenv.h>
#include <bldvariant.hrh>
#include <httpfiltercommonstringsext.h>
//------------------------------------------------------------------------

_LIT8( KDeflateFilterName, "Deflate");

#ifndef __SERIES60_
_LIT8(KGzipDeflateStr, "gzip,deflate");
#else
_LIT8(KGzipDeflateStr, "gzip, deflate, x-gzip, identity; q=0.9");
#endif
//------------------------------------------------------------------------

// format for output of data/time values
#if defined (_DEBUG) && defined (_LOGGING)
#define LOG_FILE "deflate.log"						//lint !e1923 LOG_FILE only used in debug build
_LIT(KDateFormat,"%D%M%Y%/0%1%/1%2%/2%3%/3 %:0%H%:1%T%:2%S.%C%:3");
#endif

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CDeflateFilter::CDeflateFilter
// Constructor
// -----------------------------------------------------------------------------
//
CDeflateFilter::CDeflateFilter(RHTTPSession aSession)
		: iSession(aSession)
{
	iDataSups.Reset();
} //lint !e1746			RHTTPSession is a light object, just a wrapper to CHTTPSession

// -----------------------------------------------------------------------------
// CDeflateFilter::InstallFilterL
// Initialize the filter and register it to sesssion's filter collection
// -----------------------------------------------------------------------------
//
CHttpDeflateFilter* CDeflateFilter::InstallFilterL(TAny* aSession)
{
	RHTTPSession* session = REINTERPRET_CAST(RHTTPSession*, aSession);
	CDeflateFilter* filter = new (ELeave) CDeflateFilter(*session);
	CleanupStack::PushL(filter);
	filter->ConstructL(*session);
	CleanupStack::Pop(filter);
	return filter;
}

// -----------------------------------------------------------------------------
// CDeflateFilter::ConstructL
// Memory and resource allocation, leaves
// -----------------------------------------------------------------------------
//
void CDeflateFilter::ConstructL(RHTTPSession aSession)
{
    // load resources if UI is available
	iStringPool = aSession.StringPool();
	RStringF filterName = iStringPool.OpenFStringL( KDeflateFilterName );
	CleanupClosePushL( filterName );

	iSession.FilterCollection().AddFilterL( *this, 
											THTTPEvent::ESubmit,
											MHTTPFilter::EProtocolHandler+5,			
											filterName );
	iSession.FilterCollection().AddFilterL( *this,
											THTTPEvent::EGotResponseHeaders,
											MHTTPFilter::EProtocolHandler+5,			
											filterName );								
	iSession.FilterCollection().AddFilterL( *this, 
											THTTPEvent::EGotResponseBodyData,
											MHTTPFilter::EProtocolHandler+5,	
											filterName );
	iSession.FilterCollection().AddFilterL( *this, 
											THTTPEvent::EResponseComplete,
											MHTTPFilter::EProtocolHandler+5,
											filterName );
	iSession.FilterCollection().AddFilterL( *this,
											THTTPEvent::EClosed,
											MHTTPFilter::EProtocolHandler+5,
											filterName );
	CleanupStack::PopAndDestroy();

	iXGzipVal = iStringPool.StringF(HttpFilterCommonStringsExt::EXGzip, HttpFilterCommonStringsExt::GetTable());
	// log file
	__OPEN_LOG( "deflate.log" );
	__LOG( _L( "--Initialize Deflate Filter---\n" ) );
} //lint !e1746  RHTTPSession is a light weight class, just wrapper for CHTTPSession


//------------------------------------------------------------------------
// CDeflateFilter::~CDeflateFilter
// Destructor
//------------------------------------------------------------------------
//
CDeflateFilter::~CDeflateFilter()
{
	if (iLoadCount)
	{
		iLoadCount = -1;
        RStringF filterName;	//lint !e1551
		TRAPD( error, ( filterName = iStringPool.OpenFStringL( KDeflateFilterName ) ) );	//lint !e1551  
		if( error == KErrNone ) iSession.FilterCollection().RemoveFilter( filterName ); //lint !e1551 
        filterName.Close(); //lint !e1551 
	}

	CleanupAll();															//lint !e1551 
	iDataSups.Close();														//lint !e1551 
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFLoad
// See MHTTPFilterBase::MHFRunL
//------------------------------------------------------------------------
//
void CDeflateFilter::MHFLoad(RHTTPSession, THTTPFilterHandle)
{
	__LOG( _L( "--Load Deflate Filter---\n" ) );
	++iLoadCount;
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFUnload
// See MHTTPFilterBase::MHFRunError
//------------------------------------------------------------------------
//
void CDeflateFilter::MHFUnload(RHTTPSession , THTTPFilterHandle)
{
	__LOG( _L( "--Unload Deflate Filter---\n" ) );
	if (--iLoadCount)
	{
		return;
	}

	delete this;
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFRunL
// See MHTTPFilterBase::MHFRunL 
//------------------------------------------------------------------------
//
void CDeflateFilter::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
{
	if (aEvent.iUID != KHTTPUid) return;

	switch(aEvent.iStatus)
	{
		case THTTPEvent::ESubmit:
		{
			__LOG( _L( "Event: ESubmit\n" ) );
			AlterRequestHeadersL( aTransaction );
		}
		break;
		case THTTPEvent::EGotResponseHeaders:
		{
			__LOG( _L( "Event: EGotResponseHeaders\n" ) ); 
			CheckResponseHeadersL( aTransaction );
		}
		break;
		case THTTPEvent::EGotResponseBodyData:
		{
			__LOG( _L( "Event: EGotResponseBodyData\n" ) );
			TInt idx = GetTransIdx( aTransaction );
			if( idx != KErrNotFound )
			{
				iDataSups[idx]->ProcessDataPartL();
			}
		}
		break;
		case THTTPEvent::EResponseComplete:
		{
			__LOG( _L( "Event: EResponseComplete\n" ) );
			Cleanup( aTransaction );
		}
		break;
		case THTTPEvent::EClosed:
		{
			__LOG( _L( "Event: EFailed\n" ) );
			Cleanup( aTransaction );
		}
		break;
		default: 
		{
			__LOG1( _L( "Unknow Event: ID - %d\n" ), aEvent.iStatus );
		}
		break;
	}
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFRunError
// See MHTTPFilterBase::MHFRunError
//------------------------------------------------------------------------
//
TInt CDeflateFilter::MHFRunError(TInt /*aError*/, RHTTPTransaction /*aTransaction*/, const THTTPEvent& )
{
	// error happened, cleanup the datasupplier related with this transaction
//	Cleanup( aTransaction );
//	aTransaction.SendEventL( THTTPEvent::EFailed, THTTPEvent::EIncoming, THTTPFilterHandle::ECurrentFilter );
	return KErrNone;
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFSessionRunL
// See MHTTPFilterBase::MHFSessionRunL
//------------------------------------------------------------------------
//
void CDeflateFilter::MHFSessionRunL(const THTTPSessionEvent& )
{
	// do nothing
}

//------------------------------------------------------------------------
// CDeflateFilter::MHFSessionRunL
// See MHTTPFilterBase::MHFSessionRunL
//------------------------------------------------------------------------
//
TInt CDeflateFilter::MHFSessionRunError(TInt aError, const THTTPSessionEvent& )
{
	// session problem, need to close Deflate engine
	CleanupAll();
	return aError;
}

//------------------------------------------------------------------------
// CDeflateFilter::DumpResponseHeadersL
// Dump the response headers to LOG file
//------------------------------------------------------------------------
//
#if defined (_DEBUG) && defined (_LOGGING)
void CDeflateFilter::DumpHeadersL( RHTTPHeaders aHeaders )
{
	__LOG( _L( "Dump the header...\n" ) );

	RStringPool strP = iSession.StringPool();
	THTTPHdrFieldIter it = aHeaders.Fields();
	
	while( it.AtEnd() == EFalse )
	{
		RStringTokenF fieldName = it();
		RStringF fieldNameStr = strP.StringF( fieldName );
		
		THTTPHdrVal fieldVal;

		// how many parts in this field?
		TInt parts = aHeaders.FieldPartsL( fieldNameStr );

		// dump all parts of  a  header field
		for( TInt i=0; i<parts; ++i )
		{
			if( aHeaders.GetField( fieldNameStr, i, fieldVal ) == KErrNone )
			{
				const TDesC8& fieldNameDesC = fieldNameStr.DesC();
				if( i==0 )
				{
					__DUMPBIN( fieldNameDesC );
					__LOG( _L ( " : " ) );
				}
				else
				{
					__LOG( _L ( "," ) );
				}

				switch( fieldVal.Type() )
				{
				case THTTPHdrVal::KTIntVal:
					{
						__LOG1( _L("%d"), fieldVal.Int() );
					}
					break;
				case THTTPHdrVal::KStrFVal:
					{
						RStringF fieldValStr = strP.StringF( fieldVal.StrF() );
						const TDesC8& fieldValDesC = fieldValStr.DesC();
						__DUMPBIN( fieldValDesC );
					}
					break;
				case THTTPHdrVal::KStrVal:
					{
						RString fieldValStr = strP.String( fieldVal.Str() );
						const TDesC8& fieldValDesC = fieldValStr.DesC();
						__DUMPBIN( fieldValDesC );
					}
					break;
				case THTTPHdrVal::KDateVal:
					{
						TDateTime date = fieldVal.DateTime();
						TBuf<40> dateTimeString;
						TTime t( date );
						t.FormatL( dateTimeString, KDateFormat );
						TBuf8<40> dtStr;
						dtStr.Copy( dateTimeString.Left( 128 ) );
						__DUMPBIN( dtStr );
					}
					break;
				case THTTPHdrVal::KNoType:
				default:
					{
						__LOG( _L ( "Unrecognized value type.\n" ) );
					}
					break;
				}

				__LOG( _L ( "\n" ) );
			}
		}

		++it;
	}
	__LOG( _L( "Header is Dumped already...\n" ) );
}//lint !e1746	no need to be const reference, just for debugging
#endif

//------------------------------------------------------------------------
// CDeflateFilter::AlterRequestHeadersL
// Check HTTP headers and extract MIME type
//------------------------------------------------------------------------
// 
void CDeflateFilter::AlterRequestHeadersL( const RHTTPTransaction& aTrans )
{
#if defined (_DEBUG) && defined (_LOGGING)
	__LOG( _L( "Dump the request header before modification.\n" ) );
	DumpHeadersL( aTrans.Request().GetHeaderCollection() );
#endif
	
	// remove the original Accept-Encoding field set by HTTP client
	aTrans.Request().GetHeaderCollection().RemoveField( 
			iStringPool.StringF( HTTP::EAcceptEncoding, RHTTPSession::GetTable()) );
	SetHeaderL( aTrans.Request().GetHeaderCollection(), HTTP::EAcceptEncoding, KGzipDeflateStr );

#if defined (_DEBUG) && defined (_LOGGING)
	__LOG( _L( "Dump the request header after modification.\n" ) );
	DumpHeadersL( aTrans.Request().GetHeaderCollection() );
#endif
}

//------------------------------------------------------------------------
// CDeflateFilter::CheckHeadersL
// Check HTTP headers and extract MIME type
//------------------------------------------------------------------------
// 
void CDeflateFilter::CheckResponseHeadersL( const RHTTPTransaction& aTrans )
{
	// read the header data and check the MIME type here	
	// check the status and body
	RHTTPResponse response = aTrans.Response();
	
	#if defined (_DEBUG) && defined (_LOGGING)
	DumpHeadersL( aTrans.Response().GetHeaderCollection() );
	#endif

	RHTTPHeaders headers = response.GetHeaderCollection();
	RStringF fieldNameStr = iStringPool.StringF( HTTP::EContentEncoding, RHTTPSession::GetTable() );

	// read the first part of content-encoding field
	THTTPHdrVal fieldVal;

/*
	---------
	if( headers.GetField( fieldNameStr, 0, fieldVal ) == KErrNone &&
		( fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EGzip ||
		  fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EDeflate ) )
	{
		// find gzip/deflate content-decoding header
		__LOG( _L( "gzip/deflate content-decoding exists...\n" ) );

		// we shouldn't store the same transaction twice
		TInt idx = GetTransIdx( aTrans );
		if( idx != KErrNotFound )
		{
			// remove the previous one
			CDeflateDataSupplier* iSup = iDataSups[idx];
			iDataSups.Remove( idx );
			delete iSup;
		}

		CDeflateDataSupplier* pSup = CDeflateDataSupplier::NewL( aTrans.Id(), response.Body(),
			(1<<15), fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EGzip ? 0 : 1 ); 				//CDecmpStream::EGzip : CDecmpStream::EZlib ); 
		iDataSups.Append( pSup );
		response.SetBody( *pSup );

		// remove the fields of deflate and gzip
		aTrans.Response().GetHeaderCollection().RemoveField( 
			iStringPool.StringF( HTTP::EContentEncoding, RHTTPSession::GetTable()) );
	}
	-----
*/
/*********************************************************************/
	if( headers.GetField( fieldNameStr, 0, fieldVal ) == KErrNone )
		{	
		if( ( fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EGzip ||
		 	 fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EDeflate ||
			 fieldVal.StrF().Index( HttpFilterCommonStringsExt::GetTable() ) == HttpFilterCommonStringsExt::EXGzip ) )
			{
			// find gzip/deflate/x-gzip content-decoding header
			__LOG( _L( "gzip/deflate/x-gzip content-decoding exists...\n" ) );

			/**/
			// we shouldn't store the same transaction twice
			TInt idx = GetTransIdx( aTrans );
			if( idx != KErrNotFound )
				{
					// remove the previous one
					CDeflateDataSupplier* iSup = iDataSups[idx];
					iDataSups.Remove( idx );
					delete iSup;
				}
			/**/

			CDeflateDataSupplier* pSup = NULL;

			if (fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EGzip ||
				fieldVal.StrF().Index( HttpFilterCommonStringsExt::GetTable() ) == HttpFilterCommonStringsExt::EXGzip )
				{
				pSup = CDeflateDataSupplier::NewL( aTrans.Id(), response.Body(), (1<<15), 0 ); // CDecmpStream::EGzip
				}
			else // if fieldVal.StrF().Index( RHTTPSession::GetTable() ) == HTTP::EDeflate
				{
				pSup = CDeflateDataSupplier::NewL( aTrans.Id(), response.Body(), (1<<15), 1 ); // CDecmpStream::EZlib
				}

			iDataSups.Append( pSup );
			response.SetBody( *pSup );				 

			// remove the fields of deflate and gzip
			aTrans.Response().GetHeaderCollection().RemoveField( 
				iStringPool.StringF( HTTP::EContentEncoding, RHTTPSession::GetTable()) );

			// Adding Transaction parameter so Download manager knows that content was compressed.
			RHTTPTransactionPropertySet propSet = aTrans.PropertySet();
			// Key = "ContentInflated", fieldVal = "gzip" or "deflate" or "x-gzip"
			propSet.RemoveProperty( iStringPool.StringF(HttpFilterCommonStringsExt::EContentInflated, HttpFilterCommonStringsExt::GetTable()));
			propSet.SetPropertyL( iStringPool.StringF(HttpFilterCommonStringsExt::EContentInflated, HttpFilterCommonStringsExt::GetTable()), fieldVal ); 
			}
		}
	else
		{
		TInt idx = GetTransIdx( aTrans );
		if( idx != KErrNotFound )
			{
			// remove the previous one
			CDeflateDataSupplier* iSup = iDataSups[idx];
			iDataSups.Remove( idx );
			delete iSup;
			iSup = NULL;
			}
		}
}

//-----------------------------------------------------------------------------
// CDeflateFilter::GetDeflateTransIdx
// Retrieve the Deflate datasupplier
//-----------------------------------------------------------------------------
//
TInt CDeflateFilter::GetTransIdx( const RHTTPTransaction& aTrans ) const
{
	for( TInt i=0; i<iDataSups.Count(); ++i )
		if( iDataSups[i]->GetTransId() == aTrans.Id() )
			return i;
	return KErrNotFound;
}


//-----------------------------------------------------------------------------
// CDeflateFilter::Cleanup
// Cleanup the resource related with a transaction
//-----------------------------------------------------------------------------
//
void CDeflateFilter::Cleanup( const RHTTPTransaction& aTrans )
{
	TInt idx = GetTransIdx( aTrans );
	if( idx != KErrNotFound )
	{
		// remove the problematic data supplier
		CDeflateDataSupplier* iSup = iDataSups[idx];
		iDataSups.Remove( idx );
		delete iSup;
		iSup = NULL;
	}

	// shutdown the Deflate session
	if( iDataSups.Count()==0 ) CleanupAll();
}

//-----------------------------------------------------------------------------
// CDeflateFilter::CleanupAll
// Cleanup all the Deflate transactions, in case a session error happens or a session
// is closed.
//-----------------------------------------------------------------------------
//
void CDeflateFilter::CleanupAll()
{
	iDataSups.ResetAndDestroy();
}

//-----------------------------------------------------------------------------
// CDeflateFilter::SetHeaderL
// Add string to the header
//-----------------------------------------------------------------------------
//
void CDeflateFilter::SetHeaderL(RHTTPHeaders aHeaders, TInt aHdrField, const TDesC8& aHdrValue) const
{
	RStringF valStr = iStringPool.OpenFStringL(aHdrValue);
	THTTPHdrVal val(valStr);
	aHeaders.SetFieldL(iStringPool.StringF(aHdrField,RHTTPSession::GetTable()), val);
	valStr.Close();
}