applayerpluginsandutils/httpprotocolplugins/WspProtocolHandler/CWspCOTxData.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/WspProtocolHandler/CWspCOTxData.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,660 @@
+// 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 <uri8.h>
+#include <wspstringconstants.h>
+#include <httpstringconstants.h>
+#include <wsp/mwspcapabilityviewer.h>
+#include <wsp/wsptypes.h>
+#include <wsperror.h>
+
+// User includes
+#include <http/rhttpheaders.h>
+#include <http/thttphdrval.h>
+#include "cwspcotransaction.h"
+#include "cwspheaderutils.h"
+#include "mwspcotxdatacallback.h"
+#include "wsppanic.h"
+
+// Class signature
+#include "cwspcotxdata.h"
+
+// Constants used in this module
+const TInt KPDUTypeFieldSize			= 1;	// Size in bytes
+const TInt KUIntVarOctetShift			= 7;
+const TInt KWspTxDataDefaultBufferSize	= 2048;
+
+CWspCOTxData* CWspCOTxData::NewL(CProtTransaction& aTransaction, MWspCOTxDataCallback& aObserver, MWspCapabilityViewer& aNegotiatedCapInfo)
+	{
+	return new (ELeave) CWspCOTxData(aTransaction, aObserver, aNegotiatedCapInfo);
+	}
+
+CWspCOTxData::~CWspCOTxData()
+	{
+	Cleanup();
+	}
+
+CWspCOTxData::CWspCOTxData(CProtTransaction& aTransaction, MWspCOTxDataCallback& aObserver, MWspCapabilityViewer& aNegotiatedCapInfo)
+: CTxData(aTransaction), iObserver(aObserver), iNegotiatedCapInfo(aNegotiatedCapInfo)
+	{
+	}
+
+const TDesC8& CWspCOTxData::RequestHeadersData()
+	{
+	return *iHeaderData;
+	}
+
+MHTTPDataSupplier& CWspCOTxData::RequestBodyData()
+	{
+	return *this;
+	}
+
+void CWspCOTxData::SetRequestDataL()
+	{
+	// Set the required session properties. Note - assume that the server 
+	// message size is at least the size of the server SDU size.
+	iRemainingSDUSpace		= iNegotiatedCapInfo.GetServerSDUSize();
+	if( iRemainingSDUSpace == 0 )
+		{
+		// SDU size is unlimited - set to highest value
+		iRemainingSDUSpace = KMaxTUint32;
+		}
+	iRemainingMessageSpace	= iNegotiatedCapInfo.GetServerMessageSize();
+	if( iRemainingMessageSpace != 0 )
+		{
+		// Message is not unlimited
+		iLimitedMessageSize = ETrue;
+		}
+	iSupportLDT				= iNegotiatedCapInfo.GetProtocolOptions() & ELargeDataTransfer;
+
+	// Get the request object from the transaction
+	RHTTPTransaction trans	= ProtTrans().Transaction();
+	RHTTPRequest request	= trans.Request();
+
+	// Encode the headers - this checks for trailer headers. Trailer header are 
+	// only allowed if LDT is supported in the session.
+	SetHeaderDataL(request);
+
+	// Headers encoded ok? Check for a request body.
+	if( request.HasBody() )
+		{
+		// Set the remaining server SDU space property
+		THTTPHdrVal remainingSDUProperty = iRemainingSDUSpace;
+		SetSessionPropertyL(HTTP::EWspRemainingSDUSize, remainingSDUProperty);
+
+		// Set the body data - this will set the iRequestComplete flag
+		SetBodyDataL(*request.Body());
+		}
+	else
+		{
+		// If there is no body request then the function OverallDataSize will 
+		// return zero. The transport handler should not try to access the data.
+		iRequestComplete = ETrue;
+
+		// Reset the body data
+		iBodyData = KNullDesC8().AllocL();
+
+		// Flag that there is some body data, although of zero length
+		iGotBodyData = ETrue;
+
+		// Set state so that primitive is sent
+		iState = EDone;
+		}
+	// Tell oberver what to do.
+	DecideObserverAction();
+
+	// Set the remaining message size session property
+	THTTPHdrVal remainingMessageSizeProperty = iRemainingMessageSpace;
+	SetSessionPropertyL(HTTP::EWspRemainingMessageSize, remainingMessageSizeProperty);
+	}
+
+void CWspCOTxData::NotifyMoreRequestData()
+	{
+	__ASSERT_DEBUG( iState == EWaitForMoreData, Panic(KWspPanicTxDataInInvalidState) );
+
+	// Get the next data part from the client.
+	iRequestComplete = iBodyDataSupplier->GetNextDataPart(iDataPart);
+
+	// Was the overall data size known?
+	if( iOverallDataSize == -1 )
+		{
+		TInt dataSize = iDataPart.Length();
+
+		// Is the message size limited?
+		if( iLimitedMessageSize )
+			{
+			// Check to see if this new load of data has blown the message size. 
+			// Need to include trailer info too.
+			// NOTE - if LDT is not supported, this will be reflected in the message
+			// size being the same as the SDU size.
+			if( dataSize + iTrailerInfoLength > iRemainingMessageSpace )
+				{
+				// Too much data - abort the request.
+				// NOTE - this could delete this object, so ensure that accessing data
+				// members is not done after this call.
+				iObserver.AbortInvoke();
+				return;
+				}
+			}
+		// Need to check to see if the SDU size is unlimited
+		if( iNegotiatedCapInfo.GetServerSDUSize() == 0 )
+			{
+			// It is so calculate a sensible buffer size
+			TPtr8 buf = iBodyData->Des();
+			TInt totalDataSize = dataSize + buf.Length();
+			if( totalDataSize > buf.MaxLength() )
+				{
+				TInt bufferSize = ((totalDataSize / KWspTxDataDefaultBufferSize) + 1) * KWspTxDataDefaultBufferSize;
+
+				// Reallocate to a larger buffer
+				iBodyData = iBodyData->ReAlloc(bufferSize);
+				
+				// Was there enough memory?
+				if( iBodyData == NULL )
+					{
+					// Too much data - abort the request.
+					// NOTE - this could delete this object, so ensure that accessing data
+					// members is not done after this call.
+					iObserver.AbortInvoke();
+					return;
+					}
+				}
+			}
+		}
+	// Update the body data
+	UpdateBodyData();
+
+	// Send trailer headers?
+	if( iState == EDone && iHasTrailer )
+		{
+		// Cannot send trailer in the first SDU - has the header data been 
+		// cleared? This implies that at least one SDU has been sent.
+		if( iHeaderData->Des().Length() == 0 )
+			{
+			// Ensure that they fit in either this SDU or the message as a whole.
+			CheckTrailer();
+			}
+		else
+			{
+			// This is the first SDU - send trailers in the next one, if they fit.
+			if( iLimitedMessageSize && iRemainingMessageSpace - iRemainingSDUSpace < iTrailerInfoLength )
+				{
+				// Not enough space for the trailers - abort the method.
+				iState = EAbortMethod;
+				}
+			}
+		}
+	// Tell oberver what to do.
+	DecideObserverAction();
+	}
+
+void CWspCOTxData::ReceivedCnf()
+	{
+	__ASSERT_DEBUG( iState == EDone || iState == EGotMoreData, Panic(KWspPanicTxDataInInvalidState) );
+
+	// Zero the encoded headers buffer
+	iHeaderData->Des().Zero();
+
+	// Check to see if the request is done.
+	if( iState == EDone && !iHasTrailer )
+		{
+		// Client has sent all the request data and this has all been sent in 
+		// the SDUs - done!
+		iState = EIdle;
+
+		// Was this a POST-type SDU?
+		if( iBodyDataSupplier )
+			{
+			// Tell client to release the last data chunk.
+			iBodyDataSupplier->ReleaseData();
+			}
+
+		// Release resources here
+		Cleanup();
+		}
+	else
+		{
+		// Set the remaining SDU space
+		iRemainingSDUSpace = iNegotiatedCapInfo.GetServerSDUSize();
+		if( iRemainingSDUSpace == 0 )
+			{
+			// SDU size is unlimited - set to highest value
+			iRemainingSDUSpace = KMaxTUint32;
+			}
+		if( iLimitedMessageSize && iRemainingSDUSpace > iRemainingMessageSpace )
+			{
+			// The remaining message space is less than the SDU size - adjust the 
+			// remaining SDU space.
+			iRemainingSDUSpace = iRemainingMessageSpace;
+			}
+
+		// Update the body data
+		UpdateBodyData();
+
+		// Send trailer headers?
+		if( iState == EDone && iHasTrailer )
+			{
+			// Ensure that they fit in either this SDU or the message as a whole.
+			CheckTrailer();
+			}
+
+		// Tell oberver what to do.
+		DecideObserverAction();
+		}
+	}
+
+void CWspCOTxData::SetHeaderDataL(RHTTPRequest aRequest)
+	{
+	// See how much space is required for URI and header info - initialise with
+	// type field length.
+	TUint32 infoLength = KPDUTypeFieldSize;
+
+	// Calculate length of uri info
+	TUint32 uriLength = aRequest.URI().UriDes().Length();
+	TUint32 uriInfoLength = CalculateUIntVarLength(uriLength) + uriLength;
+
+	// Check that the uri fits in the SDU
+	infoLength += uriInfoLength;
+	if( infoLength > iRemainingSDUSpace )
+		{
+		// Abort the method - SDU blown with just uri info!
+		User::Leave(KWspErrRequestTooBig);
+		}
+
+	// Get the encoded headers...
+	EncodeHeadersL(aRequest.GetHeaderCollection());
+
+	// Calculate the header info size - this depends on the method
+	TUint32 headersLength = iHeaderData->Des().Length();
+
+	switch( aRequest.Method().Index(RHTTPSession::GetTable()) )
+   		{
+	case HTTP::EOPTIONS: case HTTP::EHEAD: case HTTP::EDELETE: case HTTP::ETRACE:
+   	case HTTP::EGET:
+   		{
+		// Nothing more to do for get-type PDU.
+   		} break;
+	case HTTP::EPUT:
+   	case HTTP::EPOST:
+   		{
+		// Need to add the UIntVar representation of the headers length
+		TUint32 headerInfoLength = CalculateUIntVarLength(headersLength);
+		infoLength += headerInfoLength;
+		if( infoLength > iRemainingSDUSpace )
+			{
+			// Abort the method - SDU blown by the header length info!
+			User::Leave(KWspErrRequestTooBig);
+			}
+   		} break;
+   	default:
+   		__ASSERT_DEBUG(0, Panic(KWspPanicBadMethodType));
+   		break;
+   		}
+	// Check to see if the headers fit into the SDU
+	infoLength += headersLength;
+	if( infoLength > iRemainingSDUSpace )
+		{
+		// Abort the method - SDU blown
+		User::Leave(KWspErrRequestTooBig);
+		}
+	// Update the remaining SDU and message space
+	iRemainingSDUSpace		-= infoLength;
+	if( iLimitedMessageSize )
+		iRemainingMessageSpace	-= infoLength;
+
+	// Are there trailer headers?
+	if( iHasTrailer )
+		{
+		// Only allowed if LDT supported
+		if( iSupportLDT )
+			{
+			if( iLimitedMessageSize )
+				{
+				// Trailers allowed - update message space
+				TUint32 trailerLength = iTrailerData->Des().Length();
+				iTrailerInfoLength = CalculateUIntVarLength(trailerLength) + trailerLength;
+
+				if( iLimitedMessageSize && iTrailerInfoLength > iRemainingMessageSpace )
+					{
+					// Abort the method - Message size is blown by the trailer 
+					// headers info!
+					User::Leave(KWspErrRequestTooBig);
+					}
+				}
+			}
+		else
+			{
+			// Leave - cannot do trailer headers if LDT not supported.
+			User::Leave(KWspErrTrailerHeadersNotExpected);
+			}
+		}
+	}
+
+void CWspCOTxData::EncodeHeadersL(RHTTPHeaders aHeaders)
+	{
+	// Is there a trailer header?
+	RStringPool stringPool = ProtTrans().Transaction().Session().StringPool();
+	THTTPHdrVal trailer;
+	TInt err = aHeaders.GetField(
+								stringPool.StringF(WSP::ETrailer, WSP::Table),
+								0,		// Zero index -> first part
+								trailer
+								);
+	iHasTrailer = (err == KErrNone);
+	CWspCOTransaction& wspTrans = STATIC_CAST(CWspCOTransaction&, ProtTrans());
+	if( iHasTrailer )
+		{
+		// Encode the header and trailer data
+		iHeaderData = wspTrans.GetWspHeaderUtils().EncodeNoTrailerHeadersL(stringPool, aHeaders, iTrailerData);
+		}
+	else
+		{
+		// Encode the header data
+		iHeaderData = wspTrans.GetWspHeaderUtils().EncodeHeadersL(stringPool, aHeaders);
+		}
+	}
+
+void CWspCOTxData::SetBodyDataL(MHTTPDataSupplier& aRequestBody)
+	{
+	__ASSERT_DEBUG( iBodyData == NULL, Panic(KWspPanicTxDataInInvalidState) );
+
+	// Store the body data supplier
+	iBodyDataSupplier = &aRequestBody;
+
+	// Set the overall data size
+	iOverallDataSize = iBodyDataSupplier->OverallDataSize();
+
+	// Is the data sized known?
+	if( iLimitedMessageSize && iOverallDataSize != -1 )
+		{
+		// Check to see if it will fit in the message, including trailer info.
+		// NOTE - if LDT is not supported, this will be reflected in the message
+		// size being the same as the SDU size.
+		if( iOverallDataSize + iTrailerInfoLength > iRemainingMessageSpace )
+			{
+			// Abort the method - Message size is blown by the request data.
+			User::Leave(KWspErrRequestTooBig);
+			}
+		}
+	// Get the data...
+	iRequestComplete = iBodyDataSupplier->GetNextDataPart(iDataPart);
+
+	if( iLimitedMessageSize && iOverallDataSize == -1 )
+		{
+		// Check to see if this data part exceeds the message space.
+		// NOTE - if LDT is not supported, this will be reflected in the message
+		// size being the same as the SDU size.
+		TInt dataSize = iDataPart.Length();
+		if( dataSize + iTrailerInfoLength > iRemainingMessageSpace )
+			{
+			// Abort the method - Message size is blown by the request data.
+			User::Leave(KWspErrRequestTooBig);
+			}
+		// Check to see if there are any more chunks
+		if( iRequestComplete )
+			{
+			// The overall data size is known
+			iOverallDataSize = dataSize;
+			}
+		}
+
+	// Create the buffer
+	TInt bufferSize = iNegotiatedCapInfo.GetServerSDUSize();
+
+	// Is the SDU size unlimited?
+	if( bufferSize == 0 )
+		{
+		// Is the overall data size known?
+		if( iOverallDataSize != -1 )
+			{
+			// Set buffer size to be the data size
+			bufferSize = iOverallDataSize;
+			}
+		else
+			{
+			// Get just enough for this chunk...
+			bufferSize = ((iDataPart.Length() / KWspTxDataDefaultBufferSize) + 1) * KWspTxDataDefaultBufferSize;
+			}
+		}
+	// Allocate space for the request data.
+	iBodyData = HBufC8::NewL(bufferSize);
+
+	// Update the body data
+	UpdateBodyData();
+	}
+
+void CWspCOTxData::UpdateBodyData()
+	{
+	__ASSERT_DEBUG( !iGotBodyData, Panic(KWspPanicTxDataInInvalidState) );
+
+	// The data buffer iBodyData has (at least) a max size of the SDU size. Need
+	// to copy enough data so that the SDU (and therefore the buffer) is not 
+	// exceeded. 
+	
+	// Does the given data part exceed the SDU?
+	TInt dataLength = iDataPart.Length();
+	TInt copyLength = dataLength;
+	if( STATIC_CAST(TUint32, dataLength) > iRemainingSDUSpace )
+		{
+		// Set the copy length to the remaining SDU space.
+		copyLength = iRemainingSDUSpace;
+		}
+
+	// Copy the required amount of data.
+	iBodyData->Des().Append(iDataPart.Left(copyLength));
+
+	// Save remaining data part for later.
+	iDataPart.Set(iDataPart.Right(dataLength - copyLength));
+
+	// Update the remaining SDU and message space.
+	iRemainingSDUSpace		-= copyLength;
+	if( iLimitedMessageSize )
+		iRemainingMessageSpace	-= copyLength;
+
+	// Update the state according to the request data state.
+	UpdateState();
+
+	// Check the state...
+	if( iState == EWaitForMoreData )
+		{
+		// Release the data from the client - need to get next batch.
+		iBodyDataSupplier->ReleaseData();
+		}
+	else
+		{
+		// Flag the fact that there is body data
+		iGotBodyData = ETrue;
+		}
+	}
+
+void CWspCOTxData::UpdateState()
+	{
+	// Possible scenarios;
+	// 1) The request is complete and the received data fits into a single SDU.
+	// ACTION - MoreData flag is False. State is EDone.
+	//
+	// 2) The request is complete and the received data requires subsequent SDUs.
+	// ACTION - MoreData flag is True. State is EGotMoreData.
+	//
+	// 3) The request is not complete and the received data does not fill the SDU.
+	// ACTION - Need to wait for the next bit of data. State is EWaitForMoreData.
+	//
+	// 4) The request is not complete and the received fills the SDU.
+	// ACTION - MoreData flag is True. Ensure request data given fits in the SDU. 
+	//			State is EGotMoreData.
+	
+	if( iRequestComplete )
+		{
+		// Possible scenarios 1, 2, 5, 6 and NOTE.
+		if( iDataPart.Length() == 0 )
+			{
+			// No more data to send - scenario 1.
+			iState = EDone;
+			}
+		else
+			{
+			// Need to send remaining request data later - scenario 2.
+			iState = EGotMoreData;
+			}
+		}
+	else
+		{
+		// Possible scenarios here are 3 and 4.
+		if( iRemainingSDUSpace == 0 )
+			{
+			// No space left in the SDU and need to send it - scenario 4.
+			iState = EGotMoreData;
+			}
+		else
+			{
+			// There is still space in the SDU. Need to wait for more data from
+			// the client - scenario 3.
+			iState = EWaitForMoreData;
+			}
+		}
+	}
+
+void CWspCOTxData::CheckTrailer()
+	{
+	// Do they fit in this SDU?
+	if( iLimitedMessageSize && iTrailerInfoLength > iRemainingSDUSpace )
+		{
+		// Trailers either do not fit in this SDU. Is there enough space 
+		// to send them?
+		if( iRemainingMessageSpace - iRemainingSDUSpace < iTrailerInfoLength )
+			{
+			// Not enough space for the trailers - abort the method.
+			iState = EAbortMethod;
+			}
+		}
+	else
+		{
+		// The trailer info fits into this SDU - update the header data.
+		delete iHeaderData;
+		iHeaderData		= iTrailerData;
+		iTrailerData	= NULL;
+
+		// Clear flag indicating that the trailers have yet to be sent.
+		iHasTrailer = EFalse;
+		}
+	}
+
+void CWspCOTxData::DecideObserverAction()
+	{
+	// Posible actions for the observer - 
+	// 1)	If the state is either EDone or EGotMoreData, then the observer 
+	//		needs to send a method primitive.
+	// 2)	If the state is EAbortMethod, then the observer needs to abort the
+	//		method.
+	// 3)	If the state is EWaitForMoreData, then the observe does nothing.
+
+	if( iState == EAbortMethod )
+		{
+		// Something has gone wrong - need to tell observer to abort the
+		// method.
+		// NOTE - this could delete this object, so ensure that accessing data
+		// members is not done after this call.
+		iObserver.AbortInvoke();
+		}
+	else if( iState != EWaitForMoreData )
+		{
+		// Update the remaining message space - remove the remaining space in
+		// this SDU.
+		if( iLimitedMessageSize )
+			iRemainingMessageSpace -= iRemainingSDUSpace;
+
+		// Tell observer to send a primitive
+		iObserver.SendInvokePrimitive();
+		}
+	}
+
+void CWspCOTxData::Cleanup()
+	{
+	delete iHeaderData;
+	iHeaderData		= NULL;
+	delete iBodyData;
+	iBodyData		= NULL;
+	delete iTrailerData;
+	iTrailerData	= NULL;
+	}
+
+void CWspCOTxData::SetSessionPropertyL(TInt aProperty, THTTPHdrVal aFieldValue)
+	{
+	RHTTPSession session			= ProtTrans().Transaction().Session();
+	RHTTPConnectionInfo	connInfo	= session.ConnectionInfo();
+	RStringPool stringPool			= session.StringPool();
+
+	connInfo.SetPropertyL(
+						 stringPool.StringF(aProperty, RHTTPSession::GetTable()), 
+						 aFieldValue
+						 );
+	}
+
+TInt CWspCOTxData::CalculateUIntVarLength(TUint32 aUint)
+	{
+	TUint8 size = 0; // maximum value is 5 with a 32 bit integer
+	TUint32 value = aUint;
+	do {
+		++size;
+		value >>= KUIntVarOctetShift; ; // shift by 7 bits.
+		} while (value>0);
+
+	return size;
+	}
+
+/*
+ * Methods from MHTTPDataSupplier
+ */
+
+TBool CWspCOTxData::GetNextDataPart(TPtrC8& aDataPart)
+	{
+	__ASSERT_DEBUG( iGotBodyData, Panic(KWspPanicNoRequestDataReceived) );
+
+	// Set the data part
+	aDataPart.Set(iBodyData->Des());
+
+	// Set the return value
+	TBool lastChunk = ETrue;
+	if( iState != EDone || iHasTrailer )
+		{
+		// All the current data has not been sent  - this is not the last chunk.
+		lastChunk = EFalse;
+		}
+	return lastChunk;
+	}
+
+void CWspCOTxData::ReleaseData()
+	{
+	__ASSERT_DEBUG( iGotBodyData, Panic(KWspPanicNoRequestDataReceived) );
+
+	// Clear the flag indicating that there is request data
+	iGotBodyData = EFalse;
+
+	// Zero the old body data.
+	TPtr8 buf = iBodyData->Des();
+	buf.Zero();
+	}
+	
+TInt CWspCOTxData::OverallDataSize()
+	{
+	return iOverallDataSize;
+	}
+
+TInt CWspCOTxData::Reset()
+	{
+	// This does nothing
+	return KErrNotFound;
+	}