applayerprotocols/httptransportfw/httpmessage/chttpmessageparser.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 13:00:48 +0300
changeset 14 ce2bfba3d005
parent 0 b16258d2340f
permissions -rw-r--r--
Revision: 201015 Kit: 201018

// Copyright (c) 2003-2009 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:
//

#include "chttpmessageparser.h"

#include <inetprottextutils.h>
#include <uriutilscommon.h>

#include "mhttpmessageparserobserver.h"
#include "thttpmessagepanic.h"

const TInt KDefaultBufferSize	= 128;
const TUint KColon				= ':';
_LIT8 ( KLineFeed, "\n" );

// 'this' used in base member initializer list, The 'this' pointer being used is a base class pointer.
#pragma warning( disable : 4355 )

CHttpMessageParser* CHttpMessageParser::NewL(MHttpMessageParserObserver& aObserver)
/**
	Factory constructor. 
	@param		aObserver	The observer for the parser.
	@return		A pointer to a fully constructed and initialised object.
*/
	{
	return new (ELeave) CHttpMessageParser(aObserver);
	}

CHttpMessageParser::~CHttpMessageParser()
/**
	Destructor
*/
	{
	Cancel();

	// Cleanup
	delete iLineBuffer;
	}

CHttpMessageParser::CHttpMessageParser(MHttpMessageParserObserver& aObserver)
: CActive(CActive::EPriorityStandard + 1), iObserver(aObserver), iDataParser(*this)
/**
	Constructor.
	See note in CHttpMessageComposer
*/
	{
	CActiveScheduler::Add(this);
	}

void CHttpMessageParser::ReceivedMessageData()
/**
	Notifies the parser of more message data. The parser gets the data packet
	from the observer and continues processing its state machine.
	@panic	EHttpMessagePanicBadDataState	The current data packet has not been
											completely parsed.
*/
	{
	__ASSERT_DEBUG( iDataState == EWaitingForData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );

	// Have now got some data - update the data state
	iDataState = EGotData;

	// Get the received data packet
	TPtrC8 data;
	iObserver.GetDataPacket(data);

	// Pass it to the data parser
	iDataParser.SetData(data);

	// Continue parsing...
	CompleteSelf();
	}

void CHttpMessageParser::CompletedBodyDataL()
/**
	Tells the parser that there is no more body data. When using HTTP 1.0 style
	responses the body data maybe of unknown length so this method allows clients
	to inform the parser that the body data is completed.
*/
	{
	iDataSizeLeft = 0;
	
	// Notify the observer that the body has been received
	iObserver.BodyCompleteL();

	// Notify the observer that the message is complete.
	iObserver.MessageCompleteL(KNullDesC8());

	// Move to the Idle state.
	iParserState = EIdle;
	iDataState = EWaitingForData;
	}

void CHttpMessageParser::Reset()
/**
	Parser reset request. As the observer can reset the parser during one the 
	callback functions, the parser must check for re-entrancy to avoid releasing
	resources that are still required. If the parser is either waiting for more
	message data or is waiting to process its state machine, the parser can 
	safely reset immediately. Otherwise the parser is being reset from within 
	its RunL() and so it must defer resetting itself to a safer point. This is
	the point in the RunL() where the next step is decided.
	@panic EHttpMessagePanicDoubleReset	The parser has been reset twice in one 
										of the observer callback functions.
*/
	{
	// Check the data state of the parser - the parser cannot be reset if the 
	// Reset() was called in one of the observer callbacks. It is safe to reset
	// now if - 
	// 1) the data state is WaitingForData
	// 2) the parser is active - waiting to for its RunL() to be called.
	if( iDataState == EWaitingForData || IsActive() )
		{
		// Cancel and do the reset.
		Cancel();
		DoReset();
		}
	else
		{
		// Debug check for a double Reset() call...
		__ASSERT_DEBUG( iDataState != EReset, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicDoubleReset) );

		// The Reset() was called inside a callback - defer resetting the parser
		// until call stack back in the parser RunL().
		iDataState = EReset;
		}
	}

void CHttpMessageParser::CompleteSelf()
/**
	Self-complete function. Ensures that the state machine is processed.
*/
	{
	TRequestStatus* pStat = &iStatus;
	User::RequestComplete(pStat, KErrNone);
	SetActive();
	}

void CHttpMessageParser::DoReset()
/**
	Resets the parser. The parser moves into the Idle state. Allocated resources 
	are also reset. 
*/
	{
	// Reset the parser - parser state should be Idle and the data state should
	// be WaitingForData.
	iParserState = EIdle;
	iDataState = EWaitingForData;

	// Reset the data parser
	iDataParser.Reset();
	}

CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseStartLineL()
/**
	Parses for the start-line. The start-line is delimited by the first eol 
	marker. No further parsing of the start-line is done by the parser - it is
	left to the observer to parse for either a request-line or a status-line.
	The observer is informed of the start-line.
	@return		The parsing status for the start-line. The value ESectionDone 
				indicates the start-line has been parsed. The value EBufferEmpty
				indicates that more message data is required to parse the 
				start-line.
	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
														returned by the data 
														parser.
*/
	{
	// Get line from the parser
	TPtrC8 startLine;
	THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(startLine);

	// Check the parse status...
	TParsingStatus  startlineStatus = ESectionNotDone;
	switch( parseResult )
		{
	case THttpDataParser::ELineParsed:
		{
		// The start-line has been parsed - inform the observer.
		iObserver.StartLineL(startLine);
		startlineStatus = ESectionDone;
		} break;
	case THttpDataParser::EPartialData:
		{
		// Not all of start-line received - need more data.
		startlineStatus = EBufferEmpty;
		} break;
	case THttpDataParser::EEmptyLine:
		{
		// Although blank-lines are not allowed, be tolerent. Need to parse for 
		// the start-line again. The start-line is not complete - use default 
		// return value.
		} break;
	default:
		// This covers EGotData case.
		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
		break;
		}
	return startlineStatus;
	}

CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseSingleHeaderL()
/**
	Parses for a header field line. A header field line is delimited by an eol 
	marker. If an empty line is parser then this indicates the end of the header
	fields section. If a header field is found then the field name and field
	value are extracted. The observer is passed the field name and value.
	
	If a malformed header field is found (ie the colon ':' is missing) then that
	line is ignored. The function will leave if an error occurs in removing the 
	leading and trailing whitespace from the parsed header field name. 
	
	Note that the header field value may be empty.
	@return		The parsing status for the header fields section. The value 
				ESectionDone indicates that an empty line has been parsed which
				means that all the header fields have been parsed. The value 
				ESectionNotDone indicates that a header field has been found. 
				The	value EBufferEmpty indicates that more message data is 
				required to parse the header fields section.
	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
														returned by the data 
														parser.
*/
	{
	// Get line from the parser
	TPtrC8 line;
	THttpDataParser::TParseResult parseResult = iDataParser.GetHeaderLineL(line);

	// Check the parse status...
	TParsingStatus  headerStatus = ESectionNotDone;
	switch( parseResult )
		{
	case THttpDataParser::ELineParsed:
		{
		// Got a header field - find the field name and value.
		TInt colonPos = line.Locate(KColon);

		if( colonPos == KErrNotFound )
			{
			// No colon - syntax error. Be robust and ignore the this line. 
			// There are still more headers to find - use default return value.
			break;
			}

		// Found the field name and value
		TPtrC8 name = line.Left(colonPos);
		TPtrC8 value = line.Mid(colonPos + 1);	// move past the colon

		// Remove any leading/trailing whitespace but if it is empty, try to be robust and continue with the next one.
		TInt rwsErr = InetProtTextUtils::RemoveWhiteSpace(name, InetProtTextUtils::ERemoveBoth);

		// Field value may be empty - e.g. "host:". Ignore any returned error.
		InetProtTextUtils::RemoveWhiteSpace(value, InetProtTextUtils::ERemoveBoth);

		// Pass the header field name and value to the observer if it seemed valid.
		if( rwsErr == KErrNone )
			{
			iObserver.HeaderL(name, value);
			}
 
		// There are still more headers to find - use default return value.
		} break;
	case THttpDataParser::EPartialData:
		{
		// Not all of header field received - need more data.
		headerStatus = EBufferEmpty;
		} break;
	case THttpDataParser::EEmptyLine:
		{
		// Reached the end of the headers section - move on.
		headerStatus = ESectionDone;
		} break;
	default:
		// This covers EGotData case.
		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
		break;
		}
	return headerStatus;
	}

CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseHeadersL()
    {
    // Do parsing of 5 headers without AO scheduling
    TParsingStatus  headerStatus = ParseSingleHeaderL();
    if(headerStatus == ESectionNotDone)
        {
        headerStatus = ParseSingleHeaderL();
        if(headerStatus == ESectionNotDone)
            {
            headerStatus = ParseSingleHeaderL();
            if(headerStatus == ESectionNotDone)
                {
               headerStatus = ParseSingleHeaderL();
                if(headerStatus == ESectionNotDone)
                    {
                    headerStatus = ParseSingleHeaderL();
                    }
                }
            }
        }
    return headerStatus;
    }

CHttpMessageParser::TParsingStatus CHttpMessageParser::ReadBodyData(TPtrC8& aData)
/**
	Reads the entity body data from the current data packet. As the entity body
	data can be segmented by the tranport layer this function may need to be 
	called more than once. The remaining amount of entity body data is updated.
	@param		aData	An output argument set the entity body data extracted 
						from the current data packet.
	@return		The parsing status for the entity body. The value ESectionDone 
				indicates the entity body has been extracted. The value 
				EBufferEmpty indicates that more message data is required to 
				extract the entity body.
	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
														returned by the data 
														parser.
	@panic		EInvariantFalse	The data parser returned a status that conflicts
								with the amount of entity body data to extract.
*/
	{
	// If the body length is unknown then set the data size to a maximum value
	if(iDataSizeLeft==-1)
		iDataSizeLeft = KMaxTInt;

	// Get the remaining body data from the data parser
	THttpDataParser::TParseResult parseResult = iDataParser.GetData(aData, iDataSizeLeft);

	// Update the size of the data left to get
	iDataSizeLeft -= aData.Length();

	// Check the parse status...
	TParsingStatus  dataStatus = EBufferEmpty;
	switch( parseResult )
		{
	case THttpDataParser::EGotData:
		{
		__ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() );

		// Got all the data - section is done.
		dataStatus = ESectionDone;
		} break;
	case THttpDataParser::EPartialData:
		{
		__ASSERT_DEBUG( iDataSizeLeft > 0, User::Invariant() );

		// Use the default value...
		} break;
	default:
		// This covers ELineParsed and EEmptyLine cases.
		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
		break;
		}
	return dataStatus;
	}

CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkSizeL()
/**
	Parses for a chunk-size component. The chunk-size component specifies the 
	size of following chunk-data component. As the chunk-size can be segmented 
	at the transport layer this function may need to called several times for 
	the same chunk-size component.

	Section 3.6.1 in RFC2616 defines the following - 
		chunk			=	chunk-size [ chunk-extension ] CRLF
							chunk-data CRLF
		chunk-size		=	1*HEX
		chunk-extension	=	*( ";" chunk-ext-name [ "=" chunk-ext-val ] )

	The chunk-size component is delimited by the eol marker. There can be an 
	optional chunk-extension component between the chunk-size and the eol marker.
	This is parsed but ignored - a leading ';' is not checked for. Also, if an
	empty line is parsed this is also ignored. The function will also leave if
	there is an error in converting the hex number to its numeric value.
	@return		The parsing status for this chunk-size component. The value 
				ESectionDone indicates that all the chunk-data has been parsed
				and the value EBufferEmpty indicates that more message data is
				required to parse this chunk-size component.
	@leave		THttpDataParser::GetLineL
	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
														returned by the data 
														parser.
*/
	{
	// Get line from the parser
	TPtrC8 line;
	THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line);

	// Check the parse status...
	TParsingStatus  sizeStatus = ESectionDone;
	switch( parseResult )
		{
	case THttpDataParser::ELineParsed:
		{
		// Ok got the line - find the hex number defining the chunk-size.
		TInt val = InetProtTextUtils::ConvertDescriptorToHex(line, iDataSizeLeft);
		if( val < 0 )
			{
			// Error: invalid data.. ignore it.
			sizeStatus = ESectionDone;
			}
		// Do not care about the chunk-extension - do not bother to check that 
		// it is well formed.

		// The chunk-size has been parsed - use default return value.
		} break;
	case THttpDataParser::EPartialData:
		{
		// Not all of start-line received - need more data.
		sizeStatus = EBufferEmpty;
		} break;
	case THttpDataParser::EEmptyLine:
		{
		// The chunk-size was expected - got an empty line. Ignore.
		sizeStatus = ESectionNotDone;
		} break;
	default:
		// This covers EGotData case.
		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
		break;
		}
	return sizeStatus;
	}

CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkDataL(TPtrC8& aData)
/**
	Parses for a chunk-data component. The preceeding chunk-size component 
	specified the length of this chunk-data component. As the chunk-data can be
	segmented at the transport layer this function may need to called several
	times for the same chunk-data component.

	Section 3.6.1 in RFC2616 defines the following - 
		chunk			=	chunk-size [ chunk-extension ] CRLF
   							chunk-data CRLF
   
	The chunk-data component is delimited by the eol marker. If the amount of
	chunk-data does not match the amount defined by the chunk-size then the
	function will leave with KErrCorrupt.
	@param		aData	An output argument set to the chunk-data parsed.
	@return		The parsing status for this chunk-data component. The value 
				ESectionDone indicates that all the chunk-data has been parsed
				and the value EBufferEmpty indicates that more message data is
				required to parse this chunk-data component.
	@leave		KErrCorrupt	An incorrect amount of chunk-data was parsed.
	@panic		EInvariantFalse	The body data extraction indicated that it had
								extracted all the body data but the parser still
								expected more.
	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
														returned by the data 
														parser.
*/
   	{
   	// Has the data-chunk been read? Check iDataSizeLeft.
   	TParsingStatus dataStatus = ESectionDone;
   	if( iDataSizeLeft > 0 )
   		{
   		// There is chunk-data to extract
   		dataStatus = ReadBodyData(aData);
   		}

	// Does the end CRLF need to be parsed? The chunk-data may have been found.
	if( dataStatus == ESectionDone )
		{
		__ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() );

		// Need to extract an empty-line
		TPtrC8 line;
		THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line);

		// Check the parse status...
		switch( parseResult )
			{
		case THttpDataParser::EEmptyLine:
			{
			// Got the CRLF - section is done. Use default value.
			} break;
		case THttpDataParser::EPartialData:
			{
			dataStatus = EBufferEmpty;
			} break;
		case THttpDataParser::ELineParsed:
			{
			// There was more data than the chunk-size specified.
			
			// Error ignored to add robustness against problematic real-world servers.
			//User::Leave(KErrCorrupt);
			} break;
		default:
			// This covers EGotData case.
			THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
			break;
			}
		}
	return dataStatus;
	}

/*
 *	Methods from MHttpDataParserObserver
 */

void CHttpMessageParser::ReAllocBufferL(TInt aRequiredSize, TPtr8& aBuffer)
/**	
	Reallocates the line buffer. The parser supplies the line buffer to the data
	parser. The data parser needs more space to store the current line. If the 
	line buffer has not been created then it is created, otherwise it is 
	reallocated to at least the required size.
	@param		aRequiredSize	The minimum size of buffer required.
	@param		aBuffer			An output argument set to the reallocated buffer.
	@panic		EInvariantFalse	The required size was less then the current max
								size of the buffer.
*/
	{
	if( iLineBuffer == NULL )
		{
		// Create the buffer..
		iLineBuffer = HBufC8::NewL(aRequiredSize + KDefaultBufferSize);
		}
	else
		{
		__ASSERT_DEBUG( aRequiredSize > iLineBuffer->Des().MaxLength(), User::Invariant() );

		iLineBuffer = iLineBuffer->ReAllocL(aRequiredSize + KDefaultBufferSize);
		}
	aBuffer.Set(iLineBuffer->Des());
	}

void CHttpMessageParser::DeleteBuffer()
/**
	Deletes the line buffer.
	@internalComponent
*/
	{
	delete iLineBuffer;
	iLineBuffer = NULL;
	}

/*
 *	Methods from CActive
 */
void CHttpMessageParser::RunL()
/**
	Asynchronous request service handler. The parser state machine is processed
	in this function. Behaviour depends on the state. The parser will self-
	complete if the current data packet has unparsed data. If all the data in 
	the current data packet has been parsed then the parser suspends its state
	machine and waits for the observer to notify it when there is more data
	available. If the observer has reset the parser in one of the callback then
	the parser will defer resetting itself until it is back in the RunL().
	@panic		EHttpMessagePanicBadParserState	The parser state machine was in 
												a illegal state.
	@panic		EHttpMessagePanicBadDataState	The parser was in the incorrect
												data state.
	@panic		EHttpMessagePanicBadBodySize	The observer specified an unknown
												non-positive body size.
*/
	{
	__ASSERT_DEBUG( iDataState == EGotData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) ); 
	TParsingStatus status = ESectionNotDone;
	switch( iParserState )
		{
	case EIdle:
		{
		// Move to the ParsingStartLine and set the parsing status to move on.
		iParserState = EParsingStartLine;
		status = ESectionDone;
		} 
		//coverity [MISSING_BREAK]
		// Fallthrough is required here as we no longer parse line by line. We parse startline & headers (5) at one go.
	case EParsingStartLine:
		{
		status = ParseStartLineL();

		// Has the start-line been parsed?
		if( status == ESectionDone )
			{
			// Start-line has been parsed - parse for headers next.
			iParserState = EParsingHeaders;
			}
		else
			{
			break; //Start line is not parsed yet as we haven't received the complete data. Break and wait for more data.
			}
		}
	case EParsingHeaders:
		{
		status = ParseHeadersL();

		if( status == ESectionDone )
			{
			// No more headers - obtain entity body size from the observer.
			iDataSizeLeft = iObserver.BodySizeL();
			switch( iDataSizeLeft )
				{
			case MHttpMessageParserObserver::EChunked:
				{
				// Chunk-encoded body.
				iParserState = EParsingChunkSize;
				} break;
			case MHttpMessageParserObserver::EUnknown:
				{
				// for HTTP/1.0-style responses
				iParserState = EReadingBodyData;
				} break;
			case MHttpMessageParserObserver::ENoBody:
				{
				// No body expected - message is complete.
				iParserState = EMessageComplete;
				} break;
			default:
				__ASSERT_DEBUG( iDataSizeLeft > 0, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadBodySize) );

				// Non-encoded body.
				iParserState = EReadingBodyData;
				break;
				}
			}
		} break;
	case EParsingChunkSize:
		{
		status = ParseChunkSizeL();

		if( status == ESectionDone )
			{
			// Need to check the chunk size for last-chunk
			if( iDataSizeLeft == 0 )
				{
				// Recieved a chunk of size 0, inform the observer.
				TPtrC8 data;
				iObserver.BodyChunkL(data);

				// Notify the observer that the body has been received
				iObserver.BodyCompleteL();

				// Received the last-chunk token - now parse for trailers.
				iParserState = EParsingTrailerHeaders;
				}
			else
				{
				// Still expecting chunk-data...
				iParserState = EReadingChunkData;
				}
			}
		} break;
	case EParsingTrailerHeaders:
		{
		status = ParseHeadersL();

		if( status == ESectionDone )
			{
			// All the trailers have been found.
			iParserState = EMessageComplete;
			}
		} break;
	case EReadingBodyData:
		{
		// Read the body data...
		TPtrC8 data;
		status = ReadBodyData(data);

		// Only inform the observer if there is any data.
		if( data.Length() > 0 )
			{
			iObserver.BodyChunkL(data);
			}
		// Has all the body data been received - need to check that the parser
		// has not been reset.
		if( status == ESectionDone && iDataState != EReset )
			{
			// Notify the observer that the body has been received
			iObserver.BodyCompleteL();

			// Have received all the body data - message is complete.
			iParserState = EMessageComplete;
			}
		} break;
	case EReadingChunkData:
		{
		// Read the chunk-data...
		TPtrC8 data;
		status = ParseChunkDataL(data);

		// Only inform the observer if there is any data.
		if( data.Length() )
			{
			iObserver.BodyChunkL(data);
			}

		if( status == ESectionDone )
			{
			// Have received all the body data - get the size of the next chunk.
			iParserState = EParsingChunkSize;
			}
		} break;
	case EMessageComplete:
		{
		// Message complete - is there any excess data?
		TPtrC8 data;
		iDataParser.UnparsedData(data);

		// Notify the observer that the message is complete.
		iObserver.MessageCompleteL(data);

		// Move to the Idle state and set the parsing status to Stop.
		iParserState = EIdle;
		status = EStop;
		} break;
	default:
		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadParserState);
		break;
		}
	// Determine the next action...
	if( iDataState == EReset )
		{
		// The observer has reset the parser during one of the callbacks - it is
		// now safe to do the reset.
		DoReset();
		}
	else if( status == EBufferEmpty || status == EStop )
		{
		// Expect more data before being able to continue parsing.
		iDataState = EWaitingForData;

		// The current message component cannot be parsed due to lack of data,
		// or the parsing has stopped. Need to release the current data packet
		// (and wait for the next one in the case of EBufferEmpty).
		iObserver.ReleaseDataPacket();
		}
	else 
		{
		// Here status is ESectionNotDone or ESectionDone - either the current
		// message component has been completed and the parser needs to move 
		// onto the next component, or the current component still needs to 
		// complete. In both cases, self-complete to continue parsing.
		CompleteSelf();
		}
	}

void CHttpMessageParser::DoCancel()
/**
	Asynchronous request cancel. This function does nothing as the only asynch
	request that is made is a self-complete request.
*/
	{
	// Do nothing...
	}

TInt CHttpMessageParser::RunError(TInt aError)
/**	
	Asynchronous request service error handler. An error has occured whilst
	processing the state machine. Reset the parser and notify the observer of 
	the error.
	@param		aError	The error code.
	@return		An interger value of KErrNone indicates that the error has been
				handled.
	@post		The parser has been reset.
*/
	{
	// Stop parsing and reset.
	DoReset();

	// Notify the observer of the error
	return iObserver.HandleParserError(aError);
	}

void CHttpMessageParser::Flush ()
	{
	// Say a response like this.
	// HTTP/1.0 302 Moves\nStatus: 302 Moved\nPragma: no-cache\nLocation: http://127.0.0.1\n
	// In this response, there is no empty line after the header. Parser cannot parse the last header if 
	// it didn't find an empty line after the header. Force the parser to parse the last header line.
	if ( iDataState == EWaitingForData && iParserState == EParsingHeaders )
		{
		iDataParser.SetData ( KLineFeed() );		
		TRAP_IGNORE( ParseHeadersL () );
		}
	// Message is completed but observer notification hasn't happened
	// Notify now with uparsed data.
	if ( iParserState == EMessageComplete )
		{
		TPtrC8 unparsedData;
		iDataParser.UnparsedData(unparsedData);
		iObserver.MessageCompleteL(unparsedData);
		iParserState = EIdle;			
		}		
	}

	// Completes parsing of the message. Header parts has been parsed.
	// Complete the body part of the message. This happens in case of 3xx response
	// where HTTP FW parses the headers and notify the client/filters. The client/filter
	// will cancel the transaction and resubmit on the new URL.
TBool CHttpMessageParser::CompleteMessage ( const TDesC8& aData )
	{		
	// Set the parser data	
	if ( aData.Length () > 0 )
		iDataParser.SetData ( aData );
	
	// We are reading body data. A content length value is known.
	if ( iParserState == EReadingBodyData )		
 		{
		TPtrC8 data;
		if ( ReadBodyData(data) == ESectionDone )
			{
			iObserver.BodyCompleteL();
			iParserState = EMessageComplete;
 			}				
 		}
	// Read the chunked response.
	if ( iParserState == EReadingChunkData || iParserState == EParsingChunkSize 
		||  iParserState == EParsingTrailerHeaders )
		{
		TParsingStatus status = ESectionNotDone;
		// This tiny look executes till the buffer is empty or the parser state is EMessageComplete
		while ( status != EBufferEmpty && iParserState != EMessageComplete )
			{
			switch ( iParserState )
				{
				case EReadingChunkData:
					{
					TPtrC8 data;
					status = ParseChunkDataL(data);				
					if( status == ESectionDone )
						{				
						iParserState = EParsingChunkSize;
						}													
					}
				break;				
				case EParsingChunkSize:
				status = ParseChunkSizeL();
				if ( status == ESectionDone )
					iParserState = (iDataSizeLeft == 0) ? EParsingTrailerHeaders : EReadingChunkData;
				break;
					
				case EParsingTrailerHeaders:
				status = ParseHeadersL();
				if ( status == ESectionDone )
					iParserState = EMessageComplete;
				break;
				default:
				// Do nothing.
				break;
				}
			}				
 		}
	return iParserState == EMessageComplete;
 	}