applayerpluginsandutils/httptransportplugins/httptransporthandler/csocketconnector.cpp
// 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 "csocketconnector.h"
#include "csocket.h"
#include "msocketconnectobserver.h"
#include "msocketconnectorstore.h"
#include "msocketcontrollerfactory.h"
#include "mcommsinfoprovider.h"
#include "csocketcontroller.h"
#include "httptransporthandlercommon.h"
#include "thttptrlayerpanic.h"
#if defined (_DEBUG) && defined (_LOGGING)
const TInt KHostNameSize = 126;
#endif
CSocketConnector* CSocketConnector::NewL(
MSocketConnectorStore& aStore,
MSocketControllerFactory& aSocketControllerFactory,
MCommsInfoProvider& aCommsInfoProvider, TBool aPriority
)
/**
The factory constructor.
@param aStore The socket connector store.
@param aSocketControllerFactory The socket controller factory.
@param aCommsInfoProvider The comms info provider.
@return A pointer to a fully constructed object.
*/
{
return new (ELeave) CSocketConnector(aStore, aSocketControllerFactory, aCommsInfoProvider,aPriority);
}
CSocketConnector::~CSocketConnector()
/**
Destructor.
*/
{
// Cancel any outstanding requests
Cancel();
// Cleanup...
delete iHost;
delete iConnectingSocket;
iHostResolver.Close();
// __FLOG_CLOSE;
}
CSocketConnector::CSocketConnector(
MSocketConnectorStore& aStore,
MSocketControllerFactory& aSocketControllerFactory,
MCommsInfoProvider& aCommsInfoProvider,
TBool aPriority
)
: CActive(aPriority ? CActive::EPriorityHigh : CActive::EPriorityStandard), iStore(aStore),
iSocketControllerFactory(aSocketControllerFactory),
iCommsInfoProvider(aCommsInfoProvider)
/**
Constructor.
@param aStore The socket connector store.
@param aSocketControllerFactory The socket controller factory.
@param aCommsInfoProvider The comms info provider.
*/ {
if(aPriority)
{
CActive::SetPriority(CActive::EPriorityHigh);
}
CActiveScheduler::Add(this);
// __FLOG_OPEN("http", "httptransporthandler.txt");
}
void CSocketConnector::ConnectL(MSocketConnectObserver& aObserver, const TDesC8& aRemoteHost, TUint16 aRemotePort, TInetAddr* aRemoteAddress)
/**
Start connection to specified remote host. The socket connector starts
connecting to the specified remote host on the specified port.
@param aRemoteHost The host name/IP address of the remote host
@param aRemotePort The port number of the remote host
@pre The socket connector is in the Idle state.
@post The socket connector is not in the Idle state.
*/
{
__ASSERT_DEBUG( iState == EIdle, THttpTrLayerPanic::Panic(THttpTrLayerPanic::EBadSocketConnectorState) );
// Set the observer
iObserver = &aObserver;
// Copy the remote host IP address and port
iHost = HBufC::NewL(aRemoteHost.Length());
iHost->Des().Copy(aRemoteHost);
iPort = aRemotePort;
// Move to the PendingDNSLookup state and self complete.
if(aRemoteAddress == NULL)
{
// Address is unknown / DNS lookup is needed
iState = EPendingDNSLookup;
}
else
{
// Address is know. No lookup is needed. Just go and connect.
iHostDnsEntry().iAddr = *aRemoteAddress;
iState = EConnecting;
}
CompleteSelf();
}
void CSocketConnector::CompleteSelf()
/**
Requests that the socket connector complete itself. This will caused the
RunL() to be called by the scheduler at the next opportunity.
@pre The socket connector object is not active.
@post The socket connector object is active and the request has been
completed.
*/
{
TRequestStatus* pStat = &iStatus;
User::RequestComplete(pStat, KErrNone);
SetActive();
}
void CSocketConnector::Suicide()
/**
The socket connector is finished, it needs to remove itself from the store
and self-destruct.
*/
{
// Inform store that connection is complete and then suicide.
iStore.ConnectionCompleted(*this);
delete this;
}
/*
* Methods from MSocketConnector
*/
void CSocketConnector::StopConnect()
/**
@see MSocketConnector
*/
{
// Cancel any outstanding requests.
Cancel();
#if defined (_DEBUG) && defined (_LOGGING)
TBuf8<KHostNameSize> host;
host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
__FLOG_0(_T8("!! Stopping connection"));
__FLOG_1(_T8("-> connect to host %S stopped"), &host);
#endif
Suicide();
}
void CSocketConnector::MSocketConnector_Reserved()
/**
@see MSocketConnector
*/
{
User::Invariant();
}
/*
* Methods from CActive
*/
void CSocketConnector::RunL()
/**
The request servicing function. Behaviour depends on the state of the socket
connector.
*/
{
// Leave if there has been an error
User::LeaveIfError(iStatus.Int());
switch( iState )
{
case EPendingDNSLookup:
{
#if defined (_DEBUG) && defined (_LOGGING)
TBuf8<KHostNameSize> host;
host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
__FLOG_1(_T8("Doing DNS lookup -> searching for host %S"), &host);
#endif
__OOM_LEAVE_TEST
if ( iCommsInfoProvider.HasConnection() )
{
// Open the host resolver session with the preffered connection
User::LeaveIfError(iHostResolver.Open(
iCommsInfoProvider.SocketServer(),
iCommsInfoProvider.ProtocolDescription().iAddrFamily,
KProtocolInetUdp,
iCommsInfoProvider.Connection()
));
}
else
{
// Open the host resolver session with no connection
User::LeaveIfError(iHostResolver.Open(
iCommsInfoProvider.SocketServer(),
iCommsInfoProvider.ProtocolDescription().iAddrFamily,
KProtocolInetUdp
));
}
// Start the DNS lookup for the remote host name.
iHostResolver.GetByName(*iHost, iHostDnsEntry, iStatus);
// Move to the Connecting state and go active
iState = EConnecting;
SetActive();
} break;
case EConnecting:
{
__OOM_LEAVE_TEST
// DNS lookup successful - form the internet address object
iAddress = TInetAddr(iHostDnsEntry().iAddr);
iAddress.SetPort(iPort);
#if defined (_DEBUG) && defined (_LOGGING)
TBuf8<KHostNameSize> host;
host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
TBuf<KIpv6MaxAddrSize> ip16bit;
iAddress.Output(ip16bit);
TBuf8<KIpv6MaxAddrSize> ip;
ip.Copy(ip16bit);
__FLOG_2(_T8("DNS lookup complete -> host %S has IP address %S"), &host, &ip);
#endif
// Start a default RConnection, if one is not started and not local loopback address
if ( !iCommsInfoProvider.HasConnection() && !iAddress.IsLoopback() )
{
iCommsInfoProvider.StartDefaultCommsConnectionL ();
}
// Create the connecting socket
iConnectingSocket = CSocket::NewL(iCommsInfoProvider, CSocket::EProtocolSocket);
// Start connecting to the remote client
iConnectingSocket->Connect(iAddress, iStatus);
__FLOG_2(_T8("Connecting -> to host %S on IP address %S"), &host, &ip);
// Move to the Connected state and go active
iState = EConnected;
SetActive();
} break;
case EConnected:
{
__OOM_LEAVE_TEST
#if defined (_DEBUG) && defined (_LOGGING)
TBuf8<KHostNameSize> host;
host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
TInetAddr addr;
iConnectingSocket->RemoteName(addr);
TBuf<KIpv6MaxAddrSize> ip16bit;
addr.Output(ip16bit);
TBuf8<KIpv6MaxAddrSize> ip;
ip.Copy(ip16bit);
TInetAddr local;
iConnectingSocket->LocalName(local);
__FLOG_1(_T8("!! Connection with host %S established"), &host);
__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), local.Port(), &ip, addr.Port());
#endif
// A connection has been made with the remote client - lose ownership of
// the connecting socket.
CSocket* connectedSocket = iConnectingSocket;
iConnectingSocket = NULL;
// Create a socket controller object to own the connected socket.
CSocketController* socketController = iSocketControllerFactory.CreateSocketControllerLC(connectedSocket, *iHost, iPort, iAddress);
// Inform the socket connect observer that a TCP connection is established -
// pass back the input and output stream objects.
iObserver->ConnectionMadeL(socketController->InputStream(), socketController->OutputStream());
// Remove socket controller from cleanup stack - transferring ownership
// to the store.
CleanupStack::Pop(socketController);
// Add the socket controller in the store - ownership is transferred to
// the store.
iSocketControllerFactory.AddToStoreL(socketController);
// Socket connector is finished - suicide.
Suicide();
} break;
case EIdle:
default:
THttpTrLayerPanic::Panic(THttpTrLayerPanic::EBadSocketConnectorState);
break;
}
}
void CSocketConnector::DoCancel()
/**
The asynchronous request cancel.
*/
{
// Check state
switch( iState )
{
case EConnecting:
{
// DNS lookup is pending - cancel
iHostResolver.Cancel();
} break;
case EConnected:
{
if( iConnectingSocket )
{
// Connection is pending - cancel and delete the socket
iConnectingSocket->CancelConnect();
delete iConnectingSocket;
iConnectingSocket = NULL;
}
} break;
case EIdle:
case EPendingDNSLookup:
default:
// Do nothing...
break;
}
}
TInt CSocketConnector::RunError(TInt aError)
/**
The error handler for when RunL() leaves. This function does any necessary
cleanup. The socket connector is then set to suicide.
@param aError The leave code.
@return A value of KErrNone.if the error has been handled, any other
value if it has not been handled.
@post The socket connector is in the Suicide state.
*/
{
#if defined (_DEBUG) && defined (_LOGGING)
TBuf8<KHostNameSize> host;
host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
__FLOG_1(_T8("!! Error : %d"), aError);
__FLOG_1(_T8("-> connect to host %S failed"), &host);
#endif
TInt errorToPropagate = aError;
TInt error = KErrNone;
switch( iState )
{
case EPendingDNSLookup:
case EConnecting:
{
// In EReadyForDNS or EReadyToConnect states, KErrNotReady may be
// returned by the comms call that require a connection. The RConnection
// that is passed in MUST be started or this error will occur. If we own
// the RConnection we should start the RConnection again, else if our
// client own the RConnection, we just propagate the Comms error.
if( iCommsInfoProvider.HasConnection() && iCommsInfoProvider.OwnsConnection() && aError == KErrNotReady )
{
__FLOG_0(_T8("-> re-starting comms interface..."));
// We own the RConnection and the error is KErrNotReady
errorToPropagate = iCommsInfoProvider.Connection().Start();
if( errorToPropagate == KErrNone )
{
__FLOG_0(_T8("!! Re-started comms interface"));
__FLOG_1(_T8("-> try connecting to host %S again"), &host);
// RConnection started successfully, try to connect again
CompleteSelf();
return KErrNone;
}
#if defined (_DEBUG) && defined (_LOGGING)
else
{
__FLOG_1(_T8("!! Error : %d"), errorToPropagate);
__FLOG_0(_T8("-> failed to re-start comms interface"));
__FLOG_1(_T8("-> connect to host %S failed"), &host);
}
#endif
}
// Opening the connecting socket has failed or the DNS lookup completed
// with an error code - suicide after notifying the observer - do nothing
// except drop through to the Connected state case...
}
case EConnected:
{
// Either the socket controller factory left in AddToStoreL() or in
// CreateSocketControllerLC(), or the observer left in ConnectionMadeL()
// or the connect request completed with an error code. Inform the
// observer of the error.
error = iObserver->HandleConnectError(errorToPropagate);
} break;
case EIdle:
default:
THttpTrLayerPanic::Panic(THttpTrLayerPanic::EBadSocketConnectorState);
break;
}
Suicide();
return error;
}