servicediscoveryandcontrol/pnp/test/upnp/Server/Flow/src/httpserverflow.cpp
changeset 0 f5a58ecadc66
equal deleted inserted replaced
-1:000000000000 0:f5a58ecadc66
       
     1 // Copyright (c) 2008-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 // @internalComponent
       
    15 // 
       
    16 //
       
    17 
       
    18 //System Includes
       
    19 #include <comms-infras/ss_nodeinterfaces.h>
       
    20 
       
    21 #include <httpstringconstants.h>
       
    22 #include <http/thttphdrval.h>
       
    23 #include <httperr.h>
       
    24 #include <http/thttptable.h>
       
    25 #include <upnp/tupnptable.h>
       
    26 
       
    27 //Local Includes
       
    28 #include "httpserverflow.h"
       
    29 #include "upnppint.h"
       
    30 #include "httpserver.h"
       
    31 #include "upnpserverconstants.h"
       
    32 #include "upnplog.h"
       
    33 #include "upnpmemoryutils.h"
       
    34 
       
    35 __FLOG_STMT(_LIT8(KComponent,"Flow");)
       
    36 using namespace ESock;
       
    37 
       
    38 CHttpServerFlow* CHttpServerFlow::NewL ( CSubConnectionFlowFactoryBase& aFactory, CProtocolIntfBase* aProtocolIntf, const TDesC8& aUri, const TNodeId& aSubConnId )
       
    39 	{
       
    40 	CHttpServerFlow* self = new (ELeave) CHttpServerFlow( aFactory, aProtocolIntf, aSubConnId);
       
    41 	CleanupStack::PushL ( self );
       
    42 	self->ConstructL ( aUri );
       
    43 	CleanupStack::Pop (); // self
       
    44 	return self;
       
    45 	}
       
    46 
       
    47 CHttpServerFlow::CHttpServerFlow(CSubConnectionFlowFactoryBase& aFactory, ESock::CProtocolIntfBase* aProtocolIntf, const TNodeId& aSubConnId )
       
    48 	: CUPnPFlowBase( aFactory, aProtocolIntf, EHttpServerFlow, aSubConnId ),
       
    49 	iControlId ( 1 )
       
    50 	{
       
    51 	LOG_NODE_CREATE ( KESockFlowTag, CHttpServerFlow );
       
    52 	}
       
    53 
       
    54 void CHttpServerFlow::ConstructL ( const TDesC8& aUri )
       
    55 	{
       
    56 	iUri.CreateL ( aUri );
       
    57 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("Created CHttpServerFlow")));
       
    58 	}
       
    59 
       
    60 CHttpServerFlow::~CHttpServerFlow ()
       
    61 	{
       
    62 	iUri.Close ();
       
    63 	iSubConnectionProvider.Close ();
       
    64 	iControlTransactions.Reset();
       
    65 	iControlTransactions.Close ();
       
    66 
       
    67 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("Destroyed CHttpServerFlow")));
       
    68 	LOG_NODE_DESTROY(KESockFlowTag, CHttpServerFlow);
       
    69 	}
       
    70 
       
    71 //From CSubConnectionFlowBase MNode
       
    72 void CHttpServerFlow::ReceivedL ( const TRuntimeCtxId& aSender, const TNodeId& /*aRecipient*/, TSignatureBase& aMessage )
       
    73 	{	
       
    74 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ReceivedL")));
       
    75     
       
    76    	if ( aMessage.IsMessage<TEChild::TDestroy> () )
       
    77 		{
       
    78 		LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ReceivedL - Deleting current flow")));
       
    79         if ( iSessionControlNotify )
       
    80 	       	{
       
    81            	iSessionControlNotify->CanClose( MSessionControlNotify::EDelete );
       
    82 	       	}
       
    83        	DeleteThisFlow ();
       
    84 		}
       
    85 	else if ( aMessage.IsMessage<TCFDataClient::TStart> () )
       
    86 		{
       
    87 		LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ReceivedL - TCFMessage::TDataClientStart")));
       
    88         StartFlow ( address_cast < TNodeCtxId > ( aSender ) );
       
    89 		}
       
    90 	else if ( aMessage.IsMessage<TCFDataClient::TStop> () )
       
    91 		{
       
    92        	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ReceivedL - TCFMessage::TDataClientStop")));
       
    93        	StopFlow ( address_cast < TNodeCtxId > ( aSender ) );
       
    94 		}
       
    95 	else if ( aMessage.IsMessage<TUpnpMessage::TUPnPResponseInfo> () )
       
    96     	{
       
    97     	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ReceivedL - TUpnpMessage::TUPnPResponseInfo")));
       
    98     	const TUpnpMessage::TUPnPResponseInfo& msg = message_cast < TUpnpMessage::TUPnPResponseInfo > ( aMessage );
       
    99     	CServerTransaction* trans = static_cast < CServerTransaction* > ( msg.iTrans );
       
   100     	RMBufChain dataChain;
       
   101     	TUPnPMemoryUtils::CreateMBuf(dataChain, trans->MemParts());
       
   102     	trans->FreeMemChunk();
       
   103     	trans->AddBodyPart(dataChain);
       
   104     	HandleUPnPResponseL ( trans, msg.iStatus, msg.iInfo );
       
   105     	}
       
   106 	}
       
   107 	
       
   108 
       
   109 MFlowBinderControl* CHttpServerFlow::DoGetBinderControlL()
       
   110 	{
       
   111 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::DoGetBinderControlL")));
       
   112     return this;
       
   113 	}
       
   114 
       
   115 // MFlowBinderControl
       
   116 CSubConnectionFlowBase* CHttpServerFlow::Flow()
       
   117 	{
       
   118 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::Flow")));
       
   119 	return	this;
       
   120 	}
       
   121 
       
   122 MSessionControl* CHttpServerFlow::GetControlL (TInt /*aSessionType*/, MSessionControlNotify& aSessionControlNotify)
       
   123     {
       
   124     LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::GetControlL")));
       
   125     ASSERT(iSessionControlNotify == NULL);
       
   126 	iSessionControlNotify = &aSessionControlNotify;
       
   127 
       
   128     //As of now UPnP Flow doesn't care more abt SessionType, since i'm caring abt only CSocket Binding,
       
   129     //May be for RInternalSocket binding this needs to be iterated.
       
   130 
       
   131 	return this;
       
   132     }
       
   133 
       
   134 MSessionData* CHttpServerFlow::BindL(MSessionDataNotify& aNotify)
       
   135     {
       
   136     LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::BindL")));
       
   137 	ASSERT(iSessionDataNotify == NULL);
       
   138 	iSessionDataNotify = &aNotify;
       
   139 	iSubConnectionProvider.PostMessage ( Id (), TCFControlProvider::TActive ().CRef () );
       
   140 	return this;
       
   141     }
       
   142 
       
   143 void CHttpServerFlow::Unbind()
       
   144     {
       
   145     LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::Unbind")));
       
   146     iSessionControlNotify = NULL;
       
   147 	iSessionDataNotify = NULL;
       
   148 	}
       
   149 
       
   150 // MSessionControl
       
   151 void CHttpServerFlow::Shutdown(MSessionControl::TCloseType aOption)
       
   152 	{
       
   153 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::Shutdown")));
       
   154 
       
   155 	if ( MSessionControl::ENormal == aOption )
       
   156 		{
       
   157 		// error the pending control transactions
       
   158 		TUint count = iControlTransactions.Count ();
       
   159 
       
   160 		for ( TInt pos = 0; pos < count; pos++ )
       
   161 			{
       
   162 			CServerTransaction& trans = iControlTransactions[pos].iTransaction;
       
   163 			if ( !trans.ReadyToSend () )
       
   164 				{
       
   165 				// If we are not in ready to send state, it means that, we haven't created the response yet.
       
   166 				CHttpServerHandler::CreateResponse ( *( trans.Response() ), HTTPStatus::EInternalServerError, &trans.ServerObserver() );
       
   167 				trans.SetReadyToSend ();
       
   168 				THTTPEvent evt ( THTTPEvent::EGotResponseHeaders );
       
   169 				trans.ServerObserver ().OnHttpEvent ( &trans, evt );
       
   170 				}
       
   171 			else if ( trans.CloseNeeded () )
       
   172 				{
       
   173 				THTTPEvent evt ( THTTPEvent::EFailed );
       
   174 				trans.ServerObserver ().OnHttpEvent ( &trans, evt );
       
   175 				}
       
   176 			}
       
   177 
       
   178 		// remove control uri from http server, in order not to service further requests
       
   179 		TNodeCtxId serviceId ( MeshMachine::KActivityNull, NodeId () );
       
   180 		TNodeCtxId controlProviderId ( MeshMachine::KActivityNull, TNodeId::NullId () );
       
   181 		static_cast<CUPnPProtocolIntfBase *>( ProtocolIntf () )->RemoveServiceUri ( iUri, serviceId, controlProviderId );
       
   182 
       
   183 		// send data client idle to scpr
       
   184 		iSubConnectionProvider.PostMessage ( Id (), TCFControlProvider::TIdle ().CRef () );
       
   185 		}
       
   186 	}
       
   187 
       
   188 void CHttpServerFlow::ActiveOpen()
       
   189 	{
       
   190 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ActiveOpen")));
       
   191 	iSessionControlNotify->ConnectComplete();
       
   192 	}
       
   193 
       
   194 TInt CHttpServerFlow::GetOption(TUint aLevel, TUint aName, TDes8& anOption) const
       
   195 	{
       
   196 	if ( aLevel == KCHOptionLevel )
       
   197 		{
       
   198 
       
   199 		TControlTransaction trans ( FirstControlTransactionToNotify () );
       
   200 		switch ( aName )
       
   201 			{
       
   202 			case KCHMaxLength:
       
   203 				{
       
   204 				THTTPHdrVal value;
       
   205 				if ( GetHeaderValue ( *(trans.iTransaction.Request()), HTTP::EContentLength, value, THTTPTable::Table() ) == KErrNotFound )
       
   206 					{
       
   207 					*(TInt*) anOption.Ptr () = KErrUnknown;
       
   208 					}
       
   209 				else
       
   210 					{
       
   211 					*(TInt*) anOption.Ptr () = value.Int ();
       
   212 					}
       
   213 				}
       
   214 			break;
       
   215 
       
   216 			case KCHLastMessage:
       
   217 				{
       
   218 				*(TUint*) anOption.Ptr() = ( trans.iReadComplete && trans.iTransaction.BodyParts ().Length () == 0 );
       
   219 				}
       
   220 			break;
       
   221 
       
   222 			default:
       
   223 			ASSERT(0);
       
   224 			}
       
   225 		}
       
   226 	return 0;
       
   227 	}
       
   228 
       
   229 TInt CHttpServerFlow::SetOption(TUint level, TUint name, const TDesC8& anOption)
       
   230 	{
       
   231 	TInt err = KErrNone;
       
   232 	if ( level == KCHOptionLevel )
       
   233 		{
       
   234 		TPckgBuf <TCHMessageOption> option;
       
   235 		option.Copy ( anOption );
       
   236 		TInt pos = FindControlTransaction ( option().iId );
       
   237 		if ( pos == KErrNotFound )
       
   238 			return pos;
       
   239 		TControlTransaction trans ( iControlTransactions[pos] );
       
   240 		switch ( name )
       
   241 			{
       
   242 			case KCHMaxLength:
       
   243 				{
       
   244 				TRAP ( err, SetHeaderL ( *( trans.iTransaction.Response() ), HTTP::EContentLength, option().iValue, THTTPTable::Table() ) );
       
   245 
       
   246 				if ( err == KErrNone )
       
   247 					{
       
   248 					// This will set the basic headers
       
   249 					CHttpServerHandler::CreateResponse( *( trans.iTransaction.Response() ), HTTPStatus::EOk, &trans.iTransaction.ServerObserver() );
       
   250 					}
       
   251 				else
       
   252 					{
       
   253 					CHttpServerHandler::CreateResponse( *( trans.iTransaction.Response() ), HTTPStatus::EInternalServerError, &trans.iTransaction.ServerObserver() );
       
   254 					}
       
   255 
       
   256 				// Set the content length.
       
   257 				trans.iTransaction.SetDataLeft ( option().iValue );
       
   258 				// commented to solve race issue...
       
   259 				//trans.iTransaction.SetReadyToSend ();   // Move to the ready to send state. When the next write happens
       
   260 														// we just say body data is available
       
   261 				//THTTPEvent evt ( THTTPEvent::EGotResponseHeaders );
       
   262 				//trans.iTransaction.ServerObserver().OnHttpEvent ( &trans.iTransaction, evt );
       
   263 				}
       
   264 			break;
       
   265 
       
   266 			case KCHLastMessage:
       
   267 				{
       
   268 				trans.iTransaction.SetComplete();
       
   269 				THTTPEvent evt ( THTTPEvent::EResponseComplete );
       
   270 				trans.iTransaction.ServerObserver().OnHttpEvent ( &trans.iTransaction, evt );
       
   271 				}
       
   272 			break;
       
   273 
       
   274 			default:
       
   275 			ASSERT(0);
       
   276 			}
       
   277 		}
       
   278 	return err;
       
   279 	}
       
   280 
       
   281 // MSessionData
       
   282 
       
   283 TInt CHttpServerFlow::Write ( RMBufChain& aData, TUint aOptions, TSockAddr* /* anAddr */ )
       
   284 	{
       
   285 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::Write")));
       
   286 	TInt pos = FindControlTransaction ( aOptions );
       
   287 	TInt ret = KErrNone;
       
   288 	if ( pos == KErrNotFound )
       
   289 		return pos;
       
   290 
       
   291 	TInt len = aData.Length ();
       
   292 	CServerTransaction& trans = iControlTransactions[pos].iTransaction;
       
   293 	trans.AddBodyPart( aData );
       
   294 
       
   295 	if ( !trans.ReadyToSend() )
       
   296 		{
       
   297 		// If we are not in ready to send state, it means that, we haven't created the response yet.
       
   298 		trans.SetReadyToSend ();
       
   299 		THTTPEvent evt ( THTTPEvent::EGotResponseHeaders );
       
   300 		ret = trans.ServerObserver().OnHttpEvent ( &trans, evt );
       
   301 		}
       
   302 	else
       
   303 		{
       
   304 		THTTPEvent evt ( THTTPEvent::EGotResponseBodyData );
       
   305 		ret = trans.ServerObserver().OnHttpEvent ( &trans, evt );
       
   306 		}
       
   307 	if(ret != KErrNone)
       
   308 		{
       
   309 		RemoveControlTransaction ( iControlTransactions[pos].iId );
       
   310 		//Ownership is transferred to trans Object.
       
   311 		aData.Init ();
       
   312 		return ret;
       
   313 		}
       
   314 	
       
   315 	// remove control transaction object if complete data is received from socket
       
   316 	if ( !trans.CloseNeeded () ) // closed needed informs that complete data is not yet received
       
   317 		{
       
   318 		RemoveControlTransaction ( iControlTransactions[pos].iId );
       
   319 		}
       
   320 	
       
   321 	aData.Init ();
       
   322 	return len;
       
   323 	}
       
   324 
       
   325 TInt CHttpServerFlow::GetData ( RMBufChain& aData, TUint aLength, TUint /*aOptions*/, TSockAddr* /*anAddr*/ )
       
   326 	{
       
   327 	__ASSERT_DEBUG ( iControlTransactions.Count(), User::Invariant() );
       
   328 	CServerTransaction& trans = FirstControlTransactionToNotify ().iTransaction;
       
   329 
       
   330 	RMBufChain newChain;
       
   331 	RMBufChain& bodyChain = trans.BodyParts ();
       
   332 	TInt err = bodyChain.Split ( aLength, newChain );
       
   333 	if ( err != KErrNone )
       
   334 		{
       
   335 		// Our request validation failed. Generate the response
       
   336 		CHttpServerHandler::CreateResponse ( *( trans.Response () ), err, &trans.ServerObserver() );
       
   337 		THTTPEvent evt ( THTTPEvent::ECompleteResponse );
       
   338 		trans.ServerObserver().OnHttpEvent ( &trans, evt );
       
   339 		}
       
   340 
       
   341 	aData.Assign ( bodyChain );
       
   342 	bodyChain = newChain;
       
   343 
       
   344 	return aData.Length();
       
   345 	}
       
   346 
       
   347 // From MHttpEventObserver
       
   348 TInt CHttpServerFlow::OnHttpEvent ( CTransaction* aTransaction, THTTPEvent& aEvent )
       
   349 	{
       
   350 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::OnHttpEvent")));
       
   351 	TInt ret = KErrNone;
       
   352 
       
   353 	switch ( aEvent.iStatus  )
       
   354 		{
       
   355 		case THTTPEvent::EGotRequestHeaders:
       
   356 			{
       
   357 			LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::OnHttpEvent - THTTPEvent::EGotRequestHeaders")));
       
   358 			ret = ValidateUPnPRequest( *aTransaction );
       
   359 			}
       
   360 			break;
       
   361 
       
   362 		default:
       
   363 			ret = RouteTransaction ( static_cast < CServerTransaction& > (*aTransaction), aEvent );
       
   364 			break;
       
   365 		}
       
   366 
       
   367 	if ( ret != KErrNone )
       
   368 		{
       
   369 		LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::OnHttpEvent - THTTPEvent::EGotRequestHeaders - Creating response")));
       
   370 		// Our request validation failed. Generate the response
       
   371 		CHttpServerHandler::CreateResponse ( *( aTransaction->Response () ), ret, &(static_cast < CServerTransaction& > (*aTransaction).ServerObserver()) );
       
   372 		( static_cast < CServerTransaction* > ( aTransaction ) )->SetComplete ();
       
   373 		( static_cast < CServerTransaction* > ( aTransaction ) )->SetReadyToSend ();
       
   374 		}
       
   375 	return KErrNone;
       
   376 	}
       
   377 
       
   378 void CHttpServerFlow::StartFlow ( TNodeCtxId aSender )
       
   379 	{
       
   380 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::StartFlow")));
       
   381 
       
   382 	TNodeCtxId serviceId ( MeshMachine::KActivityNull, NodeId () );	
       
   383 	TRAPD ( err, static_cast<CUPnPProtocolIntfBase *>( ProtocolIntf() )->AddServiceUriL( iUri, *this, serviceId, aSender ) );
       
   384 	
       
   385 	if ( err != KErrNone )
       
   386 		iSubConnectionProvider.PostMessage ( Id (), TEBase::TError ( TCFDataClient::TStart::Id (), err ).CRef() );
       
   387 	}
       
   388 
       
   389 void CHttpServerFlow::StopFlow ( TNodeCtxId aSender )
       
   390 	{
       
   391 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::StopFlow")));
       
   392 
       
   393 	TNodeCtxId serviceId ( MeshMachine::KActivityNull, NodeId () );	
       
   394 	__ASSERT_DEBUG  ( iSubConnectionProvider.RecipientId () == aSender, User::Invariant () );
       
   395 
       
   396 	static_cast<CUPnPProtocolIntfBase *>( ProtocolIntf () )->RemoveServiceUri ( iUri, serviceId, aSender );
       
   397 	}
       
   398 
       
   399 TInt CHttpServerFlow::RouteTransaction ( CServerTransaction& aTrans, THTTPEvent& aEvent )
       
   400 	{
       
   401 	//  ... Support chunked-encoding here for the request body data incase of POST & MPOST.
       
   402 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::RouteTransaction")));
       
   403 	TBool dataUp = EFalse;
       
   404 
       
   405 	RStringF post = aTrans.Request()->Method().Pool().StringF(HTTP::EPOST, THTTPTable::Table());
       
   406 	RStringF mPost = aTrans.Request()->Method().Pool().StringF(UPnP::EMPost, TUPnPTable::Table());
       
   407 	
       
   408 	if((aTrans.Request()->Method() == post) || (aTrans.Request()->Method() == mPost))
       
   409 		{
       
   410 		dataUp = ETrue;
       
   411 		}
       
   412 
       
   413 	switch ( aEvent.iStatus )
       
   414 		{
       
   415 		case THTTPEvent::EGotRequestBodyData:
       
   416 			{
       
   417 			LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::RouteTransaction - THTTPEvent::EGotRequestBodyData")));
       
   418 			MHTTPDataSupplier* bodySupplier = aTrans.Request( )->Body( );
       
   419 			ASSERT(bodySupplier);
       
   420 
       
   421 			TPtrC8 bodyPtr;
       
   422 			RMBufChain bodyChain;
       
   423 			bodySupplier->GetNextDataPart ( bodyPtr );
       
   424 			bodyChain.Create ( bodyPtr );
       
   425 			aTrans.AddBodyPart( bodyChain );
       
   426 			bodySupplier->ReleaseData( );
       
   427 
       
   428 			if ( dataUp )
       
   429 				{
       
   430 				TInt index = FindOrCreateControlTransaction ( aTrans );
       
   431 				if ( index < KErrNotFound )
       
   432 					{
       
   433 					return HTTPStatus::EInternalServerError;
       
   434 					}
       
   435 
       
   436 				TInt notifyDataLen = 0;
       
   437 				if ( !iControlTransactions[index].iReadTriggered )
       
   438 					{
       
   439 					notifyDataLen = sizeof ( TInt );
       
   440 					iControlTransactions[index].iReadTriggered = ETrue;
       
   441 					}
       
   442 
       
   443 				iSessionDataNotify->NewData ( notifyDataLen + bodyPtr.Length () );
       
   444 				}
       
   445 			}
       
   446 			break;
       
   447 		case THTTPEvent::ERequestComplete:
       
   448 			{
       
   449 			if ( !dataUp )
       
   450 				{
       
   451 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::RouteTransaction - THTTPEvent::ERequestComplete")));
       
   452 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::RouteTransaction - THTTPEvent::ERequestComplete - Posting message TUpnpMessage::TUPnPRequestInfo")));
       
   453 
       
   454 				IterateHeaders ( *aTrans.Request() );		
       
   455 				// Post the response to the SCPR
       
   456 				iSubConnectionProvider.PostMessage ( Id (), TUpnpMessage::TUPnPRequestInfo ( &aTrans ).CRef () );
       
   457 				}
       
   458 			else
       
   459 				{
       
   460 				TInt index = FindOrCreateControlTransaction ( aTrans );
       
   461 				iControlTransactions[index].iReadComplete = ETrue;
       
   462 				// make sure if receive is pending, ASocket will complete it
       
   463 				iSessionDataNotify->NewData ( 0 );
       
   464 				}
       
   465 			}
       
   466 		break;
       
   467 
       
   468 		default:
       
   469 		ASSERT(0);
       
   470 		break;
       
   471 		}
       
   472 	return KErrNone;
       
   473 	}
       
   474 
       
   475 TInt CHttpServerFlow::GetHeaderValue ( const CRequest& aRequest, TInt aFieldIndex, THTTPHdrVal& aFieldVal, const TStringTable& aTable ) const
       
   476 	{
       
   477 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::GetHeaderValue")));
       
   478 	CRequest& req = const_cast < CRequest& > ( aRequest );
       
   479 	RRequest request = req.Handle ();
       
   480 	RHTTPHeaders headers = request.GetHeaderCollection ();
       
   481 	RStringF fieldStr = aRequest.StringPool ().StringF ( aFieldIndex, aTable );
       
   482 	return headers.GetField ( fieldStr, 0, aFieldVal );
       
   483 	}
       
   484 
       
   485 TBool CHttpServerFlow::MatchHeaderValue ( const CRequest& aRequest, TInt aFieldIndex, const TDesC8& aFieldValue, const TStringTable& aTable )
       
   486 	{
       
   487 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::MatchHeaderValue")));
       
   488 	THTTPHdrVal value;
       
   489 	if (  GetHeaderValue ( aRequest, aFieldIndex, value, aTable ) == KErrNone )
       
   490 		{
       
   491 		RStringF valStr = value.StrF();
       
   492 		if ( valStr.DesC ().Compare ( aFieldValue ) == 0 )
       
   493 			{
       
   494 			LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::MatchHeaderValue - Returned ETrue")));
       
   495 			return ETrue;
       
   496 			}
       
   497 		}
       
   498 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::MatchHeaderValue - Returned EFalse")));
       
   499 	return EFalse;
       
   500 	}
       
   501 
       
   502 TBool CHttpServerFlow::IsHeaderPresent ( const CRequest& aRequest, TInt aFieldIndex, const TStringTable& aTable )
       
   503 	{
       
   504 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::IsHeaderPresent")));
       
   505 	THTTPHdrVal value;
       
   506 	return GetHeaderValue ( aRequest, aFieldIndex, value, aTable ) == KErrNone;
       
   507 	}
       
   508 
       
   509 TBool CHttpServerFlow::IsValidNTHeader ( const CRequest& aRequest )
       
   510 	{
       
   511 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::IsValidNTHeader")));
       
   512 	_LIT8 ( KNTValue, "upnp:event" );
       
   513 	return MatchHeaderValue ( aRequest, UPnP::ENT, KNTValue(), TUPnPTable::Table() );
       
   514 	}
       
   515 
       
   516 TBool CHttpServerFlow::IsValidContentTypeHeader ( const CRequest& aRequest )
       
   517 	{
       
   518 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::IsValidContentTypeHeader")));
       
   519 	_LIT8 ( KContentTypeValue, "text/xml" );
       
   520 	return MatchHeaderValue ( aRequest, HTTP::EContentType, KContentTypeValue(), THTTPTable::Table() );
       
   521 	}
       
   522 
       
   523 TBool CHttpServerFlow::IsValidNTSHeader ( const CRequest& aRequest )
       
   524 	{
       
   525 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::IsValidNTSHeader")));
       
   526 	_LIT8 ( KNTSValue, "upnp:propchange" );
       
   527 	return MatchHeaderValue ( aRequest, UPnP::ENTS, KNTSValue(), TUPnPTable::Table() );
       
   528 	}
       
   529 
       
   530 TBool CHttpServerFlow::IsValidManHeader ( const CRequest& aRequest )
       
   531 	{
       
   532 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::IsValidManHeader")));
       
   533 	_LIT8 ( KManValue, "http://schemas.xmlsoap.org/soap/envelope/");
       
   534 	return MatchHeaderValue ( aRequest, UPnP::EMAN, KManValue(), TUPnPTable::Table() );
       
   535 	}
       
   536 
       
   537 void CHttpServerFlow::SetHeaderL ( CResponse& aRequest, TInt aFieldIndex, RMemChunk& aVal, const TStringTable& aTable )
       
   538 	{
       
   539 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::SetHeaderL")));
       
   540 	RStringPool sp = aRequest.StringPool ();
       
   541 	RHTTPHeaders hdr = aRequest.Handle ().GetHeaderCollection ();
       
   542 	TPtrC8 value;
       
   543 	value.Set ( aVal.First()->Ptr (), aVal.Length () );
       
   544 	RStringF valStr = sp.OpenFStringL ( value );
       
   545 	THTTPHdrVal hdrVal ( valStr );
       
   546 	hdr.SetFieldL ( sp.StringF(aFieldIndex, aTable ), hdrVal );
       
   547 	valStr.Close ();
       
   548 	}
       
   549 
       
   550 void CHttpServerFlow::SetHeaderL ( CResponse& aRequest, TInt aFieldIndex, TInt aVal, const TStringTable& aTable )
       
   551 	{
       
   552 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::SetHeaderL")));
       
   553 	RStringPool sp = aRequest.StringPool ();
       
   554 	RHTTPHeaders hdr = aRequest.Handle ().GetHeaderCollection ();
       
   555 	THTTPHdrVal hdrVal ( aVal );
       
   556 	hdr.SetFieldL ( sp.StringF(aFieldIndex, aTable ), hdrVal );
       
   557 	}
       
   558 
       
   559 
       
   560 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   561 TInt CHttpServerFlow::ValidateUPnPRequest ( const CTransaction& aTrans )
       
   562 	{
       
   563 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest")));
       
   564 	
       
   565 	RStringF get = aTrans.Request()->Method().Pool().StringF(HTTP::EGET, THTTPTable::Table());
       
   566 	RStringF post = aTrans.Request()->Method().Pool().StringF(HTTP::EPOST, THTTPTable::Table());
       
   567 	TInt method = KErrNotFound;
       
   568 	TInt ret = KErrNone;
       
   569 	
       
   570 	if((aTrans.Request()->Method() == get)||(aTrans.Request()->Method() == post))
       
   571 		{
       
   572 		method = aTrans.Request()->Method().Index(THTTPTable::Table());
       
   573 		switch ( method )
       
   574 			{
       
   575 			case HTTP::EGET:
       
   576 			break;
       
   577 
       
   578 			case HTTP::EPOST:
       
   579 				{
       
   580 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Validating POST request")));
       
   581 				ret = ValidatePostRequest ( aTrans );
       
   582 				}
       
   583 			break;
       
   584 
       
   585 			default:
       
   586 				{
       
   587 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - HTTPStatus::ENotImplemented")));
       
   588 				ret = HTTPStatus::ENotImplemented;
       
   589 				}
       
   590 			break;
       
   591 			}
       
   592 		}
       
   593 	
       
   594 	else
       
   595 		{
       
   596 		method = aTrans.Request()->Method().Index(TUPnPTable::Table());
       
   597 		switch ( method )
       
   598 			{
       
   599 			case UPnP::EMPost:
       
   600 				{
       
   601 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Validating MPOST request")));
       
   602 				ret = ValidateMPostRequest ( aTrans );
       
   603 				}
       
   604 			break;
       
   605 
       
   606 			case UPnP::ESubscribe:
       
   607 				{
       
   608 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Validating SUBSCRIBE request")));
       
   609 				ret = ValidateSubscribeRequest ( aTrans );
       
   610 				}
       
   611 			break;
       
   612 
       
   613 			case UPnP::EUnsubscribe:
       
   614 				{
       
   615 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Validating UNSUBSCRIBE request")));
       
   616 				ret = ValidateUnsubscribeRequest ( aTrans );
       
   617 				}
       
   618 			break;
       
   619 
       
   620 			case UPnP::ENotify:
       
   621 				{
       
   622 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Validating NOTIFY request")));
       
   623 				ret = ValidateNotifyRequest ( aTrans );
       
   624 				}
       
   625 			break;
       
   626 
       
   627 			default:
       
   628 				{
       
   629 				LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - HTTPStatus::ENotImplemented")));
       
   630 				ret = HTTPStatus::ENotImplemented;
       
   631 				}
       
   632 			break;
       
   633 			}
       
   634 		}
       
   635 	
       
   636 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUPnPRequest - Returned with error code = %d"), ret));
       
   637 	return ret;
       
   638 	}
       
   639 
       
   640 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   641 TInt CHttpServerFlow::ValidateSubscribeRequest ( const CTransaction& aTrans )
       
   642 	{
       
   643 	// General: SUBSCRIBE request MUST not contain a body
       
   644 	// 1. Subscribing: SUBSCRIBE request MUST contain a CALLBACK, NT headers. The NT header valus MUST be
       
   645 	// "upnp:event".
       
   646 	// Response: 412 status code ( pre-condition failed) - If NT or CALLBACK request header is not present
       
   647 	//	400 Bad request - If SID header and one of NT or CALLBACK headers are present
       
   648 	// 2. Re-subscribing: SUBSCRIBE request MUST contain a SID header and MUST not contain
       
   649 	// a CALLBACK or NT header.
       
   650 	// Response: Same as 1 except, if the SID is not valid we respond with 412 ( pre-condition failed )
       
   651 	// The 412 error condition is handled in this case by the SCPR. Here we will not check whether the
       
   652 	// SID is valid or not
       
   653 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateSubscribeRequest")));
       
   654 	CRequest& request = *( aTrans.Request () );
       
   655 
       
   656 	if ( request.Handle ().HasBody () )
       
   657 		return HTTPStatus::EBadRequest; // MUST not contain a body
       
   658 
       
   659 	TBool callback = IsHeaderPresent ( request, UPnP::ECallback, TUPnPTable::Table() );
       
   660 	TBool nt = 	IsHeaderPresent ( request, UPnP::ENT, TUPnPTable::Table() );
       
   661 	TBool sid = IsHeaderPresent ( request, UPnP::ESID, TUPnPTable::Table() );
       
   662 
       
   663 	if ( callback && nt )
       
   664 		{
       
   665 		// Subscription request
       
   666 		// Check the NT header value == MUST be "upnp:event"
       
   667 		if ( IsValidNTHeader ( request ) )
       
   668 			return sid ? HTTPStatus::EBadRequest : KErrNone;
       
   669 		}
       
   670 	if ( sid )
       
   671 		{
       
   672 		// Re-subscribe request
       
   673 		return ( callback || nt ) ? HTTPStatus::EBadRequest : KErrNone;
       
   674 		}
       
   675 
       
   676 	// Otherwise pre-condition failed
       
   677 	return HTTPStatus::EPreconditionFailed;
       
   678 	}
       
   679 
       
   680 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   681 TInt CHttpServerFlow::ValidateUnsubscribeRequest ( const CTransaction& aTrans )
       
   682 	{
       
   683 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateUnsubscribeRequest")));
       
   684 	// General: UNSUBSCRIBE request MUST not contain a body
       
   685 	CRequest& request = *( aTrans.Request () );
       
   686 
       
   687 	if ( request.Handle ().HasBody () )
       
   688 		return HTTPStatus::EBadRequest; // MUST not contain a body
       
   689 
       
   690 	TBool callback = IsHeaderPresent ( request, UPnP::ECallback, TUPnPTable::Table() );
       
   691 	TBool nt = 	IsHeaderPresent ( request, UPnP::ENT, TUPnPTable::Table() );
       
   692 	TBool sid = IsHeaderPresent ( request, UPnP::ESID, TUPnPTable::Table() );
       
   693 	if ( sid )
       
   694 		{
       
   695 		return ( callback || nt ) ? HTTPStatus::EBadRequest : KErrNone;
       
   696 		}
       
   697 	// Otherwise pre-condition failed
       
   698 	return HTTPStatus::EPreconditionFailed;
       
   699 	}
       
   700 
       
   701 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   702 TInt CHttpServerFlow::ValidateNotifyRequest ( const CTransaction& aTrans )
       
   703 	{
       
   704 	// NOTIFY request
       
   705 	// 1. Content-Type header value MUST be "text/xml"
       
   706 	// 2. NT header value MUST be "upnp:event"
       
   707 	// 3. NTS header value MUST be "upnp:propchange"
       
   708 	// 4. SID header value MUST be present. ( we check only the presence here )
       
   709 	// 5. SEQ header value MUST be present ( we check only the presence here )
       
   710 	// 6. MUST contain a body
       
   711 	// Errors:
       
   712 	// Bad request - If NT or NTS header is missing
       
   713 	// Pre-condition failed - If the above 1-5 condition is failed
       
   714 
       
   715 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::ValidateNotifyRequest")));
       
   716 	CRequest& request = *( aTrans.Request () );
       
   717 
       
   718 	if ( IsValidContentTypeHeader ( request ) )
       
   719 		{
       
   720 		if ( IsValidNTHeader ( request ) && IsValidNTSHeader ( request ) )
       
   721 			{
       
   722 			if ( IsHeaderPresent ( request, UPnP::ESID, TUPnPTable::Table() ) && IsHeaderPresent ( request, UPnP::ESEQ, TUPnPTable::Table() ) )
       
   723 				{
       
   724 				return KErrNone;
       
   725 				}
       
   726 			}
       
   727 		else if ( !IsHeaderPresent ( request, UPnP::ENT, TUPnPTable::Table() ) || !IsHeaderPresent ( request, UPnP::ENTS, TUPnPTable::Table() ) )
       
   728 			{
       
   729 			return HTTPStatus::EBadRequest;
       
   730 			}
       
   731 		}
       
   732 	// Otherwise pre-condition failed
       
   733 	return HTTPStatus::EPreconditionFailed;
       
   734 	}
       
   735 
       
   736 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   737 TInt CHttpServerFlow::ValidatePostRequest ( const CTransaction& aTrans )
       
   738 	{
       
   739 	// 1. Content-Type header MUST contain text/xml value  -- On failure responds with EUnsupportedMediaType status code
       
   740 	// 2. Content-Type header MUST contain charset as utf-8 --- On failure responds with EBadRequest status code
       
   741 	// 3. SOAPACTION header field MUST be present -- On failure responds with EPreconditionFailed status code
       
   742 	// 4. POST request MUST contain a body	-- On failure responds with EBadRequest
       
   743 	// 5. Currently we support only the POST method with Content-Length header on the request. If it is not present
       
   744 	// we return Not Implemented ( 501 ) error response. In the future it may need implementation.
       
   745 
       
   746 	CRequest& request = *( aTrans.Request () );
       
   747 
       
   748 	if ( !IsHeaderPresent ( request, HTTP::EContentLength, THTTPTable::Table() ) )
       
   749 		return HTTPStatus::ENotImplemented;
       
   750 
       
   751 	if ( IsValidContentTypeHeader ( request ) && IsValidCharset ( request ) )
       
   752 		{
       
   753 		if ( IsHeaderPresent ( request , UPnP::ESoapAction, TUPnPTable::Table() ) )
       
   754 			{
       
   755 			return KErrNone;
       
   756 			}
       
   757 		else
       
   758 			{
       
   759 			return HTTPStatus::EPreconditionFailed;
       
   760 			}
       
   761 		}
       
   762 	return HTTPStatus::EUnsupportedMediaType;
       
   763 	}
       
   764 
       
   765 // Returns KErrNone if validation is suceeded otherwise one of the HTTP error status code will be returned
       
   766 TInt CHttpServerFlow::ValidateMPostRequest ( const CTransaction& aTrans )
       
   767 	{
       
   768 	// 1. MAN header must be present with value "http://schemas.xmlsoap,org/soap/envelope/"
       
   769 	// On failure responds with KEBadRequest
       
   770 	// 2. Checks the namespace and that matches with ns-SOAPACTION... ??
       
   771 	// 3. Validate like the normal POST request
       
   772 	CRequest& request = *( aTrans.Request () );
       
   773 
       
   774 	if ( IsHeaderPresent ( request, UPnP::EMAN, TUPnPTable::Table() ) && IsValidManHeader( request ) )
       
   775 		{
       
   776 		return ValidatePostRequest ( aTrans );
       
   777 		}
       
   778 	return HTTPStatus::EBadRequest;
       
   779 	}
       
   780 
       
   781 void CHttpServerFlow::HandleUPnPResponseL ( CServerTransaction* aTrans, TInt aStatus, SMetaDataNetCtorExt* aInfo )
       
   782 	{
       
   783 	LOG(ESockLogExternal::Printf(KSubsysHttpSrvrFlow, KComponent, _L8("CHttpServerFlow::HandleUPnPResponseL")));
       
   784 	CResponse* response = aTrans->Response ();
       
   785 
       
   786 	CHttpServerHandler::CreateResponse ( *response, aStatus, &aTrans->ServerObserver ()  );
       
   787 
       
   788 	if ( aTrans->Request ()->Handle ().Method ().Index ( TUPnPTable::Table() ) ==  UPnP::ESubscribe && aStatus == HTTPStatus::EOk )
       
   789 		{
       
   790 		TSubsribeResponseInfo& info = *( reinterpret_cast < TSubsribeResponseInfo* > ( aInfo ) );
       
   791 		SetHeaderL ( *response, UPnP::ESID, info.iSid, TUPnPTable::Table());
       
   792 		SetHeaderL ( *response, UPnP::ETimeout, info.iTimeout, TUPnPTable::Table() );
       
   793 		info.iSid.Free (); // Free the RMBufChain
       
   794 		}
       
   795 
       
   796 	// Set the Content-Length header
       
   797 	SetHeaderL ( *response, HTTP::EContentLength, aTrans->BodyParts ().Length (), THTTPTable::Table() );
       
   798 	aTrans->SetDataLeft ( aTrans->BodyParts ().Length () );
       
   799 	aTrans->SetComplete ();
       
   800 	
       
   801 	THTTPEvent evt ( THTTPEvent::ECompleteResponse );	
       
   802 	aTrans->ServerObserver().OnHttpEvent ( aTrans, evt );
       
   803 	}
       
   804 
       
   805 void CHttpServerFlow::IterateHeaders ( CRequest& aRequest )
       
   806 	{
       
   807 	RHTTPHeaders hdr = aRequest.Handle ().GetHeaderCollection ();
       
   808 	RStringPool sp = aRequest.StringPool ();
       
   809 	THTTPHdrFieldIter it = hdr.Fields ();
       
   810 
       
   811 	while ( it.AtEnd () == EFalse )
       
   812 		{
       
   813 		RStringTokenF fieldName = it ();
       
   814 		RStringF fieldNameStr = sp.StringF ( fieldName );
       
   815 
       
   816 		THTTPHdrVal fieldVal;
       
   817 		hdr.GetField ( fieldNameStr, 0, fieldVal );
       
   818 
       
   819 		++it;
       
   820 		}
       
   821 	}
       
   822 
       
   823 TInt CHttpServerFlow::FindOrCreateControlTransaction ( CServerTransaction& aTrans )
       
   824 	{
       
   825 	TInt index = FindControlTransaction ( aTrans );
       
   826 	if ( index == KErrNotFound )
       
   827 		{
       
   828 		return AddControlTransaction ( iControlId++, aTrans );
       
   829 		}
       
   830 	return index;
       
   831 	}
       
   832 
       
   833 
       
   834 TInt CHttpServerFlow::FindControlTransaction ( TInt aId ) const
       
   835 	{
       
   836 	for ( TInt i = 0; i < iControlTransactions.Count (); ++i )
       
   837 		{
       
   838 		if ( iControlTransactions[i].iId == aId )
       
   839 			{
       
   840 			return i;
       
   841 			}
       
   842 		}
       
   843 	return KErrNotFound;
       
   844 	}
       
   845 
       
   846 TInt CHttpServerFlow::FindControlTransaction ( CServerTransaction& aTrans ) const
       
   847 	{
       
   848 	for ( TInt i = 0; i < iControlTransactions.Count (); ++i )
       
   849 		{
       
   850 		if ( &(iControlTransactions[i].iTransaction) == &aTrans )
       
   851 			{
       
   852 			return i;
       
   853 			}
       
   854 		}
       
   855 	return KErrNotFound;
       
   856 	}
       
   857 
       
   858 void CHttpServerFlow::RemoveControlTransaction ( TInt aId )
       
   859 	{
       
   860 	TInt pos = FindControlTransaction ( aId );
       
   861 	if ( pos != KErrNotFound )
       
   862 		{
       
   863 		iControlTransactions.Remove ( pos );
       
   864 		}
       
   865 	}
       
   866 
       
   867 TInt CHttpServerFlow::AddControlTransaction ( TInt aId, CServerTransaction& aTrans )
       
   868 	{
       
   869 	TControlTransaction trans ( aId, aTrans );
       
   870 	TInt err = iControlTransactions.Append ( trans );
       
   871 	if ( err == KErrNone )
       
   872 		{
       
   873 		const TUint KIdentifiersLen = sizeof ( TInt );
       
   874 		TBuf8<KIdentifiersLen> controlBuf;
       
   875 		controlBuf.AppendNumFixedWidth ( aId, EDecimal, KIdentifiersLen );
       
   876 
       
   877 		RMBufChain bufChain;
       
   878 		err = bufChain.Create ( controlBuf );
       
   879 		if ( err != KErrNone )
       
   880 			return err;
       
   881 		aTrans.BodyParts().Prepend ( bufChain );
       
   882 		}
       
   883 
       
   884 	return err == KErrNone ? iControlTransactions.Count() - 1 : err;
       
   885 	}
       
   886 
       
   887 CHttpServerFlow::TControlTransaction CHttpServerFlow::FirstControlTransactionToNotify () const
       
   888 	{
       
   889 	TInt i = 0;
       
   890 	for ( i = 0; i < iControlTransactions.Count (); ++i )
       
   891 		{
       
   892 		if ( iControlTransactions[i].iNotifyComplete == EFalse )
       
   893 			{
       
   894 			break;
       
   895 			}
       
   896 		}
       
   897 	return iControlTransactions[i];
       
   898 	}
       
   899 
       
   900