--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerprotocols/httptransportfw/httpmessage/chttpmessagecomposer.cpp Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,735 @@
+// Copyright (c) 2003-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:
+//
+
+#include "chttpmessagecomposer.h"
+
+#include <http/mhttpdatasupplier.h>
+#include <inetprottextutils.h>
+
+#include "mhttpmessagecomposerobserver.h"
+#include "thttpmessagepanic.h"
+
+const TInt KDefaultBufferSize = 128;
+
+// 'this' used in base member initializer list, The 'this' pointer being used is a base class pointer.
+#pragma warning( disable : 4355 )
+
+CHttpMessageComposer* CHttpMessageComposer::NewL(MHttpMessageComposerObserver& aObserver)
+/**
+ Factory constructor.
+ @param aObserver The observer for the composer.
+ @return A pointer to a fully constructed and initialised object.
+*/
+ {
+ return new (ELeave) CHttpMessageComposer(aObserver);
+ }
+
+CHttpMessageComposer::~CHttpMessageComposer()
+/**
+ Destructor
+*/
+ {
+ Cancel();
+
+ delete iDataBuffer;
+ }
+
+CHttpMessageComposer::CHttpMessageComposer(MHttpMessageComposerObserver& aObserver)
+: CActive(CActive::EPriorityStandard + 1), iObserver(aObserver), iDataComposer(*this)
+/**
+ Constructor.
+ We want the request/response to be sent/received and processed as early as possible to avoid the latency by keeping
+ it in the framework. We reduced the scheduling of AOs by 1/6th + 2 when composing and 1/5th + 2 for parsing the header. The value 2
+ indicates the EIdle state and the startline composing/parsing.
+*/
+ {
+ CActiveScheduler::Add(this);
+ }
+
+void CHttpMessageComposer::MessageInfoAvailable()
+/**
+ Notifies the composer that more message info is available. The composer can
+ continue composing the message.
+ @pre The composer is waiting for more message info.
+ @post The composer continues composing the message.
+ @panic EHttpMessagePanicBadDataState The composer was not waiting for
+ more info.
+*/
+ {
+ __ASSERT_DEBUG( iDataState == EWaitingForInfo, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );
+
+ // Message info available - update data state and continue/start composing.
+ iDataState = EGotInfo;
+ CompleteSelf();
+ }
+
+void CHttpMessageComposer::GetMessageData(TPtrC8& aData)
+/**
+ Get the current message data.
+ @panic EHttpMessagePanicBadDataState There is no current data buffer
+ ready.
+ @pre The observer has been notified of available message data.
+*/
+ {
+ __ASSERT_DEBUG( iDataState == EWaitingForRelease, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );
+
+ aData.Set(iSendBuffer);
+ }
+
+void CHttpMessageComposer::ReleaseMessageData()
+/**
+ Release the message data. The observer has finished with the current message
+ data buffer. The composer can continue composing the message.
+ @panic EHttpMessagePanicBadDataState There is no current data buffer
+ ready.
+ @pre The observer has been notified of available message data.
+ @post The composer continues composing the message.
+*/
+ {
+ __ASSERT_DEBUG( iDataState == EWaitingForRelease, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );
+
+ // Is there an entity body?
+ if (CheckMessagePendingComplete())
+ {
+ iObserver.MessageComplete();
+
+ //The composer is done.
+ iState = EIdle;
+ }
+ else
+ {
+ // Update data state and continue composing.
+ iDataState = EGotInfo;
+ CompleteSelf();
+ }
+ }
+
+void CHttpMessageComposer::Reset()
+/**
+ Composer reset request. As the observer can reset the composer during one of
+ the callback functions, the composer must check for re-entrancy to avoid
+ releasing resources that are still required. If the composer is either
+ waiting for more message info, waiting for its data part to be released or
+ is waiting to process its state machine, the composer can safely reset
+ immediately. Otherwise the composer is being reset from within its RunL()
+ and so it must defer resetting itself until a safer point - this is the
+ point in the RunL() where the next step is decided.
+ @panic EHttpMessagePanicDoubleReset The composer has been reset twice
+ in one of the callback functions.
+*/
+ {
+ // Check the data state of the composer - the composer cannot be reset if
+ // the Reset() was called in one of the observer callbacks. It is safe to
+ // reset now if -
+ // 1) the data state is WaitingForInfo
+ // 2) the data state is WaitingForRelease
+ // 3) the composer is active - waiting to for its RunL() to be called.
+ if( iDataState == EWaitingForInfo || iDataState == EWaitingForRelease || IsActive() )
+ {
+ // Cancel and do the reset.
+ Cancel();
+ DoReset();
+ }
+ else
+ {
+ // Debug check for a double Reset() call...
+ __ASSERT_DEBUG( iDataState != EReset, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicDoubleReset) );
+
+ // The Reset() was called inside a callback - defer reseting the composer
+ // until call stack is back in the composer RunL().
+ iDataState = EReset;
+ }
+ }
+
+void CHttpMessageComposer::CompleteSelf()
+/**
+ Self-complete function. Ensures that the state machine is processed.
+*/
+ {
+ TRequestStatus* pStat = &iStatus;
+ User::RequestComplete(pStat, KErrNone);
+ SetActive();
+ }
+
+void CHttpMessageComposer::DoReset()
+/**
+ Resets the composer. The composer moves into the Idle state. Allocated
+ resources are also reset.
+*/
+ {
+ // Reset the composer - composer state should be Idle and the data state
+ // should be WaitingForInfo.
+ iState = EIdle;
+ iDataState = EWaitingForInfo;
+ iLastChunk = EFalse;
+
+ // Reset the data composer
+ iDataComposer.Reset();
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeStartLineL()
+/**
+ Composes the message start-line. The start-line can be either a Request-Line
+ or a Status-Line. Both have three tokens.
+
+ generic-message = start-line
+ *(message-header CRLF)
+ CRLF
+ [ message-body ]
+
+ start-line = Request-Line | Status-Line
+
+ Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+ Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+ The observer supplies these tokens.
+ @return A value of ESectionDone indicating that the start-line has been
+ done.
+*/
+ {
+ __START_PERFORMANCE_LOGGER();
+ // Get the start-line tokens from the observer.
+ TPtrC8 token1, token2, token3;
+ iObserver.StartLineL(token1, token2, token3);
+
+ // Add the tokens to the data buffer.
+ iDataComposer.AddTokenL(token1, THttpDataComposer::ESpace);
+ iDataComposer.AddTokenL(token2, THttpDataComposer::ESpace);
+ iDataComposer.AddTokenL(token3, THttpDataComposer::ECRLF);
+ __END_PERFORMANCE_LOGGER(_L(",CHttpMessageComposer::ComposeStartLineL()"));
+
+ return ESectionDone;
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeHeadersL()
+ {
+ // Compose maximum of 6 headers at one go without AO scheduling
+ TComposingStatus status = ComposeSingleHeaderL();
+ if(status == ESectionNotDone)
+ {
+ status = ComposeSingleHeaderL();
+ if(status == ESectionNotDone)
+ {
+ status = ComposeSingleHeaderL();
+ if(status == ESectionNotDone)
+ {
+ status = ComposeSingleHeaderL();
+ if(status == ESectionNotDone)
+ {
+ status = ComposeSingleHeaderL();
+ if(status == ESectionNotDone)
+ {
+ status = ComposeSingleHeaderL();
+ }
+ }
+ }
+ }
+ }
+ return status;
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeSingleHeaderL()
+/**
+ Composes a header field or the end of headers marker. Each header field is
+ delimited by a CRLF end of line marker.
+
+ generic-message = start-line
+ *(message-header CRLF)
+ CRLF
+ [ message-body ]
+
+ message-header = field-name ":" [ field-value ]
+
+ The observer supplies the field name and value tokens. If there is no more
+ header field info then the end of headers marker is added - the composer is
+ now ready to send the start-line and headers section of the message. The
+ optional message-body will follow if there is one. The current data buffer
+ is set to contain the start-line and message-headers.
+ @return A value of ESectionNotDone indicating that there are more headers
+ to add. A value of ESendData indicating that the current message
+ buffer should be sent.
+*/
+ {
+ __START_PERFORMANCE_LOGGER();
+ // Get header field info for the next header from the observer - the return
+ // value should indicate if there is any more header info.
+ TPtrC8 name, value;
+ TInt error = iObserver.NextHeaderL(name, value);
+
+ // Was there any header info?
+ TComposingStatus status = ESectionNotDone;
+ if( error == KErrNone )
+ {
+ // Add header info to the data buffer - return default status.
+ iDataComposer.AddTokenL(name, THttpDataComposer::EColonSpace);
+ iDataComposer.AddTokenL(value, THttpDataComposer::ECRLF);
+ }
+ else
+ {
+ // No more headers - add an empty line to mark the end of the headers.
+ iDataComposer.AddTokenL(KNullDesC8(), THttpDataComposer::ECRLF);
+
+ // Set the send buffer - need to send it so return ESendData status.
+ iSendBuffer.Set(*iDataBuffer);
+ status = ESendData;
+ }
+ __END_PERFORMANCE_LOGGER(_L(",CHttpMessageComposer::ComposeSingleHeaderL()"));
+ return status;
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeChunkSizeL()
+/**
+ Compose the chunk-size component. The chunk-size component indicates the
+ size of the subsequent chunk-data component.
+
+ chunk = chunk-size [ chunk-extension ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEX
+
+ The composer is ready to send the chunk-size component before sending the
+ chunk-data. Also, an empty line marker from a previous chunk-data component
+ is included in the send buffer if this is not the first chunk-size component.
+ Note that if the chunk-data component is zero-length it should be ignored
+ since a zero value chunk-size indicates the last-chunk component.
+ @return A value of ESendData indicating that the current message buffer
+ should be sent. A value of ESectionDone if the chunk is of zero-
+ length.
+*/
+ {
+ __START_PERFORMANCE_LOGGER();
+ // Get the current data chunk from the data supplier
+ TPtrC8 chunk;
+ iBodyData->GetNextDataPart(chunk);
+
+ TComposingStatus status = ESendData;
+ TInt chunkSize = chunk.Length();
+ if( chunkSize > 0 )
+ {
+ // Convert the size to its HEX representation.
+ HBufC8* hex = NULL;
+ InetProtTextUtils::ConvertHexToDescriptorL(chunkSize, hex);
+ CleanupStack::PushL(hex);
+
+ // Add the chunk-size componennt.
+ iDataComposer.AddTokenL(*hex, THttpDataComposer::ECRLF);
+ CleanupStack::PopAndDestroy(hex);
+
+ // Set the send buffer - need to send it so return ESendData status.
+ iSendBuffer.Set(*iDataBuffer);
+ }
+ else
+ {
+ // The chunk is zero-length - this section is done.
+ status = ESectionDone;
+ }
+ __END_PERFORMANCE_LOGGER(_L(",CHttpMessageComposer::ComposeChunkSizeL()"));
+ return status;
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeLastChunkL()
+/**
+ Composes the last-chunk component. The last-chunk component marks the end
+ of the chunked data.
+
+ last-chunk = 1*("0") [ chunk-extension ] CRLF
+
+ Optional trailers may follow.
+ @return A value of ESectionDone indicating that the last-chunk component
+ has been added to the message.
+*/
+ {
+ // Add zero to mark the last-chunk.
+ _LIT8(KZero, "0");
+ iDataComposer.AddTokenL(KZero(), THttpDataComposer::ECRLF);
+
+ // The trailers now follow so this section is done.
+ return ESectionDone;
+ }
+
+CHttpMessageComposer::TComposingStatus CHttpMessageComposer::ComposeTrailerL()
+/**
+ Composes a trailer header field. Each trailer header field is delimited by
+ a CRLF end of line marker.
+
+ Chunked-Body = *chunk
+ last-chunk
+ trailer
+ CRLF
+
+ trailer = *(entity-header CRLF)
+
+ entity-header = message-header
+
+ message-header = field-name ":" [ field-value ]
+
+ The observer supplies the field name and value tokens. If there is no more
+ trailer header field info then the end of headers marker is added - the
+ composer is now ready to send the trailer headers section of the message.
+ The last-chunk section is also included in the send buffer.
+ @return A value of ESectionNotDone indicating that there are more headers
+ to add. A value of ESendData indicating that the current message
+ buffer should be sent.
+*/
+ {
+ __START_PERFORMANCE_LOGGER();
+ // Get trailer header field info for the next trailer from the observer - the
+ // return value should indicate if there is any more trailer info.
+ TPtrC8 name, value;
+ TInt error = iObserver.NextTrailerL(name, value);
+
+ // Was there any trailer info?
+ TComposingStatus status = ESectionNotDone;
+ if( error == KErrNone )
+ {
+ // Add trailer info to the data buffer - return default status.
+ iDataComposer.AddTokenL(name, THttpDataComposer::EColonSpace);
+ iDataComposer.AddTokenL(value, THttpDataComposer::ECRLF);
+ }
+ else
+ {
+ // No more trailers - add an empty line to mark the end of the trailers.
+ iDataComposer.AddTokenL(KNullDesC8(), THttpDataComposer::ECRLF);
+
+ // Set the send buffer - need to send it so return ESendData status.
+ iSendBuffer.Set(*iDataBuffer);
+ status = ESendData;
+ }
+ __END_PERFORMANCE_LOGGER(_L(",CHttpMessageComposer::ComposeTrailerL()"));
+ return status;
+ }
+
+/*
+ * Methods from MHttpBufferSupplier
+ */
+
+void CHttpMessageComposer::ReAllocBufferL(TInt aRequiredSize, TPtr8& aBuffer)
+/**
+ Reallocates the data buffer. The composer supplies the data buffer to the
+ data composer. The data composer needs more space to store the current token.
+ If the data buffer has not been created then it is created, otherwise it is
+ reallocated to at least the required size.
+ @param aRequiredSize The minimum size of buffer required.
+ @param aBuffer An output argument set to the reallocated buffer.
+ @panic EInvariantFalse The required size was less then the current max
+ size of the buffer.
+*/
+ {
+ if( iDataBuffer == NULL )
+ {
+ // Create the buffer..
+ iDataBuffer = HBufC8::NewL(aRequiredSize + KDefaultBufferSize);
+ }
+ else
+ {
+ __ASSERT_DEBUG( aRequiredSize > iDataBuffer->Des().MaxLength(), User::Invariant() );
+
+ iDataBuffer = iDataBuffer->ReAllocL(aRequiredSize + KDefaultBufferSize);
+ }
+ aBuffer.Set(iDataBuffer->Des());
+ }
+
+void CHttpMessageComposer::DeleteBuffer()
+/**
+ Deletes the data buffer.
+*/
+ {
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+ }
+
+/*
+ * Methods from CActive
+ */
+void CHttpMessageComposer::RunL()
+/**
+ Asynchronous request service handler. The composer state machine is processed
+ in this function. Behaviour depends on the state. The composer will self-
+ complete if it can continue composing the message. The composer informs its
+ observer when it has message data ready to send. It will then suspend
+ processing until the observer notifies it that it has finished with the
+ current data. If the composer requires more message info it will stop
+ processing until it is notified that more message info is available.
+
+ If the observer has reset the composer in one of the callback functions then
+ the composer will defer resetting itself until it is back in the RunL().
+ @leave KErrCorrupt The body data supplier indicated that the last data
+ part even though it had not supplied all the data it
+ specified. Or the body data supplier has supplied
+ all the data it specified but has not indicated a
+ last data part.
+ @panic EHttpMessagePanicBadDataState The composer has no info to compose
+ the message with.
+ @panic EInvarinatFalse The composer is trying to send entity body data
+ when there is none to send.
+ @panic EHttpMessagePanicBadComposerStata The composer is in an illegal
+ state.
+*/
+ {
+ __ASSERT_DEBUG( iDataState == EGotInfo, THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadDataState) );
+
+ TComposingStatus status = ESectionNotDone;
+ switch( iState )
+ {
+ case EIdle:
+ {
+ status = ESectionDone;
+ iState = ECreatingStartLine;
+ }
+ //coverity [MISSING_BREAK]
+ // Fallthrough is required here as we no longer compose line by line. We compose startline & headers (6) at one go.
+ case ECreatingStartLine:
+ {
+ status = ComposeStartLineL();
+ iState = ECreatingHeaders;
+ }
+ //coverity [MISSING_BREAK]
+ // Fallthrough is required here as we no longer compose line by line. We compose startline & headers (6) at one go.
+ case ECreatingHeaders:
+ {
+ status = ComposeHeadersL();
+ // Is the headers section complete?
+ if( status != ESectionNotDone )
+ {
+ iState = EPendingEntityBody;
+
+ // Is there an entity body?
+ iBodyData = iObserver.HasBodyL();
+ }
+ // Stay in this state if there are more headers to compose.
+ } break;
+ case EPendingEntityBody:
+ {
+ // Release the current data buffer
+ iDataComposer.Release();
+
+ // Set the amount of the entity body data
+ if( iBodyData != NULL )
+ iDataSizeLeft = iBodyData->OverallDataSize();
+ else
+ iDataSizeLeft = 0;
+
+ // Determine next action depending on amount of entity data to send.
+ if( iDataSizeLeft > 0 )
+ {
+ // A known amount of data greater than zero. Send entity body with
+ // no transfer encoding.
+ iState = ESendEntityData;
+ }
+ else if( iDataSizeLeft == 0 )
+ {
+ // No entity body or zero length entity body. The message is
+ // complete.
+ iState = EPendingIdle;
+ }
+ else
+ {
+ // An unknown amount of data. Send entity body with chunked transfer
+ // encoding.
+ iState = ECreatingChunkSize;
+ }
+ status = ESectionDone;
+ } break;
+ case ESendEntityData:
+ {
+ __ASSERT_DEBUG( !iLastChunk, User::Invariant() );
+
+ // Get the next entity body part
+ iLastChunk = iBodyData->GetNextDataPart(iSendBuffer);
+
+ // Only send data if there is any to send.
+ TInt dataLength = iSendBuffer.Length();
+ if( dataLength > 0 )
+ status = ESendData;
+ else
+ status = ESectionDone;
+
+ // Update how much data is left to send.
+ iDataSizeLeft -= dataLength;
+
+ // Check whether the client is sending more/less data than it should.
+ if( (iDataSizeLeft < 0) || (iDataSizeLeft > 0 && iLastChunk) )
+ User::Leave(KErrCorrupt);
+
+ iState = EPendingReleaseData;
+ } break;
+ case EPendingReleaseData:
+ {
+ // Release the current data part.
+ iBodyData->ReleaseData();
+
+ // Has the last entity body part been released?
+ if( iLastChunk )
+ {
+ // Yep, message is complete.
+ status = ESectionDone;
+ iState = EPendingIdle;
+ }
+ else
+ {
+ // Stop composing until more data received.
+ status = EStop;
+ iState = ESendEntityData;
+ }
+ } break;
+ case ECreatingChunkSize:
+ {
+ status = ComposeChunkSizeL();
+ iState = ESendChunkData;
+ } break;
+ case ESendChunkData:
+ {
+ // Release the current data buffer
+ iDataComposer.Release();
+
+ // Get the chunk-data from the body data supplier.
+ iLastChunk = iBodyData->GetNextDataPart(iSendBuffer);
+
+ // Only send data if there is any to send.
+ if( iSendBuffer.Length() > 0 )
+ status = ESendData;
+ else
+ status = ESectionDone;
+
+ // Move to the PendingReleaseChunk state.
+ iState = EPendingReleaseChunk;
+ } break;
+ case EPendingReleaseChunk:
+ {
+ // Release the current data part.
+ iBodyData->ReleaseData();
+
+ // RFC2616 states -
+ //
+ // chunk = chunk-size [ chunk-extension ] CRLF
+ // chunk-data CRLF
+ //
+ // Therefore need to add CRLF marker.
+ iDataComposer.AddTokenL(KNullDesC8(), THttpDataComposer::ECRLF);
+
+ // Has the last entity body part been released?
+ if ( iLastChunk )
+ {
+ // Yep - mark with last-chunk component
+ status = ComposeLastChunkL();
+ iState = ECreatingTrailers;
+ }
+ else
+ {
+ // Stop composing until more data received.
+ status = EStop;
+ iState = ECreatingChunkSize;
+ }
+ } break;
+ case ECreatingTrailers:
+ {
+ status = ComposeTrailerL();
+
+ // Have all the trailers been added?
+ if( status != ESectionNotDone )
+ {
+ iState = EPendingEndOfChunkedBody;
+ }
+ // Stay in this state if there are more trailers to compose.
+ } break;
+ case EPendingEndOfChunkedBody:
+ {
+ // Release the current data buffer.
+ iDataComposer.Release();
+
+ // The message is complete.
+ status = ESectionDone;
+ iState = EPendingIdle;
+ } break;
+ case EPendingIdle:
+ {
+ // The message is complete - inform the observer.
+ iObserver.MessageComplete();
+
+ // The composer is done.
+ iState = EIdle;
+ status = EStop;
+ } break;
+ default:
+ THttpMessagePanic::Panic(THttpMessagePanic::EHttpMessagePanicBadComposerState);
+ break;
+ }
+ // Determine what to do next...
+ if( iDataState == EReset )
+ {
+ // The observer has reset the composer during one of the callbacks - it
+ // is now safe to do the reset.
+ DoReset();
+ }
+ else if( status == ESectionDone || status == ESectionNotDone )
+ {
+ // Need to keep composing - complete-self.
+ CompleteSelf();
+ }
+ else if( status == ESendData )
+ {
+ // Update data state...
+ iDataState = EWaitingForRelease;
+
+ // Inform the observer that there is message data to send.
+ iObserver.MessageDataReadyL();
+ }
+ else
+ {
+ // Data state is EStop - stop composing and wait for more message data.
+ iDataState = EWaitingForInfo;
+ }
+ }
+
+void CHttpMessageComposer::DoCancel()
+/**
+ Asynchronous request cancel. This function does nothing as the only asynch
+ request that is made is a self-complete request.
+*/
+ {
+ // Do nothing...
+ }
+
+TInt CHttpMessageComposer::RunError(TInt aError)
+/**
+ Asynchronous request service error handler. An error has occured whilst
+ processing the state machine. Reset the composer and notify the observer of
+ the error.
+ @param aError The error code.
+ @return An interger value of KErrNone indicates that the error has been
+ handled.
+ @post The composer has been reset.
+*/
+ {
+ // Stop composing and reset.
+ DoReset();
+
+ // Notify the observer of the error
+ return iObserver.HandleComposeError(aError);
+ }
+
+TBool CHttpMessageComposer::CheckMessagePendingComplete()
+/**
+ Check if the message has been completely sent.
+ i.e. the headers have been composed and sent and there is no body to send.
+ Only waiting for a confirmation socket that it has in fact been sent.
+
+ @return TBool ETrue if the message is complete.
+ @post The composer has been reset.
+*/
+ {
+ return (iState == EPendingEntityBody && iBodyData==NULL) ? ETrue:EFalse;
+ }
+