applayerpluginsandutils/httpprotocolplugins/WspProtocolHandler/CWspCOProtocolHandler.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/WspProtocolHandler/CWspCOProtocolHandler.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1237 @@
+// Copyright (c) 2001-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:
+//
+
+// System includes
+#include <http/framework/crxdata.h>
+#include <http/rhttpheaders.h>
+#include <http/thttpevent.h>
+#include <httpstringconstants.h>
+#include <wsp/wsptypes.h>
+#include <wsp/cwsptransporthandler.h>
+#include <wsp/mwspcosessioninvoker.h>
+#include <wsp/mwspcomethodinvoker.h>
+#include <wspstringconstants.h>
+#include <wspstdconstants.h>
+#include <wsperror.h>
+
+// User includes
+#include "cwspcotransaction.h"
+#include "cwspcapabilityinfo.h"
+#include "cwspproxyinfoprovider.h"
+#include "cconnectiontimer.h"
+#include "cwspprimitivesender.h"
+#include "wsppanic.h"
+#include "cwspheaderutils.h"
+#include "cwspheadercodec.h"
+
+// Class signature
+#include "cwspcoprotocolhandler.h"
+
+const TInt KDefaultConnectionTimeMicroSeconds	= 180000000;		// 3 minutes
+
+/*
+ * CWspCOProtocolHandler
+ */
+
+CWspCOProtocolHandler* CWspCOProtocolHandler::NewL(TAny* aSession)
+	{
+	RHTTPSession* session = REINTERPRET_CAST(RHTTPSession*, aSession);
+	CWspCOProtocolHandler* self = new (ELeave) CWspCOProtocolHandler(*session);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CWspCOProtocolHandler::~CWspCOProtocolHandler()
+	{
+	
+	if( iConnectionTimer )
+		iConnectionTimer->Cancel();
+	delete iConnectionTimer;
+
+	delete iClientSessionHeaders;
+	delete iTransportHandler;
+	delete iClientCapInfo;
+	delete iNegotiatedCapInfo;
+	delete iProxyInfoProvider;
+	delete iPrimitiveSender;
+	delete iHdrUtils;
+	}
+
+CWspCOProtocolHandler::CWspCOProtocolHandler(RHTTPSession aSession)
+: CProtocolHandler(aSession), /*iSessionState(ENull),*/ iTimedOutValue(KDefaultConnectionTimeMicroSeconds)
+	{
+	__OPEN_LOG("WspProtocolHandler.txt")
+	}
+
+void CWspCOProtocolHandler::ConstructL()
+	{
+	CProtocolHandler::ConstructL(iSession);
+
+	// Open the WSP header field names table
+	iSession.StringPool().OpenL(WSP::Table);
+
+	// Create the proposed client capability object and the negotiated session capability object
+	iClientCapInfo = CWspCapabilityInfo::NewL();
+	iNegotiatedCapInfo = CWspCapabilityInfo::NewL();
+
+	// Create the proxy info provider
+	iProxyInfoProvider = CWspProxyInfoProvider::NewL(iSession);
+
+	// Create the connection timer object
+	iConnectionTimer = CConnectionTimer::NewL(*this);
+
+	// Create the primitive sender active object
+	iPrimitiveSender = CWspPrimitiveSender::NewL(*this);
+
+	// Create the transport handler
+	CreateWspTransportHandlerL();
+
+	// Create the WSP header utilities
+	iHdrUtils = CWspHeaderUtils::NewL(*STATIC_CAST(CWspHeaderCodec*, iCodec));
+	}
+
+void CWspCOProtocolHandler::CreateWspTransportHandlerL()
+	{
+	// Assert that the transport handler doesn't already exist
+	__ASSERT_DEBUG(iTransportHandler == NULL, Panic(KWspPanicTransportHandlerAlreadyExists));
+
+	// Pass through the session string pool when creating the transport handler
+	RStringPool stringPool = iSession.StringPool();
+	iTransportHandler = CWspTransportHandler::NewL(
+												  stringPool,
+												  iSecurityPolicy,
+												  *this,				// Session CB
+												  *iProxyInfoProvider,	// Proxy info
+												  *this,				// Capability info
+												  *this					// Session headers info
+												  );
+
+	// Check the supported services
+	CWspTransportHandler::TWspSupportedServices suppSvc = iTransportHandler->SupportedServices();
+	TBool sessionSupport = suppSvc & CWspTransportHandler::ECOSessionService;
+	TBool methodSupport  = suppSvc & CWspTransportHandler::ECOMethodInvocationService;
+	if( !sessionSupport || !methodSupport )
+		{
+		__LOG(_L("Session management facility or method invoke facility not supported by transport handler"));
+		User::Leave(KWspErrRequiredServicesNotSupported);
+		}
+	// Configure the session invoker object
+	iSessionInvoker = &iTransportHandler->COSessionInvoker();
+	}
+
+
+void CWspCOProtocolHandler::CheckClientCapabilities()
+	{
+	// Reset the proposed client properties - sets the capabilities to the 
+	// default values.
+	iClientCapInfo->Reset();
+
+	// Check the session properties...
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+
+	// ...client message size...
+	THTTPHdrVal clientMessageSize;
+	TBool hasClientMessageSize = connInfo.Property(stringPool.StringF(HTTP::EWspCapClientMessageSize, RHTTPSession::GetTable()), clientMessageSize);
+	if( hasClientMessageSize )
+		{
+		// Initial attempt to negotiate message size and SDU size will attempt to go
+		// for a single PDU transfers, so set them the same
+		__ASSERT_DEBUG( clientMessageSize.Type() == THTTPHdrVal::KTIntVal, Panic(KWspPanicBadClientMessageSize) );
+		iClientCapInfo->SetClientMessageSize(clientMessageSize.Int());
+		iClientCapInfo->SetClientSDUSize(clientMessageSize.Int());
+		}
+	// ... server message size...
+	THTTPHdrVal serverMessageSize;
+	TBool hasServerMessageSize = connInfo.Property(stringPool.StringF(HTTP::EWspCapServerMessageSize, RHTTPSession::GetTable()), serverMessageSize);
+	if( hasServerMessageSize )
+		{
+		// Initial attempt to negotiate message size and SDU size will attempt to go
+		// for a single PDU transfers, so set them the same
+		__ASSERT_DEBUG( serverMessageSize.Type() == THTTPHdrVal::KTIntVal, Panic(KWspPanicBadServerMessageSize) );
+		iClientCapInfo->SetServerMessageSize(serverMessageSize.Int());
+		iClientCapInfo->SetServerSDUSize(serverMessageSize.Int());
+		}
+	// ...method MOR...
+	THTTPHdrVal methodMOR;
+	TBool hasMethodMOR = connInfo.Property(stringPool.StringF(HTTP::EWspCapMaxOutstandingRequests, RHTTPSession::GetTable()), methodMOR);
+	if( hasMethodMOR )
+		{
+		__ASSERT_DEBUG( methodMOR.Type() == THTTPHdrVal::KTIntVal, Panic(KWspPanicBadMethodMOR) );
+		__ASSERT_DEBUG( STATIC_CAST(TUint8, methodMOR.Int()) <= KMaxTUint8, Panic(KWspPanicBadMethodMOR) );
+		__ASSERT_DEBUG( STATIC_CAST(TUint8, methodMOR.Int()) > 0, Panic(KWspPanicBadMethodMOR) );
+		iClientCapInfo->SetMethodMOR( STATIC_CAST(TUint8, methodMOR.Int()) );
+		}
+	// Sort out the protocol options - by default try to use Large Data Transfer
+	TUint8 protocolOptions = ELargeDataTransfer;
+	THTTPHdrVal notUsed;
+	// ...use acknowledgements...
+	TBool hasUseAcknowledgements = connInfo.Property(stringPool.StringF(HTTP::EWspCapUseAcknowledgements, RHTTPSession::GetTable()), notUsed);
+	if( hasUseAcknowledgements )
+		{
+		protocolOptions |= EAcknowledgementHeaders;
+		}
+	// ...support suspend resume facilty...
+	TBool hasSuspendResume = connInfo.Property(stringPool.StringF(HTTP::EWspCapSupportSuspendResume, RHTTPSession::GetTable()), notUsed);
+	if( hasSuspendResume )
+		{
+		protocolOptions |= ESessionResumeFacility;
+		}
+	iClientCapInfo->SetProtocolOptions(protocolOptions);
+
+	// Get the connection time-out value
+	THTTPHdrVal connectionTimeout;
+	TBool hasConnectionTimeout = connInfo.Property(stringPool.StringF(HTTP::EWspProxyConnectionTimeout, RHTTPSession::GetTable()), connectionTimeout);
+	if( hasConnectionTimeout )
+		{
+		__ASSERT_DEBUG((connectionTimeout.Type() == THTTPHdrVal::KTIntVal), Panic(KWspPanicBadConenctionTimeout));
+		iTimedOutValue = connectionTimeout.Int();
+		}
+	else
+		{
+		// Use the default value
+		iTimedOutValue = KDefaultConnectionTimeMicroSeconds;
+		}
+	}
+
+TBool CWspCOProtocolHandler::UpdateNegotiatedCapabilitiesL()
+	{
+	// Check the capabilities proposed by the client
+	TBool capabilitiesReduced = EFalse;
+
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+
+	// ...client message size...
+	TUint32 clientMessageSize = iNegotiatedCapInfo->GetClientMessageSize();
+	if( clientMessageSize != iClientCapInfo->GetClientMessageSize() )
+		{
+		// The server has reduced the client message size - update the return flag...
+		capabilitiesReduced = ETrue;
+
+		// ...and the session property...
+		THTTPHdrVal clientMessageSizeProperty = clientMessageSize;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspCapClientMessageSize, RHTTPSession::GetTable()), clientMessageSizeProperty);
+		}
+	// ...server message size...
+	TUint32 serverMessageSize = iNegotiatedCapInfo->GetServerMessageSize();
+	if( serverMessageSize != iClientCapInfo->GetServerMessageSize() )
+		{
+		// The server has reduced the server message size - update the return flag...
+		capabilitiesReduced = ETrue;
+
+		// ...and the session property...
+		THTTPHdrVal serverMessageSizeProperty = serverMessageSize;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspCapServerMessageSize, RHTTPSession::GetTable()), serverMessageSizeProperty);
+		}
+	// ...method MOR...
+	TUint32 methodMOR = iNegotiatedCapInfo->GetMethodMOR();
+	if( methodMOR != iClientCapInfo->GetMethodMOR() )
+		{
+		// The server has reduced the method MOR - update the return flag...
+		capabilitiesReduced = ETrue;
+
+		// ...and the session property...
+		THTTPHdrVal methodMORProperty = methodMOR;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspCapMaxOutstandingRequests, RHTTPSession::GetTable()), methodMORProperty);
+		}
+	// Check the protocol options
+	TUint8 protocolOptions = iNegotiatedCapInfo->GetProtocolOptions();
+
+	// ...acknowledgements...
+	if( (protocolOptions & EAcknowledgementHeaders) != (iClientCapInfo->GetProtocolOptions() & EAcknowledgementHeaders) )
+		{
+		// The server has cleared the use acknowledgement headers flag - update
+		// the return flag...
+		capabilitiesReduced = ETrue;
+
+		// ...and the session property...
+		THTTPHdrVal notUsed = 0;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspCapUseAcknowledgements, RHTTPSession::GetTable()), notUsed);
+		}
+	// ...suspend resume facility...
+	if( (protocolOptions & ESessionResumeFacility) != (iClientCapInfo->GetProtocolOptions() & ESessionResumeFacility) )
+		{
+		// The server has cleared the support suspend/resume facility flag - 
+		// update the return flag...
+		capabilitiesReduced = ETrue;
+
+		// ...and the session property...
+		THTTPHdrVal notUsed = 0;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspCapSupportSuspendResume, RHTTPSession::GetTable()), notUsed);
+		}
+
+	return capabilitiesReduced;
+	}
+
+TBool CWspCOProtocolHandler::SupportSuspendResume() const
+	{
+	return (iNegotiatedCapInfo->GetProtocolOptions() & ESessionResumeFacility);
+	}
+
+TBool CWspCOProtocolHandler::CanResume() const
+	{
+	TBool canResume = SupportSuspendResume();
+
+	if( canResume && (iSessionState != EConnected && iSessionState != ESuspending && iSessionState != ESuspended) )
+		{
+		// In the wrong state
+		canResume = EFalse;
+		}
+	return canResume;
+	}
+
+TBool CWspCOProtocolHandler::CanSuspend() const
+	{
+	TBool canSuspend = SupportSuspendResume();
+
+	if( canSuspend && (iSessionState != EConnected && iSessionState != EResuming) )
+		{
+		// In the wrong state
+		canSuspend = EFalse;
+		}
+	return canSuspend;
+	}
+
+#if defined (_DEBUG)
+void CWspCOProtocolHandler::ResetAll()
+	{
+	// Delete helper objects and reset state
+	delete iClientSessionHeaders;
+	iClientSessionHeaders = NULL;
+	// 
+	if (iClientCapInfo)
+		iClientCapInfo->Reset();
+	if (iNegotiatedCapInfo)
+		iNegotiatedCapInfo->Reset();
+	if (iConnectionTimer)
+		iConnectionTimer->Cancel();
+	if (iProxyInfoProvider)
+		iProxyInfoProvider->ResetProxyInfo();
+	//
+	iSessionState = ENull; 
+	iTimedOutValue = 0;
+	iConnectTimedOut = EFalse;
+	iWaitingMethod  = EFalse;
+	iDisconnectRequested = EFalse;
+	iPendingCompletingMethods = 0;
+	//
+	// Reset the transport handler (stub only)
+	iSessionInvoker->DisconnectReq((TWspReason)(-999));
+	}
+#endif
+
+void CWspCOProtocolHandler::SessionConnectL()
+	{
+	__ASSERT_DEBUG( iSessionState == ENull, Panic(KWspPanicSessionNotInValidState) );
+
+	__LOG(_L("Initiating a session connect."));
+
+	// Check for any client proposed capabilities
+	CheckClientCapabilities();
+
+	// Update the client session headers
+	UpdateClientSessionHeadersL();
+
+	// Update the proxy info
+	iProxyInfoProvider->UpdateProxyInfoL();
+
+	// Start connection timed-out timer
+	iConnectionTimer->Start(iTimedOutValue);
+
+	// Send S-Connect.req primitive to transport handler
+	__LOG(_L("---Sending S-Connect.req primitive."));
+	iSessionInvoker->ConnectReq();
+
+	// Update session state
+	__LOG(_L("---WSP Session in Connecting state."));
+	iSessionState = EConnecting;
+
+	// Check to see if there is a method transactions waiting.
+	CheckWaitingMethod();
+	}
+
+void CWspCOProtocolHandler::SessionResumeL()
+	{
+	__ASSERT_DEBUG( CanResume(), Panic(KWspPanicSessionNotInValidState) );
+
+	__LOG(_L("Initiating a session resume."));
+
+	// Check to see if the proxy info has not changed.
+	if( iProxyInfoProvider->ProxyInfoChangedAndUpdateL() )
+		{
+		__LOG(_L("---Proxy has changed. Disconnect from the old proxy and connect to the new one."));
+
+		// The client has specified a different - disconnect this session
+		SessionDisconnect(EChangedProxyInSuspendedSession);
+		}
+	else
+		{
+		// Check for any client proposed capabilities
+		CheckClientCapabilities();
+
+		// Update the client session headers
+		UpdateClientSessionHeadersL();
+
+		// Start connection timed-out timer
+		iConnectionTimer->Start(iTimedOutValue);
+
+		// Send S-Resume.req primitive to transport handler
+		__LOG(_L("---Sending S-Resume.req primitive."));
+		iSessionInvoker->ResumeReq();
+
+		// Update session state
+		__LOG(_L("---WSP Session in Resuming state."));
+		iSessionState = EResuming;
+
+		// Check to see if there is a method transactions waiting.
+		CheckWaitingMethod();
+		}
+	}
+
+void CWspCOProtocolHandler::SessionDisconnect(TWspReason aReason)
+	{
+	__ASSERT_DEBUG( iSessionState != ENull && iSessionState != EClosing, Panic(KWspPanicSessionNotInValidState) );
+
+	// Are there any methods waiting to send their final .res primitive
+	if( iPendingCompletingMethods > 0 )
+		{
+		// Yep, need to wait until there are done.
+		iDisconnectRequested = ETrue;
+		return;
+		}
+	__LOG(_L("Disconnecting session."));
+
+	// Need to cancel the connection timer
+	iConnectionTimer->Cancel();
+
+	// Send S-Disconnect.req primitive to transport handler
+	__LOG1(_L("---Sending S-Disconnect.req primitive. Reason code : %d"), aReason);
+	iSessionInvoker->DisconnectReq(aReason);
+
+	// Update session state
+	__LOG(_L("---WSP Session in Closing state."));
+	iSessionState = EClosing;
+	}
+
+void CWspCOProtocolHandler::SessionSuspend()
+	{
+	__ASSERT_DEBUG( CanSuspend(), Panic(KWspPanicSessionNotInValidState) );
+	
+	// Are there any methods waiting to send their final .res primitive
+	if( iPendingCompletingMethods > 0 )
+		{
+		// Yep, need to wait until there are done.
+		iDisconnectRequested = ETrue;
+		return;
+		}
+	__LOG(_L("Suspending session."));
+
+	// Need to cancel the connection timer
+	iConnectionTimer->Cancel();
+
+	// Send S-Suspend.req primitive to transport handler
+	__LOG(_L("---Sending S-Suspend.req primitive."));
+	iSessionInvoker->SuspendReq();
+
+	// Update session state
+	__LOG(_L("---WSP Session in Suspending state."));
+	iSessionState = ESuspending;
+	}
+
+void CWspCOProtocolHandler::DoSessionConnectedL()
+	{
+	// Cancel the connection timer
+	iConnectionTimer->Cancel();
+
+	// Check for the Encoding-Version header in the server session headers
+ 	RHTTPHeaders headers = iSession.ResponseSessionHeadersL();
+ 	THTTPHdrVal negotiatedVersion;
+ 	TInt err = headers.GetField(
+ 							   iSession.StringPool().StringF(WSP::EEncodingVersion, WSP::Table),
+ 							   0,		// Zero index -> first part
+ 							   negotiatedVersion
+ 							   );
+	
+	// Default version is 1.2 - this is the case if the header is missing.
+ 	CWspHeaderCodec::TWspVersion version = CWspHeaderCodec::EVersion1_2;
+
+	if( (err != KErrNotFound) && (negotiatedVersion.Type() == THTTPHdrVal::KStrFVal) )
+ 		{
+ 		// Check what version the encoding is and set it accordingly in the codec
+		switch(negotiatedVersion.StrF().Index(WSPStdConstants::Table) )
+			{
+			case WSPStdConstants::EWspVersion11:
+				version = CWspHeaderCodec::EVersion1_1;
+				break;
+			case WSPStdConstants::EWspVersion13:
+				version = CWspHeaderCodec::EVersion1_3;
+				break;
+			case WSPStdConstants::EWspVersion14:
+				version = CWspHeaderCodec::EVersion1_4;
+				break;
+			default:
+				// If the version is 1.2 or anything else, stay at 1.2
+				break;
+			}
+ 		}
+	// Set the encoding value in the codec
+	STATIC_CAST(CWspHeaderCodec*, iCodec)->SetWspVersion(version);
+
+	// Inform client that the session is connected - check negotiated capabilities
+	THTTPSessionEvent connectEvent = THTTPSessionEvent::EConnectedOK;
+
+	// Check the negotiated capabilities
+ 	TBool capabilitiesReduced = UpdateNegotiatedCapabilitiesL();
+
+	if( capabilitiesReduced )
+		{
+		// At least one of the proposed client capabilities has been negotiated 
+		// down or rejected - need to inform client of this event.
+		connectEvent = THTTPSessionEvent::EConnectedWithReducedCapabilities;
+		}
+	SendSessionEvent(connectEvent);
+
+	// WSP session is connected - update state
+	__LOG(_L("---WSP Session in Connected state"));
+	iSessionState = EConnected;
+	}
+
+void CWspCOProtocolHandler::SendSessionEvent(THTTPSessionEvent aEvent)
+	{
+	// Send the event to the client - need to TRAPD
+	TRAPD(error, iSession.SendSessionEventL(aEvent, THTTPSessionEvent::EIncoming, THTTPFilterHandle::EProtocolHandler));
+	if( error != KErrNone )
+		{
+		__LOG1(_L("Error - Could not send event to the client, error code %d"), error);
+		iSession.FailSessionEvent(THTTPFilterHandle::EProtocolHandler);
+		}
+	}
+
+void CWspCOProtocolHandler::UpdateClientSessionHeadersL()
+	{
+	// Check for Encoding-Version header
+	RStringPool stringPool = iSession.StringPool();
+	RHTTPHeaders headers = iSession.RequestSessionHeadersL();
+	THTTPHdrVal version;
+	RStringF encodingVersionField = stringPool.StringF(WSP::EEncodingVersion, WSP::Table);
+	TInt err = headers.GetField(encodingVersionField, 0 /* Zero index -> first part*/, version);
+	TBool foundHeader = EFalse;
+
+	// If the encoding-version header exists, check that it is valid
+	if( err == KErrNone )
+		{
+		if( version.Type() == THTTPHdrVal::KStrFVal )
+			{
+			switch( version.StrF().Index(WSPStdConstants::Table) )
+				{
+				case WSPStdConstants::EWspVersion11:
+				case WSPStdConstants::EWspVersion12:
+				case WSPStdConstants::EWspVersion13:
+				case WSPStdConstants::EWspVersion14:
+					foundHeader = ETrue;
+					break;
+				default:
+					break;
+				}
+			}
+		}
+
+	// If the header is not found or is invalid, then use WSP encoding v1.4
+	if(!foundHeader)
+		{
+		// Remove existing header first if its invalid, this won't do anything if it doesn't exist
+		headers.RemoveField(encodingVersionField);
+		THTTPHdrVal encodingVersionValue(stringPool.StringF(WSPStdConstants::EWspVersion14, WSPStdConstants::Table));
+		headers.SetFieldL(encodingVersionField, encodingVersionValue);
+		__LOG(_L("---Updated WSP encoding version request to 1.4"));
+		}
+
+   	// Encode the headers...
+	HBufC8* buf = iHdrUtils->EncodeHeadersL(stringPool, headers);
+
+	// Update the client session headers buffer;
+	delete iClientSessionHeaders;
+	iClientSessionHeaders = buf;
+	}
+
+void CWspCOProtocolHandler::HandleConnectRequestL()
+	{
+	// Check the WSP session state
+	switch( iSessionState )
+		{
+	case ENull:
+		{
+		// Initiate a session connect
+		SessionConnectL();
+		} break;
+	case ESuspended:
+	case ESuspending:
+		{
+		// In suspending or suspended state - this implies that the WSP 
+		// session supports the Suspend Resume facility and was suspended 
+		// rather than disconnected for a more efficient re-connection.
+
+		// Initiate a session resume
+		SessionResumeL();
+		} break;
+	case EConnecting:
+	case EResuming:
+		{
+		// A connect request has already been sent - inform the client
+		SendSessionEvent(THTTPSessionEvent::EAlreadyConnecting);
+		} break;
+	case EConnected:
+		{
+		// A connect request has already been sent and the session is 
+		// connected - inform the client
+		SendSessionEvent(THTTPSessionEvent::EAlreadyConnected);
+		} break;
+	case EClosing:
+		{
+		// A disconnect request has already been sent - inform the client
+		SendSessionEvent(THTTPSessionEvent::EAlreadyDisconnecting);
+		} break;
+	default:
+		Panic(KWspPanicIllegalSessionState);
+		break;
+		}
+	}
+
+void CWspCOProtocolHandler::HandleDisconnectRequest()
+	{
+	// Disconnect has been requestd. Action depends of the WSP session state.
+	switch( iSessionState )
+		{
+	case ENull:
+	case ESuspended:
+		{
+		// A disconnect request has already been fulfilled - inform the client
+		SendSessionEvent(THTTPSessionEvent::EAlreadyDisconnected);
+		} break;
+	case EClosing:
+	case ESuspending:
+		{
+		// A disconnect request has already been sent - inform the client
+		SendSessionEvent(THTTPSessionEvent::EAlreadyDisconnecting);
+		} break;
+	case EConnecting:
+		{
+		// The WSP session is in the Connecting state, then the session should be
+		// disconnected - do SessionDisconnect. This will issue the 
+		// S-Disconnect.req primitive and tell the transport handler to stop the 
+		// connection. The transport handler will send the S-Disconnect.ind 
+		// primitive which results in the client being notified.
+		SessionDisconnect(EUserReq);
+		} break;
+	case EResuming:
+		{
+		// If the WSP session is resuming, then the Suspend Resume facility is
+		// supported - can suspend the WSP session. This will issue the 
+		// S-Suspend.req primitive and tell the transport handler to stop the 
+		// session resume. The transport handler will send the S-Suspend.ind 
+		// primitive to the protocol handler who will then inform the client.
+		SessionSuspend();
+		} break;
+	case EConnected:
+		{
+		// Check if the Suspend Resume facility is supported. If so, then 
+		// suspend the WSP session, rather then disconnect it.
+		if( SupportSuspendResume() )
+			{
+			// Suspend the WSP session
+			SessionSuspend();
+			}
+		else
+			{
+			// Disconnect the WSP session
+			SessionDisconnect(EUserReq);
+			}
+		} break;
+	default:
+		Panic(KWspPanicIllegalSessionState);
+		break;
+		}
+	}
+
+void CWspCOProtocolHandler::CheckWaitingMethod()
+	{
+	if( iWaitingMethod )
+		{
+		// Self-complete the base class - this will start servicing the pending
+		// transaction.
+		CompleteSelf();
+
+		// Clear the flag
+		iWaitingMethod = EFalse;
+		}
+	}
+
+/*
+ * Methods from CProtocolHandler
+ */
+
+void CWspCOProtocolHandler::CreateCodecL()
+	{
+	iCodec = CWspHeaderCodec::NewL(iSession.StringPool(), WSP::Table);
+	}
+
+CProtTransaction* CWspCOProtocolHandler::CreateProtTransactionL(RHTTPTransaction aTransaction)
+	{
+	return CWspCOTransaction::NewL(aTransaction, iTransportHandler->COTransactionInvoker(), *iNegotiatedCapInfo, *this, *iHdrUtils);
+	}
+
+TBool CWspCOProtocolHandler::ServiceL(CProtTransaction& aTrans)
+	{
+	__LOG(_L("Attempting to invoke request."));
+
+	// Check the WSP session state
+	if( iSessionState == EClosing || iSessionState == ESuspending )
+		{
+		__LOG(_L("---Session is Closing or Suspending - cannot invoke methods."));
+
+		// The WSP session is either disconnecting or suspending - leave
+		User::Leave(KWspErrSessionClosingOrSuspending);
+		}
+	if( iSessionState == ENull || iSessionState == ESuspended )
+		{
+		__LOG(_L("---Session is Null or Suspended - cannot invoke methods. Wait for session connect initiation."));
+		
+		// Send back an event saying that session is not connected yet
+		SendSessionEvent(THTTPSessionEvent::ENotConnected);
+
+		// Set a flag to indicate that there is a transaction waiting to be 
+		// serviced - need to self-complete once connected.
+		iWaitingMethod = ETrue;
+
+		//  Cannot service the method transaction now.
+		return EFalse;
+		}
+	// Check to see if the method MOR has been reached.
+	TUint32 activeTransactions = NumActiveTransactions();
+	if( activeTransactions == iNegotiatedCapInfo->GetMethodMOR() )
+		{
+		__LOG1(_L("---Have reached method MOR (=%d) - cannot invoke request."), iNegotiatedCapInfo->GetMethodMOR());
+
+		// Have reached the method MOR - cannot service this method transaction.
+		return EFalse;
+		}
+	// Have not yet reached the negotiated method MOR - can service this 
+	// transaction.
+
+	// Create the Tx Data object for this transaction
+	aTrans.CreateTxDataL();
+
+	// Do the method invocation
+	STATIC_CAST(CWspCOTransaction&, aTrans).InitRequestL();
+
+	__LOG1(_L("---Initiated request - trans %d is active"), aTrans.Transaction().Id() );
+
+	return ETrue;
+	}
+
+void CWspCOProtocolHandler::ClosedTransactionHook(CProtTransaction* aTrans)
+	{
+	__LOG1(_L("Trans %d has been closed"), aTrans->Transaction().Id() );
+
+	// Down-cast the CProtTransaction
+	CWspCOTransaction& wspTransaction = *STATIC_CAST(CWspCOTransaction*, aTrans);
+
+	// Abort this transaction
+	wspTransaction.AbortRequest();
+
+	// Tell the method transaction to suicide - the transaction will ensure that 
+	// it deletes itself at the appropriate time.
+	wspTransaction.Suicide();
+	}
+
+void CWspCOProtocolHandler::CancelTransactionHook(CProtTransaction& aTransaction)
+	{
+	__LOG1(_L("Trans %d has been cancelled"), aTransaction.Transaction().Id() );
+
+	// Abort this transaction
+	STATIC_CAST(CWspCOTransaction&, aTransaction).AbortRequest();
+	}
+
+void CWspCOProtocolHandler::NotifyNewRequestBodyPart(CProtTransaction& aTransaction)
+	{
+	__LOG(_L("Client has more request data..."));
+
+	// Send the S-MethodInvokeData primitive
+	STATIC_CAST(CWspCOTransaction&, aTransaction).NotifyMoreRequestData();
+	}
+
+void CWspCOProtocolHandler::GetInterfaceL(TUid aInterfaceId, MProtHandlerInterface*& aInterfacePtr)
+	{
+	switch(aInterfaceId.iUid)
+		{
+	case KProtHandlerSessionServerCertUid:
+		{
+		aInterfacePtr = this;
+		break;
+		}
+	default:
+		{
+		CProtocolHandler::GetInterfaceL(aInterfaceId, aInterfacePtr);
+		break;
+		}
+		}
+	}
+
+/*
+ * Methods from MHTTPFilterBase
+ */
+
+void CWspCOProtocolHandler::MHFSessionRunL(const THTTPSessionEvent& aEvent)
+	{
+	__ASSERT_DEBUG( iTransportHandler, Panic(KWspPanicTransportHandlerDoesNotExist) );
+
+	// Check the session event
+	switch(aEvent.iStatus)
+		{
+	case THTTPSessionEvent::EConnect:
+		{
+		__LOG(_L("Processing EConnect session event"));
+
+		// Handle the connect request
+		HandleConnectRequestL();
+		} break;
+	case THTTPSessionEvent::EDisconnect:
+		{
+		__LOG(_L("Processing EDisconnect session event"));
+
+		// Handle the disconnect request
+		HandleDisconnectRequest();
+		} break;
+#if defined (_DEBUG)
+	case -999:
+		{
+		// This unpublished value is used in testing to cause a whole protocol
+		// handler reset and to reset the transport handler. It should be used with caution!
+		__LOG(_L("PROTOCOL HANDLER RESETTING!"));
+		ResetAll();
+
+		// Ack the owner
+		SendSessionEvent(-999);
+		} break;
+#endif
+	default:
+		// Ignore the unknown session event.
+		__LOG1(_L("Received unknown session event : %d"), aEvent.iStatus);
+		break;
+		}
+	}
+
+TInt CWspCOProtocolHandler::MHFSessionRunError(TInt aError, const THTTPSessionEvent& /*aEvent*/)
+	{
+	// Send the error as a session event.
+	SendSessionEvent(aError);
+	return KErrNone;
+	}
+
+/*
+ * Methods from MWspCOSessionCallback
+ */
+
+void CWspCOProtocolHandler::ConnectCnf()
+	{
+	__LOG(_L("Received S-Connect.cnf primitive"));
+
+	// Did we connect to a secure proxy?  If so, send the 'authenticated OK' session event
+	MWspProxyInfoProvider& proxyInfo = STATIC_CAST(MWspProxyInfoProvider&, *iProxyInfoProvider);
+	if( proxyInfo.SecureConnection() )
+		{
+		__LOG(_L("---WTLS authentication was successful."));
+
+		// Send EAuthenticatedOK event...
+		SendSessionEvent(THTTPSessionEvent::EAuthenticatedOK);
+		}
+
+	// Process the session connected event
+	DoSessionConnected();
+	}
+
+void CWspCOProtocolHandler::ResumeCnf()
+	{
+	__LOG(_L("Received S-Resume.cnf primitive"));
+
+	// Process the session connected event
+	DoSessionConnected();
+	}
+
+void CWspCOProtocolHandler::DoSessionConnected()
+	{
+	// Process the session connected event
+	TRAPD(error, DoSessionConnectedL());
+ 	if( error != KErrNone )
+ 		{
+ 		__LOG(_L("Error - Could not process S-Connect.cnf/S-Resume.cnf. Disconnect the WSP session."));
+ 
+ 		// Disconnect this session
+ 		SessionDisconnect(EPeerReq);
+ 		}
+	}
+
+void CWspCOProtocolHandler::DisconnectInd(TWspReason					aReason, 
+										  TBool							/*aRedirectSecurity*/, 
+										  TWspRedirectedAddress&		aRedirectAddress, 
+										  const TDesC8&					/*aErrorHeader*/, 
+										  const TDesC8&					/*aErrorBody*/)
+	{
+	__ASSERT_DEBUG( NumActiveTransactions() == 0, Panic(KWspPanicMethodsStillOutstanding) );
+
+	__LOG(_L("Received S-Disconnect.ind primitive"));
+	__LOG1(_L("---Reason code : %d"), aReason);
+
+	// Cancel the connection timer
+	iConnectionTimer->Cancel();
+
+	// Check the reason for the disconnect
+	switch( aReason )
+		{
+	case EPermanentRedirectedProxy:
+		{
+		__LOG(_L("---Permanent proxy redirection"));
+
+		// Update the proxy info with the redirected proxy addresses. This will
+		// place the new address in the EWspProxyAddress session property.
+		TRAPD(error, iProxyInfoProvider->SetPermanentRedirectedProxyL(aRedirectAddress) );
+
+		if( error != KErrNone )
+			{
+			// Something went wrong - just disconnect
+			SendSessionEvent(THTTPSessionEvent::EDisconnected);
+			}
+		else
+			{
+			// Need to inform client that the proxy has been redirected permanently.
+			SendSessionEvent(THTTPSessionEvent::ERedirected);
+
+			// Send the S-Connect primitive
+			iPrimitiveSender->InitiateSend(ESConnect);
+			}
+		} break;
+	case ETemporaryRedirectedProxy:
+		{
+		__LOG(_L("---Temporary proxy redirection"));
+
+		// Update the proxy info with the redirected proxy addresses.
+		TRAPD(error, iProxyInfoProvider->SetTemporaryRedirectedProxyL(aRedirectAddress) );;
+
+		if( error != KErrNone )
+			{
+			// Something went wrong - just disconnect
+			SendSessionEvent(THTTPSessionEvent::EDisconnected);
+			}
+		else
+			{
+			// Send the S-Connect primitive
+			iPrimitiveSender->InitiateSend(ESConnect);
+			}
+		} break;
+	case EChangedProxyInSuspendedSession:
+		{
+		__LOG(_L("---Client changed proxy whilst suspended"));
+		__LOG(_L("---Forced disconnect, then connection to new proxy"));
+
+		// The client had disconnected the session (resulting in the session
+		// being suspended) and then did a connect, but with a different 
+		// proxy. This resulted in the suspended session being disconnected.
+		// Now need to initiate a session connect to the new proxy.
+		iPrimitiveSender->InitiateSend(ESConnect);
+		} break;
+	case EWtlsConfigurationFailed:
+	case EWtlsPhase1HandshakeFailed:
+	case EWtlsPhase2HandshakeFailed:
+	case EWtlsInvalidServerCert:
+	case EWtlsUntrustedServerCert:
+	case EWtlsNegotiatedConfigRejected:
+		{
+		__LOG(_L("---WTLS handshake failure"));
+
+		// Various failure modes for the WTLS handshake.  All result in a
+		// 'authentication failure' event being sent to the client, followed by
+		// a 'disconnected' event.
+		SendSessionEvent(THTTPSessionEvent::EAuthenticationFailure);
+		SendSessionEvent(THTTPSessionEvent::EDisconnected);
+		} break;
+	case EOutOfMemory:
+		{
+		// There was a memory allocation failure in the transport handler - inform
+		// the client.
+		SendSessionEvent(KErrNoMemory);
+
+		// Inform the client that the sesion is disconnected
+		SendSessionEvent(THTTPSessionEvent::EDisconnected);
+		} break;
+	case ESessionStateFailure:
+		{
+		// The session is effectively disconnected
+		SendSessionEvent(THTTPSessionEvent::EDisconnected);
+
+		// The transport handler has entered an invalid or indeterminate state - possibly due
+		// to a memory allocation failure - the session is no longer valid and should be
+		// discontinued by the client - inform the client.
+		__LOG(_L("Transport Handler has signalled a session state failure"));
+		iSession.FailSessionEvent(THTTPFilterHandle::EProtocolHandler);
+		} break;
+	case EUserReq:
+		{
+		// This is ind to a S-Disconnect.req. Was it due to a connect time-out?
+		if( iConnectTimedOut )
+			{
+			__LOG(_L("---Connection has timed-out"));
+
+			// Inform the client that the resume timed-out
+			SendSessionEvent(THTTPSessionEvent::EConnectionTimedOut);
+			iConnectTimedOut = EFalse;
+			break;
+			}
+		// Drop-through if there was no connect time-out
+		} 
+	default:
+		// Session disconnected for some other reason. Inform the client, but 
+		// ignore the information in aErrorHeader and aErrorBody. The WSP 
+		// Specification states that the provider MAY choose not to communicate
+		// this information - the protocol handler is making that choice!!
+		SendSessionEvent(THTTPSessionEvent::EDisconnected);
+		break;
+		}
+	// WSP session is disconnected - update state
+	__LOG(_L("---WSP Session in Null state"));
+	iSessionState = ENull;
+	}
+
+void CWspCOProtocolHandler::SuspendInd(TWspReason /*aReason*/)
+	{
+	__ASSERT_DEBUG( NumActiveTransactions() == 0, Panic(KWspPanicMethodsStillOutstanding) );
+
+	__LOG(_L("Received S-Suspend.ind primitive"));
+
+	// Do not care about the reason, just inform the client. First check if this
+	// suspend is the result of a session resume timed-out.
+	if( iConnectTimedOut )
+		{
+		__LOG(_L("---Resume has timed-out"));
+
+		// Inform the client that the resume timed-out
+		SendSessionEvent(THTTPSessionEvent::EConnectionTimedOut);
+		iConnectTimedOut = EFalse;
+		}
+	else
+		{
+		// Inform the client that the session is disconnected - this event is 
+		// sent as the the client does not understand suspend/resume and the 
+		// Suspend Resume facility is being used for efficiency.
+		SendSessionEvent(THTTPSessionEvent::EDisconnected);
+		}
+	// WSP session is suspended - update state
+	__LOG(_L("---WSP Session in Suspended state"));
+	iSessionState = ESuspended;
+	}
+
+void CWspCOProtocolHandler::ExceptionInd(const TDesC8& aExceptionData)
+	{
+	RStringPool stringPool = iSession.StringPool();
+
+	// Generic handling for exception data
+	__LOG(_L("Have received exception information."));
+
+	// Add the exception data as the EWspProxyExceptionInfo property
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	TRAP_IGNORE(
+		RStringF exceptionData = stringPool.OpenFStringL(aExceptionData);
+		CleanupClosePushL(exceptionData);
+		THTTPHdrVal exceptionProperty = exceptionData;
+		connInfo.SetPropertyL(stringPool.StringF(HTTP::EWspProxyExceptionInfo, RHTTPSession::GetTable()), exceptionProperty);
+		CleanupStack::PopAndDestroy(&exceptionData);
+		);
+	
+	// Send the exception info event
+	SendSessionEvent(THTTPSessionEvent::EExceptionInfo);
+	}
+
+/*
+ * Methods from MWspCapabilityInfoProvider
+ */
+
+const MWspCapabilityViewer& CWspCOProtocolHandler::ClientCapabilities() const
+	{
+	return *iClientCapInfo;
+	}
+
+MWspCapabilitySetter& CWspCOProtocolHandler::ServerCapabilities() const
+	{
+	return *iNegotiatedCapInfo;
+	}
+
+/*
+ * Methods from MWspSessionHeadersProvider
+ */
+
+const TDesC8& CWspCOProtocolHandler::ClientHeaders() const
+	{
+	return *iClientSessionHeaders;
+	}
+
+void CWspCOProtocolHandler::SetServerHeadersL(const TDesC8& aBuffer)
+	{
+	RHTTPHeaders serverHeaders = iSession.ResponseSessionHeadersL();
+	iHdrUtils->DecodeHeadersL(iSession.StringPool(), aBuffer, serverHeaders);
+	}
+
+/*
+ *	Methods from MConnectionTimerCallback
+ */
+
+void CWspCOProtocolHandler::HandleConnectionTimedOut()
+	{
+	__LOG(_L("Connection timed-out - appropriate action taken"));
+
+	// The connection has timed-out - flag
+	iConnectTimedOut = ETrue;
+
+	// Request a disconnect. 
+	HandleDisconnectRequest();
+	}
+
+/*
+ * Methods from MWspPrimitiveSenderCallback
+ */
+
+void CWspCOProtocolHandler::SendPrimitiveL(TWspPrimitive aPrimitive)
+	{
+	// Check that the primitive is one that is supported
+	switch( aPrimitive )
+		{
+	case ESConnect:
+		{
+		SessionConnectL();
+		} break;
+	case ESDisconnect:
+	case ESSuspend:
+		{
+		HandleDisconnectRequest();
+		} break;
+	default:
+		// Unsupported primitive
+		User::Leave(KWspErrUnsupportedSendPrimitive);
+		break;
+		}
+	}
+
+TInt CWspCOProtocolHandler::WspPrimitiveSenderCallbackError(TInt /*aError*/)
+	{
+	// The initiate session connect has failed. Need to unlock the proxy info.
+	iProxyInfoProvider->UnlockProxyInfo();
+
+	// Inform the client - send disconnected signal
+	SendSessionEvent(THTTPSessionEvent::EDisconnected);
+
+	return KErrNone;
+	}
+
+/*
+ * Methods from MWspCOMethodObserver
+ */
+
+void CWspCOProtocolHandler::HandleMethodAbort(CWspCOTransaction& aTransaction)
+	{
+	// The CWspCOTransaction has failed the transaction - inform the base class
+	// protocol handler to change the state of this transaction.
+	TransactionFailed(aTransaction.Transaction());
+
+	__LOG1(_L("Transaction %d has failed."), aTransaction.Transaction().Id() );
+	}
+
+void CWspCOProtocolHandler::NotifyPendingCompletingMethod()
+	{
+	// Increment the number of pending methods waiting to move from Completing
+	// to Null state.
+	++iPendingCompletingMethods;
+
+	__LOG1(_L("Number of methods waiting to send final .res primitive : %d"), iPendingCompletingMethods);
+	}
+
+void CWspCOProtocolHandler::NotifyMethodComplete()
+	{
+	// The final .res primitive has been sent for one of the methods - decrement
+	// count.
+	--iPendingCompletingMethods;
+
+	__LOG1(_L("Number of methods waiting to send final .res primitive : %d"), iPendingCompletingMethods);
+
+	if( iPendingCompletingMethods == 0 && iDisconnectRequested )
+		{
+		__LOG(_L("---All methods have sent final .res primitive. Requesting disconnect."));
+
+		// No more methods waiting to send the final .res primitive and the
+		// client has requested the session be disconnected - request it now.
+		iPrimitiveSender->InitiateSend(ESDisconnect);
+		}
+	}
+
+
+/*
+ * Methods from MRxDataObserver
+ */
+
+void CWspCOProtocolHandler::SetStatusL(CRxData& aRxData, TInt aStatus)
+	{
+	// Have received a status message from an Rx data object - check the status.
+	switch( aStatus )
+		{
+	case THTTPEvent::EResponseComplete:
+		{
+		__LOG1(_L("Transaction %d has completed"), aRxData.ProtTrans().Transaction().Id() );
+		// The response is complete - the client has been passed all the data
+		// and released it. It has no further use for the Rx data object. The
+		// transaction is now complete - inform the base class.
+		TransactionCompletedL(aRxData.ProtTrans().Transaction(), THTTPEvent::EResponseComplete);
+		} break;
+	default:
+		// Unknown status - do nothing.
+		__LOG1(_L("Received unknown status %d"), aStatus);
+		break;
+		}
+	}
+
+TInt CWspCOProtocolHandler::SessionServerCert(TCertInfo& aServerCert)
+	{
+	return iTransportHandler->ServerCert(aServerCert);
+	}
+
+const CCertificate* CWspCOProtocolHandler::SessionServerCert()
+	{
+	return iTransportHandler->ServerCert();
+	}
+
+/*
+ * Methods from MProtHandlerInterface
+ */
+
+TInt CWspCOProtocolHandler::TransactionServerCert(TCertInfo& /*aServerCert*/, RHTTPTransaction /*aTransaction*/)
+	{
+	// Transaction Server certificate does not make sense for WSP
+	return KErrNotSupported;
+	}
+
+const CCertificate* CWspCOProtocolHandler::TransactionServerCert(RHTTPTransaction /*aTransaction*/)
+
+	{
+	return NULL;
+	}