applayerpluginsandutils/httpprotocolplugins/httpclient/chttpconnectionmanager.cpp
changeset 0 b16258d2340f
child 3 5ee1d9ce5878
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpclient/chttpconnectionmanager.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1449 @@
+// 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 "chttpconnectionmanager.h"
+
+#include <authority8.h>
+#include <httperr.h>
+#include <x509cert.h>
+
+#include "msocketfactory.h"
+#include "msocketconnector.h"
+#include "moutputstream.h"
+#include "minputstream.h"
+#include "mhttprequest.h"
+#include "mhttpresponse.h"
+#include "chttpconnectioninfo.h"
+#include "chttprequestbatcher.h"
+
+
+CHttpConnectionManager* CHttpConnectionManager::NewL(MSocketFactory& aSocketFactory, 
+													 MHttpBatchingPropertiesCallback& aCallback,
+													 CHttpPipelineFallback& aPipelineFallback, 
+													 TInt aMaxTransactionsToPipeline,
+													 TBool aEnableOptimalPipeline)
+	{
+	CHttpConnectionManager* self = new (ELeave) CHttpConnectionManager(aSocketFactory, aCallback, aPipelineFallback, aMaxTransactionsToPipeline, aEnableOptimalPipeline);
+	return self;
+	}
+	
+CHttpConnectionManager::~CHttpConnectionManager()
+	{
+	iPendingRequests.Reset();
+	iPendingResponses.Reset();
+	
+	// Close down connect, if exists
+	if( iSocketConnector )
+		{
+		iSocketConnector->StopConnect();
+		}
+	CloseConnection();
+		
+	delete iConnectionInfo;
+	iTunnelHost.Close();
+
+	delete iRequestBatcher;
+	delete iOptimiser;
+	}
+	
+CHttpConnectionManager::CHttpConnectionManager(MSocketFactory& aSocketFactory, 
+											   MHttpBatchingPropertiesCallback& aCallback,
+											   CHttpPipelineFallback& aPipelineFallback,
+											   TInt aMaxTransactionsToPipeline,
+											   TBool aEnableOptimalPipeline): 
+	iEnableOptimalPipeline(aEnableOptimalPipeline), iMaxTransactionsToPipeline(aMaxTransactionsToPipeline),
+	iSocketFactory(aSocketFactory), iCallback(aCallback), iPipelineFallback(aPipelineFallback)
+	{
+	}
+	
+void CHttpConnectionManager::SubmitL(CHttpConnectionInfo& aConnectionInfo, MHttpRequest& aRequest, MHttpResponse& aResponse)
+	{
+	// Check state - may need to close connection
+	TBool secure = EFalse;
+	if( iState == EIdleConnected )
+		{
+		secure = iConnectionInfo->IsSecure();
+		// Can the current connection be re-used?
+		if( !iConnectionInfo->HostAndPortMatches(aConnectionInfo) ||
+			secure && !aConnectionInfo.IsSecure() )
+			{
+			__ASSERT_DEBUG( iPendingResponses.Count() == 0, User::Invariant() );
+
+			// Nope - either the current connection is to the wrong host or the
+			// current connection is secure and need a non-secure connection.
+			CloseConnection();
+			}
+		}
+	else if( iState == EClosing )
+		{
+		__ASSERT_DEBUG( iConnectionInfo->IsNonPersistent(), User::Invariant() );
+		__ASSERT_DEBUG( iPendingResponses.Count() == 0, User::Invariant() );
+
+		// The connection was non-persistent and the remote host has yet to 
+		// close the connection - close it now.
+		CloseConnection();
+		}
+	if ( iState == EIdleConnected || iState == EConnected )	
+		{
+		__ASSERT_DEBUG( iInputStream, User::Invariant() );
+		iInputStream->Restart ();			
+		}
+
+	// Cleanup old connection info and take ownership of the new connection info.
+	delete iConnectionInfo;
+	iConnectionInfo = &aConnectionInfo;
+
+	// Store the request and response in appropriate place
+	if( iCurrentRequest == NULL )
+		{
+		// Make this the current request
+		iCurrentRequest = &aRequest;
+		}
+	else
+		{
+		__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+		// Append to the pending request queue
+		User::LeaveIfError(iPendingRequests.Append(&aRequest));
+		}
+
+	if( iCurrentResponse == NULL )
+		{
+		// Make this the current response
+		iCurrentResponse = &aResponse;
+		}
+	else
+		{
+		__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+		// Append to the pending response queue		
+		TInt err = iPendingResponses.Append( &aResponse );
+		
+		// The request is already added into the array. Remove it from the array
+		// in case of any error and leave with the error code. 
+		// This is required because a checking is going on in CancelSubmission fn.
+		// That function expects: if a request object is present in the array there "must" be a 
+		// corresponding response object.
+		if ( err != KErrNone )
+			{
+			TInt requestIndex = FindRequest( aRequest );
+			if( requestIndex != KErrNotFound )
+				{
+				iPendingRequests.Remove( requestIndex );
+				}
+			User::Leave ( err );
+			}
+
+		}
+	
+	switch( iState )
+		{
+	case EIdle:
+		{
+		__FLOG_4(
+				_T8("Start connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+
+		// Need to start connection to the appropriate host
+		iSocketConnector = &iSocketFactory.ConnectL(
+												   *this, 
+												   iConnectionInfo->Host(), 
+												   iConnectionInfo->Port()
+												   );
+
+		if (!CannotPipeline())
+			{
+			//  we are pipelining so set this flag
+			iFlags.Set(EFirstTransaction);
+			}
+		// Move to Connecting state
+		iState = EConnecting;
+		} break;
+	case EIdleConnected:
+		{
+		// A connection is already established with the appropriate host and it
+		// is currently not being used by any other transaction. Check to see if
+		// an upgrade to a secure connection is required.
+		if( iConnectionInfo->IsSecure() && !secure )
+			{
+			// Upgrade the connection to be secure.
+			UpgradeConnectionL();
+			}
+		else 
+			{
+			// Move to Connected state and notify the current request to start.
+			// Specifies that this transaction is waiting to write its
+			// request to the supposedly connected connection. Due timing issues
+			// the server may have closed connection but connection manager has
+			// not been notified yet.
+			iState = EConnected;
+			iFlags.Set(EPendingWriteInConnectedState);
+			iCurrentRequest->StartRequest();
+			}
+		} break;
+	case EConnecting:
+		{
+		__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+		// Can only submit in this state is a transaction is being pipelined and 
+		// pipelining is enabled. A connection is being established with the 
+		// appropriate host for an earlier request. Do nothing.
+		} break;
+	case EConnected:
+		{
+		__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+		// Can only submit in this state is a transaction is being pipelined and 
+		// pipelining is enabled. A connection has already been establised with 
+		// the appropriate host but is currently being used by other transactions.
+		// Check to see if this request is now the current request.
+		if( iCurrentRequest == &aRequest )
+			{
+			// It is - start the request...
+			iCurrentRequest->StartRequest();
+			}			
+		} break;
+	case EUpgrading:
+		{
+		__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+		// Can only submit in this state is a transaction is being pipelined and 
+		// pipelining is enabled. A connection has already been established with
+		// the appropriate host and is currently being upgraded to be secure. Do 
+		// nothing. 
+		} break;
+	default:
+		User::Invariant();
+		break;
+		}
+	}
+	
+CHttpConnectionManager::TConnectionStatus CHttpConnectionManager::Status() const
+	{
+	// Check the state for correct status value.
+	CHttpConnectionManager::TConnectionStatus status;
+
+	switch( iState )
+		{
+	case EIdle:
+	case EClosing:
+		{
+		status = ENotConnected;
+		} break;
+	case EIdleConnected:
+		{
+		status = EConnectedAndAvailable;
+		} break;
+	case EConnecting:
+	    {
+	    if(iEnableOptimalPipeline)
+	        {
+	        status = EConnectingNotAvailable;
+	        break;
+	        }
+	    }
+	case EUpgrading:
+	case EConnected:
+	default:
+		{
+		// Number of requests is the number of Pending Responses + the current response
+		TInt numberResponses = iPendingResponses.Count();
+		if (iCurrentResponse != NULL)
+			{
+			numberResponses +=1;
+			}
+			
+		if( (!CannotPipeline() && !iConnectionInfo->IsNonPersistent()) &&
+			numberResponses < iMaxTransactionsToPipeline)
+			{
+			// The connection can be used to pipeline requests - connect and busy.
+			status = EConnectedAndBusy;
+			}
+		else 
+			{
+			// Other states, connected and not available.
+			status = EConnectedNotAvailable;
+			}
+		}
+		break;
+		}
+	return status;
+	}
+	
+const CHttpConnectionInfo& CHttpConnectionManager::ConnectionInfo() const
+	{
+	return *iConnectionInfo;
+	}
+
+
+void CHttpConnectionManager::CancelSubmission(MHttpRequest& aRequest, MHttpResponse& aResponse)
+	{
+	__FLOG_0(_T8("!! Cancel submission"));
+
+	// If the request has not yet been sent, then just remove the request and
+	// response from the pending queues. Everything can carry on as normal. But
+	// the request is in the process of being sent or has already been sent and
+	// is waiting for a response, then need to close the connection, notifying
+	// the other responses of the connection closure.
+
+	TInt requestIndex = FindRequest(aRequest);
+	TInt responseIndex = FindResponse(aResponse);
+	TBool stopConnection = EFalse;
+
+	if( requestIndex != KErrNotFound )
+		{
+		__ASSERT_DEBUG( responseIndex != KErrNotFound, User::Invariant() );
+
+		// The request has not been made - just need to remove the request and
+		// response objects from the pending queues.
+		iPendingRequests.Remove(requestIndex);
+		iPendingResponses.Remove(responseIndex);
+		}
+	else if( responseIndex != KErrNotFound )
+		{
+		// The request has been sent but the response has not yet been received
+		// and therefore need to stop the connection and remove response from 
+		// the pending queue.
+		iPendingResponses.Remove(responseIndex);
+		stopConnection = ETrue;
+		}
+
+	// Is this request/response active?
+	if( stopConnection || iCurrentRequest == &aRequest || iCurrentResponse == &aResponse )
+		{
+		// Yep - need to stop the connection. Check state as could be still
+		// trying to connect.
+		if( iState == EConnecting )
+			{
+			__FLOG_4(
+					_T8("-> stopping connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+					&iConnectionInfo->Host(), 
+					iConnectionInfo->Port(), 
+					iConnectionInfo->IsSecure(), 
+					iConnectionInfo->IsNonPersistent()
+					);
+
+			// Stop the connection - move to Idle state
+			iSocketConnector->StopConnect();
+			iSocketConnector = NULL;
+			
+			SetIdleState();				
+			}
+		else
+			{
+			// As the connection had been established, then the request/response 
+			// could be in progress - cancel them.
+		   if( iCurrentRequest && iCurrentRequest == &aRequest)
+				{
+				__FLOG_0(_T8("-> cancelling request"));
+				iCurrentRequest->CancelRequest();
+				}
+				
+		   if( iCurrentResponse == NULL && iPendingResponses.Count() == 0 )
+				{
+				iCurrentResponse = &aResponse;	
+				}
+
+		   if( iCurrentResponse && iCurrentResponse == &aResponse )
+				{
+			   __FLOG_0(_T8("-> completing response"));
+				DoResponseCompletion ();
+				__FLOG_0(_T8("-> cancelling response"));
+				iCurrentResponse->CancelResponse();	
+				
+				// Need connection closure?
+				if ( stopConnection || 
+					( iCurrentResponse && iCurrentResponse == &aResponse && !iCurrentResponse->ResponseCompleted () ) )
+					{
+					__FLOG_4(
+							_T8("-> closing connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+							&iConnectionInfo->Host(), 
+							iConnectionInfo->Port(), 
+							iConnectionInfo->IsSecure(), 
+							iConnectionInfo->IsNonPersistent()
+							);
+					CloseConnection ();												
+					}
+				else
+					{
+					ResponseDataParsed ();						
+					}
+				}				
+			}
+			
+			if (  iCurrentResponse && iState == EIdle )
+				{
+					if ( stopConnection )
+						{
+						__FLOG_0(_T8("-> Notifying current and pending transactions"));
+
+						// The current transaction was not the one to cancel - need to
+						// notify it. This request has already sent and no response has been received.
+						// Need to resend the request to maintain the correct request/response state
+						// which is needed by the request/response composer/parser.
+						iCurrentResponse->ConnectionError(KErrHttpNonPipeliningError);
+						}
+						
+						NotifyPendingTransactions(KErrHttpNonPipeliningError);
+						
+						// Current request and response are no longer valid				
+						iCurrentRequest = NULL;
+						iCurrentResponse = NULL;				
+													
+					return; 				
+				}
+				
+			if ( iState == EIdle && iPendingRequests.Count() > 0 )
+				{				
+				// connection is closed explicitly by the connection manager or not active. If we are 
+				// having pending requests to be sent need to reconnect here.
+				__FLOG_0(_T8("-> Reconnecting and sending the first pending request."));
+				// Set the current request & response
+				iCurrentRequest = iPendingRequests[0];
+				iCurrentResponse = iPendingResponses[0];
+				// Remove it from the pending queue
+				iPendingRequests.Remove (0);
+				iPendingResponses.Remove (0);			
+				
+				// Now do a reconnect
+				TRAPD ( err, ReconnectSocketL () );
+				if ( err != KErrNone )
+					{
+					ReportSocketError ( err );
+					}
+				return; 
+				}
+			if ( iCurrentRequest && iCurrentRequest == &aRequest )					
+				{
+				iCurrentRequest = NULL;
+				}
+			if ( iCurrentResponse && iCurrentResponse == &aResponse )					
+				{
+				iCurrentResponse = NULL;			
+				}				
+		}
+	else if( CannotPipeline() && !iCurrentRequest && !iCurrentResponse )
+ 	    {
+			// Closing the connection since pipelining has been disabled and both the request and 
+		    // response have been completed.
+	   __FLOG_4(
+			   _T8("-> closing connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+			   &iConnectionInfo->Host(), 
+			   iConnectionInfo->Port(), 
+			   iConnectionInfo->IsSecure(), 
+			   iConnectionInfo->IsNonPersistent()
+			   );
+		 CloseConnection();
+		}	
+	// Otherwise - do nothing. This submission has already completed.
+#if defined (_DEBUG) && defined (_LOGGING)
+	__FLOG_0(_T8("-> This submission has already completed. Do nothing"));
+#endif
+	}
+
+const CX509Certificate* CHttpConnectionManager::ServerCert()
+	{
+	__ASSERT_DEBUG( iOutputStream != NULL, User::Invariant() );
+
+	return iOutputStream->ServerCert();
+	}
+
+TInt CHttpConnectionManager::CipherSuite(TDes8& aCipherSuite)
+	{
+	__ASSERT_DEBUG( iOutputStream != NULL, User::Invariant() );
+	
+	return iOutputStream->CipherSuite(aCipherSuite);
+	}
+
+void CHttpConnectionManager::TunnelConnection(RStringF aHost)
+	{
+	__ASSERT_DEBUG( iState == EConnected, User::Invariant() );
+
+	// Copy the host information (includes port info) for where the tunnel leads.
+	iTunnelHost.Close();
+	iTunnelHost = aHost.Copy();
+	iTunnel = ETrue;
+
+	__FLOG_0(_T8("!! Tunnel establised"));
+	__FLOG_5(
+			_T8("-> tunnel to %S via host %S, remote port %d (secure : %d, nonpersistent : %d)"),
+			&iTunnelHost.DesC(), 
+			&iConnectionInfo->Host(), 
+			iConnectionInfo->Port(), 
+			iConnectionInfo->IsSecure(), 
+			iConnectionInfo->IsNonPersistent()
+			);
+	}
+
+TBool CHttpConnectionManager::TunnelMatches(RStringF aHost) const
+	{
+	return (iTunnel && iTunnelHost == aHost);
+	}
+
+void CHttpConnectionManager::CloseConnection()
+	{
+	if( iInputStream )
+		{
+		// Flag that connection
+		iState = EClosing;
+
+		// Close the input stream	
+		iInputStream->Close();
+		iInputStream = NULL;
+		}
+	// NOTE - there is no need to close the output stream as by closing the
+	// input stream it would have been notified and closed anyway. Also, the
+	// state should have moved to Idle.
+
+	__ASSERT_DEBUG( iState == EIdle, User::Invariant() );
+	}
+
+void CHttpConnectionManager::UpgradeConnectionL()
+	{
+	__ASSERT_DEBUG( iOutputStream != NULL, User::Invariant() );
+
+	__FLOG_0(_T8("!! Upgrading to secure"));
+
+	// Request upgrade to a secure connection - move to Upgrading state.
+	TPtrC8 host;
+	if( iTunnel )
+		{
+		__FLOG_5(
+				_T8("-> tunnel to %S via host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iTunnelHost.DesC(), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+
+		// Host info is in the tunnel host name - this is an authority form data
+		// and so needs to be parsed.
+		TAuthorityParser8 authority;
+		User::LeaveIfError(authority.Parse(iTunnelHost.DesC()));
+		host.Set(authority.Extract(EAuthorityHost));
+		}
+	else
+		{
+		__FLOG_4(
+				_T8("-> connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+
+		// Just a normal connection - host info is in the connection info.
+		host.Set(iConnectionInfo->Host());
+		}
+	iOutputStream->SecureClientReq(host);
+	iState = EUpgrading;
+	}
+
+void CHttpConnectionManager::HandleSocketError(TInt aError)
+	{
+	// Is this due to the connection manager closing the connection?
+	if( iState != EClosing )
+		{
+#if defined (_DEBUG) && defined (_LOGGING)
+		if( iState == EConnecting )
+			{
+			__FLOG_1(_T8("!! Error : %d"), aError);
+			__FLOG_4(
+					_T8("-> could not connect to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+					&iConnectionInfo->Host(), 
+					iConnectionInfo->Port(), 
+					iConnectionInfo->IsSecure(), 
+					iConnectionInfo->IsNonPersistent()
+					);
+			}
+		else 
+			{
+			__FLOG_1(_T8("!! Error : %d"), aError);
+			__FLOG_4(
+					_T8("-> connection closed to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+					&iConnectionInfo->Host(), 
+					iConnectionInfo->Port(), 
+					iConnectionInfo->IsSecure(), 
+					iConnectionInfo->IsNonPersistent()
+					);
+			}
+#endif
+		if ( ( aError == KErrEof || aError == KErrCancel  ) )
+ 			{
+			if ( IsPendingWriteInConnectedState() && !iCurrentRequest->NeedDisconnectNotification() )
+ 				{
+				// Server shut down the connect before the current transaction had 
+ 				// a chance to send any of its data - attempt re-connect to server.	
+ 				// Cancel the current request
+ 				iCurrentRequest->CancelRequest ();		
+ 				TRAPD(err, ReconnectSocketL());
+ 				if(err != KErrNone)
+ 					{
+ 					// Re-connection to server failed at 1st hurdle - give up and
+ 					// report the error.
+ 					ReportSocketError(aError);
+ 					}
+ 				else
+ 					return;	// need early exit to maintain correct state - doh!		
+ 				}
+			else
+ 				{
+ 				// Report the socket error
+ 				ReportSocketError(aError);					
+ 				}
+ 			}
+		else
+ 			{
+			// Report the socket error 
+			ReportSocketError(aError);			
+ 			}
+		}
+#if defined (_DEBUG) && defined (_LOGGING)
+	else
+		{
+		__FLOG_4(_T8("!! Connection closed to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+		if( iConnectionInfo->IsNonPersistent() && aError == KErrEof )
+			{
+			__FLOG_0(_T8("-> non-persistent connection : expected shutdown"));
+			}
+		}
+#endif
+
+	// Move to Idle state...
+	SetIdleState();
+	}
+
+
+void CHttpConnectionManager::ReportSocketError(TInt aError)
+	{
+	if( iCurrentResponse != NULL )
+		{
+		__FLOG_0(_T8("-> notifying waiting response(s)"));
+
+		TInt error = aError;
+	
+		
+		// If we have a current request/response, cancel them
+		if( iCurrentRequest )
+			{
+			__FLOG_0(_T8("-> cancelling request"));
+			iCurrentRequest->CancelRequest();
+			}
+				
+		if( iCurrentResponse )
+			{
+			__FLOG_0(_T8("-> cancelling response"));
+			iCurrentResponse->CancelResponse();
+			}
+	
+	
+		iCurrentResponse->ConnectionError(error);
+
+		if( iState != EConnecting && iPendingResponses.Count() > 0 )
+			{
+			__FLOG_0(_T8("-> there are pipelined transactions - re-submit without pipelining"));
+			__FLOG_2(_T8("-> reporting %d (KErrHttpPipeliningError) instead of %d"), KErrHttpPipeliningError, aError);
+
+			// Change reported error as KErrHttpPipeliningError - this 
+			// indicates that those transaction can be re-submitted but 
+			// should not be pipelined.
+			error = KErrHttpPipeliningError;
+			
+			// Insert the host to probable pipeline list
+			if(iConnectionInfo)
+			    {
+			    __FLOG_1(_T8("-> Insert host: %S to probable pipelined host list"), &iConnectionInfo->Host());
+			    iPipelineFallback.InsertPipelineFailedHost(iConnectionInfo->Host());
+			    }
+			
+			}
+		
+		// Notify any pending (pipelined) transactions and the current
+		// transaction of the error.
+		NotifyPendingTransactions(error);
+	
+		// The current response and request are now no longer valid.
+		iCurrentRequest = NULL;
+		iCurrentResponse = NULL;
+		}
+#if defined (_DEBUG) && defined (_LOGGING)
+	else
+		{
+		__FLOG_0(_T8("-> connection not being used - no waiting transactions"));
+		}
+#endif
+	}
+
+void CHttpConnectionManager::ReconnectSocketL()
+	{
+	__FLOG_0(_T8("-> Attempting to re-connect to server"));
+
+	// Need to re-connect to the server. Clear the flags
+	iFlags.Clear(EPendingWriteInConnectedState);
+
+	__FLOG_4(
+			_T8("Re-connecting to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+			&iConnectionInfo->Host(), 
+			iConnectionInfo->Port(), 
+			iConnectionInfo->IsSecure(), 
+			iConnectionInfo->IsNonPersistent()
+			);
+
+	// Need to start connection to the appropriate host
+	iSocketConnector = &iSocketFactory.ConnectL(
+											   *this, 
+											   iConnectionInfo->Host(), 
+											   iConnectionInfo->Port()
+											   );
+	// Move to Connecting state...
+	iState = EConnecting;
+	}
+
+void CHttpConnectionManager::MakeConnectionNonPersistent()
+	{
+	// Is the connection already non-persistent?
+	if( !iConnectionInfo->IsNonPersistent() )
+		{
+		// Nope - change to be non-persistent...
+		iConnectionInfo->SetPersistentState(CHttpConnectionInfo::ENonPersistent);
+
+		__FLOG_0(_T8("!! Converting to non-persistent connection"));
+		__FLOG_4(
+				_T8("-> connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+
+		// Flag that this connection now cannot pipeline - cancel any pending 
+		// trasactions with a non-pipelining error code. This indicates that 
+		// those transaction can be re-submitted and still be pipelined.
+		iFlags.Set(ECannotPipeline);
+		NotifyPendingTransactions(KErrHttpPipeliningError);
+
+		iCurrentRequest = NULL;
+		}
+	}
+
+void CHttpConnectionManager::CheckRequestComplete(MHttpRequest& aRequest)
+	{
+	// Check to see if the current request matches the request to be stopped.
+	if( !IsFirstTransaction() && iCurrentRequest == &aRequest)
+		{
+		__FLOG_0(_T8("!! Incomplete request"));
+		__FLOG_0(_T8("-> cancelling request and moving to non-persistent connection"));
+		
+		// Ok, this request is not complete - cancel it.
+
+		if (aRequest.CheckRequestPendingComplete())
+			{
+			// Request was done anyway. Just make sure to discard the next confirmation of 
+			// data sent.
+			iFlags.Set(EDiscardSndDataCnf);
+			}
+		else
+			{
+			// Change connection to non-persistent - this should take care of any
+			// pipelined requests.
+			MakeConnectionNonPersistent();
+			}
+
+		if(iCurrentRequest)
+			{
+			iCurrentRequest->CancelRequest();
+			iCurrentRequest = NULL;
+			}
+		}
+	}
+
+TInt CHttpConnectionManager::FindRequest(MHttpRequest& aRequest)
+	{
+	TInt ii = iPendingRequests.Count();
+
+	while( ii > 0)
+		{
+		if( &aRequest == iPendingRequests[--ii] )
+			{
+			// Found the request - pass the appropriate index value.
+			return ii;
+			}
+		}
+
+	return KErrNotFound;
+	}
+
+TInt CHttpConnectionManager::FindResponse(MHttpResponse& aResponse)
+	{
+	TInt ii = iPendingResponses.Count();
+
+	while( ii > 0)
+		{
+		if( &aResponse == iPendingResponses[--ii] )
+			{
+			// Found the response - pass the appropriate index value.
+			return ii;
+			}
+		}
+
+	return KErrNotFound;
+	}
+
+void CHttpConnectionManager::NotifyPendingTransactions(TInt aError)
+	{
+	TInt ii = iPendingResponses.Count();
+
+	while( ii > 0 )
+		{
+		// Notify the waiting transaction of the error 
+		iPendingResponses[--ii]->ConnectionError(aError);
+		}
+
+	// Reset the pending request and response queues.
+	iPendingRequests.Reset();
+	iPendingResponses.Reset();
+	}
+
+void CHttpConnectionManager::DisablePipelining()
+	{
+	__ASSERT_DEBUG( iState == EIdle || iState == EIdleConnected || iState == EClosing, User::Invariant() );
+	__ASSERT_DEBUG( !CannotPipeline(), User::Invariant() );
+
+	iFlags.Set(ECannotPipeline);
+	}
+
+void CHttpConnectionManager::SetIdleState()
+	{
+	// The connection is now Idle - reset tunnel and pipeline flags.
+	iState = EIdle;
+	iTunnel = EFalse;
+	iFlags.Clear(ECannotPipeline);
+	iFlags.Clear(EPendingWriteInConnectedState);
+	}
+
+/*
+ *	Methods from MHttpRequestObserver
+ */
+
+void CHttpConnectionManager::SendRequestDataL(const TDesC8& aData)
+	{
+	__ASSERT_DEBUG( iState == EConnected, User::Invariant() );
+
+	iOutputStream->SendDataReqL(aData);	
+	}
+	
+void CHttpConnectionManager::RequestComplete()
+	{
+	__ASSERT_DEBUG( iState == EConnected, User::Invariant() );
+    
+	//Start the Receive Timer
+	StartRecvTimer();
+
+	// Current request is complete.
+	if( !IsFirstTransaction() )
+		{
+		iCurrentRequest = NULL;
+
+		if( iPendingRequests.Count() > 0 )
+			{
+			// More requests are pending - start the next one. Remove it from the
+			// pending queue.
+			iCurrentRequest = iPendingRequests[0];
+			iPendingRequests.Remove(0);
+
+			iCurrentRequest->StartRequest();
+			}
+		}
+	}
+
+void CHttpConnectionManager::SendingBodyData(TBool aValue)
+    {
+    if(iOutputStream)
+        {
+        iOutputStream->SetTCPCorking(aValue);
+        }
+    }
+
+/*
+ *	Methods from MHttpResponseObserver
+ */
+
+void CHttpConnectionManager::ResponseDataParsed()
+	{
+	__ASSERT_DEBUG( iState == EConnected, User::Invariant() );
+	
+	if( iCurrentResponse == NULL )
+		{
+		// The current response has finished with the connection - was it a 
+		// non-persistent connection?
+		if( IsFirstTransaction() )
+			{
+			iFlags.Clear(EFirstTransaction);
+			iCurrentRequest = NULL;
+			}
+
+		// More requests are pending - start the next one, 
+		// and remove it from the pending queue.
+		if ( iPendingRequests.Count() > 0 )
+			{
+			iCurrentRequest = iPendingRequests[0];
+			iPendingRequests.Remove(0);
+			if( iPendingResponses.Count() > 0 )
+				{
+				iCurrentResponse = iPendingResponses[0];
+				iPendingResponses.Remove(0);
+				}
+				iState = EConnected;
+				iFlags.Set(EPendingWriteInConnectedState);
+				iCurrentRequest->StartRequest();
+			}
+		else if( iConnectionInfo->IsNonPersistent() )
+			{
+			__FLOG_0(_T8("!! Non-persistent connection : expect server to close connection"));
+			__FLOG_4(
+					_T8("-> moving to Closing state on connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+					&iConnectionInfo->Host(), 
+					iConnectionInfo->Port(), 
+					iConnectionInfo->IsSecure(), 
+					iConnectionInfo->IsNonPersistent()
+					);
+
+			__ASSERT_DEBUG( iPendingResponses.Count() == 0, User::Invariant() );
+			
+			iFlags.Clear(ECannotPipeline);
+			iState = EClosing;		
+			}
+		else
+			{
+			// Connection is persistent and no longer waiting for any more
+			// response data - going to IdleConnected state. Also, allow 
+			// pipelining to occur on this connection.
+			iState = EIdleConnected;
+			iFlags.Clear(ECannotPipeline);
+			}
+			
+        //Start the Receive Timer
+		StartRecvTimer();
+
+		// Update the state and notify the input stream that the received data 
+		// is no longer needed.
+		iInputStream->ReceivedDataRes();
+		}
+	else
+		{
+		// Check to see if there is any excess data.
+		if( iExcessData.Length() > 0 )
+			{
+			// Spoof receiving this data.
+			__FLOG_5(_T8("Received %d bytes of data on connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"),
+					iExcessData.Length(),
+					&iConnectionInfo->Host(), 
+					iConnectionInfo->Port(), 
+					iConnectionInfo->IsSecure(), 
+					iConnectionInfo->IsNonPersistent()
+					);
+
+			// The current transaction should be given the data. Once passed to 
+			// the response, reset the excess data.
+			iCurrentResponse->ResponseDataReceived(iExcessData);	
+			iExcessData.Set(KNullDesC8());
+			}
+		else
+			{
+			//Start the Receive Timer
+			if(!iCurrentResponse->ResponseInformational())
+				{
+				StartRecvTimer();
+				}
+			// Notify the input stream that the received data is no longer needed
+			iInputStream->ReceivedDataRes();
+			}
+		}
+	}
+	
+void CHttpConnectionManager::ResponseComplete(const TDesC8& aExcessData)
+	{
+	// Remove the current response.
+	iCurrentResponse = NULL;
+	__FLOG_1(_T8("Received  %d bytes of excess data"), aExcessData.Length());
+
+	// Need to store the excess data.
+	if( iPendingResponses.Count() > 0  )
+		{
+		iExcessData.Set(aExcessData);
+
+		if( !IsFirstTransaction())
+			{
+			// More responses are pending - get the next one. Remove it from the
+			// pending queue.
+			iCurrentResponse = iPendingResponses[0];
+			iPendingResponses.Remove(0);
+			}
+		}
+	}
+
+/*
+ *	Methods from MSocketConnectObserver
+ */
+ 
+void CHttpConnectionManager::ConnectionMadeL(MInputStream& aInputStream, MOutputStream& aOutputStream)
+	{
+	// Connector object no longer valid
+	iSocketConnector = NULL;
+	
+	// Bind to the input stream
+	iInputStream = &aInputStream;
+	
+	iOutputStream = &aOutputStream;
+
+	if(iOptimiser)
+		{
+		delete iOptimiser;
+		iOptimiser = NULL;
+		}
+			
+	__FLOG_4(
+			_T8("Connected to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+			&iConnectionInfo->Host(), 
+			iConnectionInfo->Port(), 
+			iConnectionInfo->IsSecure(), 
+			iConnectionInfo->IsNonPersistent()
+			);
+			
+	TBool batchingEnabled = EFalse;
+	MHttpDataOptimiser* dataOptimiser = iCurrentRequest->HttpDataOptimiser( batchingEnabled );
+	
+	if (!CannotPipeline())
+		{
+		TInt bufferSize = 0;
+		bufferSize = iCallback.GetMaxBatchingBufferSize();
+
+		if (bufferSize > 0)	// Therefore, implicitly, batching is supported
+			{
+			if (iRequestBatcher)
+				{
+				delete iRequestBatcher;
+				iRequestBatcher = NULL;
+				}
+			
+			if( dataOptimiser && batchingEnabled )
+				{
+				// dataOptimiser has been set for the current session
+				iOptimiser = CHttpClientOptimiser::NewL(*iOutputStream, *this);
+				iOptimiser->BindOptimiser(*dataOptimiser);
+				iRequestBatcher = CHttpRequestBatcher::NewL(*iOptimiser, bufferSize);
+				__FLOG_0(_T8("-> Created request batcher"));
+				__FLOG_0(_T8("-> HTTP Data Optimiser has been set"));
+				iInputStream->Bind(*iOptimiser);
+				}
+
+			else
+				{
+				iRequestBatcher = CHttpRequestBatcher::NewL(*iOutputStream, bufferSize);
+				__FLOG_0(_T8("-> Created request batcher"));
+				iOutputStream->Bind(*iRequestBatcher);
+				iInputStream->Bind(*this);	
+				}
+			iRequestBatcher->Bind(*this);
+			iOutputStream = iRequestBatcher;
+			}
+		else
+			{
+			// We are pipelining but not batching
+			__FLOG_0(_T8("-> Did not create request batcher as batching disabled"));			
+
+			CreateOptimiserL( dataOptimiser );
+			}
+		}
+	else
+		{
+		// We are not pipelining
+		__FLOG_0(_T8("-> Did not create request batcher as pipelining disabled"));
+		
+		CreateOptimiserL( dataOptimiser );
+		}
+
+	if( iConnectionInfo->IsSecure() )
+		{
+		// Upgrade the connection to be secure.
+		UpgradeConnectionL();
+		}
+	else
+		{
+		// Move to Connected state and notify the current request to start.
+		iState = EConnected;
+		iCurrentRequest->StartRequest();
+		}
+	}
+
+TInt CHttpConnectionManager::HandleConnectError(TInt aError)
+	{
+	// Ok, the connector object is now invalid
+	iSocketConnector = NULL;
+
+	// Handle the error...
+	HandleSocketError(aError);
+
+	__ASSERT_DEBUG( iState == EIdle, User::Invariant() );
+
+	return KErrNone;
+	}
+
+void CHttpConnectionManager::MSocketConnectObserver_Reserved()
+	{
+	User::Invariant();
+	}
+
+/*
+ * Methods from MInputStreamObserver
+ */
+
+void CHttpConnectionManager::ReceivedDataIndL(const TDesC8& aBuffer)
+	{
+	if( iCurrentResponse != NULL )
+		{
+		__FLOG_5(_T8("Received %d bytes of data on connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"),
+				aBuffer.Length(),
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+		
+		// The current transaction should be given the data.
+		iCurrentResponse->ResponseDataReceived(aBuffer);
+		}
+	else
+		{
+		__FLOG_1(_T8("!! Spurious data : %d bytes of data ignored"), aBuffer.Length());
+		__FLOG_4(
+				_T8("-> data received on connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+        
+		//Start the Receive Timer
+		StartRecvTimer();
+
+		// This data does not belong to any transaction - ignore it.
+		iInputStream->ReceivedDataRes();
+		}
+	}
+
+void CHttpConnectionManager::SecureServerCnf()
+	{
+	User::Invariant();
+	}
+
+void CHttpConnectionManager::InputStreamCloseInd(TInt aError)
+	{
+	// The input stream is no longer of any use as the connection has closed.
+	iInputStream = NULL;
+
+	// Handle the error only if the output stream observer has not done so 
+	// already.
+	if( iOutputStream != NULL )
+		{
+		HandleSocketError(aError);
+		}
+	}
+
+void CHttpConnectionManager::MInputStreamObserver_Reserved()
+	{
+	User::Invariant();	
+	}
+
+/*
+ * Methods from MOutputStreamObserver
+ */
+
+void CHttpConnectionManager::SendDataCnfL()
+	{
+	if (DiscardSndDataCnf())
+		{
+		__FLOG_0(_T8("-> Disgarding Confirmation that data was sent"));
+		iFlags.Clear(EDiscardSndDataCnf);
+		if (iState == EConnected)
+			{
+			RequestComplete();
+			}
+		else
+			{
+			__FLOG_0(_T8("-> Initialising iCurrentRequest"));
+			iCurrentRequest=NULL;
+			}
+		}
+	// Notify the current request that its data has been sent.
+	else if( iCurrentRequest)
+		{
+		iCurrentRequest->RequestDataSent();
+
+		// The request has now sent data - reset the pending write flag.
+		iFlags.Clear(EPendingWriteInConnectedState);
+		}
+	}
+
+void CHttpConnectionManager::SecureClientCnf()
+	{
+	__ASSERT_DEBUG( iState == EUpgrading, User::Invariant() );
+
+#if defined (_DEBUG) && defined (_LOGGING)
+	__FLOG_0(_T8("!! Secure connection establised"));
+
+	if( iTunnel )
+		{
+		__FLOG_5(
+				_T8("-> secure tunnel to %S via host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iTunnelHost.DesC(), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+		}
+	else
+		{
+		__FLOG_4(
+				_T8("-> secure connection to host %S, remote port %d (secure : %d, nonpersistent : %d)"), 
+				&iConnectionInfo->Host(), 
+				iConnectionInfo->Port(), 
+				iConnectionInfo->IsSecure(), 
+				iConnectionInfo->IsNonPersistent()
+				);
+		}
+#endif
+
+	// Move to Connected state and notify the current request to start.
+	iState = EConnected;
+	iCurrentRequest->StartRequest();		
+	}
+
+void CHttpConnectionManager::OutputStreamCloseInd(TInt aError)
+	{
+	// The output stream is no longer of any use as the connection has closed.
+	iOutputStream = NULL;
+
+	// Handle the error only if the input stream observer has not done so 
+	// already.
+	if( iInputStream != NULL )
+		{
+		HandleSocketError(aError);
+		}
+	}
+
+void CHttpConnectionManager::DoResponseCompletion ()
+	{
+	__ASSERT_DEBUG( iCurrentResponse != NULL, User::Invariant() );
+	// We do these operations only for 3xx responses and the response parsing
+	// hasn't happened completely
+	if ( iCurrentResponse->NeedCompletion() && !iCurrentResponse->ResponseCompleted () )
+		{
+		// Try to complete the response. Parser may have the data but not processed.
+		TBool responseCompleted = iCurrentResponse->CompleteResponse ( KNullDesC8() );		
+		while ( !responseCompleted )
+			{
+			// Read and try complete the response
+			TPtrC8 data;
+			__FLOG_0(_T8("!! doing an immediate socket read"));					
+			TInt ret = iInputStream->ImmediateRead ( data );
+			if ( ( iPendingResponses.Count() == 0 ) || ret <= KErrNone )
+				{
+				__FLOG_0(_T8("!! Breaking from the loop"));
+				// no further data is expected or there is a socket error
+				break;					
+				}
+			responseCompleted = iCurrentResponse->CompleteResponse ( data );
+			}
+		}
+	}
+
+void CHttpConnectionManager::MOutputStreamObserver_Reserved()
+	{
+	User::Invariant();
+	}
+	
+MHttpResponse* CHttpConnectionManager::CurrentResponse()
+	{
+	return iCurrentResponse;	
+	}
+
+void CHttpConnectionManager::CreateOptimiserL(MHttpDataOptimiser* aDataOptimiser)
+	{
+	if( aDataOptimiser )
+		{
+		// The dataOptimiser has been set, it does not matter if it is for session or transaction
+		iOptimiser = CHttpClientOptimiser::NewL(*iOutputStream, *this);
+		iOptimiser->BindOptimiser(*aDataOptimiser);
+		__FLOG_0(_T8("-> HTTP Data Optimiser has been set"));
+		iInputStream->Bind(*iOptimiser);
+		iOutputStream = iOptimiser;
+		iOutputStream->Bind(*this);
+		}
+	
+	else
+		{
+		iInputStream->Bind(*this);
+		iOutputStream->Bind(*this);	
+		}
+	}
+
+void CHttpConnectionManager::OnReceiveTimeOut()
+	{
+	if(iCurrentResponse)
+		{
+		iCurrentResponse->OnResponseReceiveTimeOut();
+		}
+	}
+
+void CHttpConnectionManager::OnSendTimeOut()
+	{
+	if(iCurrentRequest)
+		{
+		iCurrentRequest->OnRequestSendTimeOut();
+		}
+	}
+
+TInt CHttpConnectionManager::SendTimeOutVal()
+	{
+	if(iCurrentRequest)
+		{
+		return iCurrentRequest->SendTimeOutValue();
+		}
+	return 0;
+	}
+
+void CHttpConnectionManager::StartRecvTimer()
+	{
+	if(iCurrentResponse)
+		{
+		TInt timeoutValue = iCurrentResponse->ReceiveTimeOutValue();
+		if(iInputStream)
+			{
+			iInputStream->StartReceieveTimer(timeoutValue);
+			}
+		}
+	}
+
+void CHttpConnectionManager::InsertPipelineFailedHost(const TDesC8& aHost)
+ 	{
+ 	iPipelineFallback.InsertPipelineFailedHost(aHost);
+ 	}
+
+
+CHttpHostElement* CHttpHostElement::New(const TDesC8& aHost)
+    {
+    CHttpHostElement* self = new CHttpHostElement;
+    if(self && self->Construct(aHost))
+        {
+        return self;
+        }
+    delete self;
+    return NULL;
+    }
+
+CHttpHostElement::~CHttpHostElement()
+    {
+    delete iHost;
+    }
+
+TBool CHttpHostElement::Construct(const TDesC8& aHost)
+    {
+    iHost = aHost.Alloc();
+    return (iHost != NULL);
+    }
+
+CHttpPipelineFallback::CHttpPipelineFallback()  
+: iPipelineFailedHosts(16), // With granularity 16
+iProbablePipelineFailedHosts(CHttpHostElement::LinkOffset())
+    {
+    
+    }
+
+CHttpPipelineFallback* CHttpPipelineFallback::NewL()
+    {
+    return new(ELeave)CHttpPipelineFallback;
+    }
+
+CHttpPipelineFallback::~CHttpPipelineFallback()
+    {
+    iPipelineFailedHosts.ResetAndDestroy();
+    // Free up the objects pointed by the list
+    CHttpHostElement* element;
+    TSglQueIter<CHttpHostElement> it(iProbablePipelineFailedHosts);
+    while((element = it++) != NULL)
+        {
+        iProbablePipelineFailedHosts.Remove(*element);
+        delete element;
+        }    
+    }
+
+TBool CHttpPipelineFallback::NeedPipelineFallback(const TDesC8& aHost)
+    {    
+    TInt count = iPipelineFailedHosts.Count();
+    for(TInt i = count - 1; i >= 0; --i)
+        {
+        if(aHost.CompareF(*(iPipelineFailedHosts[i])) == 0)
+            return ETrue;
+        }
+    return EFalse;
+    }
+
+void  CHttpPipelineFallback::InsertPipelineFailedHost(const TDesC8& aHost)
+    {
+    // Already failed. no need to check further.
+    if(NeedPipelineFallback(aHost))
+        {
+        return;
+        }
+    CHttpHostElement* element = NULL;
+    TSglQueIter<CHttpHostElement> it(iProbablePipelineFailedHosts);
+    while((element = it++) != NULL)
+        {
+        if(element->Host().CompareF(aHost) == 0)
+            {                                     
+            // Remove from the list and push into the pipeline failed array
+            iProbablePipelineFailedHosts.Remove(*element);
+            iPipelineFailedHosts.Append(element->AcquireHost()); // No need to check the return code 
+                                                                     // as, a failure does not matter here.
+            delete element; // Delete the host element.
+            return;
+            }
+        }
+    // A new host has failed on the pipelining. Add into the list
+    element = CHttpHostElement::New(aHost);
+    if(element)
+        {        
+        iProbablePipelineFailedHosts.AddLast(*element);
+        }
+    
+    }
+
+
+
+
+	
+