applayerprotocols/httptransportfw/httpmessage/chttpmessageparser.cpp
changeset 0 b16258d2340f
equal deleted inserted replaced
-1:000000000000 0:b16258d2340f
       
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "chttpmessageparser.h"
       
    17 
       
    18 #include <inetprottextutils.h>
       
    19 #include <uriutilscommon.h>
       
    20 
       
    21 #include "mhttpmessageparserobserver.h"
       
    22 #include "thttpmessagepanic.h"
       
    23 
       
    24 const TInt KDefaultBufferSize	= 128;
       
    25 const TUint KColon				= ':';
       
    26 _LIT8 ( KLineFeed, "\n" );
       
    27 
       
    28 // 'this' used in base member initializer list, The 'this' pointer being used is a base class pointer.
       
    29 #pragma warning( disable : 4355 )
       
    30 
       
    31 CHttpMessageParser* CHttpMessageParser::NewL(MHttpMessageParserObserver& aObserver)
       
    32 /**
       
    33 	Factory constructor. 
       
    34 	@param		aObserver	The observer for the parser.
       
    35 	@return		A pointer to a fully constructed and initialised object.
       
    36 */
       
    37 	{
       
    38 	return new (ELeave) CHttpMessageParser(aObserver);
       
    39 	}
       
    40 
       
    41 CHttpMessageParser::~CHttpMessageParser()
       
    42 /**
       
    43 	Destructor
       
    44 */
       
    45 	{
       
    46 	Cancel();
       
    47 
       
    48 	// Cleanup
       
    49 	delete iLineBuffer;
       
    50 	}
       
    51 
       
    52 CHttpMessageParser::CHttpMessageParser(MHttpMessageParserObserver& aObserver)
       
    53 : CActive(CActive::EPriorityStandard + 1), iObserver(aObserver), iDataParser(*this)
       
    54 /**
       
    55 	Constructor.
       
    56 	See note in CHttpMessageComposer
       
    57 */
       
    58 	{
       
    59 	CActiveScheduler::Add(this);
       
    60 	}
       
    61 
       
    62 void CHttpMessageParser::ReceivedMessageData()
       
    63 /**
       
    64 	Notifies the parser of more message data. The parser gets the data packet
       
    65 	from the observer and continues processing its state machine.
       
    66 	@panic	EHttpMessagePanicBadDataState	The current data packet has not been
       
    67 											completely parsed.
       
    68 */
       
    69 	{
       
    70 	__ASSERT_DEBUG( iDataState == EWaitingForData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );
       
    71 
       
    72 	// Have now got some data - update the data state
       
    73 	iDataState = EGotData;
       
    74 
       
    75 	// Get the received data packet
       
    76 	TPtrC8 data;
       
    77 	iObserver.GetDataPacket(data);
       
    78 
       
    79 	// Pass it to the data parser
       
    80 	iDataParser.SetData(data);
       
    81 
       
    82 	// Continue parsing...
       
    83 	CompleteSelf();
       
    84 	}
       
    85 
       
    86 void CHttpMessageParser::CompletedBodyDataL()
       
    87 /**
       
    88 	Tells the parser that there is no more body data. When using HTTP 1.0 style
       
    89 	responses the body data maybe of unknown length so this method allows clients
       
    90 	to inform the parser that the body data is completed.
       
    91 */
       
    92 	{
       
    93 	iDataSizeLeft = 0;
       
    94 	
       
    95 	// Notify the observer that the body has been received
       
    96 	iObserver.BodyCompleteL();
       
    97 
       
    98 	// Notify the observer that the message is complete.
       
    99 	iObserver.MessageCompleteL(KNullDesC8());
       
   100 
       
   101 	// Move to the Idle state.
       
   102 	iParserState = EIdle;
       
   103 	iDataState = EWaitingForData;
       
   104 	}
       
   105 
       
   106 void CHttpMessageParser::Reset()
       
   107 /**
       
   108 	Parser reset request. As the observer can reset the parser during one the 
       
   109 	callback functions, the parser must check for re-entrancy to avoid releasing
       
   110 	resources that are still required. If the parser is either waiting for more
       
   111 	message data or is waiting to process its state machine, the parser can 
       
   112 	safely reset immediately. Otherwise the parser is being reset from within 
       
   113 	its RunL() and so it must defer resetting itself to a safer point. This is
       
   114 	the point in the RunL() where the next step is decided.
       
   115 	@panic EHttpMessagePanicDoubleReset	The parser has been reset twice in one 
       
   116 										of the observer callback functions.
       
   117 */
       
   118 	{
       
   119 	// Check the data state of the parser - the parser cannot be reset if the 
       
   120 	// Reset() was called in one of the observer callbacks. It is safe to reset
       
   121 	// now if - 
       
   122 	// 1) the data state is WaitingForData
       
   123 	// 2) the parser is active - waiting to for its RunL() to be called.
       
   124 	if( iDataState == EWaitingForData || IsActive() )
       
   125 		{
       
   126 		// Cancel and do the reset.
       
   127 		Cancel();
       
   128 		DoReset();
       
   129 		}
       
   130 	else
       
   131 		{
       
   132 		// Debug check for a double Reset() call...
       
   133 		__ASSERT_DEBUG( iDataState != EReset, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicDoubleReset) );
       
   134 
       
   135 		// The Reset() was called inside a callback - defer resetting the parser
       
   136 		// until call stack back in the parser RunL().
       
   137 		iDataState = EReset;
       
   138 		}
       
   139 	}
       
   140 
       
   141 void CHttpMessageParser::CompleteSelf()
       
   142 /**
       
   143 	Self-complete function. Ensures that the state machine is processed.
       
   144 */
       
   145 	{
       
   146 	TRequestStatus* pStat = &iStatus;
       
   147 	User::RequestComplete(pStat, KErrNone);
       
   148 	SetActive();
       
   149 	}
       
   150 
       
   151 void CHttpMessageParser::DoReset()
       
   152 /**
       
   153 	Resets the parser. The parser moves into the Idle state. Allocated resources 
       
   154 	are also reset. 
       
   155 */
       
   156 	{
       
   157 	// Reset the parser - parser state should be Idle and the data state should
       
   158 	// be WaitingForData.
       
   159 	iParserState = EIdle;
       
   160 	iDataState = EWaitingForData;
       
   161 
       
   162 	// Reset the data parser
       
   163 	iDataParser.Reset();
       
   164 	}
       
   165 
       
   166 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseStartLineL()
       
   167 /**
       
   168 	Parses for the start-line. The start-line is delimited by the first eol 
       
   169 	marker. No further parsing of the start-line is done by the parser - it is
       
   170 	left to the observer to parse for either a request-line or a status-line.
       
   171 	The observer is informed of the start-line.
       
   172 	@return		The parsing status for the start-line. The value ESectionDone 
       
   173 				indicates the start-line has been parsed. The value EBufferEmpty
       
   174 				indicates that more message data is required to parse the 
       
   175 				start-line.
       
   176 	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
       
   177 														returned by the data 
       
   178 														parser.
       
   179 */
       
   180 	{
       
   181 	// Get line from the parser
       
   182 	TPtrC8 startLine;
       
   183 	THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(startLine);
       
   184 
       
   185 	// Check the parse status...
       
   186 	TParsingStatus  startlineStatus = ESectionNotDone;
       
   187 	switch( parseResult )
       
   188 		{
       
   189 	case THttpDataParser::ELineParsed:
       
   190 		{
       
   191 		// The start-line has been parsed - inform the observer.
       
   192 		iObserver.StartLineL(startLine);
       
   193 		startlineStatus = ESectionDone;
       
   194 		} break;
       
   195 	case THttpDataParser::EPartialData:
       
   196 		{
       
   197 		// Not all of start-line received - need more data.
       
   198 		startlineStatus = EBufferEmpty;
       
   199 		} break;
       
   200 	case THttpDataParser::EEmptyLine:
       
   201 		{
       
   202 		// Although blank-lines are not allowed, be tolerent. Need to parse for 
       
   203 		// the start-line again. The start-line is not complete - use default 
       
   204 		// return value.
       
   205 		} break;
       
   206 	default:
       
   207 		// This covers EGotData case.
       
   208 		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
       
   209 		break;
       
   210 		}
       
   211 	return startlineStatus;
       
   212 	}
       
   213 
       
   214 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseSingleHeaderL()
       
   215 /**
       
   216 	Parses for a header field line. A header field line is delimited by an eol 
       
   217 	marker. If an empty line is parser then this indicates the end of the header
       
   218 	fields section. If a header field is found then the field name and field
       
   219 	value are extracted. The observer is passed the field name and value.
       
   220 	
       
   221 	If a malformed header field is found (ie the colon ':' is missing) then that
       
   222 	line is ignored. The function will leave if an error occurs in removing the 
       
   223 	leading and trailing whitespace from the parsed header field name. 
       
   224 	
       
   225 	Note that the header field value may be empty.
       
   226 	@return		The parsing status for the header fields section. The value 
       
   227 				ESectionDone indicates that an empty line has been parsed which
       
   228 				means that all the header fields have been parsed. The value 
       
   229 				ESectionNotDone indicates that a header field has been found. 
       
   230 				The	value EBufferEmpty indicates that more message data is 
       
   231 				required to parse the header fields section.
       
   232 	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
       
   233 														returned by the data 
       
   234 														parser.
       
   235 */
       
   236 	{
       
   237 	// Get line from the parser
       
   238 	TPtrC8 line;
       
   239 	THttpDataParser::TParseResult parseResult = iDataParser.GetHeaderLineL(line);
       
   240 
       
   241 	// Check the parse status...
       
   242 	TParsingStatus  headerStatus = ESectionNotDone;
       
   243 	switch( parseResult )
       
   244 		{
       
   245 	case THttpDataParser::ELineParsed:
       
   246 		{
       
   247 		// Got a header field - find the field name and value.
       
   248 		TInt colonPos = line.Locate(KColon);
       
   249 
       
   250 		if( colonPos == KErrNotFound )
       
   251 			{
       
   252 			// No colon - syntax error. Be robust and ignore the this line. 
       
   253 			// There are still more headers to find - use default return value.
       
   254 			break;
       
   255 			}
       
   256 
       
   257 		// Found the field name and value
       
   258 		TPtrC8 name = line.Left(colonPos);
       
   259 		TPtrC8 value = line.Mid(colonPos + 1);	// move past the colon
       
   260 
       
   261 		// Remove any leading/trailing whitespace but if it is empty, try to be robust and continue with the next one.
       
   262 		TInt rwsErr = InetProtTextUtils::RemoveWhiteSpace(name, InetProtTextUtils::ERemoveBoth);
       
   263 
       
   264 		// Field value may be empty - e.g. "host:". Ignore any returned error.
       
   265 		InetProtTextUtils::RemoveWhiteSpace(value, InetProtTextUtils::ERemoveBoth);
       
   266 
       
   267 		// Pass the header field name and value to the observer if it seemed valid.
       
   268 		if( rwsErr == KErrNone )
       
   269 			{
       
   270 			iObserver.HeaderL(name, value);
       
   271 			}
       
   272  
       
   273 		// There are still more headers to find - use default return value.
       
   274 		} break;
       
   275 	case THttpDataParser::EPartialData:
       
   276 		{
       
   277 		// Not all of header field received - need more data.
       
   278 		headerStatus = EBufferEmpty;
       
   279 		} break;
       
   280 	case THttpDataParser::EEmptyLine:
       
   281 		{
       
   282 		// Reached the end of the headers section - move on.
       
   283 		headerStatus = ESectionDone;
       
   284 		} break;
       
   285 	default:
       
   286 		// This covers EGotData case.
       
   287 		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
       
   288 		break;
       
   289 		}
       
   290 	return headerStatus;
       
   291 	}
       
   292 
       
   293 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseHeadersL()
       
   294     {
       
   295     // Do parsing of 5 headers without AO scheduling
       
   296     TParsingStatus  headerStatus = ParseSingleHeaderL();
       
   297     if(headerStatus == ESectionNotDone)
       
   298         {
       
   299         headerStatus = ParseSingleHeaderL();
       
   300         if(headerStatus == ESectionNotDone)
       
   301             {
       
   302             headerStatus = ParseSingleHeaderL();
       
   303             if(headerStatus == ESectionNotDone)
       
   304                 {
       
   305                headerStatus = ParseSingleHeaderL();
       
   306                 if(headerStatus == ESectionNotDone)
       
   307                     {
       
   308                     headerStatus = ParseSingleHeaderL();
       
   309                     }
       
   310                 }
       
   311             }
       
   312         }
       
   313     return headerStatus;
       
   314     }
       
   315 
       
   316 CHttpMessageParser::TParsingStatus CHttpMessageParser::ReadBodyData(TPtrC8& aData)
       
   317 /**
       
   318 	Reads the entity body data from the current data packet. As the entity body
       
   319 	data can be segmented by the tranport layer this function may need to be 
       
   320 	called more than once. The remaining amount of entity body data is updated.
       
   321 	@param		aData	An output argument set the entity body data extracted 
       
   322 						from the current data packet.
       
   323 	@return		The parsing status for the entity body. The value ESectionDone 
       
   324 				indicates the entity body has been extracted. The value 
       
   325 				EBufferEmpty indicates that more message data is required to 
       
   326 				extract the entity body.
       
   327 	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
       
   328 														returned by the data 
       
   329 														parser.
       
   330 	@panic		EInvariantFalse	The data parser returned a status that conflicts
       
   331 								with the amount of entity body data to extract.
       
   332 */
       
   333 	{
       
   334 	// If the body length is unknown then set the data size to a maximum value
       
   335 	if(iDataSizeLeft==-1)
       
   336 		iDataSizeLeft = KMaxTInt;
       
   337 
       
   338 	// Get the remaining body data from the data parser
       
   339 	THttpDataParser::TParseResult parseResult = iDataParser.GetData(aData, iDataSizeLeft);
       
   340 
       
   341 	// Update the size of the data left to get
       
   342 	iDataSizeLeft -= aData.Length();
       
   343 
       
   344 	// Check the parse status...
       
   345 	TParsingStatus  dataStatus = EBufferEmpty;
       
   346 	switch( parseResult )
       
   347 		{
       
   348 	case THttpDataParser::EGotData:
       
   349 		{
       
   350 		__ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() );
       
   351 
       
   352 		// Got all the data - section is done.
       
   353 		dataStatus = ESectionDone;
       
   354 		} break;
       
   355 	case THttpDataParser::EPartialData:
       
   356 		{
       
   357 		__ASSERT_DEBUG( iDataSizeLeft > 0, User::Invariant() );
       
   358 
       
   359 		// Use the default value...
       
   360 		} break;
       
   361 	default:
       
   362 		// This covers ELineParsed and EEmptyLine cases.
       
   363 		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
       
   364 		break;
       
   365 		}
       
   366 	return dataStatus;
       
   367 	}
       
   368 
       
   369 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkSizeL()
       
   370 /**
       
   371 	Parses for a chunk-size component. The chunk-size component specifies the 
       
   372 	size of following chunk-data component. As the chunk-size can be segmented 
       
   373 	at the transport layer this function may need to called several times for 
       
   374 	the same chunk-size component.
       
   375 
       
   376 	Section 3.6.1 in RFC2616 defines the following - 
       
   377 		chunk			=	chunk-size [ chunk-extension ] CRLF
       
   378 							chunk-data CRLF
       
   379 		chunk-size		=	1*HEX
       
   380 		chunk-extension	=	*( ";" chunk-ext-name [ "=" chunk-ext-val ] )
       
   381 
       
   382 	The chunk-size component is delimited by the eol marker. There can be an 
       
   383 	optional chunk-extension component between the chunk-size and the eol marker.
       
   384 	This is parsed but ignored - a leading ';' is not checked for. Also, if an
       
   385 	empty line is parsed this is also ignored. The function will also leave if
       
   386 	there is an error in converting the hex number to its numeric value.
       
   387 	@return		The parsing status for this chunk-size component. The value 
       
   388 				ESectionDone indicates that all the chunk-data has been parsed
       
   389 				and the value EBufferEmpty indicates that more message data is
       
   390 				required to parse this chunk-size component.
       
   391 	@leave		THttpDataParser::GetLineL
       
   392 	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
       
   393 														returned by the data 
       
   394 														parser.
       
   395 */
       
   396 	{
       
   397 	// Get line from the parser
       
   398 	TPtrC8 line;
       
   399 	THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line);
       
   400 
       
   401 	// Check the parse status...
       
   402 	TParsingStatus  sizeStatus = ESectionDone;
       
   403 	switch( parseResult )
       
   404 		{
       
   405 	case THttpDataParser::ELineParsed:
       
   406 		{
       
   407 		// Ok got the line - find the hex number defining the chunk-size.
       
   408 		TInt val = InetProtTextUtils::ConvertDescriptorToHex(line, iDataSizeLeft);
       
   409 		if( val < 0 )
       
   410 			{
       
   411 			// Error: invalid data.. ignore it.
       
   412 			sizeStatus = ESectionDone;
       
   413 			}
       
   414 		// Do not care about the chunk-extension - do not bother to check that 
       
   415 		// it is well formed.
       
   416 
       
   417 		// The chunk-size has been parsed - use default return value.
       
   418 		} break;
       
   419 	case THttpDataParser::EPartialData:
       
   420 		{
       
   421 		// Not all of start-line received - need more data.
       
   422 		sizeStatus = EBufferEmpty;
       
   423 		} break;
       
   424 	case THttpDataParser::EEmptyLine:
       
   425 		{
       
   426 		// The chunk-size was expected - got an empty line. Ignore.
       
   427 		sizeStatus = ESectionNotDone;
       
   428 		} break;
       
   429 	default:
       
   430 		// This covers EGotData case.
       
   431 		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
       
   432 		break;
       
   433 		}
       
   434 	return sizeStatus;
       
   435 	}
       
   436 
       
   437 CHttpMessageParser::TParsingStatus CHttpMessageParser::ParseChunkDataL(TPtrC8& aData)
       
   438 /**
       
   439 	Parses for a chunk-data component. The preceeding chunk-size component 
       
   440 	specified the length of this chunk-data component. As the chunk-data can be
       
   441 	segmented at the transport layer this function may need to called several
       
   442 	times for the same chunk-data component.
       
   443 
       
   444 	Section 3.6.1 in RFC2616 defines the following - 
       
   445 		chunk			=	chunk-size [ chunk-extension ] CRLF
       
   446    							chunk-data CRLF
       
   447    
       
   448 	The chunk-data component is delimited by the eol marker. If the amount of
       
   449 	chunk-data does not match the amount defined by the chunk-size then the
       
   450 	function will leave with KErrCorrupt.
       
   451 	@param		aData	An output argument set to the chunk-data parsed.
       
   452 	@return		The parsing status for this chunk-data component. The value 
       
   453 				ESectionDone indicates that all the chunk-data has been parsed
       
   454 				and the value EBufferEmpty indicates that more message data is
       
   455 				required to parse this chunk-data component.
       
   456 	@leave		KErrCorrupt	An incorrect amount of chunk-data was parsed.
       
   457 	@panic		EInvariantFalse	The body data extraction indicated that it had
       
   458 								extracted all the body data but the parser still
       
   459 								expected more.
       
   460 	@panic		EHttpMessagePanicBadDataParserResult	An unexpected status was 
       
   461 														returned by the data 
       
   462 														parser.
       
   463 */
       
   464    	{
       
   465    	// Has the data-chunk been read? Check iDataSizeLeft.
       
   466    	TParsingStatus dataStatus = ESectionDone;
       
   467    	if( iDataSizeLeft > 0 )
       
   468    		{
       
   469    		// There is chunk-data to extract
       
   470    		dataStatus = ReadBodyData(aData);
       
   471    		}
       
   472 
       
   473 	// Does the end CRLF need to be parsed? The chunk-data may have been found.
       
   474 	if( dataStatus == ESectionDone )
       
   475 		{
       
   476 		__ASSERT_DEBUG( iDataSizeLeft == 0, User::Invariant() );
       
   477 
       
   478 		// Need to extract an empty-line
       
   479 		TPtrC8 line;
       
   480 		THttpDataParser::TParseResult parseResult = iDataParser.GetLineL(line);
       
   481 
       
   482 		// Check the parse status...
       
   483 		switch( parseResult )
       
   484 			{
       
   485 		case THttpDataParser::EEmptyLine:
       
   486 			{
       
   487 			// Got the CRLF - section is done. Use default value.
       
   488 			} break;
       
   489 		case THttpDataParser::EPartialData:
       
   490 			{
       
   491 			dataStatus = EBufferEmpty;
       
   492 			} break;
       
   493 		case THttpDataParser::ELineParsed:
       
   494 			{
       
   495 			// There was more data than the chunk-size specified.
       
   496 			
       
   497 			// Error ignored to add robustness against problematic real-world servers.
       
   498 			//User::Leave(KErrCorrupt);
       
   499 			} break;
       
   500 		default:
       
   501 			// This covers EGotData case.
       
   502 			THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataParserResult);
       
   503 			break;
       
   504 			}
       
   505 		}
       
   506 	return dataStatus;
       
   507 	}
       
   508 
       
   509 /*
       
   510  *	Methods from MHttpDataParserObserver
       
   511  */
       
   512 
       
   513 void CHttpMessageParser::ReAllocBufferL(TInt aRequiredSize, TPtr8& aBuffer)
       
   514 /**	
       
   515 	Reallocates the line buffer. The parser supplies the line buffer to the data
       
   516 	parser. The data parser needs more space to store the current line. If the 
       
   517 	line buffer has not been created then it is created, otherwise it is 
       
   518 	reallocated to at least the required size.
       
   519 	@param		aRequiredSize	The minimum size of buffer required.
       
   520 	@param		aBuffer			An output argument set to the reallocated buffer.
       
   521 	@panic		EInvariantFalse	The required size was less then the current max
       
   522 								size of the buffer.
       
   523 */
       
   524 	{
       
   525 	if( iLineBuffer == NULL )
       
   526 		{
       
   527 		// Create the buffer..
       
   528 		iLineBuffer = HBufC8::NewL(aRequiredSize + KDefaultBufferSize);
       
   529 		}
       
   530 	else
       
   531 		{
       
   532 		__ASSERT_DEBUG( aRequiredSize > iLineBuffer->Des().MaxLength(), User::Invariant() );
       
   533 
       
   534 		iLineBuffer = iLineBuffer->ReAllocL(aRequiredSize + KDefaultBufferSize);
       
   535 		}
       
   536 	aBuffer.Set(iLineBuffer->Des());
       
   537 	}
       
   538 
       
   539 void CHttpMessageParser::DeleteBuffer()
       
   540 /**
       
   541 	Deletes the line buffer.
       
   542 	@internalComponent
       
   543 */
       
   544 	{
       
   545 	delete iLineBuffer;
       
   546 	iLineBuffer = NULL;
       
   547 	}
       
   548 
       
   549 /*
       
   550  *	Methods from CActive
       
   551  */
       
   552 void CHttpMessageParser::RunL()
       
   553 /**
       
   554 	Asynchronous request service handler. The parser state machine is processed
       
   555 	in this function. Behaviour depends on the state. The parser will self-
       
   556 	complete if the current data packet has unparsed data. If all the data in 
       
   557 	the current data packet has been parsed then the parser suspends its state
       
   558 	machine and waits for the observer to notify it when there is more data
       
   559 	available. If the observer has reset the parser in one of the callback then
       
   560 	the parser will defer resetting itself until it is back in the RunL().
       
   561 	@panic		EHttpMessagePanicBadParserState	The parser state machine was in 
       
   562 												a illegal state.
       
   563 	@panic		EHttpMessagePanicBadDataState	The parser was in the incorrect
       
   564 												data state.
       
   565 	@panic		EHttpMessagePanicBadBodySize	The observer specified an unknown
       
   566 												non-positive body size.
       
   567 */
       
   568 	{
       
   569 	__ASSERT_DEBUG( iDataState == EGotData, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) ); 
       
   570 	TParsingStatus status = ESectionNotDone;
       
   571 	switch( iParserState )
       
   572 		{
       
   573 	case EIdle:
       
   574 		{
       
   575 		// Move to the ParsingStartLine and set the parsing status to move on.
       
   576 		iParserState = EParsingStartLine;
       
   577 		status = ESectionDone;
       
   578 		} 
       
   579 		//coverity [MISSING_BREAK]
       
   580 		// Fallthrough is required here as we no longer parse line by line. We parse startline & headers (5) at one go.
       
   581 	case EParsingStartLine:
       
   582 		{
       
   583 		status = ParseStartLineL();
       
   584 
       
   585 		// Has the start-line been parsed?
       
   586 		if( status == ESectionDone )
       
   587 			{
       
   588 			// Start-line has been parsed - parse for headers next.
       
   589 			iParserState = EParsingHeaders;
       
   590 			}
       
   591 		else
       
   592 			{
       
   593 			break; //Start line is not parsed yet as we haven't received the complete data. Break and wait for more data.
       
   594 			}
       
   595 		}
       
   596 	case EParsingHeaders:
       
   597 		{
       
   598 		status = ParseHeadersL();
       
   599 
       
   600 		if( status == ESectionDone )
       
   601 			{
       
   602 			// No more headers - obtain entity body size from the observer.
       
   603 			iDataSizeLeft = iObserver.BodySizeL();
       
   604 			switch( iDataSizeLeft )
       
   605 				{
       
   606 			case MHttpMessageParserObserver::EChunked:
       
   607 				{
       
   608 				// Chunk-encoded body.
       
   609 				iParserState = EParsingChunkSize;
       
   610 				} break;
       
   611 			case MHttpMessageParserObserver::EUnknown:
       
   612 				{
       
   613 				// for HTTP/1.0-style responses
       
   614 				iParserState = EReadingBodyData;
       
   615 				} break;
       
   616 			case MHttpMessageParserObserver::ENoBody:
       
   617 				{
       
   618 				// No body expected - message is complete.
       
   619 				iParserState = EMessageComplete;
       
   620 				} break;
       
   621 			default:
       
   622 				__ASSERT_DEBUG( iDataSizeLeft > 0, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadBodySize) );
       
   623 
       
   624 				// Non-encoded body.
       
   625 				iParserState = EReadingBodyData;
       
   626 				break;
       
   627 				}
       
   628 			}
       
   629 		} break;
       
   630 	case EParsingChunkSize:
       
   631 		{
       
   632 		status = ParseChunkSizeL();
       
   633 
       
   634 		if( status == ESectionDone )
       
   635 			{
       
   636 			// Need to check the chunk size for last-chunk
       
   637 			if( iDataSizeLeft == 0 )
       
   638 				{
       
   639 				// Recieved a chunk of size 0, inform the observer.
       
   640 				TPtrC8 data;
       
   641 				iObserver.BodyChunkL(data);
       
   642 
       
   643 				// Notify the observer that the body has been received
       
   644 				iObserver.BodyCompleteL();
       
   645 
       
   646 				// Received the last-chunk token - now parse for trailers.
       
   647 				iParserState = EParsingTrailerHeaders;
       
   648 				}
       
   649 			else
       
   650 				{
       
   651 				// Still expecting chunk-data...
       
   652 				iParserState = EReadingChunkData;
       
   653 				}
       
   654 			}
       
   655 		} break;
       
   656 	case EParsingTrailerHeaders:
       
   657 		{
       
   658 		status = ParseHeadersL();
       
   659 
       
   660 		if( status == ESectionDone )
       
   661 			{
       
   662 			// All the trailers have been found.
       
   663 			iParserState = EMessageComplete;
       
   664 			}
       
   665 		} break;
       
   666 	case EReadingBodyData:
       
   667 		{
       
   668 		// Read the body data...
       
   669 		TPtrC8 data;
       
   670 		status = ReadBodyData(data);
       
   671 
       
   672 		// Only inform the observer if there is any data.
       
   673 		if( data.Length() > 0 )
       
   674 			{
       
   675 			iObserver.BodyChunkL(data);
       
   676 			}
       
   677 		// Has all the body data been received - need to check that the parser
       
   678 		// has not been reset.
       
   679 		if( status == ESectionDone && iDataState != EReset )
       
   680 			{
       
   681 			// Notify the observer that the body has been received
       
   682 			iObserver.BodyCompleteL();
       
   683 
       
   684 			// Have received all the body data - message is complete.
       
   685 			iParserState = EMessageComplete;
       
   686 			}
       
   687 		} break;
       
   688 	case EReadingChunkData:
       
   689 		{
       
   690 		// Read the chunk-data...
       
   691 		TPtrC8 data;
       
   692 		status = ParseChunkDataL(data);
       
   693 
       
   694 		// Only inform the observer if there is any data.
       
   695 		if( data.Length() )
       
   696 			{
       
   697 			iObserver.BodyChunkL(data);
       
   698 			}
       
   699 
       
   700 		if( status == ESectionDone )
       
   701 			{
       
   702 			// Have received all the body data - get the size of the next chunk.
       
   703 			iParserState = EParsingChunkSize;
       
   704 			}
       
   705 		} break;
       
   706 	case EMessageComplete:
       
   707 		{
       
   708 		// Message complete - is there any excess data?
       
   709 		TPtrC8 data;
       
   710 		iDataParser.UnparsedData(data);
       
   711 
       
   712 		// Notify the observer that the message is complete.
       
   713 		iObserver.MessageCompleteL(data);
       
   714 
       
   715 		// Move to the Idle state and set the parsing status to Stop.
       
   716 		iParserState = EIdle;
       
   717 		status = EStop;
       
   718 		} break;
       
   719 	default:
       
   720 		THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadParserState);
       
   721 		break;
       
   722 		}
       
   723 	// Determine the next action...
       
   724 	if( iDataState == EReset )
       
   725 		{
       
   726 		// The observer has reset the parser during one of the callbacks - it is
       
   727 		// now safe to do the reset.
       
   728 		DoReset();
       
   729 		}
       
   730 	else if( status == EBufferEmpty || status == EStop )
       
   731 		{
       
   732 		// Expect more data before being able to continue parsing.
       
   733 		iDataState = EWaitingForData;
       
   734 
       
   735 		// The current message component cannot be parsed due to lack of data,
       
   736 		// or the parsing has stopped. Need to release the current data packet
       
   737 		// (and wait for the next one in the case of EBufferEmpty).
       
   738 		iObserver.ReleaseDataPacket();
       
   739 		}
       
   740 	else 
       
   741 		{
       
   742 		// Here status is ESectionNotDone or ESectionDone - either the current
       
   743 		// message component has been completed and the parser needs to move 
       
   744 		// onto the next component, or the current component still needs to 
       
   745 		// complete. In both cases, self-complete to continue parsing.
       
   746 		CompleteSelf();
       
   747 		}
       
   748 	}
       
   749 
       
   750 void CHttpMessageParser::DoCancel()
       
   751 /**
       
   752 	Asynchronous request cancel. This function does nothing as the only asynch
       
   753 	request that is made is a self-complete request.
       
   754 */
       
   755 	{
       
   756 	// Do nothing...
       
   757 	}
       
   758 
       
   759 TInt CHttpMessageParser::RunError(TInt aError)
       
   760 /**	
       
   761 	Asynchronous request service error handler. An error has occured whilst
       
   762 	processing the state machine. Reset the parser and notify the observer of 
       
   763 	the error.
       
   764 	@param		aError	The error code.
       
   765 	@return		An interger value of KErrNone indicates that the error has been
       
   766 				handled.
       
   767 	@post		The parser has been reset.
       
   768 */
       
   769 	{
       
   770 	// Stop parsing and reset.
       
   771 	DoReset();
       
   772 
       
   773 	// Notify the observer of the error
       
   774 	return iObserver.HandleParserError(aError);
       
   775 	}
       
   776 
       
   777 void CHttpMessageParser::Flush ()
       
   778 	{
       
   779 	// Say a response like this.
       
   780 	// HTTP/1.0 302 Moves\nStatus: 302 Moved\nPragma: no-cache\nLocation: http://127.0.0.1\n
       
   781 	// In this response, there is no empty line after the header. Parser cannot parse the last header if 
       
   782 	// it didn't find an empty line after the header. Force the parser to parse the last header line.
       
   783 	if ( iDataState == EWaitingForData && iParserState == EParsingHeaders )
       
   784 		{
       
   785 		iDataParser.SetData ( KLineFeed() );		
       
   786 		TRAP_IGNORE( ParseHeadersL () );
       
   787 		}
       
   788 	// Message is completed but observer notification hasn't happened
       
   789 	// Notify now with uparsed data.
       
   790 	if ( iParserState == EMessageComplete )
       
   791 		{
       
   792 		TPtrC8 unparsedData;
       
   793 		iDataParser.UnparsedData(unparsedData);
       
   794 		iObserver.MessageCompleteL(unparsedData);
       
   795 		iParserState = EIdle;			
       
   796 		}		
       
   797 	}
       
   798 
       
   799 	// Completes parsing of the message. Header parts has been parsed.
       
   800 	// Complete the body part of the message. This happens in case of 3xx response
       
   801 	// where HTTP FW parses the headers and notify the client/filters. The client/filter
       
   802 	// will cancel the transaction and resubmit on the new URL.
       
   803 TBool CHttpMessageParser::CompleteMessage ( const TDesC8& aData )
       
   804 	{		
       
   805 	// Set the parser data	
       
   806 	if ( aData.Length () > 0 )
       
   807 		iDataParser.SetData ( aData );
       
   808 	
       
   809 	// We are reading body data. A content length value is known.
       
   810 	if ( iParserState == EReadingBodyData )		
       
   811  		{
       
   812 		TPtrC8 data;
       
   813 		if ( ReadBodyData(data) == ESectionDone )
       
   814 			{
       
   815 			iObserver.BodyCompleteL();
       
   816 			iParserState = EMessageComplete;
       
   817  			}				
       
   818  		}
       
   819 	// Read the chunked response.
       
   820 	if ( iParserState == EReadingChunkData || iParserState == EParsingChunkSize 
       
   821 		||  iParserState == EParsingTrailerHeaders )
       
   822 		{
       
   823 		TParsingStatus status = ESectionNotDone;
       
   824 		// This tiny look executes till the buffer is empty or the parser state is EMessageComplete
       
   825 		while ( status != EBufferEmpty && iParserState != EMessageComplete )
       
   826 			{
       
   827 			switch ( iParserState )
       
   828 				{
       
   829 				case EReadingChunkData:
       
   830 					{
       
   831 					TPtrC8 data;
       
   832 					status = ParseChunkDataL(data);				
       
   833 					if( status == ESectionDone )
       
   834 						{				
       
   835 						iParserState = EParsingChunkSize;
       
   836 						}													
       
   837 					}
       
   838 				break;				
       
   839 				case EParsingChunkSize:
       
   840 				status = ParseChunkSizeL();
       
   841 				if ( status == ESectionDone )
       
   842 					iParserState = (iDataSizeLeft == 0) ? EParsingTrailerHeaders : EReadingChunkData;
       
   843 				break;
       
   844 					
       
   845 				case EParsingTrailerHeaders:
       
   846 				status = ParseHeadersL();
       
   847 				if ( status == ESectionDone )
       
   848 					iParserState = EMessageComplete;
       
   849 				break;
       
   850 				default:
       
   851 				// Do nothing.
       
   852 				break;
       
   853 				}
       
   854 			}				
       
   855  		}
       
   856 	return iParserState == EMessageComplete;
       
   857  	}
       
   858 
       
   859 
       
   860