applayerprotocols/httpexamples/httpexampleclient/httpexampleclient.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerprotocols/httpexamples/httpexampleclient/httpexampleclient.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1123 @@
+// Copyright (c) 2001-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 <uri8.h>
+#include <e32base.h>
+#include <http.h>
+#include <chttpformencoder.h>
+#include <ssl.h>
+#include <signed.h>
+#include "httpexampleclient.h"
+#include "httpexampleutils.h"
+ 
+
+// format for output of data/time values
+_LIT(KDateFormat,"%D%M%Y%/0%1%/1%2%/2%3%/3 %:0%H%:1%T%:2%S.%C%:3");
+
+// Format for hook file name and location for hooks
+_LIT(KHookFormat, "Z:\\httptest\\%S.esk");
+_LIT(KHookDir, "C:\\System\\Data\\");
+
+_LIT(KEnterProx, "Enter Proxy authority");
+_LIT(KEnterHook, "Enter Hook name (preface with '-' to delete)");
+_LIT(KEnterSessId, "Enter Session ID (positive integer or 'none')");
+
+
+/* 	If the file httpexampleclient.txt exists in the root directory of a drive
+	it will be used to provide default initial values for when setting the URL, 
+	proxy, etc. The file format is:
+		[var] = [value]
+	for example:
+		PROXY = my.proxy.co.uk:5473
+		URL = http://www.example.org/dir/file.html
+	whitespace is ignored.
+	
+	Note: these are the the values set at run time, they are merely the default
+	options presented to the user. In the above example the proxy is not turned
+	on by default, but the string "my.proxy.co.uk:5473" is presetned as the default
+	option when the use hits "p", saving them the need to have to type it in. 
+	*/
+// Defaults
+_LIT(KDefaultFileFormat,"%c:\\httpexampleclient.txt");
+_LIT(KHookString, "HOOK");
+_LIT(KProxyString, "PROXY");
+_LIT(KURLString, "URL");
+_LIT(KSessionIdString, "SESSIONID");
+_LIT(KNone, "none");
+_LIT(KSchemeHttps, "https");
+
+
+
+// File system root
+_LIT(KFileSystemRoot,"C:\\");
+
+// Standard headers used by default
+_LIT8(KUserAgent, "HTTPExampleClient (1.0)");
+_LIT8(KAccept, "*/*");
+
+_LIT(KHttpExampleClientPanic, "HTTP-EC");
+
+enum THttpExampleClientPanics
+	{
+	EReqBodySumitBufferNotAllocated,
+	KBodyWithInvalidSize,
+	KCouldntNotifyBodyDataPart
+	};
+
+// Size of buffer used when submitting request bodies
+const TInt KMaxSubmitSize = 1024;
+const TInt KMaxHeaderNameLen = 32;
+const TInt KMaxHeaderValueLen = 128;
+
+
+//
+// Implementation of CHttpClient
+//
+
+
+// Supplied as the name of the test program to CHttpExampleUtils
+_LIT(KHttpExampleClientTestName, "HttpExample");
+
+CHttpClient::CHttpClient()
+	: iReqBodySubmitBufferPtr(0,0)
+	{
+	// Initial timestamp is time now
+	ResetTimeElapsed();
+	}
+
+
+CHttpClient::~CHttpClient()
+	{
+	iSess.Close();
+	iFileServ.Close();
+	delete iReqBodySubmitBuffer;
+	delete iTransObs;
+	delete iFormEncoder;
+	delete iUtils;
+	}
+
+CHttpClient* CHttpClient::NewLC()
+	{
+	CHttpClient* me = new(ELeave) CHttpClient;
+	CleanupStack::PushL(me);
+	me->ConstructL();
+	return me;
+	}
+
+CHttpClient* CHttpClient::NewL()
+	{
+	CHttpClient* me = NewLC();
+	CleanupStack::Pop(me);
+	return me;
+	}
+
+void CHttpClient::ConstructL()
+	{
+	iUtils = CHttpExampleUtils::NewL(KHttpExampleClientTestName);
+	
+	// Open the RHTTPSession
+	iSess.OpenL();
+
+	// Install this class as the callback for authentication requests
+	InstallAuthenticationL(iSess);
+	
+	User::LeaveIfError(iFileServ.Connect());
+	iTransObs = CHttpEventHandler::NewL(*iUtils);
+	}
+
+
+TBool CHttpClient::ClearHook(TPtrC aHook)
+	{
+	TInt err = KErrNone;
+	if(aHook.Length()!=0)
+		{
+		err=iFileServ.Delete(aHook);
+		}
+	else if(iHookFileName.Length()>0) 
+		{
+		err=iFileServ.Delete(iHookFileName);
+		if(err==KErrNone) 
+			{
+			iHookFileName.Zero();
+			}
+		}
+	if(err == KErrNone)
+		{
+		User::InfoPrint(_L("Hook revmoved. Need to restart "));
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+// return true if need to quit
+void CHttpClient::SetHookL(TDesC& aHook)
+	{
+	iHookFileName.Format(KHookFormat, &aHook);
+	CFileMan* fileMan = CFileMan::NewL(iFileServ);
+	CleanupStack::PushL(fileMan);
+	TInt err = fileMan->Copy( iHookFileName, KHookDir, CFileMan::EOverWrite );
+	if( err != KErrNone && err != KErrNotFound) 
+		{
+		User::Leave(err);
+		}
+	CleanupStack::PopAndDestroy(fileMan);
+	User::InfoPrint(_L("Hook installed. Need to restart "));
+	}
+
+// to avoid code bloat
+#define CHECKVAL(name)  if(var==K ##name##String)	{a##name.Copy(val);}
+
+void CHttpClient::SetDefaults(TDes& aURL, TDes& aProxy, TDes& aHook, TDes& aSessionId)
+	{
+	TFileName filename;
+	RFile file;
+	filename.Format(KDefaultFileFormat, 'C');
+	TInt err = file.Open(iFileServ, filename, EFileRead);
+	if(err != KErrNone) 
+		{
+		filename.Format(KDefaultFileFormat, 'Z');
+		err = file.Open(iFileServ, filename, EFileRead);
+		}
+	if(err == KErrNone) 
+		{
+		TFileText line;
+		line.Set(file);
+		TInt err = line.Read(filename);
+		while(err == KErrNone || err ==KErrTooBig) 
+			{
+			filename.Trim();
+			TInt div = filename.Locate('=');
+			if(div>0) 
+				{
+				TInt i;
+				for (i=div-1;i>0 && filename[i]==' ';i--);	// ibid
+				TPtrC var = filename.Left(i+1);
+				for (i=div+1;i<filename.Length() && filename[i]==' ';i++);	//ibid
+				TPtrC val = filename.Right(filename.Length()-i);
+				CHECKVAL(Hook)
+				else CHECKVAL(Proxy)
+				else CHECKVAL(URL)
+				else CHECKVAL(SessionId);	
+				}
+			err = line.Read(filename);
+			}
+		}
+		file.Close();
+	}
+
+
+void CHttpClient::StartClientL()
+	{
+
+	ResetTimeElapsed();
+	TBuf<256> url;
+	TBuf<256> prox;
+	TBuf<256> hook;
+	TBuf<16> sessid;
+	SetDefaults(url, prox, hook, sessid);
+	
+	RStringPool strP = iSess.StringPool();
+
+	// Repeat until user selects quit
+	TBool done = EFalse;
+	while (!done)
+		{
+		// Get user command from menu
+		iUtils->Test().Console()->ClearScreen();
+		_LIT(KSelectOption, " Select an option \n\n");
+		iUtils->Test().Printf(KSelectOption);
+		_LIT(KPossibleSelectionsText, " 1 Get \n 2 Post \n 3 Head \n 4 Trace \n 5 Toggle Verbose \n p Set Proxy\n s Set Session ID\n h Set Hook\n 6 Quit \n");
+		_LIT(KPossibleSelections,"123456psh");
+		TInt selection = iUtils->GetSelection(KPossibleSelectionsText, KPossibleSelections);
+		RStringF method;
+		iHasARequestBody = EFalse;
+
+
+		// Set the method to use and ask for a url
+		switch (selection)
+			{
+		case EGet: 
+			method = strP.StringF(HTTP::EGET,RHTTPSession::GetTable());
+			break;
+		case EPost:
+			method = strP.StringF(HTTP::EPOST,RHTTPSession::GetTable());
+			iHasARequestBody = ETrue; 
+			break;
+		case EHead: 
+			method = strP.StringF(HTTP::EHEAD,RHTTPSession::GetTable()); 
+			break;
+		case ETrace:
+			method = strP.StringF(HTTP::ETRACE,RHTTPSession::GetTable()); 
+			break;
+		case EToggleVerbosity: 
+			{
+			TBool verbose = iTransObs->Verbose();
+			iTransObs->SetVerbose(!verbose);
+			if (!verbose)
+				User::InfoPrint(_L("Verbosity is ON "));
+			else
+				User::InfoPrint(_L("Verbosity is OFF"));
+			}
+			break;
+		case EQuit:
+			done = ETrue;
+			break;
+		case EHook:
+			iUtils->GetAnEntry(KEnterHook, hook);
+			if(hook.Length() > 0) 
+				{		
+				if(hook[0]=='-')
+					{
+					hook.Replace(0,1,KHookDir);
+					hook.Append(_L(".esk"));
+					done = ClearHook(hook);
+					}
+				else 
+					{
+					SetHookL(hook);
+					done = ETrue;
+					}
+				}
+			break;
+		case ESession:
+			iUtils->GetAnEntry(KEnterSessId, sessid);
+			if(sessid.Length() > 0) 
+				{
+				TLex number(sessid);
+				TInt val;
+				if(number.Val(val)==KErrNone)
+					{
+					iSess.ConnectionInfo().SetPropertyL(strP.StringF(HTTP::ESessionId,RHTTPSession::GetTable()), val);
+					}
+				else if (sessid==KNone) 
+					{				
+					iSess.ConnectionInfo().RemoveProperty(strP.StringF(HTTP::ESessionId,RHTTPSession::GetTable()));
+					}
+				}
+			break;
+		case EProxy:
+			iUtils->GetAnEntry(KEnterProx, prox);
+			if(prox.Length() > 0) 
+				{
+			  	  iSess.ConnectionInfo().SetPropertyL(iSess.StringPool().StringF(HTTP::EProxyUsage,RHTTPSession::GetTable()), iSess.StringPool().StringF(HTTP::EUseProxy,RHTTPSession::GetTable()));
+				TBuf8<256> prox8;
+				prox8.Copy(prox);
+				 RStringF proxy = iSess.StringPool().OpenFStringL(prox8);
+				CleanupClosePushL(proxy);
+				 iSess.ConnectionInfo().SetPropertyL(iSess.StringPool().StringF(HTTP::EProxyAddress,RHTTPSession::GetTable()), proxy);
+				CleanupStack::PopAndDestroy(&proxy);
+				}
+			else
+				{
+			 	iSess.ConnectionInfo().SetPropertyL(iSess.StringPool().StringF(HTTP::EProxyUsage,RHTTPSession::GetTable()), iSess.StringPool().StringF(HTTP::EDoNotUseProxy,RHTTPSession::GetTable()));
+				}
+			break;
+		default: 
+			 break;
+			} 
+
+		if (!done && (selection >= 0) && (selection <= 3))
+			{
+			// Get the URL
+			_LIT(KEnterUrl, "Enter Url");
+			iUtils->GetAnEntry(KEnterUrl, url);
+			if (url.Length() > 0) // back to main menu if we still have an empty URL
+				{
+				// Get a filename to submit as our request body, for methods that need one
+				if (iHasARequestBody)
+					{
+					GetRequestBodyL(method);
+					}
+
+				// Start the method off
+				TBuf8<256> url8;
+				url8.Copy(url);
+				InvokeHttpMethodL(url8, method);
+				}
+			else
+				{
+				_LIT(KEmptyUrlErrorText, "You must supply a non empty url");
+				iUtils->Test().Printf(KEmptyUrlErrorText);
+				User::After(1000000);
+				}
+			}
+
+		} // while
+
+	// End of tests
+	DisplayTimeElapsed();
+	iUtils->PressAnyKey();
+}
+
+/** Get the post body data from the user. 
+The user supplies name and value pairs. These are then encoded as added by CHTTPFormEncoder which acts as a data supplier
+for this request. This is typically how a form body for a html form submission that uses POST might be constructed.
+*/
+void CHttpClient::GetPostBodyManuallyL()
+	{
+	if (iFormEncoder)
+		{
+		delete iFormEncoder;
+		iFormEncoder = NULL;
+		}
+
+	iFormEncoder = CHTTPFormEncoder::NewL(); 
+	TBuf<256> name;
+	TBuf<256> value;
+	TBuf8<256> name8;
+	TBuf8<256> value8;
+	
+	_LIT(KGetPostName, "Enter Name (END to finish)");
+	_LIT(KGetPostValue, "Enter Value ");
+	_LIT(KEnd, "END");
+	do
+		{
+		iUtils->GetAnEntry(KGetPostName, name);
+		if (name.CompareF(KEnd) != 0)
+			{
+			iUtils->GetAnEntry(KGetPostValue, value);
+			name8.Copy(name);
+			value8.Copy(value);
+			iFormEncoder->AddFieldL(name8, value8);
+			}
+		}
+		while (name.CompareF(KEnd) != 0);
+	}
+
+
+/** Gets the body that you wish to submit when using a POST Method
+This can be supplied as a file or can be input manually in name,value pairs
+*/
+void CHttpClient::GetRequestBodyL(RStringF& aMethod)
+	{
+	if (aMethod== iSess.StringPool().StringF(HTTP::EPOST,RHTTPSession::GetTable()))
+		{
+		// get a post body by file or contruct manually?
+		_LIT(KConstructPostManually, "\n> Construct Post Manually?  Yes | No ");
+		_LIT(KYesNo, "YyNn");
+		TInt selection = iUtils->GetSelection(KConstructPostManually,KYesNo);
+		if (selection < 2) // 2 is NO
+			{
+			iManualPost = ETrue;
+			GetPostBodyManuallyL();
+			return;
+			}
+		// else carry on as usual and get post data from a file
+		}
+
+	iManualPost = EFalse;
+	_LIT(KRequestPathPrompt, "Enter path to request body file: ");
+	iUtils->GetAnEntry(KRequestPathPrompt, iReqBodyFileName);
+	_LIT(KRequestBodyContentType, "Enter request body content-type: ");
+	iUtils->GetAnEntry(KRequestBodyContentType, iReqBodyContentType);
+	iParsedFileName.Set(KFileSystemRoot,&iReqBodyFileName,NULL);
+
+	// Check it exists and open a file handle
+	if (iFileServ.IsValidName(iReqBodyFileName))
+		{
+		TInt err= iReqBodyFile.Open(iFileServ, iParsedFileName.FullName(), EFileRead);
+		if(err!=KErrNone)
+		   {
+		   _LIT(KFileNotOpen, "Unable to open a file.\n");
+		   iUtils->Test().Printf(KFileNotOpen);
+		   User::Leave(KErrNotFound);			
+		   } 
+		delete iReqBodySubmitBuffer;
+		iReqBodySubmitBuffer = NULL;
+		iReqBodySubmitBuffer = HBufC8::NewMaxL(KMaxSubmitSize);
+		iReqBodySubmitBufferPtr.Set(iReqBodySubmitBuffer->Des());
+		}
+	else
+		{
+		_LIT(KFileNameNotValid, "The specified filename is not valid!.\n");
+		iUtils->Test().Printf(KFileNameNotValid);
+		User::Leave(KErrBadName);
+		}
+	}
+
+TBool CHttpClient::GetNextDataPart(TPtrC8& aDataPart)
+	{
+	__ASSERT_DEBUG(iReqBodySubmitBuffer, User::Panic(KHttpExampleClientPanic, EReqBodySumitBufferNotAllocated));
+	// Read from the request body file
+	iNoMoreDate = EFalse;
+	TInt err = iReqBodyFile.Read(iReqBodySubmitBufferPtr);
+	if (err == KErrNone)
+		{
+		aDataPart.Set(iReqBodySubmitBufferPtr);
+		++iDataChunkCount;
+		iNoMoreDate = (iReqBodySubmitBufferPtr.Length() == 0);
+		}
+	return iNoMoreDate;
+	}
+
+void CHttpClient::ReleaseData()
+	{
+	// Clear out the submit buffer
+	TPtr8 buff = iReqBodySubmitBuffer->Des();
+	buff.Zero();
+	if (iNoMoreDate==EFalse)
+		{
+		// Notify HTTP of more data available immediately, since it's being read from file
+		TRAPD(err, iTrans.NotifyNewRequestBodyPartL());
+		if (err != KErrNone)
+			User::Panic(KHttpExampleClientPanic, KCouldntNotifyBodyDataPart);
+		}
+	}
+
+TInt CHttpClient::OverallDataSize()
+	{
+	TInt size = 0;
+	TInt err = iReqBodyFile.Size(size);
+	if (err < 0)
+		User::Panic(KHttpExampleClientPanic,KBodyWithInvalidSize);
+
+	return size;
+	}
+
+TInt CHttpClient::Reset()
+  {
+  if (iHasARequestBody)
+    {
+    // Reset to beginning of file
+	TInt pos = 0;
+    iReqBodyFile.Seek(ESeekStart, pos);
+    return KErrNone;
+    }
+  else
+    {
+    return KErrNotSupported;
+    }
+ } 
+
+/** Invoke the http method
+This actually creates the transaction, sets the headers and body and then starts the transaction 
+*/
+void CHttpClient::InvokeHttpMethodL(const TDesC8& aUri, RStringF aMethod)
+	{
+	// Set the protocol, before the first transaction gets started
+/*	if (iUseWspProtocol)
+		{
+		RHTTPConnectionInfo ci = iSess.ConnectionInfo();
+		ci.SetPropertyL(iSess.StringPool().StringF(HTTP::EProtocol,RHTTPSession::GetTable()),
+						THTTPHdrVal(iSess.StringPool().StringF(HTTP::EWSP,RHTTPSession::GetTable())));
+		}*/
+
+	iDataChunkCount = 0;
+	TUriParser8 uri; 
+	uri.Parse( aUri );
+	HBufC* scheme = uri.DisplayFormL( EUriScheme );
+	
+	iTransObs->SetSecuredHttp( !scheme->CompareF( KSchemeHttps ) );
+	delete scheme;
+	
+	iTrans = iSess.OpenTransactionL(uri, *iTransObs, aMethod);
+	RHTTPHeaders hdr = iTrans.Request().GetHeaderCollection();
+
+	// Add headers appropriate to all methods
+	SetHeaderL(hdr, HTTP::EUserAgent, KUserAgent);
+	SetHeaderL(hdr, HTTP::EAccept, KAccept);
+
+	// Add headers and body data for methods that use request bodies
+	if (iHasARequestBody)
+		{
+		// Content type header
+		TBuf8<KMaxContentTypeSize> contTypeBuf;
+		contTypeBuf.Copy(iReqBodyContentType);
+		RStringF contTypeStr = iSess.StringPool().OpenFStringL(contTypeBuf);
+		CleanupClosePushL(contTypeStr);
+		THTTPHdrVal contType(contTypeStr);
+		hdr.SetFieldL(iSess.StringPool().StringF(HTTP::EContentType,RHTTPSession::GetTable()), contType);
+		CleanupStack::PopAndDestroy(&contTypeStr);
+		
+		MHTTPDataSupplier* dataSupplier = this;
+		if (iManualPost)
+			dataSupplier = iFormEncoder;
+		iTrans.Request().SetBody(*dataSupplier);
+		}
+    
+	/*
+	//Set TimeOut Values
+	RStringF sendTimeOutProp = iSess.StringPool().StringF(HTTP::ESendTimeOutValue, iSess.GetTable());
+	THTTPHdrVal sendTimeOutPropValue;
+	sendTimeOutPropValue.SetInt(10);
+	iTrans.PropertySet().SetPropertyL(sendTimeOutProp, sendTimeOutPropValue);
+ 
+	RStringF recvTimeOutProp = iSess.StringPool().StringF(HTTP::EReceiveTimeOutValue, iSess.GetTable());
+	THTTPHdrVal recvTimeOutPropValue;
+	recvTimeOutPropValue.SetInt(10);
+	iTrans.PropertySet().SetPropertyL(recvTimeOutProp, recvTimeOutPropValue);
+	*/
+
+	// submit the transaction
+	iTrans.SubmitL();
+
+	// Start the scheduler, once the transaction completes or is cancelled on an error the scheduler will be
+	// stopped in the event handler
+	CActiveScheduler::Start();
+
+	// all done
+	iUtils->PressAnyKey();
+
+	// close the request body file, if one was opened
+	if (iHasARequestBody)
+		iReqBodyFile.Close();
+	}
+
+void CHttpClient::SetHeaderL(RHTTPHeaders aHeaders, TInt aHdrField, const TDesC8& aHdrValue)
+	{
+	RStringF valStr = iSess.StringPool().OpenFStringL(aHdrValue);
+	CleanupClosePushL(valStr);
+	THTTPHdrVal val(valStr);
+	aHeaders.SetFieldL(iSess.StringPool().StringF(aHdrField,RHTTPSession::GetTable()), val);
+	CleanupStack::PopAndDestroy(&valStr); 
+	}
+
+void CHttpClient::ResetTimeElapsed()
+// Resets timestamp to time now
+	{
+	iLastTimeStamp.UniversalTime(); 
+	}
+
+
+void CHttpClient::DisplayTimeElapsed()
+// Calculate elapsed time since last measurement, and display
+	{
+	TTime timeNow;
+	timeNow.UniversalTime();
+	TTimeIntervalMicroSeconds elapsedMicroSec =
+									timeNow.MicroSecondsFrom(iLastTimeStamp);
+	iLastTimeStamp = timeNow;
+	iUtils->Test().Printf(
+		_L("Time elapsed since last measurement is: %d ms\n"),
+		elapsedMicroSec.Int64()/1000
+		);
+	}
+
+
+/** Called when a authenticated page is requested
+Asks the user for a username and password that would be appropriate for the url that was
+supplied.
+*/
+TBool CHttpClient::GetCredentialsL(const TUriC8& aURI, RString aRealm, 
+								   RStringF aAuthenticationType,
+								   RString& aUsername, 
+								   RString& aPassword)
+
+	{
+	TBuf<KMaxUserEntrySize> scratch;
+	TBuf8<KMaxUserEntrySize> scratch8;
+
+	// Convert to 16 bit to display
+	HBufC* uriDesBuf = HBufC::NewLC(aURI.UriDes().Length());
+	TPtr uriDesPtr = uriDesBuf->Des();
+	uriDesPtr.Copy(aURI.UriDes());
+
+	HBufC* uriRealmBuf = HBufC::NewLC(aRealm.DesC().Length());
+	TPtr uriRealmPtr = uriRealmBuf->Des();
+	uriRealmPtr.Copy(aRealm.DesC());
+
+	// Prompt user for credentials.
+	iUtils->Test().Printf(_L("Enter credentials for URL %S, realm %S\n"), &uriDesPtr, & uriRealmPtr);
+
+	CleanupStack::PopAndDestroy(2, uriDesBuf);
+
+	scratch.Copy(aAuthenticationType.DesC());
+	iUtils->Test().Printf(_L("Using %S authentication\n"), &scratch);
+	iUtils->GetAnEntry(_L("Username (or QUIT to give up): "), scratch);
+	scratch8.Copy(scratch);
+	if (scratch8.CompareF(_L8("quit")))
+		{
+		TRAPD(err, aUsername = aRealm.Pool().OpenStringL(scratch8));
+		if (!err)
+			{
+			iUtils->GetAnEntry(_L("Password: "), scratch);
+			scratch8.Copy(scratch);
+			TRAP(err, aPassword = aRealm.Pool().OpenStringL(scratch8));
+			if (!err)
+				return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+
+//
+// Implementation of class CHttpEventHandler
+//
+
+void CHttpEventHandler::ConstructL()
+	{
+	User::LeaveIfError(iFileServ.Connect());
+	}
+
+
+CHttpEventHandler::CHttpEventHandler(CHttpExampleUtils& aUtils) :
+	iUtils(aUtils)
+	{
+	}
+
+
+CHttpEventHandler::~CHttpEventHandler()
+	{
+	iFileServ.Close();
+	}
+
+
+CHttpEventHandler* CHttpEventHandler::NewLC(CHttpExampleUtils& aUtils)
+	{
+	CHttpEventHandler* me = new(ELeave)CHttpEventHandler(aUtils);
+	CleanupStack::PushL(me);
+	me->ConstructL();
+	return me;
+	}
+
+
+CHttpEventHandler* CHttpEventHandler::NewL(CHttpExampleUtils& aUtils)
+	{
+	CHttpEventHandler* me = NewLC(aUtils);
+	CleanupStack::Pop(me);
+	return me;
+	}
+
+
+void CHttpEventHandler::SetVerbose(TBool aVerbose)
+	{
+	iVerbose = aVerbose;
+	}
+
+
+TBool CHttpEventHandler::Verbose() const
+	{
+	return iVerbose;
+	}
+	
+void CHttpEventHandler::SetSecuredHttp( TBool aSecuredHttp )
+	{
+	iSecuredHttp = aSecuredHttp;
+	}
+
+
+TBool CHttpEventHandler::SecuredHttp() const
+	{
+	return iSecuredHttp;
+	}
+
+
+void CHttpEventHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
+	{
+	switch (aEvent.iStatus)
+		{
+		case THTTPEvent::EGotResponseHeaders:
+			{
+			// HTTP response headers have been received. We can determine now if there is
+			// going to be a response body to save.
+			RHTTPResponse resp = aTransaction.Response();
+			TInt status = resp.StatusCode();
+			RStringF statusStr = resp.StatusText();
+			TBuf<32> statusStr16;
+			statusStr16.Copy(statusStr.DesC());
+			iUtils.Test().Printf(_L("Status: %d (%S)\n"), status, &statusStr16);
+
+			// Dump the headers if we're being verbose
+			if (iVerbose)
+			{
+				DumpRespHeadersL(aTransaction);
+				
+				if ( iSecuredHttp )
+					{
+					CheckCertificatesL( aTransaction );
+					}
+			}
+
+			// Determine if the body will be saved to disk
+			iSavingResponseBody = EFalse;
+			TBool cancelling = EFalse;
+			if (resp.HasBody() && (status >= 200) && (status < 300) && (status != 204))
+				{
+				TInt dataSize = resp.Body()->OverallDataSize();
+				if (dataSize >= 0)
+					iUtils.Test().Printf(_L("Response body size is %d\n"), dataSize);
+				else
+					iUtils.Test().Printf(_L("Response body size is unknown\n"));
+
+				TInt selection = iUtils.GetSelection(_L("\n> Save response to disk? Yes | No | Cancel"), _L("YyNnCc"));
+				iSavingResponseBody = (selection < 2);
+				cancelling = (selection == 4) || (selection == 5);
+				}
+
+			// If we're cancelling, must do it now..
+			if (cancelling)
+				{
+				iUtils.Test().Printf(_L("\nTransaction Cancelled\n"));
+				aTransaction.Close();
+				CActiveScheduler::Stop();
+				}
+			else if (iSavingResponseBody) // If we're saving, then open a file handle for the new file
+				{
+				iUtils.GetAnEntry(_L("Enter filename including path to save response body"), iRespBodyFileName);
+				iParsedFileName.Set(KFileSystemRoot,&iRespBodyFileName,NULL);
+
+				// Check it exists and open a file handle
+				TInt valid = iFileServ.IsValidName(iRespBodyFileName);
+				if (!valid)
+					{
+					iUtils.Test().Printf(_L("The specified filename is not valid!.\n"));
+					iSavingResponseBody = EFalse;
+					}
+				else
+					{
+					TInt err = iRespBodyFile.Replace(iFileServ,
+													 iParsedFileName.FullName(),
+													 EFileWrite|EFileShareExclusive);
+					if (err)
+						{
+						iSavingResponseBody = EFalse;
+						User::Leave(err);
+						}
+					}
+				}
+
+			} break;
+		case THTTPEvent::EGotResponseBodyData:
+			{
+			// Get the body data supplier
+			iRespBody = aTransaction.Response().Body();
+
+			// Some (more) body data has been received (in the HTTP response)
+			if (iVerbose)
+				DumpRespBody(aTransaction);
+			else
+				iUtils.Test().Printf(_L("*"));
+
+			// Append to the output file if we're saving responses
+			if (iSavingResponseBody)
+				{
+				TPtrC8 bodyData;
+				TBool lastChunk = iRespBody->GetNextDataPart(bodyData);
+				iRespBodyFile.Write(bodyData);
+				if (lastChunk)
+					iRespBodyFile.Close();
+				}
+
+			// Done with that bit of body data
+			iRespBody->ReleaseData();
+			} break;
+		case THTTPEvent::EResponseComplete:
+			{
+			// The transaction's response is complete
+			iUtils.Test().Printf(_L("\nTransaction Complete\n"));
+			} break;
+		case THTTPEvent::ESucceeded:
+			{
+			iUtils.Test().Printf(_L("Transaction Successful\n"));
+			aTransaction.Close();
+			CActiveScheduler::Stop();
+			} break;
+		case THTTPEvent::EFailed:
+			{
+			iUtils.Test().Printf(_L("Transaction Failed\n"));
+			aTransaction.Close();
+			CActiveScheduler::Stop();
+			} break;
+		case THTTPEvent::ERedirectedPermanently:
+			{
+			iUtils.Test().Printf(_L("Permanent Redirection\n"));
+			} break;
+		case THTTPEvent::ERedirectedTemporarily:
+			{
+			iUtils.Test().Printf(_L("Temporary Redirection\n"));
+			} break;
+		case THTTPEvent::ERedirectRequiresConfirmation:
+ 			{
+			// 301(Moved Permanently), 302(Found) or 307(Temporary Redirect) status is received 
+			// from a transaction and hence ERedirectRequiresConfirmation is sent by filter
+			// client has opted to close the transaction
+			iUtils.Test().Printf(_L("Redirect requires confirmation\n"));
+ 			aTransaction.Close();
+ 			CActiveScheduler::Stop();
+ 			} break;
+	    case THTTPEvent::EReceiveTimeOut:
+			{
+			iUtils.Test().Printf(_L("Receive TimeOut\n"));
+			}break;
+		case THTTPEvent::ESendTimeOut:
+			{
+			iUtils.Test().Printf(_L("Send TimeOut\n"));
+			}break;
+		default:
+			{
+			iUtils.Test().Printf(_L("<unrecognised event: %d>\n"), aEvent.iStatus);
+			// close off the transaction if it's an error
+			if (aEvent.iStatus < 0)
+				{
+				aTransaction.Close();
+				CActiveScheduler::Stop();
+				}
+			} break;
+		}
+	}
+
+TInt CHttpEventHandler::MHFRunError(TInt aError, RHTTPTransaction /*aTransaction*/, const THTTPEvent& /*aEvent*/)
+	{
+	iUtils.Test().Printf(_L("MHFRunError fired with error code %d\n"), aError);
+
+	return KErrNone;
+	}
+
+
+void CHttpEventHandler::DumpRespHeadersL(RHTTPTransaction& aTrans)
+	{
+	RHTTPResponse resp = aTrans.Response();
+	RStringPool strP = aTrans.Session().StringPool();
+	RHTTPHeaders hdr = resp.GetHeaderCollection();
+	THTTPHdrFieldIter it = hdr.Fields();
+
+	TBuf<KMaxHeaderNameLen>  fieldName16;
+	TBuf<KMaxHeaderValueLen> fieldVal16;
+
+	while (it.AtEnd() == EFalse)
+		{
+		RStringTokenF fieldName = it();
+		RStringF fieldNameStr = strP.StringF(fieldName);
+		THTTPHdrVal fieldVal;
+		if (hdr.GetField(fieldNameStr,0,fieldVal) == KErrNone)
+			{
+			const TDesC8& fieldNameDesC = fieldNameStr.DesC();
+			fieldName16.Copy(fieldNameDesC.Left(KMaxHeaderNameLen));
+			switch (fieldVal.Type())
+				{
+			case THTTPHdrVal::KTIntVal:
+				iUtils.Test().Printf(_L("%S: %d\n"), &fieldName16, fieldVal.Int());
+				break;
+			case THTTPHdrVal::KStrFVal:
+				{
+				RStringF fieldValStr = strP.StringF(fieldVal.StrF());
+				const TDesC8& fieldValDesC = fieldValStr.DesC();
+				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
+				iUtils.Test().Printf(_L("%S: %S\n"), &fieldName16, &fieldVal16);
+				}
+				break;
+			case THTTPHdrVal::KStrVal:
+				{
+				RString fieldValStr = strP.String(fieldVal.Str());
+				const TDesC8& fieldValDesC = fieldValStr.DesC();
+				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
+				iUtils.Test().Printf(_L("%S: %S\n"), &fieldName16, &fieldVal16);
+				}
+				break;
+			case THTTPHdrVal::KDateVal:
+				{
+				TDateTime date = fieldVal.DateTime();
+				TBuf<40> dateTimeString;
+				TTime t(date);
+				t.FormatL(dateTimeString,KDateFormat);
+
+				iUtils.Test().Printf(_L("%S: %S\n"), &fieldName16, &dateTimeString);
+				} 
+				break;
+			default:
+				iUtils.Test().Printf(_L("%S: <unrecognised value type>\n"), &fieldName16);
+				break;
+				}
+
+			// Display realm for WWW-Authenticate header
+			RStringF wwwAuth = strP.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable());
+			if (fieldNameStr == wwwAuth)
+				{
+				// check the auth scheme is 'basic'
+				RStringF basic = strP.StringF(HTTP::EBasic,RHTTPSession::GetTable());
+				RStringF realm = strP.StringF(HTTP::ERealm,RHTTPSession::GetTable());
+				THTTPHdrVal realmVal;
+				if ((fieldVal.StrF() == basic) && 
+					(!hdr.GetParam(wwwAuth, realm, realmVal)))
+					{
+					RStringF realmValStr = strP.StringF(realmVal.StrF());
+					fieldVal16.Copy(realmValStr.DesC());
+					iUtils.Test().Printf(_L("Realm is: %S\n"), &fieldVal16);
+					}
+				}
+			}
+		++it;
+		}
+	}
+
+void CHttpEventHandler::DumpRespBody(RHTTPTransaction& aTrans)
+	{
+	MHTTPDataSupplier* body = aTrans.Response().Body();
+	TPtrC8 dataChunk;
+	TBool isLast = body->GetNextDataPart(dataChunk);
+	DumpIt(dataChunk);
+	if (isLast)
+		iUtils.Test().Printf(_L("Got last data chunk.\n"));
+	}
+
+
+void CHttpEventHandler::DumpIt(const TDesC8& aData)
+//Do a formatted dump of binary data
+	{
+	// Iterate the supplied block of data in blocks of cols=80 bytes
+	const TInt cols=16;
+	TInt pos = 0;
+	TBuf<KMaxFileName - 2> logLine;
+	TBuf<KMaxFileName - 2> anEntry;
+	const TInt dataLength = aData.Length();
+	while (pos < dataLength)
+		{
+		//start-line hexadecimal( a 4 digit number)
+		anEntry.Format(TRefByValue<const TDesC>_L("%04x : "), pos);
+		logLine.Append(anEntry);
+
+		// Hex output
+		TInt offset;
+		for (offset = 0; offset < cols; ++offset)
+			{
+			if (pos + offset < aData.Length())
+				{
+				TInt nextByte = aData[pos + offset];
+				anEntry.Format(TRefByValue<const TDesC>_L("%02x "), nextByte);
+				logLine.Append(anEntry);
+				}
+			else
+				{
+				//fill the remaining spaces with blanks untill the cols-th Hex number 
+				anEntry.Format(TRefByValue<const TDesC>_L("   "));
+				logLine.Append(anEntry);
+				}
+			}
+			anEntry.Format(TRefByValue<const TDesC>_L(": "));
+			logLine.Append(anEntry);
+
+		// Char output
+		for (offset = 0; offset < cols; ++offset)
+			{
+			if (pos + offset < aData.Length())
+				{
+				TInt nextByte = aData[pos + offset];
+				if ((nextByte >= ' ') && (nextByte <= '~'))
+					{
+					anEntry.Format(TRefByValue<const TDesC>_L("%c"), nextByte);
+					logLine.Append(anEntry);
+					}
+				else
+					{
+					anEntry.Format(TRefByValue<const TDesC>_L("."));
+					logLine.Append(anEntry);
+					}
+				}
+			else
+				{
+				anEntry.Format(TRefByValue<const TDesC>_L(" "));
+				logLine.Append(anEntry);
+				}
+			}
+			iUtils.Test().Printf(TRefByValue<const TDesC>_L("%S\n"), &logLine);	
+			logLine.Zero();
+
+		// Advance to next  byte segment (1 seg= cols)
+		pos += cols;
+		}
+	}
+	
+	
+void CHttpEventHandler::CheckCertificatesL(RHTTPTransaction& aTrans)
+	{
+	
+	if ( aTrans.Session().ServerCert() )
+		{
+		iUtils.Test().Printf( _L("Session CCertificate found\n") );
+		
+		const CCertificate* sessTCert;
+		sessTCert = aTrans.ServerCert();
+		
+		if( sessTCert )
+			{
+			TPtrC8 serialNo8(sessTCert->SerialNumber());
+			TBuf<KMaxUserEntrySize> serialNo16;
+			serialNo16.Copy(serialNo8);
+			HBufC16* issuer(sessTCert->IssuerL());
+			iUtils.Test().Printf( _L("Serial No: %S, \n"),&serialNo16 );
+			iUtils.Test().Printf( _L("Issuer: %S, "),issuer );
+			delete issuer;
+			}
+		}
+	else
+		{
+		iUtils.Test().Printf( _L("Session CCertificate NULL\n") );
+		}
+		
+	if ( aTrans.ServerCert() )
+		{
+		iUtils.Test().Printf( _L("Transaction CCertificate found\n") );
+		const CCertificate* transTCert;
+		transTCert = aTrans.ServerCert();
+		
+		if( transTCert )
+			{
+			TPtrC8 serialNo8(transTCert->SerialNumber());
+			TBuf<KMaxUserEntrySize> serialNo16;
+			serialNo16.Copy(serialNo8);
+			HBufC16* issuer(transTCert->IssuerL());
+			iUtils.Test().Printf( _L("Serial No: %S, \n"),&serialNo16 );
+			iUtils.Test().Printf( _L("Issuer: %S, \n"),issuer );
+			delete issuer;
+			}
+		}
+	else
+		{
+		iUtils.Test().Printf( _L("Transaction CCertificate NULL\n") );
+		}
+	}
+
+
+//
+// Main implementation
+//
+
+LOCAL_D void TestL()
+// Create a test object, invoke the tests using it and remove
+	{
+	// Start C32 and initalize some device drivers. This is necessary when running a test console as these won't 
+	// have been started
+	CHttpExampleUtils::InitCommsL();
+
+	// create an active scheduler to use
+	CActiveScheduler* scheduler = new(ELeave) CActiveScheduler();
+	CleanupStack::PushL(scheduler);
+	CActiveScheduler::Install(scheduler);
+
+	// Create and start the client
+	CHttpClient* httpCli = CHttpClient::NewLC();
+	httpCli->StartClientL();
+
+	CleanupStack::PopAndDestroy(2); // httpCli, scheduler
+	}
+
+
+GLDEF_C TInt E32Main()
+// Main program - run the tests within a TRAP harness, reporting any errors that
+// occur via the panic mechanism. Test for memory leaks using heap marking.
+	{
+
+	__UHEAP_MARK;
+	CTrapCleanup* tc=CTrapCleanup::New();
+
+	TRAPD(err,TestL());
+	if (err!=KErrNone)
+		User::Panic(_L("Test failed with error code: %i"), err);
+	delete tc;
+	__UHEAP_MARKEND;
+	return KErrNone;
+	}
+