--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpclient/chttpresponseparser.cpp Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,797 @@
+// 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 "chttpresponseparser.h"
+
+#include <inetprottextutils.h>
+#include <httpstringconstants.h>
+#include <http/rhttpheaders.h>
+#include <http/framework/mrxdataobserver.h>
+#include <httperr.h>
+
+#include "chttpclienttransaction.h"
+#include "chttpconnectionmanager.h"
+#include "chttprequestcomposer.h"
+#include "mhttpresponseobserver.h"
+#include "thttpclientpanic.h"
+#include "chttpconnectioninfo.h"
+
+_LIT8(KHeaderSeparator, "\n");
+
+CHttpResponseParser* CHttpResponseParser::NewL(CProtTransaction& aProtTrans, MRxDataObserver& aObserver, MHttpResponseObserver& aResponseObserver)
+ {
+ CHttpResponseParser* self = new (ELeave) CHttpResponseParser(aProtTrans, aObserver, aResponseObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CHttpResponseParser::~CHttpResponseParser()
+ {
+ iBodyParts.Reset();
+ iMessageParser.Close();
+// __FLOG_CLOSE;
+ }
+
+CHttpResponseParser::CHttpResponseParser(CProtTransaction& aProtTrans, MRxDataObserver& aObserver, MHttpResponseObserver& aResponseObserver)
+: CRxData(aProtTrans, aObserver), iStringTable(RHTTPSession::GetTable()), iResponseObserver(aResponseObserver)
+ {
+// __FLOG_OPEN("http", "httpclienthandler.txt");
+ }
+
+void CHttpResponseParser::ConstructL()
+ {
+ iMessageParser.OpenL(*this);
+ }
+
+TInt CHttpResponseParser::CancelTransaction(TInt aError)
+ {
+ __FLOG_0(_T8("!! Cancelling transaction - internal error"));
+
+ // Tell the connection manager to cancel this submission.
+ CHttpClientTransaction& trans = static_cast<CHttpClientTransaction&>(*iProtTrans);
+ CHttpConnectionManager* manager = trans.ConnectionManager();
+
+ __ASSERT_DEBUG( manager != NULL, User::Invariant() );
+
+ __FLOG_1(_T8("-> Trans %d : cancelling its connection manager"), iProtTrans->Transaction().Id());
+
+ // Transaction is still alive - ask its connection manager to cancel it.
+ MHttpRequest& request = static_cast<CHttpRequestComposer&>(iProtTrans->TxData());
+ MHttpResponse& response = *this;
+ manager->CancelSubmission(request, response);
+
+ // Connection is now cancelled - remove the connection manager from the
+ // transaction.
+ trans.RemoveConnectionManager();
+
+ // Notify the rx-data observer that an error has occurred
+ NotifyObserverError(aError);
+
+ return KErrNone;
+ }
+
+void CHttpResponseParser::NotifyObserverError(TInt aError)
+ {
+ TRAPD( err, iObserver->SetStatusL(*this, aError) );
+ if(err != KErrNone)
+ {
+ // Something has gone seriously wrong - fail the transaction
+ iProtTrans->Transaction().Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ }
+
+/*
+ * Methods from CRxData
+ */
+
+void CHttpResponseParser::ResetRxData()
+ {
+ iMessageParser.Reset();
+ iBodyParts.Reset();
+ iOverallDataSize = 0;
+ iFlags = 0;
+ }
+
+/*
+ * Methods from MHTTPDataSupplier via CRxData
+ */
+
+TBool CHttpResponseParser::GetNextDataPart(TPtrC8& aDataPart)
+ {
+ TInt bodyPartsCount = iBodyParts.Count();
+ __ASSERT_ALWAYS(bodyPartsCount > 0, THttpClientPanic::Panic(THttpClientPanic::ENoBodyPartInDataSupplier));
+
+ // Provide the first chunk.
+ aDataPart.Set(iBodyParts[0]);
+
+ return (BodyComplete() && bodyPartsCount == 1);
+ }
+
+void CHttpResponseParser::ReleaseData()
+ {
+ // Remove the oldest chunk.
+ if( iBodyParts.Count() > 0 )
+ {
+ iBodyParts.Remove(0);
+ }
+
+ // Are there any more chunks?
+ if( iBodyParts.Count() > 0 )
+ {
+ // Yep - notify the client of more body data
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ if(trans.SendEvent(THTTPEvent::EGotResponseBodyData, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+ trans.Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ else
+ {
+ // Does this data packet need to be released?
+ if( NotifyReleaseData() )
+ {
+ iResponseObserver.ResponseDataParsed();
+ iFlags &= ~ENotifyReleaseData;
+ }
+
+ // Is the message complete?
+ if( MessageComplete() )
+ {
+ // All the body data has been read by the client - this transaction
+ // is complete.
+ TRAPD(err, iObserver->SetStatusL(*this, THTTPEvent::EResponseComplete));
+ if(err != KErrNone)
+ iProtTrans->Transaction().Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ }
+ }
+
+TInt CHttpResponseParser::OverallDataSize()
+ {
+ return (iOverallDataSize < 0) ? KErrNotFound : iOverallDataSize;
+ }
+
+TInt CHttpResponseParser::Reset()
+ {
+ return KErrNotSupported;
+ }
+
+void CHttpResponseParser::OnResponseReceiveTimeOut ()
+ {
+ // Notify the client of Response Receive TimeOut.
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ if(trans.SendEvent(THTTPEvent::EReceiveTimeOut, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+ {
+ trans.Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ else
+ {
+ // Notify the client that Transaction Failed.
+ if(trans.SendEvent(THTTPEvent::EFailed, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+ {
+ trans.Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ }
+ }
+
+TInt CHttpResponseParser::ReceiveTimeOutValue ()
+ {
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RStringPool stringPool = trans.Session().StringPool();
+ RStringF receiveTimeOut = stringPool.StringF(HTTP::EReceiveTimeOutValue, iStringTable);
+ THTTPHdrVal receiveTimeOutVal;
+
+ TBool ret = trans.PropertySet().Property(receiveTimeOut,receiveTimeOutVal);
+ if(ret && (receiveTimeOutVal.Type() == THTTPHdrVal::KTIntVal))
+ {
+ return receiveTimeOutVal.Int();
+ }
+ return 0;
+ }
+
+TBool CHttpResponseParser::ResponseInformational ()
+ {
+ return (HTTPStatus::IsInformational(iProtTrans->Transaction().Response().StatusCode()));
+ }
+
+
+/*
+ * Methods from MHttpResponse
+ */
+
+void CHttpResponseParser::ResponseDataReceived(const TDesC8& aData)
+ {
+ __FLOG_2(_T8("Trans %d : parsing %d bytes of data"), iProtTrans->Transaction().Id(), aData.Length());
+
+ iRawData.Set(aData);
+ iMessageParser.ReceivedMessageData();
+ }
+
+void CHttpResponseParser::CancelResponse()
+ {
+ __FLOG_1(_T8("-> Trans %d : response cancelled"), iProtTrans->Transaction().Id());
+
+ iCancellingResponse = ETrue;
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RHTTPResponse response = trans.Response();
+
+ iMessageParser.Flush ();
+ iMessageParser.Reset();
+ iCancellingResponse = EFalse;
+ }
+
+void CHttpResponseParser::ConnectionError(TInt aError)
+ {
+ // Check whether we need to resubmit this request once again.
+ CHttpClientTransaction* clientTrans = static_cast<CHttpClientTransaction*>(iProtTrans);
+ if ( !clientTrans->NeedDisconnectNotification() && (iOverallDataSize != MHttpMessageParserObserver::EUnknown)
+ && (clientTrans->RetryNeeded () && ( aError == KErrEof || aError == KErrCancel || aError == KErrDisconnected ) ) )
+ {
+ if ( !clientTrans->ConnectionManager()->ConnectionInfo().IsNonPersistent () )
+ aError = KErrHttpNonPipeliningError;
+ }
+
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RHTTPResponse response = trans.Response();
+
+ // Check there is a status code, no body and error status ( KErrEof & KErrDisconnected )
+ if ( response.StatusCode() && !response.HasBody() && ( aError == KErrEof || aError == KErrDisconnected ) )
+ {
+ // Notify the client that all the response headers have been parsed.
+ trans.SendEvent(THTTPEvent::EGotResponseHeaders, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler));
+ }
+
+// Is the server closing the connection been expected?
+ if( iOverallDataSize == MHttpMessageParserObserver::EUnknown && !BodyComplete() &&
+ (aError == KErrEof || aError == KErrDisconnected) )
+ {
+
+ __FLOG_1(_T8("Trans %d : waiting for connection close to mark end of response body"), iProtTrans->Transaction().Id());
+
+ if (BodyPresent())
+ {
+ iFlags |= ( EMessageComplete | EBodyComplete ) ;
+
+ // Append NULL to the body chunk
+
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ TInt err = iBodyParts.Append(TPtrC8());
+ if ( err!=KErrNone )
+ trans.Fail(THTTPFilterHandle::EProtocolHandler);
+
+
+ // Notify the client that there is a body part
+ if(iProtTrans->Transaction().SendEvent(THTTPEvent::EGotResponseBodyData,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+ {
+ trans.Fail (THTTPFilterHandle::EProtocolHandler);
+ }
+
+ }
+ else
+ {
+ // Yep, since from RFC2616 section 4.4 Message Length, the end of a
+ // response body can be determined by the server closing the connection
+ // Notify the parser that the connection close has happened.
+ TRAPD( err, iMessageParser.CompletedBodyDataL() );
+ if( err != KErrNone )
+ {
+ // Something has gone seriously wrong - fail the transaction
+ iProtTrans->Transaction().Fail(THTTPFilterHandle::EProtocolHandler);
+ }
+ }
+
+ }
+ else
+ {
+ // Need to map to appropriate error code if the disconnect notification is
+ // asked by the client
+ if ( clientTrans->NeedDisconnectNotification() )
+ {
+ CHttpRequestComposer& request = static_cast<CHttpRequestComposer&>(iProtTrans->TxData());
+ // if we are consuming response then the request has been not sent fully.
+ if ( ConsumingResponse () || !request.RequestSent() )
+ aError = KErrHttpRequestNotSent;
+ // Check if we received some response
+ // Check for status code that means we received something as response
+ // for this transaction
+ else if ( response.StatusCode () )
+ aError = KErrHttpPartialResponseReceived;
+ else
+ aError = KErrHttpResponseNotReceived;
+ }
+
+ // Notify the rx-data observer that an error has occurred
+ NotifyObserverError(aError);
+ }
+
+ }
+
+/*
+ * Methods from MHttpMessageParserObserver
+ */
+
+void CHttpResponseParser::GetDataPacket(TPtrC8& aData)
+ {
+ aData.Set(iRawData);
+ }
+
+void CHttpResponseParser::ReleaseDataPacket()
+ {
+ 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.
+ ResetRxData();
+ iMessageParser.ReceivedMessageData();
+ }
+ else if( iBodyParts.Count() == 0 )
+ {
+ // Can release as there are no body chunks waiting to be passed to the
+ // client.
+ iResponseObserver.ResponseDataParsed();
+ }
+ else
+ {
+ // Flag that the data needs to be released
+ iFlags |= ENotifyReleaseData;
+ }
+ }
+
+void CHttpResponseParser::StartLineL(const TDesC8& aStartLine)
+ {
+ __START_PERFORMANCE_LOGGER();
+ // The RFC2616 defines the Status-Line as follows -
+ //
+ // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ //
+ // First extract the HTTP-Version
+ TPtrC8 data = aStartLine;
+ User::LeaveIfError(InetProtTextUtils::RemoveWhiteSpace(data, InetProtTextUtils::ERemoveLeft));
+
+ // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+ TInt consumed = data.Locate('/');
+ User::LeaveIfError(consumed);
+
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RStringPool stringPool = trans.Session().StringPool();
+
+ TPtrC8 http = data.Left(consumed);
+ if( http.CompareF(stringPool.StringF(HTTP::EHTTP, iStringTable).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);
+
+ if( data.Length() > consumed )
+ {
+ // Skip past major version number and the "."
+ data.Set(data.Mid(consumed + 1));
+ }
+ else
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ // Extract the minor version number
+ TInt minor;
+ consumed = InetProtTextUtils::ConvertDescriptorToInt(data, minor);
+ User::LeaveIfError(consumed);
+
+ if( data.Length() > 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));
+ }
+ else
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ // 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 transaction with Status-Line info
+ RStringF reason = stringPool.OpenFStringL(data);
+
+ RHTTPResponse response = trans.Response();
+
+ TVersion version(major, minor, 0);
+ response.SetVersion(version);
+ response.SetStatusCode(status);
+ response.SetStatusText(reason);
+ reason.Close();
+
+ __FLOG_5(_T8("Trans %d : status-line -> HTTP/%d.%d %d %S"), iProtTrans->Transaction().Id(), major, minor, status, &response.StatusText().DesC());
+
+ // Check to see if a body is expected
+ if( HTTPStatus::IsInformational(status) )
+ {
+ // 1xx status - no body and need to consume this respons
+ iFlags |= EConsumingResponse;
+
+ __FLOG_1(_T8("Trans %d : informational 1xx status - consuming response"), iProtTrans->Transaction().Id());
+ }
+ __END_PERFORMANCE_LOGGER(_L(",CHttpResponseParser::StartLineL()"));
+ }
+
+void CHttpResponseParser::HeaderL(const TDesC8& aFieldName, TDesC8& aFieldValue)
+ {
+ // Only set the headers if we are not consuming the response.
+ if( !ConsumingResponse() )
+ {
+ // Add the header to the response header collection
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RStringPool stringPool = trans.Session().StringPool();
+
+ RStringF name = stringPool.OpenFStringL(aFieldName);
+ CleanupClosePushL(name);
+
+ RHTTPHeaders headers = trans.Response().GetHeaderCollection();
+ headers.SetRawFieldL(name, aFieldValue, KHeaderSeparator);
+
+ /**************************************************************************************
+ * DEF143319 - HTTPS page stops downloading between ~25 and 50 KBytes
+ * When multiple requests are concatenated in ONE buffer and sent to the server,
+ * the WebLogic server does not process all requests.
+ * So pipelining feature has to be disabled.
+ * This fix enhances the robustness of HTTP stack to interoperate with different servers.
+ ***************************************************************************************/
+ if (name.DesC().CompareF(stringPool.StringF(HTTP::EServer,RHTTPSession::GetTable()).DesC()) == KErrNone)
+ {
+ _LIT8(KWebLogicServerName,"WebLogic");
+ if (aFieldValue.FindF(KWebLogicServerName)!= KErrNotFound)
+ {
+ RHTTPHeaders headReq = trans.Request().GetHeaderCollection();
+ RStringF hostStr = stringPool.StringF(HTTP::EHost, RHTTPSession::GetTable());
+ THTTPHdrVal hostVal;
+
+ if( headReq.GetField(hostStr, 0, hostVal) == KErrNotFound )
+ {
+ // No Host header - do not know which host to connect to.
+ User::Leave(KErrHttpGeneralHeaderMissingHost);
+ }
+
+ __ASSERT_DEBUG( hostVal.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+ CHttpClientTransaction& protTran = static_cast<CHttpClientTransaction&>(*iProtTrans);
+ CHttpConnectionManager* manager = protTran.ConnectionManager();
+ #if defined (_DEBUG) && defined (_LOGGING)
+ __FLOG_0(_T8("Pipelining is disabled for WebLogic Server- \n"));
+ #endif
+ manager->InsertPipelineFailedHost(hostVal.StrF().DesC());
+ }
+ }
+
+ CleanupStack::PopAndDestroy(&name);
+
+ if( BodyComplete() && !GotTrailers() )
+ {
+ // This was a trailer header - flag that there are trailers.
+ iFlags |= EGotTrailers;
+ }
+
+ #if defined (_DEBUG) && defined (_LOGGING)
+ if( GotTrailers() )
+ __FLOG_3(_T8("Trans %d : response trailer -> %S: %S"), iProtTrans->Transaction().Id(), &aFieldName, &aFieldValue);
+ else
+ __FLOG_3(_T8("Trans %d : response header -> %S: %S"), iProtTrans->Transaction().Id(), &aFieldName, &aFieldValue);
+ #endif
+ }
+ else
+ {
+ __FLOG_1(_T8("Trans %d : informational 1xx status - Headers will not be added"), iProtTrans->Transaction().Id());
+ // Do nothing
+ }
+ }
+
+TInt CHttpResponseParser::BodySizeL()
+ {
+ __START_PERFORMANCE_LOGGER();
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RHTTPResponse response = trans.Response();
+
+ if( !ConsumingResponse() )
+ {
+ // Notify the client that all the response headers have been parsed.
+ trans.SendEventL(
+ THTTPEvent::EGotResponseHeaders,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)
+ );
+ }
+
+ RStringPool stringPool = trans.Session().StringPool();
+ RHTTPHeaders headers = response.GetHeaderCollection();
+
+ if (CheckForNonPersistentConnection())
+ {
+ // Notify the connection manager that the connection should be
+ // non-persistent.
+ CHttpClientTransaction& trans = static_cast<CHttpClientTransaction&>(*iProtTrans);
+ CHttpConnectionManager* manager = trans.ConnectionManager();
+
+ __ASSERT_DEBUG( manager != NULL, User::Invariant() );
+
+ manager->MakeConnectionNonPersistent();
+ }
+
+ // Check for a body...
+ if( ConsumingResponse() || // this implies that the status code was 1xx - no body
+ response.StatusCode() == 204 ||
+ response.StatusCode() == 304 ||
+ trans.Request().Method().Index(iStringTable) == HTTP::EHEAD ||
+ trans.Request().Method().Index(iStringTable) == HTTP::ECONNECT && // if 2xx response...
+ ( HTTPStatus::IsSuccessful(response.StatusCode()) ) )
+ {
+ __FLOG_1(_T8("Trans %d : no response entity body"), iProtTrans->Transaction().Id());
+
+ // No entity body is expected as specified in RFC2616 section 4.4.
+ iOverallDataSize = MHttpMessageParserObserver::ENoBody;
+ iFlags |= EBodyComplete;
+ __END_PERFORMANCE_LOGGER(_L(",CHttpResponseParser::BodySizeL()"));
+ return iOverallDataSize;
+ }
+
+ // A body is expected - find the length. First check for a Transfer-Encoding
+ // header field.
+ response.SetBody(*this);
+ THTTPHdrVal value;
+ RStringF name = stringPool.StringF(HTTP::ETransferEncoding, iStringTable);
+
+ if( headers.GetField(name, 0, value) == KErrNone )
+ {
+ // It exists - what's the value?
+ if( value.Type() == THTTPHdrVal::KStrFVal &&
+ value.StrF().Index(iStringTable) == HTTP::EChunked )
+ {
+ // The Transfer-Encoding header is Chunked and as the chunked
+ // encoding is removed, we remove the header.
+ headers.RemoveField(name);
+
+ __FLOG_1(_T8("Trans %d : chunked response entity body"), iProtTrans->Transaction().Id());
+
+ // As the entity body is chunked the overall data size is unknown.
+ iOverallDataSize = MHttpMessageParserObserver::EChunked;
+ __END_PERFORMANCE_LOGGER(_L(",CHttpResponseParser::BodySizeL()"));
+ return iOverallDataSize;
+ }
+ }
+
+ // Either no Transfer-Encoding header was present - now check for a
+ // Content-Length header.
+ name = stringPool.StringF(HTTP::EContentLength, iStringTable);
+ TInt err = headers.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_2(_T8("Trans %d : response entity body length = %d"), iProtTrans->Transaction().Id(), iOverallDataSize);
+
+ __END_PERFORMANCE_LOGGER(_L(",CHttpResponseParser::BodySizeL()"));
+ return iOverallDataSize;
+ }
+
+ __FLOG_1(_T8("Trans %d : unknown entity body length"), iProtTrans->Transaction().Id());
+
+ // 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;
+ __END_PERFORMANCE_LOGGER(_L(",CHttpResponseParser::BodySizeL()"));
+ return iOverallDataSize;
+ }
+
+void CHttpResponseParser::BodyChunkL(const TDesC8& aData)
+ {
+ // Append the body chunk to the array.
+ iFlags |= EBodyPresent;
+ TPtrC8 chunk = aData;
+ User::LeaveIfError(iBodyParts.Append(chunk));
+
+ __FLOG_2(_T8("Trans %d : received %d bytes of body data"), iProtTrans->Transaction().Id(), chunk.Length());
+
+
+ // Only notify the client that there is a body part if there is only one.
+ if( iBodyParts.Count() == 1 )
+ {
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ trans.SendEventL(
+ THTTPEvent::EGotResponseBodyData,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)
+ );
+ }
+}
+
+void CHttpResponseParser::BodyCompleteL()
+ {
+ __FLOG_1(_T8("Trans %d : body complete"), iProtTrans->Transaction().Id());
+
+ iFlags |= EBodyComplete;
+ }
+
+void CHttpResponseParser::MessageCompleteL(const TPtrC8& aExcessData)
+ {
+ __FLOG_2(_T8("Trans %d : message complete - received %d bytes of excess data"), iProtTrans->Transaction().Id(), aExcessData.Length());
+
+ iFlags |= EMessageComplete;
+
+ if( !ConsumingResponse() )
+ {
+ iResponseObserver.ResponseComplete(aExcessData);
+
+ if ( iCancellingResponse )
+ {
+ return;
+ }
+ if( GotTrailers() )
+ {
+ // Inform the client that there were trailers
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ trans.SendEventL(
+ THTTPEvent::EGotResponseTrailerHeaders,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)
+ );
+ }
+ if( iBodyParts.Count() == 0 )
+ {
+ // All the body data has been read by the client - this transaction is
+ // complete.
+ iObserver->SetStatusL(*this, THTTPEvent::EResponseComplete);
+ }
+ }
+ else
+ {
+ // There could be excess data - this data is for this response and needs
+ // to be parsed. Set it as the raw data.
+ CHttpRequestComposer& request = static_cast<CHttpRequestComposer&>(iProtTrans->TxData());
+ if(request.IsSuspendedRequest())
+ {
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ trans.SendEventL(
+ THTTPEvent::EReceived100Continue,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)
+ );
+
+ request.ResumeSuspendedRequest();
+ }
+
+ else
+ {
+ iRawData.Set(aExcessData);
+ }
+ }
+ }
+
+TInt CHttpResponseParser::HandleParserError(TInt aError)
+ {
+ __FLOG_1(_T8("!! Error : %d"), aError);
+ __FLOG_1(_T8("-> Trans %d : response parsing error - cancelling transaction"), iProtTrans->Transaction().Id());
+
+ // Cancel the transaction - this also notifies the rx-data observer that an
+ // error has occurred.
+ return CancelTransaction(aError);
+ }
+
+TBool CHttpResponseParser::CheckForNonPersistentConnection()
+ {
+ RHTTPTransaction trans = iProtTrans->Transaction();
+ RStringPool stringPool = trans.Session().StringPool();
+ TBool keepAlive = EFalse;
+
+ // Check for a Connection: close header
+ //
+ RStringF name = stringPool.StringF(HTTP::EConnection, iStringTable);
+ THTTPHdrVal value;
+ RHTTPHeaders headers = trans.Response().GetHeaderCollection();
+ if( headers.GetField(name, 0, value) == KErrNone && value.Type() == THTTPHdrVal::KStrFVal)
+ {
+ // It exists - what's the value?
+ if( value.StrF().Index(iStringTable) == HTTP::EClose )
+ {
+ // The Connection header has a value of close.
+ __FLOG_1(_T8("Trans %d : server has specified a non-persistent connection"), iProtTrans->Transaction().Id());
+ return ETrue;
+ }
+ else if( value.StrF().Index(iStringTable) == HTTP::EKeepAlive )
+ {
+ // This request by the server to persist the connection is used by HTTP/1.0
+ // servers to override default behaviour
+ keepAlive = ETrue;
+ }
+ }
+
+
+ // Is this a HTTP/1.0 response?
+ //
+ TVersion version = trans.Response().Version();
+ if( !keepAlive && version.iMinor == 0 && version.iMajor == 1 &&
+ trans.Request().Method().Index(iStringTable) != HTTP::ECONNECT )
+ {
+ __FLOG_1(_T8("Trans %d : an HTTP/1.0 server, default to non-persistent connection"), iProtTrans->Transaction().Id());
+ return ETrue;
+ }
+
+ // So treat connection as persistent.
+ return EFalse;
+ }
+
+TBool CHttpResponseParser::ResponseCompleted ()
+ {
+ // Check if we received a full response. No matter if client is not fully notified
+ __FLOG_2 (_T8 ("Message completed? %d Body Completed? %d"), MessageComplete (), BodyComplete () );
+ return MessageComplete ();
+ }
+
+TBool CHttpResponseParser::NeedCompletion ()
+ {
+ RHTTPTransaction trans = iProtTrans->Transaction ();
+ if ( HTTPStatus::IsRedirection ( trans.Response().StatusCode() ) )
+ {
+ // If it is a redirection message then we need to complete the response
+ return ETrue;
+ }
+ return EFalse;
+ }
+
+TBool CHttpResponseParser::CompleteResponse ( const TDesC8& aData )
+ {
+ return iMessageParser.CompleteMessage ( aData );
+ }
+
+void CHttpResponseParser::FailTransactionL()
+ {
+ iProtTrans->Transaction().SendEventL(KErrHttpOptimiserFailsTrans,
+ THTTPEvent::EIncoming,
+ THTTPFilterHandle::EProtocolHandler);
+ }
+
+void CHttpResponseParser::Reserved_MHttpMessageParserObserver()
+ {
+ User::Invariant();
+ }
+
+