--- /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;
+ }