--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servicediscoveryandcontrol/pnp/test/upnp/upnpmessage/src/cupnpresponseparser.cpp Tue Feb 02 01:12:20 2010 +0200
@@ -0,0 +1,463 @@
+// Copyright (c) 2008-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 <httpstringconstants.h>
+#include <inetprottextutils.h>
+#include <httperr.h>
+#include <rmemcell.h>
+
+#include "cupnpresponseparser.h"
+#include "tupnpmessagepanic.h"
+
+_LIT8(KHeaderSeparator, "\n");
+
+__FLOG_STMT(_LIT8(KSubsys,"UPnPResParser");)
+__FLOG_STMT(_LIT8(KComponent,"UPnPMessage");)
+
+_LIT8(KHTTP, "HTTP");
+
+EXPORT_C CUpnpResponseParser* CUpnpResponseParser::NewL(MParserObserver& aObserver)
+ {
+ CUpnpResponseParser* self = new(ELeave) CUpnpResponseParser(aObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CUpnpResponseParser::CUpnpResponseParser(MParserObserver& aObserver)
+ :iObserver(aObserver)
+ {
+ }
+
+EXPORT_C CUpnpResponseParser::~CUpnpResponseParser()
+ {
+ iMessageParser.Close();
+ iBodyParts.Close();
+ if(!iMsgBuf.IsEmpty())
+ {
+ iMsgBuf.Free();
+ }
+ iRawDataArray.Close();
+ __FLOG(_L8("-> Response parser destroyed"));
+ __FLOG_CLOSE;
+ }
+
+void CUpnpResponseParser::ConstructL()
+ {
+ iMessageParser.OpenL(*this);
+ __FLOG_OPEN(KSubsys, KComponent);
+ __FLOG(_L8("-> Response parser created"));
+ }
+
+void CUpnpResponseParser::ResetData()
+ {
+ iMessageParser.Reset();
+ iBodyParts.Reset();
+ iOverallDataSize = 0;
+ iFlags = 0;
+ }
+
+TBool CUpnpResponseParser::GetNextDataPart(TPtrC8& aDataPart)
+ {
+ __FLOG(_L8("-> Supplying response body part"));
+
+ TInt bodyPartsCount = iBodyParts.Count();
+ __ASSERT_ALWAYS((bodyPartsCount > 0), TUPnPMessagePanic::Panic(TUPnPMessagePanic::ENoBodyPartInDataSupplier));
+
+ // Provide the first chunk.
+ aDataPart.Set(iBodyParts[0]);
+ __FLOG_1(_L8("%S"), &aDataPart);
+
+ return (BodyComplete() && bodyPartsCount == 1);
+ }
+
+void CUpnpResponseParser::ReleaseData()
+ {
+ __FLOG(_L8("-> Releasing response body part"));
+
+ // Remove the oldest chunk.
+ if( iBodyParts.Count() > 0 )
+ {
+ iBodyParts.Remove(0);
+ }
+
+ // Are there any more chunks?
+ if(iBodyParts.Count() > 0)
+ {
+ // Notify the sender about the presence of the body. He can then call
+ // MHTTPDataSupplier::GetNextDataPart() to get the body data.
+ iObserver.GotBodyData();
+ }
+
+ else
+ {
+ // Does this data packet need to be released?
+ if( NotifyReleaseData() )
+ {
+ // Notify sender that the current data is no longer needed.
+ // Can release as there are no body chunks waiting to be passed to the
+ // client.
+ iObserver.DataParsed();
+ iFlags &= ~ENotifyReleaseData;
+ }
+ }
+ }
+
+TInt CUpnpResponseParser::OverallDataSize()
+ {
+ return (iOverallDataSize < 0) ? KErrNotFound : iOverallDataSize;
+ }
+
+TInt CUpnpResponseParser::Reset()
+ {
+ return KErrNotSupported;
+ }
+
+void CUpnpResponseParser::GetDataPacket(TPtrC8& aData)
+ {
+ __FLOG(_L8("-> Supplying response data to the HTTP message parser"));
+ aData.Set(iRawDataArray[0]);
+ iRawDataArray.Remove(0);
+ }
+
+void CUpnpResponseParser::ReleaseDataPacket()
+ {
+ __FLOG(_L8("-> Releasing response data"));
+
+ if( ConsumingResponse() && MessageComplete() )
+ {
+ // Ok, the response was a 1xx message which has been consumed. Reset the
+ // this object and continue parsing the next part of the response.
+ ResetData();
+ iMessageParser.ReceivedMessageData();
+ }
+ else if(iBodyParts.Count() == 0)
+ {
+ if(iRawDataArray.Count() > 0 && !IsExcessData())
+ {
+ iMessageParser.ReceivedMessageData();
+ }
+ else
+ {
+ // Notify sender that the current data is no longer needed.
+ // Can release as there are no body chunks waiting to be passed to the
+ // client.
+ iObserver.DataParsed();
+ }
+ }
+ else
+ {
+ // Flag that the data needs to be released
+ iFlags |= ENotifyReleaseData;
+ }
+ }
+
+void CUpnpResponseParser::StartLineL(const TDesC8& aStartLine)
+ {
+ // The RFC2616 defines the Status-Line as follows -
+ //
+ // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ //
+ // First extract the HTTP-Version
+ __FLOG(_L8("-> Got the reponse start line"));
+ __FLOG_1(_L8("%S"), &aStartLine);
+
+ TPtrC8 data = aStartLine;
+ User::LeaveIfError(InetProtTextUtils::RemoveWhiteSpace(data, InetProtTextUtils::ERemoveLeft));
+
+ // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+ TInt consumed = data.Locate('/');
+ User::LeaveIfError(consumed);
+
+ if((consumed > KHTTP().Length()) || (consumed < KHTTP().Length()))
+ User::Leave(KErrCorrupt);
+
+ TPtrC8 http = data.Left(consumed);
+ if( http.CompareF(iResponse->StringPool().StringF(HTTP::EHTTP, THTTPTable::Table()).DesC()) != 0 )
+ User::Leave(KErrCorrupt);
+
+ // Skip past the HTTP and "/"
+ data.Set(data.Mid(consumed + 1));
+
+ // Extract the major version number
+ TInt major;
+ consumed = InetProtTextUtils::ConvertDescriptorToInt(data, major);
+ User::LeaveIfError(consumed);
+
+ // Skip past major version number and the "."
+ data.Set(data.Mid(consumed + 1));
+
+ // Extract the minor version number
+ TInt minor;
+ consumed = InetProtTextUtils::ConvertDescriptorToInt(data, minor);
+ User::LeaveIfError(consumed);
+
+ // Skip past minor version number and the SP
+ data.Set(data.Mid(consumed + 1));
+
+ // Clear any extra surrounding whitespace
+ User::LeaveIfError(InetProtTextUtils::RemoveWhiteSpace(data, InetProtTextUtils::ERemoveBoth));
+
+ // Extract the status code
+ TInt status;
+ consumed = InetProtTextUtils::ConvertDescriptorToInt(data, status);
+ User::LeaveIfError(consumed);
+
+ if( data.Length() > consumed )
+ {
+ // Skip past status code and the SP
+ data.Set(data.Mid(consumed + 1));
+
+ // Remaining data is the status reason - trim any leading whitespace as right side already trimmed
+ User::LeaveIfError(InetProtTextUtils::RemoveWhiteSpace(data, InetProtTextUtils::ERemoveLeft));
+ }
+ else
+ {
+ // No reason phrase following the status code
+ data.Set(KNullDesC8());
+ }
+ // Populate the Status-Line info
+ RStringF reason = iResponse->StringPool().OpenFStringL(data);
+
+ RResponse response = iResponse->Handle();
+
+ TVersion version(major, minor, 0);
+ response.SetVersion(version);
+ response.SetStatusCode(status);
+ response.SetStatusText(reason);
+ reason.Close();
+
+ // Check to see if a body is expected
+ if( HTTPStatus::IsInformational(status) )
+ {
+ // 1xx status - no body and need to consume this response
+ iFlags |= EConsumingResponse;
+ }
+ }
+
+void CUpnpResponseParser::HeaderL(const TDesC8& aFieldName, TDesC8& aFieldValue)
+ {
+ // Only set the headers if we are not consuming the response.
+ __FLOG(_L8("-> Got header value pair"));
+ __FLOG_2(_L8("%S: %S"), &aFieldName, &aFieldValue);
+
+ if( !ConsumingResponse() )
+ {
+ RStringF name = iResponse->StringPool().OpenFStringL(aFieldName);
+ CleanupClosePushL(name);
+
+ iResponse->Handle().GetHeaderCollection().SetRawFieldL(name, aFieldValue, KHeaderSeparator);
+
+ CleanupStack::PopAndDestroy(&name);
+ }
+ }
+
+TInt CUpnpResponseParser::BodySizeL()
+ {
+ if( !ConsumingResponse() )
+ {
+ // Notify the sender that all the response headers have been parsed.
+ iObserver.GotHeaders();
+ }
+
+ // Check for a body...
+ if( ConsumingResponse() || // this implies that the status code was 1xx - no body
+ iResponse->Handle().StatusCode() == 204 ||
+ iResponse->Handle().StatusCode() == 304 &&
+ // if 2xx response...
+ ( HTTPStatus::IsSuccessful(iResponse->Handle().StatusCode()) ) )
+ {
+ // No entity body is expected as specified in RFC2616 section 4.4.
+ iOverallDataSize = MHttpMessageParserObserver::ENoBody;
+ iFlags |= EBodyComplete;
+ __FLOG(_L8("-> Response has no body"));
+ return iOverallDataSize;
+ }
+
+ // A body is expected - find the length. First check for a Transfer-Encoding
+ // header field.
+ iResponse->Handle().SetBody(*this);
+ THTTPHdrVal value;
+ RStringF name = iResponse->StringPool().StringF(HTTP::ETransferEncoding, THTTPTable::Table());
+
+ TInt err = iResponse->Handle().GetHeaderCollection().GetField(name, 0, value);
+ if( err != KErrNone && err != KErrNotFound )
+ User::Leave(err);
+
+ // It exists - what's the value?
+ if( err == KErrNone && value.Type() == THTTPHdrVal::KStrFVal &&
+ value.StrF().Index(THTTPTable::Table()) == HTTP::EChunked )
+ {
+ // The Transfer-Encoding header is Chunked and as the chunked
+ // encoding is removed, we remove the header.
+ iResponse->Handle().GetHeaderCollection().RemoveField(name);
+
+ // As the entity body is chunked the overall data size is unknown.
+ iOverallDataSize = MHttpMessageParserObserver::EChunked;
+ __FLOG(_L8("-> Response has chunked body"));
+ return iOverallDataSize;
+ }
+
+ // Either no Transfer-Encoding header was present - now check for a
+ // Content-Length header.
+ err = KErrNone;
+ name = iResponse->StringPool().StringF(HTTP::EContentLength, THTTPTable::Table());
+ err = iResponse->Handle().GetHeaderCollection().GetField(name, 0, value);
+
+ if( err != KErrNone && err != KErrNotFound )
+ User::Leave(err);
+
+ if( err == KErrNone && value.Type() == THTTPHdrVal::KTIntVal )
+ {
+ // Content-Length header value specified the length of entity in bytes.
+ iOverallDataSize = value.Int();
+ __FLOG_1(_L8("-> Response body length = %d"), iOverallDataSize);
+ return iOverallDataSize;
+ }
+
+ // There was no Content-Length header either, so the server will signal the
+ // end of the message by closing the connection - overall data size unknown.
+ iOverallDataSize = MHttpMessageParserObserver::EUnknown;
+ __FLOG(_L8("-> Response body size is unknown"));
+ return iOverallDataSize;
+ }
+
+void CUpnpResponseParser::BodyChunkL(const TDesC8& aData)
+ {
+ __FLOG(_L8("-> Got response body chunk"));
+ __FLOG_1(_L8("%S"), &aData);
+
+ iFlags |= EBodyPresent;
+ if (aData.Length() > 0)
+ {
+ iBodyParts.Append(aData);
+ }
+
+ // Only notify the client that there is a body part if there is one.
+ if(iBodyParts.Count() > 0)
+ {
+ // Notify the sender about the presence of the body. He can then call
+ // MHTTPDataSupplier::GetNextDataPart() to get the body data.
+ iObserver.GotBodyData();
+ }
+ }
+
+void CUpnpResponseParser::BodyCompleteL()
+ {
+ __FLOG(_L8("-> Response body complete"));
+ iFlags |= EBodyComplete;
+ }
+
+void CUpnpResponseParser::MessageCompleteL(const TPtrC8& aExcessData)
+ {
+ __FLOG(_L8("-> Message complete"));
+
+ iFlags |= EMessageComplete;
+
+ if ( aExcessData.Length() > 0 || iRawDataArray.Count() > 0 )
+ {
+ __FLOG(_L8("->Excess Data:"));
+ __FLOG_1(_L8("%S"), &aExcessData);
+
+ iFlags |= EExcessData;
+ }
+ else
+ {
+ __FLOG(_L8("-> No excess data"));
+ }
+
+ TInt excessDataLen = aExcessData.Length();
+ TInt count = iRawDataArray.Count();
+ while(count > 0)
+ {
+ excessDataLen += iRawDataArray[count-1].Length();
+ count--;
+ }
+ TInt lenToTrim = iMsgBuf.Length() - excessDataLen;
+ iMsgBuf.TrimStart(lenToTrim);
+
+ if( !ConsumingResponse() )
+ {
+ iObserver.ParsingComplete(iMsgBuf);
+ }
+ else
+ {
+ // There could be excess data - this data is for this response and needs
+ // to be parsed. Set it as the raw data.
+ iRawDataArray.Reset();
+ TPtrC8 rMBufPtr;
+ RMemCell* rMBuf;
+ TMemCellIterator mBufIter(iMsgBuf);
+ while((rMBuf = mBufIter++) != NULL)
+ {
+ rMBufPtr.Set(rMBuf->Ptr(), rMBuf->Length());
+ iRawDataArray.AppendL(rMBufPtr);
+ }
+ }
+ }
+
+TInt CUpnpResponseParser::HandleParserError(TInt aError)
+ {
+ __FLOG_1(_L8("-> HTTP message parser received error: %d"), aError);
+ iRawDataArray.Reset();
+ iBodyParts.Reset();
+ iObserver.ParserError(aError);
+ return KErrNone;
+ }
+
+EXPORT_C void CUpnpResponseParser::ParseResponse(RMemChunk& aMessage, CResponse* aResponse)
+ {
+ __FLOG(_L8("-> Parsing response"));
+ __ASSERT_DEBUG(aResponse, TUPnPMessagePanic::Panic(TUPnPMessagePanic::EMissingResponse));
+ __ASSERT_DEBUG(!aMessage.IsEmpty(), TUPnPMessagePanic::Panic(TUPnPMessagePanic::ENoMessage));
+
+ iResponse = aResponse;
+ if(!iMsgBuf.IsEmpty())
+ {
+ iMsgBuf.Free();
+ }
+ iMsgBuf.Assign(aMessage);
+ TPtrC8 rMBufPtr;
+ RMemCell* rMBuf;
+ TMemCellIterator mBufIter(iMsgBuf);
+ TInt err = KErrNone;
+ while((rMBuf = mBufIter++) != NULL)
+ {
+ rMBufPtr.Set(rMBuf->Ptr(), rMBuf->Length());
+ err = iRawDataArray.Append(rMBufPtr);
+ if(err != KErrNone)
+ {
+ HandleParserError(err);
+ break;
+ }
+ }
+
+ if(err == KErrNone)
+ {
+ // Initiate the message parser
+ iMessageParser.ReceivedMessageData();
+ }
+ }
+
+EXPORT_C void CUpnpResponseParser::ResetParser()
+ {
+ __FLOG(_L8("-> Response parser reset"));
+ iMessageParser.Reset();
+ iRawDataArray.Reset();
+ iBodyParts.Reset();
+ iOverallDataSize = 0;
+ iFlags &= ~EExcessData;
+ }