applayerpluginsandutils/httpprotocolplugins/httpclient/chttpclienthandler.cpp
changeset 0 b16258d2340f
child 7 2611c08ee28e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpclient/chttpclienthandler.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1697 @@
+// 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 "chttpclienthandler.h"
+
+#include <http.h>
+#include <http/framework/crxdata.h>
+#include <http/framework/csecuritypolicy.h>
+#include <inetprottextutils.h>
+#include <uriutilscommon.h>
+#include <uriutils.h>
+#include <authority8.h>
+#include <in_sock.h>
+#include <securesocket.h>
+#include <x509certext.h>
+
+#include "chttptransportlayer.h"
+#include "chttpclientfilter.h"
+#include "chttpconnectfilter.h"
+#include "chttpclientheadercodec.h"
+#include "chttpclienttransaction.h"
+#include "chttpconnectioninfo.h"
+#include "chttpconnectionmanager.h"
+#include "chttprequestcomposer.h"
+#include "chttpresponseparser.h"
+#include "thttpclientpanic.h"
+
+const TInt KHttpDefaultPort			= 80;
+const TInt KHttpDefaultSecurePort	= 443;
+const TInt KHttpDefaultProxyPort	= 8080;
+const TInt KMaxConnectionManagers	= 4;
+const TInt KDefaultBatchingBufSize	= 1400;
+const TInt KDefaultMaxNumberTransactionsToPipeline = KMaxTInt;
+const TInt KDefaultBufferSize		= 32*1024;
+
+
+_LIT8(KSecureHttpScheme, "https");
+_LIT8(KHttpClientCodecName, "HTTP/client");
+
+_LIT8(KUAProfile, "x-wap-profile");
+
+const TUint KIPv6HostOpenBrace		= '[';
+const TUint KIPv6HostCloseBrace		= ']';
+class InetProtTextUtils;
+
+CHttpClientHandler* CHttpClientHandler::NewL(TAny* aSession)
+	{
+	RHTTPSession* httpSession = REINTERPRET_CAST(RHTTPSession*, aSession);
+	CHttpClientHandler* self = new (ELeave) CHttpClientHandler(*httpSession);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+CHttpClientHandler::~CHttpClientHandler()
+	{
+	iProxyAddress.Close();
+	iConnectionManagers.ResetAndDestroy();
+	delete iTransportLayer;
+	delete iPipelineFallback;
+	__FLOG_CLOSE;
+	}
+	
+CHttpClientHandler::CHttpClientHandler(RHTTPSession aSession)
+: CProtocolHandler(aSession),
+iStringTable(RHTTPSession::GetTable())
+	{
+	__FLOG_OPEN("http", "httpclienthandler.txt");
+	__FLOG(_T8("HTTP Client Protocol Handler Log"));
+	}
+	
+void CHttpClientHandler::ConstructL()
+	{	
+	CProtocolHandler::ConstructL(iSession);
+	
+	CHttpClientFilter* clientFilter = CHttpClientFilter::NewL(iSession);
+	CleanupStack::PushL ( clientFilter );
+	CHttpConnectFilter::NewL(iSession);
+	CleanupStack::Pop (); // clientFilter
+
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+	THTTPHdrVal value;
+	TBool hasValue = connInfo.Property(stringPool.StringF(HTTP::ESessionClosing, iStringTable), value);
+	if( hasValue && value.Type() == THTTPHdrVal::KTIntVal )  
+		{
+		iSessionClosingPtr = reinterpret_cast<TBool*>(value.Int());
+		}
+	else
+		{
+		__FLOG_0(_T8("!! Session Closing Flag NOT set"));
+		User::Leave(KErrNotFound);
+		}
+	iPipelineFallback = CHttpPipelineFallback::NewL();	
+	}
+
+CHttpConnectionInfo* CHttpClientHandler::PrepareTransactionL(RHTTPTransaction aTrans, TBool& aNeedTunnel, TBool& aCanPipeline)
+	{
+    // Check that filters are not added headers that are not needed for CONNECT method.
+    // Remove the unwanted headers. But do only if the property has been set for the 
+    // strict CONNECT method.
+    EnsureStrictConnectMethodHeaders (aTrans);
+	
+	// To prepare the transaction need to create the Request-URI, set the Host
+	// header and establish the connection info.
+	RStringPool stringPool = iSession.StringPool();
+	RHTTPRequest request = aTrans.Request();
+
+	SetupProxyInformation(aTrans);
+	
+	// Store the method index from string table
+	TInt methodIndex = request.Method().Index(iStringTable); 
+	// Check for https scheme.
+	const TUriC8& uri = request.URI();
+	// secure doesn't count if it's a connect request	
+	TBool secure = methodIndex != HTTP::ECONNECT &&
+		(uri.Extract(EUriScheme).CompareF(KSecureHttpScheme) == 0);
+	// Create the Request-URI for this transaction...
+	TInt port = (secure) ? KHttpDefaultSecurePort : KHttpDefaultPort;
+	TPtrC8 host;
+	CreateRequestUriL(methodIndex, aTrans, host, port);
+
+	// If the host name is empty leave as the URI is not complete - a CONNECT
+	// method request has an empty host as it will go to the proxy.
+	if( host.Length() == 0 )
+		{
+		__FLOG_0(_T8("!! Invalid uri"));
+		__FLOG_2(_T8("-> Trans %d, Con %d : missing host!"), aTrans.Id(),GetConnectionID(aTrans));
+
+		User::Leave(KErrHttpInvalidUri);
+		}
+
+	// Set the Host header...
+	TBool isHttp10 = SetHostHeaderL(aTrans, host, port);
+
+	// Create the connection info. Is a proxy being used?
+	if( iUseProxy )
+		{
+		// Need a tunnel if request is going via a proxy and request is using 
+		// the https scheme
+		if( secure )
+			{
+			__FLOG_4(_T8("Trans %d, Con %d : tunnel required to host %S, port %d"), aTrans.Id(),GetConnectionID(aTrans), &host, port);
+
+			// This request needs a tunnel to be established to the specified 
+			// host/port - add the ETunnel property to the transaction.
+			aNeedTunnel = ETrue;
+			AddTunnelInfoL(aTrans, host, port);
+			}
+
+		// Need to obtain the proxy info.
+		const TDesC8& proxy = iProxyAddress.DesC();
+		TAuthorityParser8 auth;
+		auth.Parse(proxy);
+
+		host.Set(auth.Extract(EAuthorityHost));
+
+		// Read the proxy port if present
+		port = KHttpDefaultProxyPort; // Initialise with default proxy port
+		if( auth.IsPresent(EAuthorityPort) )
+			{
+			TPtrC8 portDesc = auth.Extract(EAuthorityPort);
+			TInt error = InetProtTextUtils::ConvertDescriptorToInt(portDesc, port);
+			if( error != KUriUtilsErrEmptyData )
+				{
+				// An empty port component is allowed - just ignore it.
+				User::LeaveIfError(error);
+				}
+			}
+		}
+		
+	// Is a non-persistent connection required? The connection is non-persistent
+	// if either it is a HTTP/1.0 request or it is a HTTP/1.1 request and it has
+	// a Connection: close header.
+	RHTTPHeaders headers = request.GetHeaderCollection();
+	CHttpConnectionInfo::THttpPersistent persistentState = isHttp10 ? 
+														   CHttpConnectionInfo::ENonPersistent : 
+														   CHttpConnectionInfo::EPersistent;
+	THTTPHdrVal connVal;
+	RStringF connStr = stringPool.StringF(HTTP::EConnection, iStringTable);
+	if( headers.GetField(connStr,0,connVal) == KErrNone )
+		{
+		__ASSERT_DEBUG( !isHttp10, THttpClientPanic::Panic(THttpClientPanic::EInvalidHeaderForHTTP10) ); // no connection headers are allowed in HTTP/1.0
+		
+		if( (connVal.Type() == THTTPHdrVal::KStrFVal) && 
+			(connVal.StrF() == stringPool.StringF(HTTP::EClose, iStringTable)) )
+			{
+			// Client has specified a Connection: close header - non-persistent
+			// connection.
+			persistentState = CHttpConnectionInfo::ENonPersistent;
+			}
+		}
+	
+	// Create the connection info object
+	CHttpConnectionInfo* connectionInfo = CHttpConnectionInfo::NewL(stringPool, host, static_cast<TUint16>(port));
+	connectionInfo->SetSecureState(secure);
+	connectionInfo->SetPersistentState(persistentState);
+
+	// Check to see if this transaction can be pipelined.
+	switch(methodIndex)
+		{
+	case HTTP::EGET:
+		{
+		// Can only pipeline non-HTTP/1.0 requests...
+		if( !isHttp10 )
+			{
+			// These methods can be pipelined but only if pipelining has not been 
+			// specifically disabled for the transaction.
+            aCanPipeline = (CheckPipelineSupport(aTrans) && !iPipelineFallback->NeedPipelineFallback(host));
+            __FLOG_4(_T8("Pipelining --- enabled: %d Support: %d Fallback: %d  Host %S"), aCanPipeline, CheckPipelineSupport(aTrans), !iPipelineFallback->NeedPipelineFallback(host), &host);
+			break;
+			}
+		// Allow the HTTP/1.0 requests to drop through to default case.
+		}
+	default:
+		// All other methods are not to be pipelined.
+		aCanPipeline = EFalse;
+		break;
+		};
+
+	return connectionInfo;
+	} 
+
+void CHttpClientHandler::CreateRequestUriL(TInt aMethodIndex, RHTTPTransaction aTrans, TPtrC8& aHost, TInt& aPort)
+	{
+	RStringPool stringPool = iSession.StringPool();
+	RHTTPRequest request = aTrans.Request();
+	const TUriC8& uri = request.URI();
+	TBool secure = EFalse;
+	
+	RStringF scheme = stringPool.OpenFStringL(uri.Extract(EUriScheme));
+	CleanupClosePushL(scheme);
+	if( aMethodIndex == HTTP::ECONNECT )
+		{
+		__ASSERT_DEBUG( iUseProxy, User::Invariant() );
+		aHost.Set(uri.Extract(EUriHost));
+		}
+	else if( uri.IsPresent(EUriHost) )
+		{
+		// The request URI is absolute - check the scheme is http or https
+		secure = uri.IsPresent(EUriScheme) && (scheme.Index(iStringTable) == HTTP::EHTTPS);
+		if( !uri.IsPresent(EUriScheme) || !secure &&
+			scheme.Index(iStringTable) != HTTP::EHTTP)
+			{
+			__FLOG_0(_T8("!! Invalid uri."));
+			__FLOG_3(_T8("-> Trans %d, Con %d: %S scheme is not supported!"), aTrans.Id(),GetConnectionID(aTrans), &scheme.DesC());
+
+			User::Leave(KErrHttpInvalidUri);
+			}
+
+		// Get the host and port info from it.
+		aHost.Set(uri.Extract(EUriHost));
+
+		// Check to see if a port has been specified	
+		if( uri.IsPresent(EUriPort) )
+			{
+			TPtrC8 port = uri.Extract(EUriPort);
+			TInt error = InetProtTextUtils::ConvertDescriptorToInt(port, aPort);
+			if( error != KUriUtilsErrEmptyData )
+				{
+				// An empty port component is allowed - just ignore it.
+				User::LeaveIfError(error);
+				}
+			}
+		}
+	else
+		{
+		// The request URI is relative - update the request URI to be absolute.
+		// The client MUST have supplied a Host header.
+		RHTTPHeaders headers = request.GetHeaderCollection();
+		RStringF hostStr = stringPool.StringF(HTTP::EHost, iStringTable);
+		THTTPHdrVal hostVal;
+
+		if( headers.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() );
+
+		// Set the host output argument
+		aHost.Set(hostVal.StrF().DesC());
+
+		// Create the absolute uri
+		CUri8* absoluteUri = CUri8::NewLC(uri);
+
+		RStringF httpStr = stringPool.StringF(HTTP::EHTTP, iStringTable);
+		absoluteUri->SetComponentL(httpStr.DesC(), EUriScheme);
+		absoluteUri->SetComponentL(aHost, EUriHost);
+
+		// Check for port info in the Host header
+		RStringF portStr = stringPool.StringF(HTTP::EPort, iStringTable);
+		THTTPHdrVal portVal;
+		TBool hasPort = (headers.GetParam(hostStr, portStr, portVal) == KErrNone);
+		if( hasPort && portVal.Type() == THTTPHdrVal::KTIntVal )
+			{
+			// Set the port output argument
+			aPort = portVal.Int();
+
+			// Convert the int value to its descriptor format
+			HBufC8* portBuf = NULL;
+			InetProtTextUtils::ConvertIntToDescriptorL(aPort, portBuf);
+			CleanupStack::PushL(portBuf);
+
+			absoluteUri->SetComponentL(*portBuf, EUriPort);
+			CleanupStack::PopAndDestroy(portBuf);
+			}
+
+		// Now set the absolute URI back into the transaction
+		request.SetURIL(absoluteUri->Uri());
+		CleanupStack::PopAndDestroy(absoluteUri);
+		}
+
+	// Create the Request-URI based on the request URI. 
+	// NOTE - might have changed from earlier
+
+	const TUriC8 reqURI = request.URI();
+	// CONNECT uses authority while everything else uses absoluteURI or absolutePath
+	// use absolutePath for direct connections and tunnels, and absoluteURI for proxy
+	// note: OPTIONS uses "*" as well, but that should still work
+	CAuthority8* authToUse=NULL;
+	CUri8* uriToUse = NULL;
+	if( aMethodIndex == HTTP::ECONNECT)
+		{
+		authToUse = CAuthority8::NewLC();
+		authToUse->SetComponentL(reqURI.Extract(EUriUserinfo),EAuthorityUserinfo);
+		authToUse->SetComponentL(reqURI.Extract(EUriHost),EAuthorityHost);
+		authToUse->SetComponentL(reqURI.Extract(EUriPort),EAuthorityPort);	
+		}
+	else 
+		{
+		uriToUse = CUri8::NewLC(reqURI);
+		if( !iUseProxy || secure ) // if (useproxy and not secure) or (not useproxy) = not useproxy or secure
+			{// Not going via a proxy - need to remove the scheme and authority parts
+			uriToUse->RemoveComponentL(EUriScheme);
+			uriToUse->RemoveComponentL(EUriHost);	// this also removes the userinfo + port
+			}
+		}
+	// Set the Request-URI for the request
+	RString uriStr = stringPool.OpenStringL(
+		authToUse ? authToUse->Authority().AuthorityDes() : uriToUse->Uri().UriDes());
+	CleanupClosePushL(uriStr);
+	THTTPHdrVal uriVal(uriStr);
+	aTrans.PropertySet().SetPropertyL(stringPool.StringF(HTTP::EUri, iStringTable), uriVal);
+	CleanupStack::PopAndDestroy(3, &scheme); // uriToUse or authToUse, uriStr 
+	}
+
+TBool CHttpClientHandler::SetHostHeaderL(RHTTPTransaction aTrans, const TDesC8& aHost, TInt aPort)
+	{
+		__START_PERFORMANCE_LOGGER();
+	// Set the Host header only if the request is not an HTTP/1.0 request.
+	RStringPool stringPool = iSession.StringPool();
+	RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+	THTTPHdrVal httpVersion;
+	TBool isHttp10 = EFalse;
+	if( connInfo.Property(stringPool.StringF(HTTP::EHTTPVersion,iStringTable), httpVersion) )
+		{
+		__ASSERT_DEBUG( httpVersion.Type() == THTTPHdrVal::KStrFVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidHeaderValueType) );
+		isHttp10 = (httpVersion.StrF() == stringPool.StringF(HTTP::EHttp10, iStringTable));
+		}
+
+	// Regardless of proxy settings a Host header based on the URI authority must be added unless
+	// this is an HTTP/1.0 request in which case a Host header should not exist.
+	RHTTPRequest request = aTrans.Request();
+	RHTTPHeaders headers = request.GetHeaderCollection();
+	RStringF hostStr = stringPool.StringF(HTTP::EHost, iStringTable);
+	if( !isHttp10 )
+		{
+		// If the Host header does not already exist, add it!
+		THTTPHdrVal hostValue;
+		if( headers.GetField(hostStr, 0, hostValue) == KErrNotFound )
+			{				
+			// Set the Host header...
+			RStringF hostValStr;
+
+			// Check if its a literal IPV6 address
+			UriUtils::TUriHostType aHostsType = UriUtils::HostType( aHost );
+			if ( ( aHostsType != UriUtils::ETextHost ) && ( aHostsType != UriUtils::EIPv4Host ) ) // is an IPv6 or other future protocol address
+				{
+
+				HBufC8* ipv6LiteralHost = HBufC8::NewLC( aHost.Length() + 2 ); // add 2 for the braces
+			
+				TPtr8 ipv6LiteralHostPtr = ipv6LiteralHost->Des();	
+				
+				ipv6LiteralHostPtr.Append( KIPv6HostOpenBrace );
+				ipv6LiteralHostPtr.Append( aHost );
+				ipv6LiteralHostPtr.Append( KIPv6HostCloseBrace );
+
+				hostValStr = stringPool.OpenFStringL( ipv6LiteralHostPtr );
+				CleanupStack::PopAndDestroy( ipv6LiteralHost );
+				
+				}
+			else
+				{
+				hostValStr = stringPool.OpenFStringL( aHost );
+				}
+			
+			CleanupClosePushL(hostValStr);
+
+	
+			THTTPHdrVal hostVal(hostValStr);	
+			headers.SetFieldL(hostStr, hostVal);
+			CleanupStack::PopAndDestroy(&hostValStr);
+			
+			// Also set the port number if Host header is not empty and a port 
+			// number is not the default.
+			if( aPort != KHttpDefaultPort && aPort != KHttpDefaultSecurePort )
+				{
+				THTTPHdrVal portVal(aPort);
+				RStringF portStr = stringPool.StringF(HTTP::EPort,iStringTable);
+				headers.SetParamL(hostStr, portStr, portVal, 0);
+				}
+			}
+		// else the Host header already exists, so do nothing
+		}
+	else // This is an HTTP/1.0 request
+		headers.RemoveField(hostStr);
+		
+	__END_PERFORMANCE_LOGGER(_L(",CHttpClientHandler::SetHostHeaderL()"));	
+	return isHttp10;
+	}
+
+void CHttpClientHandler::AddTunnelInfoL(RHTTPTransaction aTrans, const TDesC8& aHost, TInt aPort)
+	{
+	// Convert the port number into a descriptor...
+	HBufC8* port = NULL;
+	InetProtTextUtils::ConvertIntToDescriptorL(aPort, port);
+	CleanupStack::PushL(port);
+
+	// Create the Request-URI for the CONNECT request - it is in the 'authority'
+	// form, as described in RFC2616, section 5.1.2.
+	CAuthority8* authority = CAuthority8::NewLC();
+	
+	authority->SetComponentL(aHost, EAuthorityHost);
+	authority->SetComponentL(*port, EAuthorityPort);
+
+	// Set this as the ETunnel property in the transaction.
+	RStringPool stringPool = iSession.StringPool();
+	RStringF tunnelStr = stringPool.OpenFStringL(authority->Authority().AuthorityDes());
+	CleanupClosePushL(tunnelStr);
+	THTTPHdrVal tunnelVal(tunnelStr);
+	aTrans.PropertySet().SetPropertyL(stringPool.StringF(HTTP::ETunnel, iStringTable), tunnelVal);
+	CleanupStack::PopAndDestroy(3, port); // delete authority and close tunnelStr
+	}
+
+TBool CHttpClientHandler::SelectConnectionManagerL(const CHttpConnectionInfo& aConnectionInfo, RHTTPTransaction aTrans, TBool aCanPipeline, CHttpConnectionManager *&aManager )
+	{
+	// Selecting a connection manager depends on whether the transaction can be
+	// pipelined. If so, then the connection manager of choice would be the one
+	// that is connected to the correct location and busy with other transactions.
+	// If there is no connection manager that fits this criteria, then the 
+	// selection process follows that for a transaction that cannot be pipelined.
+
+	// The order of preference for selecting a connection manager - 
+	// 1) A manager that is connected to the correct location and is available.
+	//	  This is the backup-choice for a transaction that can be pipelined.
+	// 2) A manager that is not connected to anywhere.
+	// 3) Create a new manager if the limit has not been reached.
+	// 4) Use a manager that is connected to a different host (but not being 
+	//    used) and will therefore need disconnecting and reconnecting.
+	
+	CHttpConnectionManager* backupChoice = NULL;
+	CHttpConnectionManager* secondChoice = NULL;
+	CHttpConnectionManager* fourthChoice = NULL;
+    CHttpConnectionManager* managerConnecting = NULL;	
+	TBool newConnection = ETrue;
+    TInt numConnectionsToSingleServer = 0;
+    
+	TBool connectMethod = aTrans.Request().Method().Index(iStringTable) == HTTP::ECONNECT;
+	
+	const TInt numConnMan = iConnectionManagers.Count();
+
+	for( TInt ii=0; (ii < numConnMan && aManager == NULL ); ++ii )
+		{
+		CHttpConnectionManager::TConnectionStatus status = iConnectionManagers[ii]->Status();
+		switch( status )
+			{
+		case CHttpConnectionManager::ENotConnected:
+			{
+			// This is a pretty good option because it just needs a connection 
+			// to be established
+			secondChoice = iConnectionManagers[ii];
+			} break;
+		case CHttpConnectionManager::EConnectedAndAvailable:
+			{
+			// This is the ideal situation if the location matches, otherwise it
+			// becomes the fourth choice
+			const CHttpConnectionInfo& connectionInfo = iConnectionManagers[ii]->ConnectionInfo();
+			if( connectionInfo.HostAndPortMatches(aConnectionInfo) )
+				{
+				if(!connectMethod)
+				     {
+	                ++numConnectionsToSingleServer;					
+				     if( aCanPipeline )
+					     {
+						  // This is the backup-choice
+						  backupChoice = iConnectionManagers[ii];
+						 }
+				     else
+					    {
+						// This is the one!
+						aManager = iConnectionManagers[ii];
+						newConnection = EFalse;
+					    }
+				     }
+				 }
+			else
+				{
+				// Non-matching connection info - fourth choice.
+				fourthChoice = iConnectionManagers[ii];
+				}
+			} break;
+		case CHttpConnectionManager::EConnectedAndBusy:
+			{
+			// This is the ideal choice if the pipelining can done and the 
+			// location and secure status match.
+			const CHttpConnectionInfo& connectionInfo = iConnectionManagers[ii]->ConnectionInfo();
+            TBool hostAndPortMatches = connectionInfo.HostAndPortMatches(aConnectionInfo);
+            if(hostAndPortMatches)
+                {
+                ++numConnectionsToSingleServer;
+                }
+			if( !connectMethod && aCanPipeline && 
+			    hostAndPortMatches &&
+				connectionInfo.IsSecure() == aConnectionInfo.IsSecure() )
+				{
+				// This is the one!
+				aManager = iConnectionManagers[ii];
+				newConnection = EFalse;
+				}
+			} break;
+		case CHttpConnectionManager::EConnectedNotAvailable:
+			{
+			// Do nothing - continue the search...
+			} break;
+	     case CHttpConnectionManager::EConnectingNotAvailable:
+	         {
+	         const CHttpConnectionInfo& connectionInfo = iConnectionManagers[ii]->ConnectionInfo();
+             TBool hostAndPortMatches = connectionInfo.HostAndPortMatches(aConnectionInfo);
+	         if(hostAndPortMatches)
+	             {
+	             ++numConnectionsToSingleServer;
+	             }
+             if (aCanPipeline &&  hostAndPortMatches && connectionInfo.IsSecure() == aConnectionInfo.IsSecure())
+	           {
+	           // We are connecting. So do not initiate another connection.
+	           // We will be able to send the request via the same connection.
+	           managerConnecting = iConnectionManagers[ii];
+	           }
+	         }
+	         break;			
+		default:
+			// There are no other transport handler states - should not reach here
+			User::Invariant();
+			break;
+			}
+		}
+
+	if( aManager == NULL)
+		{
+		if( aCanPipeline && backupChoice != NULL )
+			{
+			// Use the connection manager that is connected to the correct 
+			// location	but not busy.
+			aManager = backupChoice;
+			newConnection = EFalse;
+			}
+		else if( secondChoice != NULL )	
+			{
+			// Use the second choice connection manager - idle one.
+			aManager = secondChoice;
+			}
+		else if( numConnMan < MaxNumConnectionManagers() )
+			{
+			// Have not reached the max number of connection managers and so 
+			// can create a new connection manager - check for a transport layer
+			if( iTransportLayer == NULL )
+				{
+				// Create the transport layer
+				_LIT8(KTcpProtocol, "TCP");
+				THttpTransportConstructionParams params = THttpTransportConstructionParams(*this);
+				
+				RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+				RStringPool stringPool = iSession.StringPool();
+				THTTPHdrVal valPriority;
+				RStringF strConnMan = stringPool.StringF(HTTP::ETranspHndlrPriority , iStringTable);
+				params.iPriority = EFalse;
+				if( connInfo.Property(strConnMan, valPriority) )
+					{
+					if(valPriority== stringPool.StringF(HTTP::EEnableTranspHndlrPriority , iStringTable))
+						params.iPriority = ETrue;
+					}				
+				
+				iTransportLayer = CHttpTransportLayer::NewL(KTcpProtocol, params);
+				}
+			// Check we are doing an optimal pipelining. Read the property value only
+			// if we haven't created atleast one connection manager yet
+			if(numConnMan == 0)
+			    {			  
+                RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+                RStringPool stringPool = iSession.StringPool();
+			    __ASSERT_DEBUG(!managerConnecting, User::Invariant());
+			    THTTPHdrVal optimalPipelineValue;
+			    RStringF strOptimalPipeline = stringPool.StringF(HTTP::EHttpOptimalPipelining, iStringTable);
+			    if(connInfo.Property(strOptimalPipeline, optimalPipelineValue))
+			        {
+			        iEnableOptimalPipeline = (optimalPipelineValue == stringPool.StringF(HTTP::EHttpEnableOptimalPipelining, iStringTable)); 
+			        }
+			    }
+			if(managerConnecting)
+			    {
+                newConnection = EFalse;			    
+			    }
+			else
+			    {			    
+	            // Create the new connection manager
+	            const TInt maxNumberTransactionsToPipeline = MaxNumTransactionsToPipeline(); 
+	            aManager = CHttpConnectionManager::NewL(iTransportLayer->SocketFactory(), *this, *iPipelineFallback, maxNumberTransactionsToPipeline, iEnableOptimalPipeline);
+	            CleanupStack::PushL(aManager);
+
+			__RecordConnectionManagerCreationL();
+
+
+#if defined (_DEBUG) && defined (_LOGGING)
+	            aManager->__logger__ = this->__logger__;
+#endif
+
+	            // Append to the store
+	            User::LeaveIfError(iConnectionManagers.Append(aManager));
+	            CleanupStack::Pop(aManager);			    
+			    }
+			}
+		else if( fourthChoice != NULL )	
+			{
+			// As a last resort reuse one that is connected to the wrong host but 
+			// not being used.
+			aManager = fourthChoice;
+			}
+		}
+	return newConnection;
+	}
+
+CHttpConnectionManager* CHttpClientHandler::SelectTunnelConnectionL(const CHttpConnectionInfo& aConnectionInfo, RHTTPTransaction aTrans, TBool aCanPipeline)
+	{
+	// Look for connection manager that is a tunnel connection via appropriate
+	// proxy to appropriate host. If the transaction can be pipelined, then the
+	// first choice is a connection manager that is connected and busy. 
+	RStringPool stringPool = iSession.StringPool();
+	THTTPHdrVal hostVal;
+#ifdef _DEBUG
+	TBool found = 
+#endif
+	aTrans.PropertySet().Property(stringPool.StringF(HTTP::ETunnel, iStringTable), hostVal);
+
+	__ASSERT_DEBUG( found, User::Invariant() );
+	__ASSERT_DEBUG( hostVal.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+	RStringF host = hostVal.StrF();
+	TBool notifyCreateTunnel = ETrue;	
+	CHttpConnectionManager* manager = NULL;
+	CHttpConnectionManager* backupChoice = NULL;
+	
+	const TInt numConnMan = iConnectionManagers.Count();
+
+	for( TInt ii=0; (ii < numConnMan && manager == NULL ); ++ii )
+		{
+		CHttpConnectionManager::TConnectionStatus status = iConnectionManagers[ii]->Status();
+		switch( status )
+			{
+		case CHttpConnectionManager::ENotConnected:
+		case CHttpConnectionManager::EConnectedNotAvailable:
+		case CHttpConnectionManager::EConnectingNotAvailable:
+			{
+			// Do nothing - continue search.
+			} break;
+		case CHttpConnectionManager::EConnectedAndAvailable:
+			{
+			const CHttpConnectionInfo& connectionInfo = iConnectionManagers[ii]->ConnectionInfo();
+			if( connectionInfo.HostAndPortMatches(aConnectionInfo) && 
+				iConnectionManagers[ii]->TunnelMatches(host) )
+				{
+				// Location (ie the proxy) and tunnel host match - this is the 
+				// backup choice if pipelining allowed, otherwise it is the one.
+				if( aCanPipeline )
+					{
+					// This is the backup choice...
+					backupChoice = iConnectionManagers[ii];
+					}
+				else
+					{
+					// This is the one! There is no need to create a tunnel.
+					manager = iConnectionManagers[ii];
+					notifyCreateTunnel = EFalse;
+					}
+				}
+			} break;
+		case CHttpConnectionManager::EConnectedAndBusy:
+			{
+			const CHttpConnectionInfo& connectionInfo = iConnectionManagers[ii]->ConnectionInfo();
+			if( aCanPipeline &&
+				connectionInfo.HostAndPortMatches(aConnectionInfo) && 
+				iConnectionManagers[ii]->TunnelMatches(host) )
+				{
+				// Location (ie the proxy) and tunnel host match and pipelining
+				// is allowed - this is the one! There is no need to create a 
+				// tunnel.
+				manager = iConnectionManagers[ii];
+				notifyCreateTunnel = EFalse;
+				}
+			} break;
+		default:
+			// There are no other transport handler states - should not reach here
+			User::Invariant();
+			break;
+			}
+		}
+
+	if( manager == NULL && backupChoice != NULL )
+		{
+		// Use the back-up choice - now no need to create a tunnel.
+		manager = backupChoice;
+		notifyCreateTunnel = EFalse;
+		}
+#if defined (_DEBUG) && defined (_LOGGING)
+	if( manager != NULL )
+		{
+		__FLOG_1(_T8("!! Tunnel to %S available"), &host.DesC());
+		__FLOG_6(
+				_T8("-> Trans %d, Con %d : can service via host %S, remote port %d (secure : %d, nonpersistent : %d)"),
+				aTrans.Id(),
+				GetConnectionID(aTrans),
+				&manager->ConnectionInfo().Host(), 
+				manager->ConnectionInfo().Port(), 
+				manager->ConnectionInfo().IsSecure(), 
+				manager->ConnectionInfo().IsNonPersistent()						
+				);
+		}
+#endif
+	if( notifyCreateTunnel )
+		{
+		__FLOG_1(_T8("!! No tunnel to %S"), &host.DesC());
+		__FLOG_2(_T8("-> Trans %d, Con %d : cannot service until tunnel established"), aTrans.Id(),GetConnectionID(aTrans));
+
+		// Notify client (or filter) that a tunnel needs to be established
+		// before this transaction can be serviced.
+		aTrans.SendEventL(
+						 THTTPEvent::ENeedTunnel, 
+						 THTTPEvent::EIncoming,
+						 THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)
+						 );
+		}
+	return manager;
+	}
+
+TInt CHttpClientHandler::MaxNumConnectionManagers() const
+	{
+	// Has this value been previously cached?
+	if( iMaxNumConnectionManagers == 0 )
+		{
+		// No. Use this default should the property not be set
+		iMaxNumConnectionManagers = KMaxConnectionManagers;
+
+		// Check session properties
+		RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+		RStringPool stringPool = iSession.StringPool();
+		THTTPHdrVal maxConnMan;
+		RStringF strConnMan = stringPool.StringF(HTTP::EMaxNumTransportHandlers, iStringTable);
+		if( connInfo.Property(strConnMan, maxConnMan) )
+			{
+			if( maxConnMan.Type() == THTTPHdrVal::KTIntVal )
+				iMaxNumConnectionManagers = maxConnMan.Int();
+			}
+		}
+	return iMaxNumConnectionManagers;
+	}
+	
+void CHttpClientHandler::SetupProxyInformation(RHTTPTransaction aTrans)
+	{
+	// Assume a direct connection unless the properties specifically indicate 
+	// that a proxy should be used.
+	iUseProxy = EFalse;
+	iProxyAddress.Close();
+	
+	RStringPool stringPool(iSession.StringPool());
+	THTTPHdrVal useProxy;
+	THTTPHdrVal address;
+	RStringF proxyUsage = stringPool.StringF(HTTP::EProxyUsage, iStringTable);
+	RStringF proxyAddress = stringPool.StringF(HTTP::EProxyAddress, iStringTable);
+
+	RHTTPPropertySet transactionProperties = aTrans.PropertySet();
+	RHTTPPropertySet sessionProperties = iSession.ConnectionInfo();
+
+	// First check the transaction properties for proxy info. If the transaction
+	// has its own proxy info set then this should be used, including the fact 
+	// that a proxy should not be used. Otherwise check the session properties 
+	// for proxy info.
+	if( transactionProperties.Property(proxyUsage, useProxy) )
+		{
+		__ASSERT_DEBUG( useProxy.Type() == THTTPHdrVal::KStrFVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting) );
+
+		// The transaction has proxy info set...
+		iUseProxy = (useProxy.StrF().Index(iStringTable) == HTTP::EUseProxy);
+	
+		if( iUseProxy )
+			{
+			if( transactionProperties.Property(proxyAddress, address) )
+				{
+				__ASSERT_DEBUG( address.Type() == THTTPHdrVal::KStrFVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting) );
+
+				iProxyAddress = address.StrF().Copy();
+				}
+			else
+				{
+				// It is invalid to specify using a proxy and not set a proxy
+				// address!
+				THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting);
+				}
+			}	
+		}
+	else if( sessionProperties.Property(proxyUsage, useProxy) )
+		{
+		__ASSERT_DEBUG( useProxy.Type() == THTTPHdrVal::KStrFVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting) );
+
+		// The session has proxy info set...
+		iUseProxy = (useProxy.StrF().Index(iStringTable) == HTTP::EUseProxy);
+		
+		if( iUseProxy )
+			{
+			if( sessionProperties.Property(proxyAddress, address) )
+				{
+				__ASSERT_DEBUG( address.Type() == THTTPHdrVal::KStrFVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting) );
+				
+				iProxyAddress = address.StrF().Copy();
+				}
+			else
+				{
+				// It is invalid to specify using a proxy and not set a proxy
+				// address!
+				THttpClientPanic::Panic(THttpClientPanic::EInvalidProxySetting);
+				}
+			}
+		}
+	}
+
+TBool CHttpClientHandler::CheckPipelineSupport(RHTTPTransaction aTrans)
+	{
+	// Check to see if the pipelining support has been disabled/enabled for this
+	// transaction.
+	RStringPool stringPool = iSession.StringPool();
+	RStringF pipeline = stringPool.StringF(HTTP::EHttpPipelining, iStringTable);
+	THTTPHdrVal value;
+	TBool canPipeline = ETrue;
+
+	if(	aTrans.PropertySet().Property(pipeline, value) )
+		{
+		__ASSERT_DEBUG( value.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+		canPipeline = (value.StrF().Index(iStringTable) != HTTP::EDisablePipelining);
+		}
+	return canPipeline;
+	}
+
+/*
+ *	Methods from CProtocolHandler
+ */
+
+TInt CHttpClientHandler::SessionServerCert(TCertInfo& /*aServerCert*/)
+	{
+	return KErrNotSupported;
+	}
+
+
+TInt CHttpClientHandler::TransactionServerCert(TCertInfo& aServerCert, RHTTPTransaction aTransaction)
+	{
+	TInt error = KErrNotFound;
+	const CX509Certificate* cert = static_cast<const CX509Certificate*>(TransactionServerCert(aTransaction));
+	if(cert) 
+		{
+		TRAPD( failed, GetCertInfoL(*cert, aServerCert));
+
+		// pass back the leaving system error.
+		error = (failed < 0 ) ? failed : KErrNone;
+		}
+	return error;
+	}
+
+void CHttpClientHandler::CreateCodecL()
+	{
+	iCodec = CHeaderCodecPlugin::NewL( KHttpClientCodecName, iSession.StringPool());
+	}
+
+CProtTransaction* CHttpClientHandler::CreateProtTransactionL(RHTTPTransaction aTransaction)
+	{
+	// Create the appropriate CProtTransaction object
+	CHttpClientTransaction* transaction = CHttpClientTransaction::NewL(aTransaction);
+	return transaction;
+	}
+	
+TBool CHttpClientHandler::ServiceL(CProtTransaction& aTrans)
+	{
+		__START_PERFORMANCE_LOGGER();
+	// Prepare the transaction...
+	TBool needTunnel = EFalse;
+	TBool canPipeline = EFalse;
+	CHttpConnectionInfo* info = PrepareTransactionL(aTrans.Transaction(), needTunnel, canPipeline);
+	CleanupStack::PushL(info);
+
+	CHttpConnectionManager* manager = NULL;
+	TBool isNewConnection = ETrue;
+	
+	if( needTunnel )
+		{
+		// Find a connection that is tunnelling via appropriate proxy to the
+		// appropriate host.
+		manager = SelectTunnelConnectionL(*info, aTrans.Transaction(), canPipeline);
+		}
+	else
+		{
+		// Look for a normal connection.
+		isNewConnection = SelectConnectionManagerL(*info, aTrans.Transaction(), canPipeline, manager);
+		}
+
+	if( manager != NULL )
+		{
+		// Pass the connection manager to the transaction - need to do this 
+		// before creating tx- and rx- data objects.
+		CHttpClientTransaction* trans = static_cast<CHttpClientTransaction*>(&aTrans);
+		trans->SetConnectionManager(*manager);
+		
+		if(isNewConnection)
+			{
+			//Increment retry count only if it is a new connection.
+			trans->IncRetryCount();
+			}
+
+		// Create the tx- and rx- objects in the transaction
+		aTrans.CreateTxDataL();
+		aTrans.CreateRxDataL(*this);
+
+#if defined (_DEBUG) && defined (_LOGGING)
+		CHttpRequestComposer* composer = static_cast<CHttpRequestComposer*>(&aTrans.TxData());
+		CHttpResponseParser* parser = static_cast<CHttpResponseParser*>(&aTrans.RxData());
+
+		composer->__logger__ = this->__logger__;
+		parser->__logger__ = this->__logger__;
+#endif
+
+		// If the transaction cannot be pipelined, then set the connection 
+		// manager to not allow pipelining. Once this transaction has completed
+		// the manager will revert to allowing pipelining.
+		if( !canPipeline )
+			manager->DisablePipelining();
+				
+		// Remove connection info from cleanup stack before submiting to the 
+		// connection manager - ownership is passed to the connection manager.
+		CleanupStack::Pop(info);
+		MHttpRequest& request = static_cast<CHttpRequestComposer&>(aTrans.TxData());
+		MHttpResponse& response = static_cast<CHttpResponseParser&>(aTrans.RxData());
+
+		manager->SubmitL(*info, request, response);
+		__END_PERFORMANCE_LOGGER(_L(",CHttpClientHandler::ServiceL()"));
+		return ETrue;
+		}
+	CleanupStack::PopAndDestroy(info);
+		__END_PERFORMANCE_LOGGER(_L(",CHttpClientHandler::ServiceL()"));
+	return EFalse;
+	}
+	
+void CHttpClientHandler::ClosedTransactionHook(CProtTransaction* aTrans)
+	{
+	__FLOG_0(_T8("!! Closing transaction - client request"));
+	__FLOG_1(_T8("-> Trans %d : closed"), aTrans->Transaction().Id());
+
+	delete aTrans;	
+	}
+	
+void CHttpClientHandler::CancelTransactionHook(CProtTransaction& aTransaction)
+	{
+	// Is this transaction still alive - check to see if it still has a 
+	// connection manager.
+	CHttpClientTransaction& trans = static_cast<CHttpClientTransaction&>(aTransaction);
+	CHttpConnectionManager* manager = trans.ConnectionManager();
+
+	__FLOG_0(_T8("!! Cancelling transaction - client request"));
+	
+	if( manager != NULL )
+		{
+		__FLOG_2(_T8("-> Trans %d, Con %d : still alive - cancelling its connection manager"), aTransaction.Transaction().Id(),GetConnectionID(manager));
+	
+		// Transaction is still alive - ask its connection manager to cancel it.
+		MHttpRequest& request = static_cast<CHttpRequestComposer&>(aTransaction.TxData());
+		MHttpResponse& response = static_cast<CHttpResponseParser&>(aTransaction.RxData());
+		manager->CancelSubmission(request, response);
+		
+		// Connection is now cancelled - remove the connection manager from the
+		// transaction.
+		trans.RemoveConnectionManager();		
+		}
+#if defined (_DEBUG) && defined (_LOGGING)
+	else
+		__FLOG_1(_T8("-> Trans %d : already finished - nothing to do"), aTransaction.Transaction().Id());
+#endif
+	}
+	
+void CHttpClientHandler::NotifyNewRequestBodyPart(CProtTransaction& aTransaction)
+	{
+	// Notify the transaction of more data.
+	static_cast<CHttpRequestComposer&>(aTransaction.TxData()).NotifyMoreRequestBodyData();
+	}
+	
+void CHttpClientHandler::GetInterfaceL(TUid aInterfaceId, MProtHandlerInterface*& aInterfacePtr)
+	{
+	switch(aInterfaceId.iUid)
+		{
+	case KProtHandlerTransactionServerCertUid:
+		{
+		aInterfacePtr = this;
+		break;
+		}
+	default:
+		{
+		CProtocolHandler::GetInterfaceL(aInterfaceId, aInterfacePtr);
+		break;
+		}
+		}
+	}
+	
+/*
+ *	Methods from MConnectionPrefsProvider
+ */
+
+TBool CHttpClientHandler::SupplyCommsConnection( RConnection*& aConnectionPtr )
+	{
+	aConnectionPtr = NULL;
+
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+	THTTPHdrVal value;
+	
+	TBool hasValue = connInfo.Property( stringPool.StringF(HTTP::EHttpSocketConnection, iStringTable), value );
+	if( hasValue && value.Type() == THTTPHdrVal::KTIntVal )
+		{
+		aConnectionPtr = reinterpret_cast<RConnection*>(value.Int());
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+TBool CHttpClientHandler::SupplySocketServerHandle ( TInt& aSocketServerHandle ) 
+	{
+	aSocketServerHandle = 0;
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+	THTTPHdrVal value;
+
+	TBool hasValue = connInfo.Property(stringPool.StringF(HTTP::EHttpSocketServ, iStringTable), value);
+	if( hasValue && value.Type() == THTTPHdrVal::KTIntVal )  
+		{
+		aSocketServerHandle = value.Int();
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+void CHttpClientHandler::SetCommsConnectionL( RConnection* aConnectionPtr )
+	{
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+	TInt connectionPtrVal = reinterpret_cast<TInt>(aConnectionPtr);
+	connInfo.SetPropertyL ( stringPool.StringF(HTTP::EHttpSocketConnection, iStringTable ), THTTPHdrVal (connectionPtrVal) );
+	}
+	
+void CHttpClientHandler::SetSocketServerHandleL ( TInt aSocketServerHandle )
+	{
+	RHTTPConnectionInfo	connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+	connInfo.SetPropertyL ( stringPool.StringF(HTTP::EHttpSocketServ, iStringTable ), THTTPHdrVal (aSocketServerHandle) );		
+	}
+
+void CHttpClientHandler::GetSecurityPrefs(TBool& aDialogPrompt, MSecurityPolicy*& aSecurityPolicy)
+	{
+	// Set the security policy
+	aSecurityPolicy = iSecurityPolicy;
+
+	// Set the dialog info - check the session properties
+	THTTPHdrVal value;
+	RStringF secureDialog = iSession.StringPool().StringF(HTTP::ESecureDialog, iStringTable);
+	TBool hasValue = iSession.ConnectionInfo().Property(secureDialog, value);
+	if( hasValue && value.Type() == THTTPHdrVal::KStrFVal && 
+		value.StrF().Index(iStringTable) == HTTP::EDialogNoPrompt )
+		{
+		// Client has requested to not be prompted
+		aDialogPrompt = EFalse;
+		}
+	else
+		{
+		// The default value - the client will be prompted
+		aDialogPrompt = ETrue;
+		}
+	}
+
+TBool CHttpClientHandler::ImmediateSocketShutdown()
+	{
+	TBool immediateSocketShutdown = EFalse;
+	if( *iSessionClosingPtr ) // iSessionClosingPtr cannot be NULL as its is set in ConstructL
+		{
+		// Session is closing down, check the session properties to check if the client has requested
+		// an immediate socket shutdown
+		THTTPHdrVal value;
+		RStringF socketShutdownMode = iSession.StringPool().StringF(HTTP::ESocketShutdownMode, iStringTable);
+		TBool hasValue = iSession.ConnectionInfo().Property(socketShutdownMode, value);
+		if( hasValue && value.Type() == THTTPHdrVal::KStrFVal && 
+			value.StrF().Index(iStringTable) == HTTP::ESocketShutdownImmediate )
+			{
+			__FLOG_0(_T8("!! Immediate socket shutdown requested by client"));
+			immediateSocketShutdown = ETrue;
+			}
+		}
+
+	return immediateSocketShutdown;
+	}
+	
+TInt CHttpClientHandler::SessionId()
+	{
+	THTTPHdrVal value;
+	TInt result = KErrNotFound;
+	const TBool hasValue = iSession.ConnectionInfo().Property(iSession.StringPool().StringF(HTTP::ESessionId, iStringTable), value);
+	if( hasValue && value.Type()==THTTPHdrVal::KTIntVal) // silently ignore inappropriate types
+		{
+		result = value.Int();
+		if(result<0)
+			{
+			result = KErrNotFound;
+			}
+		}
+	return result;
+	}
+
+
+/*
+ *	Methods from MRxDataObserver
+ */
+ 
+TInt CHttpClientHandler::SetStatus(CRxData& aRxData, TInt aStatus)
+	{
+	// Have received a status message from an Rx data object - check the status.
+	CHttpClientTransaction& protTrans = static_cast<CHttpClientTransaction&>(aRxData.ProtTrans());
+	RHTTPTransaction trans = protTrans.Transaction();
+	TInt err = KErrNone;
+	switch( aStatus )
+		{
+	case THTTPEvent::EResponseComplete:
+		{
+		__FLOG_2(_T8("Trans %d, Con %d : transaction completed"), trans.Id(), GetConnectionID(trans));
+
+		// The response is complete - the client has been passed all the data
+		// and released it. Check to see if this was a CONNECT request.
+		RStringPool stringPool = iSession.StringPool();
+
+		TInt status = aStatus;
+		if( trans.Request().Method().Index(iStringTable) == HTTP::ECONNECT )
+			{
+			if( HTTPStatus::IsSuccessful(trans.Response().StatusCode()) )
+				{
+				// A 2xx status code - tunnel has been successfully established. 
+				// Mark the connection manager as tunnelled connection, providing 
+				// the host to which the tunnel leads.
+				THTTPHdrVal hostVal;
+#ifdef _DEBUG
+				TBool found = 
+#endif
+				trans.PropertySet().Property(stringPool.StringF(HTTP::ETunnel, iStringTable), hostVal);
+
+				__ASSERT_DEBUG( found, User::Invariant() );
+				__ASSERT_DEBUG( hostVal.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+				protTrans.ConnectionManager()->TunnelConnection(hostVal.StrF());
+				}
+			else
+				{
+				status = THTTPEvent::EFailed;
+				
+#if defined (_DEBUG) && defined (_LOGGING)
+				THTTPHdrVal hostVal;
+				TBool found = trans.PropertySet().Property(stringPool.StringF(HTTP::ETunnel, iStringTable), hostVal);
+
+				__ASSERT_DEBUG( found, User::Invariant() );
+				__ASSERT_DEBUG( hostVal.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+				const CHttpConnectionInfo& connectionInfo = protTrans.ConnectionManager()->ConnectionInfo();
+
+				__FLOG_2(
+						_T8("!! Tunnel failed : %d %S"),
+						trans.Response().StatusCode(), 
+						&trans.Response().StatusText().DesC()
+						);
+				__FLOG_5(
+						_T8("-> Tunnel to %S on connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"),
+						&hostVal.StrF().DesC(), 
+						&connectionInfo.Host(), 
+						connectionInfo.Port(), 
+						connectionInfo.IsSecure(), 
+						connectionInfo.IsNonPersistent()
+						);
+#endif
+				}
+			}
+
+		// Ensure that the connection manager is still not dealing with this 
+		// request.
+		MHttpRequest& request = static_cast<CHttpRequestComposer&>(protTrans.TxData());
+		protTrans.ConnectionManager()->CheckRequestComplete(request);
+
+		// The transaction has no further use for the connection manager - it 
+		// can now be removed.
+		protTrans.RemoveConnectionManager();
+			
+		// The transaction is now complete - inform the base class.
+		err = TransactionCompleted(trans, status);
+		} break;
+	default:
+		// Unknown status - do nothing, unless an error
+		if( aStatus < 0 )
+			{
+			__FLOG_1(_T8("!! Error : %d"), aStatus);
+
+			if( aStatus == KErrHttpPipeliningError )
+				{
+				__FLOG_2(_T8("-> Trans %d, Con %d : transaction was being pipelined"), trans.Id(), GetConnectionID(trans));
+				__FLOG_2(_T8("-> Trans %d, Con %d : re-try without pipelining"), trans.Id(), GetConnectionID(trans));
+
+				// Specify that this transaction should not be pipelined when 
+				// it is re-submitted.
+				RStringPool stringPool = iSession.StringPool();
+				RStringF pipeline = stringPool.StringF(HTTP::EHttpPipelining, iStringTable);
+				RStringF disable = stringPool.StringF(HTTP::EDisablePipelining, iStringTable);
+
+				err = trans.PropertySet().SetProperty(pipeline, disable);
+				if(err != KErrNone)
+				    {
+				    break;
+				    }
+				}
+#if defined (_DEBUG) && defined (_LOGGING)
+			else if( aStatus == KErrHttpNonPipeliningError )
+				{
+				__FLOG_2(_T8("-> Trans %d, Con %d : transaction was being pipelined"), trans.Id(), GetConnectionID(trans));
+				__FLOG_2(_T8("-> Trans %d, Con %d : re-try with pipelining again"), trans.Id(), GetConnectionID(trans));
+				}
+			else
+				{
+				__FLOG_2(_T8("-> Trans %d, Con %d : transaction failed"), trans.Id(), GetConnectionID(trans));
+				}
+#endif
+			// An error code has occured. As no further data will be exchanged
+			// with the origin server. The connection manager can now be removed.
+			protTrans.RemoveConnectionManager();
+
+			if( aStatus == KErrEof )
+				{
+				__FLOG_4(_T8("-> Trans %d, Con %d : reporting %d (KErrDisconnected) instead of %d"), trans.Id(), GetConnectionID(trans), KErrDisconnected, aStatus);				
+
+				// Convert these errors to KErrDisconnected
+				aStatus = KErrDisconnected;
+				}
+
+			// Propagate the error back to the client and mark this transaction
+			// as completed.
+			err = TransactionCompleted(trans, aStatus);
+			}
+		break;
+		}
+	return err;
+	}
+
+void CHttpClientHandler::SetStatusL(CRxData& aRxData, TInt aStatus)
+    {
+    User::LeaveIfError(SetStatus(aRxData, aStatus));
+    }
+
+/*
+ *	Methods from MHttpBatchingPropertiesCallback
+ */
+
+TInt CHttpClientHandler::GetMaxBatchingBufferSize()
+	{
+	TInt batchingBuffer = 0;
+	RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+	RStringPool stringPool = iSession.StringPool();
+
+	THTTPHdrVal doBatching;
+	RStringF batchingSetting = stringPool.StringF(HTTP::EHttpBatching, iStringTable);
+
+	if (connInfo.Property(batchingSetting, doBatching))
+		{
+		TBool batchingSupported = EFalse;	// default behaviour is batching disabled
+
+		// First of all check if batching has been enabled
+		batchingSupported = (doBatching.StrF().Index(iStringTable) == HTTP::EEnableBatching);
+		if (batchingSupported)
+			{
+			THTTPHdrVal bufferSize;
+			RStringF buffer = stringPool.StringF(HTTP::EBatchingBufferSize, iStringTable);
+
+			// If batching has been enabled, check for a client-specified buffer size to use
+			if (connInfo.Property(buffer, bufferSize))
+				{
+				__ASSERT_DEBUG(bufferSize.Type() == THTTPHdrVal::KTIntVal, THttpClientPanic::Panic(THttpClientPanic::EInvalidBatchingSetting));
+				batchingBuffer = bufferSize.Int();
+				}
+			// No client-specified buffer size therefore use the default value
+			else
+				batchingBuffer = KDefaultBatchingBufSize;
+			}
+		}
+	return batchingBuffer;
+	}
+
+
+/*
+ *	Methods from MProtHandlerInterface
+ */
+
+const CCertificate*  CHttpClientHandler::SessionServerCert()
+	{
+	return NULL;
+	}
+	
+const CCertificate* CHttpClientHandler::TransactionServerCert(RHTTPTransaction aTransaction)
+	{
+	const CProtTransaction* trans = FindProtocolTransaction(aTransaction);
+	if(trans != NULL)
+		{
+		CHttpConnectionManager* manager = static_cast<const CHttpClientTransaction*>(trans)->ConnectionManager();
+		if( manager )
+			{
+			return manager->ServerCert();
+			}
+		}
+	return NULL;
+	}
+
+
+void CHttpClientHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
+	{
+	// Handle the event
+	switch (aEvent.iStatus)
+		    {
+	case THTTPEvent::EGetCipherSuite:
+		    {
+		    GetCipherSuiteL(aTransaction);
+		    }
+		    break;
+	case THTTPEvent::ECancelWaitFor100Continue:
+		{
+		const CProtTransaction* pT = FindProtocolTransaction(aTransaction);
+		if(pT != NULL)
+			{
+			static_cast<CHttpRequestComposer&>(pT->TxData()).CancelWaitFor100Continue();
+			}
+		}
+		break;
+	default:
+		   CProtocolHandler::MHFRunL(aTransaction,aEvent);
+		   }
+	}
+
+void CHttpClientHandler::GetCipherSuiteL(RHTTPTransaction aTransaction)
+	{
+	RStringPool stringPool = iSession.StringPool();
+	RHTTPTransactionPropertySet properties(aTransaction.PropertySet());
+	const CProtTransaction* transaction = FindProtocolTransaction(aTransaction);
+	
+	if (transaction != NULL)
+		{
+		CHttpConnectionManager* connectionManager = static_cast<const CHttpClientTransaction*>(transaction)->ConnectionManager();
+		if(connectionManager)
+			{
+			TBuf8<8> cipherSuite;
+			TInt error = connectionManager->CipherSuite(cipherSuite);
+			
+			if (error == KErrNone)
+				{
+				RString cipherSuiteString = stringPool.OpenStringL(cipherSuite);
+				THTTPHdrVal hdrValue(cipherSuiteString);
+				CleanupClosePushL(cipherSuiteString);
+				properties.SetPropertyL(stringPool.StringF(HTTP::ECipherSuiteValue, iStringTable), hdrValue);
+				CleanupStack::PopAndDestroy(&cipherSuiteString);	
+				return;
+				}		
+			}
+		}
+	// No cipher suite could be obtained so set the ECipherSuiteValue property to an empty string.
+	properties.SetPropertyL(stringPool.StringF(HTTP::ECipherSuiteValue, iStringTable), THTTPHdrVal(RString()));
+	}
+
+TInt CHttpClientHandler::MaxNumTransactionsToPipeline() const
+	{
+	/* This is called when the first transaction is submitted. This is so filters can set the
+	   session setting HTTP::EMaxNumTransactionsToPipeline
+	   However it is only required to be set when the first transaction is submitted. Therefore
+	   check to see if the value is already cached.
+	*/
+ 	if (iMaxNumTransactionsToPipeline == 0)
+ 		{
+ 		iMaxNumTransactionsToPipeline = KDefaultMaxNumberTransactionsToPipeline;
+ 		
+ 		// Check session properties
+		RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+		RStringPool stringPool = iSession.StringPool();
+		THTTPHdrVal maxToPipeline;
+		RStringF maxToPipelineString = stringPool.StringF(HTTP::EMaxNumTransactionsToPipeline, iStringTable);
+		if(connInfo.Property(maxToPipelineString, maxToPipeline))
+			{
+			if(maxToPipeline.Type() == THTTPHdrVal::KTIntVal)
+				{
+				iMaxNumTransactionsToPipeline = maxToPipeline.Int();
+				}
+			}
+		}
+ 	
+ 	return iMaxNumTransactionsToPipeline;
+	}
+
+
+void CHttpClientHandler::__RecordConnectionManagerCreationL()
+	// In debug mode update a session property to record when each connection manager is created
+	// This is used to validate the runtime behavior of pipelining use cases
+	{
+	#if defined (_DEBUG)
+	_LIT8(KNumberConnectionManagers, "__NumConnectionManagers");
+	
+	RStringPool stringPool = iSession.StringPool();
+	RStringF numberConnectionsString = stringPool.OpenFStringL(KNumberConnectionManagers);
+	CleanupClosePushL(numberConnectionsString);
+	RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+	TInt numberConnections =0;
+	
+	THTTPHdrVal numberConnectionsVal;
+	if (connInfo.Property(numberConnectionsString, numberConnectionsVal))
+		{
+		numberConnections = numberConnectionsVal.Int();
+		connInfo.RemoveProperty(numberConnectionsString);
+		}
+	
+	numberConnections++;
+	numberConnectionsVal.SetInt(numberConnections);
+	connInfo.SetPropertyL(numberConnectionsString, numberConnectionsVal);
+	
+	CleanupStack::PopAndDestroy(&numberConnectionsString);
+	#endif	
+	}
+	
+#if defined (_DEBUG)
+	TInt CHttpClientHandler::GetConnectionID( const CHttpConnectionManager* aConnectionManager )
+	//in debug mode it returns the connection ID, if not in the list returns 0
+	{
+	TInt i = iConnectionManagers.Count();
+	for( ;i > 0; --i )
+		{
+		if( iConnectionManagers[i-1] == aConnectionManager )
+			{
+			break;
+			}
+		}
+	return 	i;	
+	}
+	
+	TInt CHttpClientHandler::GetConnectionID( const RHTTPTransaction &aTrans )
+	{
+	CHttpConnectionManager* manager = NULL;
+	const CProtTransaction* trans = FindProtocolTransaction(aTrans);
+	if(trans != NULL)
+		{
+		manager = static_cast< const CHttpClientTransaction* >( trans )->ConnectionManager();
+		}
+		
+	return GetConnectionID( manager );
+	}
+
+
+
+
+#endif
+
+void CHttpClientHandler::GetCertInfoL(const CX509Certificate& aSource, TCertInfo& aDest)
+	{
+	TInt len;
+
+	//Fetch Fingerprint
+	len = Min(aSource.Fingerprint().Length(),aDest.iFingerprint.MaxLength());
+	aDest.iFingerprint.Copy(aSource.Fingerprint().Ptr(),len);
+
+	//Fetch SerialNumber
+	len = Min(aSource.SerialNumber().Length(),aDest.iSerialNo.MaxLength());
+	aDest.iSerialNo.Copy(aSource.SerialNumber().Ptr(),len);
+
+	//Fetch PublicKey
+	const CSubjectPublicKeyInfo& publicKeyInfo = aSource.PublicKey();
+	len = Min(publicKeyInfo.KeyData().Length(),aDest.iPublicKey.MaxLength());
+	aDest.iPublicKey.Copy(publicKeyInfo.KeyData().Ptr(),len);
+
+	//Fetch PublicKeyAlg
+	aDest.iPkAlg= publicKeyInfo.AlgorithmId();
+
+	//Fetch VersionNo
+	aDest.iVersionNo = aSource.Version();
+
+	//Fetch StartValDate
+	const CValidityPeriod& validityPeriod = aSource.ValidityPeriod();
+	aDest.iStartValDate = validityPeriod.Start();
+
+	//Fetch EndValDate
+	aDest.iEndValDate = validityPeriod.Finish();
+
+	//Fetch SubjectDNInfo
+	const CX500DistinguishedName& subjectName = aSource.SubjectName();
+	GetDNInfo(subjectName,aDest.iSubjectDNInfo);
+
+	//Fetch IssuerDNInfo;
+	const CX500DistinguishedName& issuerName = aSource.IssuerName();
+	GetDNInfo(issuerName,aDest.iIssuerDNInfo);
+
+	// Fetch Alt Name
+	aDest.iDNSName.Copy(KNullDesC);
+
+	//fetch digest alg
+	aDest.iDigAlg = aSource.SigningAlgorithm().DigestAlgorithm().Algorithm();
+	const CX509CertExtension* ext = aSource.Extension(KSubjectAltName); 
+
+	if( ext )
+		{
+		CX509AltNameExt* subjectAltName = CX509AltNameExt::NewLC(ext->Data());
+		const CArrayPtrFlat<CX509GeneralName>& gNs = subjectAltName->AltName();
+		const TInt count = gNs.Count(); 
+
+		for( TInt i = 0; i < count; ++i )
+			{ 
+			const CX509GeneralName* gN = gNs.At(i);
+			
+			if( gN->Tag() == EX509DNSName )
+				{ 
+				CX509DNSName* dNS = CX509DNSName::NewLC(gN->Data());
+				const TPtrC dNSValue = dNS->Name();
+				aDest.iDNSName.Copy(dNSValue);
+				CleanupStack::PopAndDestroy(dNS);
+				break;
+				} 
+			}
+		CleanupStack::PopAndDestroy(subjectAltName);
+		} 
+
+	}
+
+void CHttpClientHandler::GetDNInfo(const CX500DistinguishedName& aSource, TDNInfo& aDest)
+	{
+	const TInt count = aSource.Count();
+	for( TInt i=0; i<count; ++i )
+		{
+		const CX520AttributeTypeAndValue& attribute = aSource.Element(i);
+
+		HBufC* valuePtr = NULL;
+		TDes8* destination = NULL;
+		TInt maxLength = 0 ;
+		TBool found = ETrue;
+
+		const TDesC& type = attribute.Type();
+		if( type.Compare(KX520CountryName) == 0 )
+			{
+			maxLength = aDest.iCountry.MaxLength();
+			destination = &aDest.iCountry;
+			}
+		else if( type.Compare(KX520OrganizationalUnitName) == 0 )
+			{
+			maxLength = aDest.iOrganizationUnit.MaxLength();
+			destination = &aDest.iOrganizationUnit;
+			}
+		else if( type.Compare(KX520OrganizationName) == 0 )
+			{
+			maxLength = aDest.iOrganization.MaxLength();
+			destination = &aDest.iOrganization;
+			}
+		else if( type.Compare(KX520CommonName) == 0 )
+			{
+			maxLength = aDest.iCommonName.MaxLength();
+			destination = &aDest.iCommonName;
+			}
+		else if( type.Compare(KX520LocalityName) == 0 )
+			{
+			maxLength = aDest.iLocality.MaxLength();
+			destination = &aDest.iLocality;
+			}
+		else
+			{
+			found = EFalse;
+			}
+
+		if( found )
+			{
+			TRAPD(ret, (valuePtr = attribute.ValueL()));
+
+			if( ret != KErrNone )
+				{
+				// Non fatal error - just zero the descriptor
+				destination->Zero();
+				}
+			else
+				{
+				TInt len = Min(valuePtr->Length(), maxLength);
+				TPtrC value = TPtrC(valuePtr->Des().Ptr(), len);
+				destination->Copy(value);
+				delete valuePtr;
+				}
+			}
+		}
+	}
+
+TInt CHttpClientHandler::GetRecvBufferSize()
+	{
+	
+		iRecvBufferSize = KDefaultBufferSize;
+ 		// Check session properties
+		RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
+		RStringPool stringPool = iSession.StringPool();
+		THTTPHdrVal value;
+		RStringF recvBufferSize = stringPool.StringF(HTTP::ERecvBufferSize, iStringTable);
+		if(connInfo.Property(recvBufferSize, value))
+			{
+			if(value.Type() == THTTPHdrVal::KTIntVal && value.Int() > 0)
+				{
+				iRecvBufferSize = value.Int();
+				}
+			}
+	
+ 	return iRecvBufferSize;
+	}
+
+void CHttpClientHandler::EnsureStrictConnectMethodHeaders(RHTTPTransaction aTransaction)
+    {
+    // Check only for CONNECT method request
+    // Strict & mandatory headers as needed by CONNECT request are
+    // User-Agent, x-wap-profile, EProxyAuthorization
+    
+    RHTTPRequest request = aTransaction.Request();
+    RHTTPSession session = aTransaction.Session();
+    RStringPool sp = session.StringPool();
+    if(aTransaction.Request().Method().Index(iStringTable) == HTTP::ECONNECT)
+        {
+        RStringF strictConnectHeaders = sp.StringF(HTTP::EStrictConnectHeaders, iStringTable);
+        RHTTPPropertySet sessionProperties = session.ConnectionInfo();
+        THTTPHdrVal hdrVal;
+        if(sessionProperties.Property(strictConnectHeaders, hdrVal) && 
+                (hdrVal.Type() == THTTPHdrVal::KStrFVal) && 
+                (hdrVal.StrF().Index(iStringTable) == HTTP::EEnableStrictConnectHeaders))
+            {
+            RStringF profileHeader = sp.OpenFStringL(KUAProfile);
+            CleanupClosePushL(profileHeader);
+
+            
+            RHTTPHeaders hdr = request.GetHeaderCollection();
+            THTTPHdrFieldIter it = hdr.Fields();
+            while(!it.AtEnd())
+                {
+                RStringTokenF hdrStr = it();
+                RStringF hdrNameStr = sp.StringF(hdrStr);
+                TInt hdrIndex = hdrNameStr.Index(iStringTable);
+                // Check if it is a UA/User-Agent/Proxy-Auhorization Profile header
+                if((hdrNameStr == profileHeader) || (hdrIndex == HTTP::EUserAgent) || (hdrIndex == HTTP::EProxyAuthorization))
+                    {
+                    ++it; // Keep going
+                    }
+                else
+                    {
+                    // Anything else remove the header and reset the iterator
+                    hdr.RemoveField(hdrNameStr);
+                    it.First(); // Not so efficient. 
+                    }
+                }
+            CleanupStack::PopAndDestroy(&profileHeader);            
+            }        
+        }
+    }
+