// 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
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->AppendPipelineFailedHost(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();
}
void CHttpResponseParser::FlushBodyDataIfNotRead()
{
// Message is completed but the client is not yet read the complete body data
// and we are cancelling. So we need to clear the parsed body data as another
// request would have been sent via the connection. The flushing of the body
// data is needed to make the connection manager to read further response from the
// socket otherwise it hangs. See the error:
// Note: This function should be called from CancelTransactionHook and only in the
// case of client cancelling the transaction. ie; using RHTTPTransaction::Cancel();
if(MessageComplete() && iBodyParts.Count() > 0)
{
iBodyParts.Reset(); // Reset the body array.
}
}