applayerpluginsandutils/httpprotocolplugins/httpclient/chttprequestcomposer.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpclient/chttprequestcomposer.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,510 @@
+// 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 "chttprequestcomposer.h"
+
+#include <httpstringconstants.h>
+#include <http/rhttpheaders.h>
+
+#include "chttpresponseparser.h"
+#include "mhttprequestobserver.h"
+#include "chttpclienttransaction.h"
+
+
+CHttpRequestComposer* CHttpRequestComposer::NewL(CProtTransaction& aProtTrans, MHttpRequestObserver& aObserver)
+	{
+	CHttpRequestComposer* self = new (ELeave) CHttpRequestComposer(aProtTrans, aObserver);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CHttpRequestComposer::~CHttpRequestComposer()
+	{
+    if(iCorkingEnabled)
+        {
+        // Disable corking.
+        iObserver.SendingBodyData(EFalse);
+        }   
+    
+	iMessageComposer.Close();
+	iTrailerHeaders.Reset();
+	
+//	__FLOG_CLOSE;
+	}
+	
+CHttpRequestComposer::CHttpRequestComposer(CProtTransaction& aProtTrans, MHttpRequestObserver& aObserver)
+: CTxData(aProtTrans), iStringTable(RHTTPSession::GetTable()), 
+                        iObserver(aObserver),
+					   iStringPool(aProtTrans.Transaction().Session().StringPool()),
+					   iRequest(aProtTrans.Transaction().Request()),
+					   iFields(aProtTrans.Transaction().Request().GetHeaderCollection().Fields()),
+					   iPropertySet(aProtTrans.Transaction().PropertySet())
+
+	{
+//	__FLOG_OPEN("http", "httpclienthandler.txt");
+	}
+	
+void CHttpRequestComposer::ConstructL()
+	{
+	iMessageComposer.OpenL(*this);
+	}
+	
+void CHttpRequestComposer::NotifyMoreRequestBodyData()
+	{
+	__FLOG_1(_T8("Trans %d : notify more request body data"), iProtTrans->Transaction().Id());
+
+	iMessageComposer.MessageInfoAvailable();
+	}
+
+void CHttpRequestComposer::InitHeadersL()
+	{
+	// Extract header fields from the Trailer header, if it exists.
+	THTTPHdrVal trailerHeaderVal;
+	RHTTPHeaders headers = iRequest.GetHeaderCollection();
+	RStringF trailerName = iStringPool.StringF(HTTP::ETrailer, iStringTable);
+	TInt index =0;
+	while( headers.GetField(
+						   trailerName,
+						   index,
+						   trailerHeaderVal
+						   ) != KErrNotFound )
+		{
+		__ASSERT_DEBUG( trailerHeaderVal.Type() == THTTPHdrVal::KStrFVal, User::Invariant() );
+
+		// @todo should we check to see if header is not Content-Length,
+		// @todo Transfer-Encoding or Trailer?
+
+		// Got a trailer header - append to the list
+		RStringF header = trailerHeaderVal.StrF();
+		User::LeaveIfError(iTrailerHeaders.Append(header));
+
+		// Next...
+		++index;
+		}
+
+	if( iTrailerHeaders.Count() > 0 )
+		{
+		// There are trailer headers - can only send as trailers if the request
+		// has a body and it is going to be chunk encoded.
+		MHTTPDataSupplier* body = iRequest.Body();
+
+		if( body == NULL || body->OverallDataSize() != KErrNotFound )
+			{
+			// Either there is no body, or the body is not going to be chunk
+			// encoded - remove the Trailer header field.
+			headers.RemoveField(trailerName);
+			iTrailerHeaders.Reset();
+			}
+		}
+
+	// Reset the fields iterator
+	iFields.First();	
+	}
+
+TBool CHttpRequestComposer::IsTrailerHeader(RStringF aHeader)
+	{
+	TInt count = iTrailerHeaders.Count();
+	TBool found = EFalse;	
+	while( count > 0 && !found )
+		{
+		if( iTrailerHeaders[--count] == aHeader )
+			{
+			// aHeader is a trailer header
+			found = ETrue;
+			}
+		}
+	return found;
+	}
+	
+/*
+ *	Methods from MHTTPDataSupplier, via CTxData
+ */
+
+TBool CHttpRequestComposer::GetNextDataPart(TPtrC8& /*aDataPart*/)
+	{
+	// @todo should we deprecate derivation from MHTTPDataSupplier?
+	User::Invariant();
+	return EFalse;
+	}
+
+void CHttpRequestComposer::ReleaseData()
+	{
+	// @todo should we deprecate derivation from MHTTPDataSupplier?
+	User::Invariant();
+	}
+
+TInt CHttpRequestComposer::OverallDataSize()
+	{
+	// @todo should we deprecate derivation from MHTTPDataSupplier?
+	User::Invariant();
+	return KErrNotFound;
+	}
+
+TInt CHttpRequestComposer::Reset()
+	{
+	// @todo should we deprecate derivation from MHTTPDataSupplier?
+	User::Invariant();
+	return KErrNotFound;
+	}
+	
+/*
+ *	Methods from MHttpRequest
+ */
+	
+void CHttpRequestComposer::StartRequest()
+	{
+	__FLOG_1(_T8("Trans %d : starting request"), iProtTrans->Transaction().Id());
+	
+	RStringF notifyContinue = iStringPool.StringF(HTTP::ENotify100Continue, iStringTable);
+	RStringF enableNotification = iStringPool.StringF(HTTP::EEnableNotification, iStringTable);
+	THTTPHdrVal notifyContinueVal;
+	iPropertySet.Property(notifyContinue, notifyContinueVal);
+	
+	((notifyContinueVal.Type()==THTTPHdrVal::KStrFVal) && (notifyContinueVal.StrF() == enableNotification))?(iSuspendRequest = ETrue):(iSuspendRequest = EFalse);
+
+	iMessageComposer.MessageInfoAvailable();
+	}
+
+void CHttpRequestComposer::CancelRequest()
+	{
+	__FLOG_1(_T8("-> Trans %d : request cancelled"), iProtTrans->Transaction().Id());
+
+	iMessageComposer.Reset();
+	}
+	
+void CHttpRequestComposer::RequestDataSent()
+	{	
+	if(!IsSuspendedRequest())
+		{
+		iMessageComposer.ReleaseMessageData();
+		}
+	}
+
+void CHttpRequestComposer::OnRequestSendTimeOut()
+	{
+	// Notify the client of Request Send TimeOut.
+	RHTTPTransaction trans = iProtTrans->Transaction();
+	if(trans.SendEvent(THTTPEvent::ESendTimeOut, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+		{
+		trans.Fail(THTTPFilterHandle::EProtocolHandler);
+		}
+	else
+		{
+        // Notify the client that Transaction Failed.
+		if(trans.SendEvent(THTTPEvent::EFailed, THTTPEvent::EIncoming, THTTPFilterHandle(THTTPFilterHandle::EProtocolHandler)) != KErrNone)
+             {
+             trans.Fail(THTTPFilterHandle::EProtocolHandler);             
+             }
+		}
+	}
+
+TInt CHttpRequestComposer::SendTimeOutValue()
+	{
+	RHTTPTransaction trans = iProtTrans->Transaction();
+	RStringPool stringPool = trans.Session().StringPool();
+	RStringF sendTimeOut = stringPool.StringF(HTTP::ESendTimeOutValue, iStringTable);
+	THTTPHdrVal sendTimeOutVal;
+	
+	TBool ret = trans.PropertySet().Property(sendTimeOut,sendTimeOutVal); 
+	if(ret && (sendTimeOutVal.Type() == THTTPHdrVal::KTIntVal))
+		{
+		return sendTimeOutVal.Int();
+		}
+	return 0;
+	}
+
+/*
+ *	Methods from MHttpMessageComposerObserver
+ */
+
+void CHttpRequestComposer::StartLineL(TPtrC8& aMethod, TPtrC8& aRequestUri, TPtrC8& aVersion)
+	{
+	// The RFC2616 defines the Request-Line as follows - 
+	// 
+	// Request-Line = Method SP Request-URI SP HTTPVersion CRLF
+	//
+	// Get Method...
+	RStringF method = iStringPool.StringF(iRequest.Method());
+	aMethod.Set(method.DesC());
+	
+	RHTTPTransaction trans = iProtTrans->Transaction();
+	
+	// Get the Request-URI...
+	THTTPHdrVal uri;
+	if( !trans.PropertySet().Property(iStringPool.StringF(HTTP::EUri, iStringTable), uri) )
+		{
+		// The EUri property has not been set - leave!
+		User::Leave(KErrCorrupt);
+		}
+	aRequestUri.Set(uri.Str().DesC());
+	
+	// Get HTTPVersion - check to see if client has set a version
+	RHTTPConnectionInfo connectionInfo = trans.Session().ConnectionInfo();
+	THTTPHdrVal httpVersion;
+	if( connectionInfo.Property(iStringPool.StringF(HTTP::EHTTPVersion, iStringTable), httpVersion) )
+		{
+		// Use the client specified version
+		aVersion.Set(httpVersion.StrF().DesC());		
+		}
+	else
+		{
+		// Assume HTTP/1.1
+		aVersion.Set(iStringPool.StringF(HTTP::EHttp11, iStringTable).DesC());
+		}
+	
+	__FLOG_4(_T8("Trans %d : request-line -> %S %S %S"), iProtTrans->Transaction().Id(), &aMethod, &aRequestUri, &aVersion);
+	
+	// Initialise header info
+	InitHeadersL();
+	}
+
+TInt CHttpRequestComposer::NextHeaderL(TPtrC8& aHeaderName, TPtrC8& aHeaderValue)
+	{
+	// Are there any more headers?
+	TInt err = KErrNotFound;
+	TBool done = EFalse;
+	while( !iFields.AtEnd() && !done )
+		{
+		// Get field current field.
+		RStringF name = iStringPool.StringF(iFields());
+
+		// Check to see if a trailer header
+		if( !IsTrailerHeader(name) )
+			{
+			// Ok, found a header - done!
+			err = KErrNone;
+			done = ETrue;
+				
+			// Get the OTA version of the field value
+			TPtrC8 value;
+			iRequest.GetHeaderCollection().GetRawFieldL(name, value);
+			
+			// Pass back these values
+			aHeaderName.Set(name.DesC());
+			aHeaderValue.Set(value);
+
+			__FLOG_3(_T8("Trans %d : request header -> %S: %S"), iProtTrans->Transaction().Id(), &aHeaderName, &aHeaderValue);
+			}
+		
+		// Move onto next header field...
+		++iFields;		
+		}
+	return err;
+	}
+
+MHTTPDataSupplier* CHttpRequestComposer::HasBodyL()
+	{
+#if defined (_DEBUG) && defined (_LOGGING)
+	MHTTPDataSupplier* body = iRequest.Body();
+
+	if( body == NULL )
+		{
+		__FLOG_1(_T8("Trans %d : no request entity body"), iProtTrans->Transaction().Id());
+		}
+	else if( body->OverallDataSize() == KErrNotFound )
+		{
+		__FLOG_1(_T8("Trans %d : chunked request entity body"), iProtTrans->Transaction().Id());
+		}
+	else
+		{
+		__FLOG_2(_T8("Trans %d : request entity body length = %d"), iProtTrans->Transaction().Id(), body->OverallDataSize());
+		}
+#endif
+
+	return iRequest.Body();
+	}
+	
+TInt CHttpRequestComposer::NextTrailerL(TPtrC8& aHeaderName, TPtrC8& aHeaderValue)
+	{
+	TInt err = KErrNotFound;
+
+	if( iTrailerHeaders.Count() > 0 )
+		{
+		// Ok, still got trailers.
+		err = KErrNone;
+
+		// Get trailer field.
+		RStringF name = iTrailerHeaders[0];
+
+		// Get the OTA version of the field value
+		TPtrC8 value;
+		iRequest.GetHeaderCollection().GetRawFieldL(name, value);
+		
+		// Pass back these values
+		aHeaderName.Set(name.DesC());
+		aHeaderValue.Set(value);
+		
+		// Remove this trailer from the array.
+		iTrailerHeaders.Remove(0);
+
+		__FLOG_3(_T8("Trans %d : request trailer -> %S: %S"), iProtTrans->Transaction().Id(), &aHeaderName, &aHeaderValue);
+		}
+	return err;
+	}
+
+void CHttpRequestComposer::MessageComplete()
+	{
+	__FLOG_1(_T8("Trans %d : request complete"), iProtTrans->Transaction().Id());
+	if(iCorkingEnabled)
+	    {
+	    // Disable corking.
+	    iCorkingEnabled = iRequestHeaderSent = EFalse;
+	    iObserver.SendingBodyData(EFalse);
+	    }	
+		
+	iObserver.RequestComplete();
+	iRequestSent = ETrue;
+	}
+
+void CHttpRequestComposer::MessageDataReadyL()
+	{
+    if(iRequestHeaderSent)
+        {
+        // If we hit this function that means we need to send body data.
+        // So we are sending the body data. Enable corking.
+        if(!iCorkingEnabled)
+            {
+            __FLOG_1(_T8("Trans %d : Corking is enabled"), iProtTrans->Transaction().Id());            
+            iCorkingEnabled = ETrue;
+            iObserver.SendingBodyData(ETrue);
+            }       
+        else
+            {
+            __FLOG_1(_T8("Trans %d : Corking is already enabled"), iProtTrans->Transaction().Id());
+            }
+        }
+    else
+        {
+        __FLOG_1(_T8("Trans %d : request header is set"), iProtTrans->Transaction().Id());        
+        iRequestHeaderSent = ETrue;
+        }		
+    // Notify the observer that there is message data ready to send.
+	iMessageComposer.GetMessageData(iData);
+	
+	iObserver.SendRequestDataL(iData);
+	}
+
+TInt CHttpRequestComposer::HandleComposeError(TInt aError)
+	{
+	__FLOG_1(_T8("!! Error : %d"), aError);
+	__FLOG_1(_T8("-> Trans %d : request composing error - cancelling transaction"), iProtTrans->Transaction().Id());
+
+	return static_cast<CHttpResponseParser&>(iProtTrans->RxData()).CancelTransaction(aError);
+	}
+	
+TBool CHttpRequestComposer::CheckRequestPendingComplete()
+	{
+	return iMessageComposer.CheckMessagePendingComplete();
+	}
+TBool CHttpRequestComposer::RequestSent () const
+	{
+	return iRequestSent;		
+	}
+
+TBool CHttpRequestComposer::NeedDisconnectNotification ()
+	{
+	CHttpClientTransaction* clientTrans = static_cast < CHttpClientTransaction* > ( iProtTrans );
+    return clientTrans->NeedDisconnectNotification ();
+	}
+
+void CHttpRequestComposer::Reserved_MHttpMessageComposerObserver()
+	{
+	User::Invariant();
+	}
+
+void CHttpRequestComposer::ResumeSuspendedRequest()
+	{
+	iSuspendRequest = EFalse;
+	iMessageComposer.ReleaseMessageData();	
+	}
+
+TBool CHttpRequestComposer::IsSuspendedRequest() const
+	{
+	return iSuspendRequest;	
+	}
+
+void CHttpRequestComposer::CancelWaitFor100Continue()
+	{
+	__FLOG_1(_T8("Trans %d : client declined to wait for the 100-Continue Response"), iProtTrans->Transaction().Id());
+	if(IsSuspendedRequest())
+		{
+		ResumeSuspendedRequest();	
+		}
+	}
+	
+MHttpDataOptimiser* CHttpRequestComposer::HttpDataOptimiser(TBool& aBatchingEnabled)
+	{
+	RHTTPTransaction trans = iProtTrans->Transaction();
+ 	MHttpDataOptimiser* httpOptimiser = trans.HttpDataOptimiser();
+ 	RHTTPSession sess = trans.Session();
+ 	
+ 	RStringPool stringPool = sess.StringPool();
+	THTTPHdrVal value;
+	RStringF string = stringPool.StringF(HTTP::EHttpBatching, iStringTable);
+	RStringF str = stringPool.StringF(HTTP::EEnableBatching, iStringTable);
+	if(sess.ConnectionInfo().Property(string, value))
+		{
+		if(value.Type() == THTTPHdrVal::KStrFVal)
+			{
+			aBatchingEnabled = value.StrF() == str;
+			}
+		}
+	 	
+ 	if( httpOptimiser && !aBatchingEnabled )
+ 		{
+		// the optimiser has not been set for the session.
+ 		__FLOG_1(_T8("-> Trans %d : Http optimiser has been set for this transaction"), iProtTrans->Transaction().Id());
+ 		return (httpOptimiser);
+ 		}
+ 	
+ 	// the MHttpDataOptimiser hasn't been set for the transaction
+ 	// check whether the session encapsulating the transaction is having MHttpDataOptimiser.
+ 	httpOptimiser  = sess.HttpDataOptimiser();
+ 	if( httpOptimiser )
+ 		{
+ 		// the optimiser has been set for the session.
+ 		__FLOG_1(_T8("-> Trans %d : HTTP optimiser has been set for the session encapsulating this transaction"), iProtTrans->Transaction().Id());
+ 		// check if the client has disabled optimiser for a particular transaction.
+ 		TBool disableTransOptimiser = EFalse;
+ 		string = stringPool.StringF(HTTP::EHTTPTransOptimiser, iStringTable);
+ 		str = stringPool.StringF(HTTP::EDisableHTTPTransOptimiser, iStringTable);
+ 		if(iProtTrans->Transaction().PropertySet().Property(string, value))
+ 			{
+ 			if(value.Type() == THTTPHdrVal::KStrFVal)
+ 				{
+ 				disableTransOptimiser = ( value.StrF() == str );
+ 				}
+ 			}
+ 		
+ 		if ( disableTransOptimiser )
+ 			{
+ 			// the client has disabled the optimiser for a particular transaction.
+ 			__FLOG_1(_T8("-> Trans %d : HTTP optimiser has been disabled by the client"), iProtTrans->Transaction().Id());
+ 			// do not return the optimiser.
+ 			return NULL;
+ 			}
+ 		
+ 		// the client has not diasabled the optimiser for any transaction in particular.
+ 		// return the optimiser.
+ 		return (httpOptimiser);
+ 		}
+ 	
+ 	// MHttpDataOptimiser has not been set for the session and transaction
+ 	__FLOG_1(_T8("-> Trans %d : HTTP optimiser was never set for the transaction and the session enclosing it"), iProtTrans->Transaction().Id());
+ 	return NULL;	
+	}
+