httpfilters/deflatefilter/src/DeflateFilter.cpp
changeset 0 b16258d2340f
child 8 fa2fd8b2d6cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/httpfilters/deflatefilter/src/DeflateFilter.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,533 @@
+/*
+* 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();
+}