--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerprotocols/httptransportfw/core/CTransaction.cpp Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,707 @@
+// 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:
+//
+
+// System includes
+#include <e32std.h>
+
+
+#include <http/mhttptransactioncallback.h>
+#include <http/thttpfilterregistration.h>
+#include <httpstringconstants.h>
+#include <httperr.h>
+
+// Local includes
+#include "CHTTPSession.h"
+#include "CRequest.h"
+#include "CResponse.h"
+#include "CHTTPManager.h"
+#include "CHeaderField.h"
+
+// Class signature
+#include "CTransaction.h"
+
+#define LOGFILE "events.csv"
+
+void CTransaction::CancelTransaction(THTTPFilterHandle aStart)
+ {
+ // Delete all current events.
+ iEventQueue.Reset();
+ // Cancel the active object
+ Cancel();
+ // And send the cancel event
+ SynchronousSendEvent(THTTPEvent::ECancel, THTTPEvent::EOutgoing, aStart);
+ if (iStatus != EInFilter && iStatus != ECancelled)
+ iStatus = EPassive;
+ else
+ iStatus = ECancelled;
+ }
+
+void CTransaction::Close()
+ {
+ // If the request and the response don't exist yet, the object
+ // isn't fully constructed and so shouldn't send any cancel or
+ // close messages as its part-constructed state could confuse
+ // filters.
+ if (!iResponse)
+ {
+ CHeaderFieldPart::ClosePropertySet(iPropertySet);
+ delete iRequest;
+ delete this;
+ return;
+ }
+
+ // If we're currently processing a close event we should stop
+ // right now, to prevent infinite loops.
+ if (iProcessingSyncEvent &&
+ iCurrentSyncEvent.iEvent.iStatus == THTTPEvent::EClosed)
+ return;
+
+ // Cancel the transaction
+ CancelTransaction(THTTPFilterHandle::EClient);
+ // Remember the status
+ TInt status = iStatus.Int();
+ // And send out the 'close' message.
+ SynchronousSendEvent(THTTPEvent(THTTPEvent::EClosed),
+ THTTPEvent::EOutgoing, THTTPFilterHandle::EClient);
+ iStatus = status;
+ // The state will now be cancelled if and only if we were in a filter
+
+ iSession.RemoveTransaction(this);
+ CHeaderFieldPart::ClosePropertySet(iPropertySet);
+ delete iRequest;
+ delete iResponse;
+
+ // Cancel the active object
+ Cancel();
+
+ if (status == CTransaction::ECancelled)
+ {
+ // If we're our transaction's RunL (which we probably are),
+ // wait until we're back in the CTransaction before we delete it.
+ iStatus = CTransaction::EExiting;
+ }
+ else
+ {
+ // We can safeley close straight away
+ delete this;
+ }
+
+ }
+
+void CTransaction::ConstructL(const TUriC8& aURI, RStringF aMethod)
+ {
+ RStringPool strP = iSession.Manager().StringPool();
+ // If the string is the empty string, this means the default value
+ // (of GET) should be used.
+ if (aMethod == RStringF())
+ aMethod = strP.StringF(HTTP::EGET,RHTTPSession::GetTable());
+ aMethod.Copy();
+ // The 0 header val in the property set has no meaning; the value
+ // of the part is not used by property sets.
+ CHeaderFieldPart* fieldPart = CHeaderFieldPart::NewL(THTTPHdrVal(0));
+ fieldPart->SetPropertySet(iPropertySet);
+ iRequest = CRequest::NewL(aMethod, aURI, this);
+ iResponse = CResponse::NewL(this);
+ iId = iSession.NextTransactionID();
+ CActiveScheduler::Add(this);
+ iSession.AddTransactionL(this);
+ __OPEN_LOG(LOGFILE);
+ __LOG(_L(",Transaction ID, Event, Direction, Filter"));
+ }
+
+void CTransaction::RunL()
+ {
+ // If we're in another transaction's RunL (which has called a
+ // filter that has in turn run a nested active scheduler) we
+ // should block now and try again when that transaction has
+ // finished. This is to prevent obscure re-entrancy problems in
+ // client code. See the comments on the blocking functions in the
+ // session's header file for more details.
+ THTTPEvent currentEvent = CurrentEvent().iEvent;
+ if(currentEvent == THTTPEvent::ESuspend)
+ {
+ __ASSERT_DEBUG(iStatus != ESuspended, HTTPPanic::Panic(HTTPPanic::EInvalidEvent));
+ iStatus = ESuspended;
+ iSession.TransactionHasBlocked();
+ RemoveEvent();
+ return;
+ }
+ if(currentEvent == THTTPEvent::EResume)
+ {
+ if(iStatus != ESuspended)
+ HTTPPanic::Panic(HTTPPanic::EInvalidEvent);
+ RemoveEvent();
+ iSession.Unblock();
+ return;
+ }
+ if (iSession.IsBlocking())
+ {
+ if(iStatus.Int() != ESuspended)
+ {
+ iStatus = EBlocked;
+ iSession.TransactionHasBlocked();
+ }
+ return;
+ }
+ iSession.Block();
+ TBool runNext = RunOneFilterL();
+ iSession.Unblock();
+ if (iStatus.Int() == EExiting)
+ {
+ iStatus = EPassive;
+ delete this;
+ }
+ else if (runNext)
+ {
+ // Move on to the next filter and find out what to do next.
+ Activate(FindNextFilter());
+ }
+ }
+
+
+
+TInt CTransaction::RunError(TInt aError)
+ {
+ TInt error = KErrNone;
+ RHTTPTransaction t;
+ t.iImplementation = this;
+ switch (iStatus.Int())
+ {
+ case EInFilter:
+ case ECancelled:
+ {
+ // Get the right event, according to whether it's being sent synchronously or not
+ THTTPEvent currentEvent;
+ if (iProcessingSyncEvent==1)
+ currentEvent = CurrentEvent().iEvent;
+ else
+ currentEvent = iASyncEvent;
+
+ // Is it a session event
+ TBool isSessionEvent = currentEvent.IsSessionEvent();
+
+ // Now, offer to the appropriate handler - MHFRunError for transaction events
+ // or MHFSessionRunError for session events
+ if (iProcessingSyncEvent==1)
+ {
+ MHTTPFilter* filter = iSession.FilterQueue()[iNextFilter].iFilter;
+ if (isSessionEvent)
+ error = filter->MHFSessionRunError(aError, STATIC_CAST(THTTPSessionEvent&, currentEvent));
+ else
+ error = filter->MHFRunError(aError, t, currentEvent);
+ }
+ else
+ {
+ MHTTPFilter* filter = iSession.FilterQueue()[iCurrentASyncFilter].iFilter;
+ if (isSessionEvent)
+ error = filter->MHFSessionRunError(aError, STATIC_CAST(THTTPSessionEvent&, currentEvent));
+ else
+ error = filter->MHFRunError(aError, t, currentEvent);
+ }
+
+ // Unless we've been deleted, move on to the next filter and
+ // find out what to do next.
+ if (iStatus.Int() != EExiting)
+ Activate(FindNextFilter());
+ } break;
+ case EExiting:
+ break;
+ default:
+ __ASSERT_DEBUG(EFalse, HTTPPanic::Panic(HTTPPanic::ETransactionUnhandledError));
+ }
+
+ if (!iProcessingSyncEvent)
+ iSession.Unblock();
+
+ // Panic if MHFRunError hasn't handled the error
+ if (error)
+ HTTPPanic::Panic(HTTPPanic::ETransactionUnhandledError);
+
+ if (iStatus.Int() == EExiting)
+ {
+ delete this;
+ }
+
+ return KErrNone;
+ }
+
+void CTransaction::DoCancel()
+ {
+ // Nothing to do here. This only happens when the transaction is
+ // cancelled or deleted.
+ }
+
+
+
+
+
+#if defined (_DEBUG)
+
+void CTransaction::LogEvent(TInt aTransactionId, const THTTPEvent& aEvent,
+ TInt aDirection, const TDesC8& aFilterName)
+ /* creates a single line comma separated list comment of
+ Transaction Number, Event, Direction, Filter Name
+ */
+ {
+ _LIT(KSubmit,"Submit");
+ _LIT(KCancel,"Cancel");
+ _LIT(KNotifyNewRequestBodyPart,"NotifyNewRequestBodyPart");
+ _LIT(KClosed,"Closed");
+ _LIT(KGotResponseHeaders,"GotResponseHeaders");
+ _LIT(KGotResponseBodyData,"GotResponseBodyData");
+ _LIT(KResponseComplete, "ResponseComplete");
+ _LIT(KGotResponseTrailerHeaders,"GotResponseTrailerHeaders");
+ _LIT(KSucceeded,"Succeeded");
+ _LIT(KFailed, "Failed");
+ _LIT(KUnrecoverableError, "Unrecoverable Error");
+ _LIT(KTooMuchRequestData, "TooMuchRequestData");
+ _LIT(KRedirectRequiresConfirmation, "RedirectRequiresConfirmation");
+ _LIT(KNeedTunnel, "NeedTunnel");
+ _LIT(KGetCipherSuite, "GetCipherSuite");
+
+ _LIT(KUnknownEvent, "UnknownEvent");
+
+ TPtrC event(KNullDesC);
+
+ switch (aEvent.iStatus)
+ {
+ case THTTPEvent::ESubmit:
+ event.Set(KSubmit);
+ break;
+ case THTTPEvent::ECancel:
+ event.Set(KCancel);
+ break;
+ case THTTPEvent::ENotifyNewRequestBodyPart:
+ event.Set(KNotifyNewRequestBodyPart);
+ break;
+ case THTTPEvent::EClosed:
+ event.Set(KClosed);
+ break;
+ case THTTPEvent::EGotResponseHeaders:
+ event.Set(KGotResponseHeaders);
+ break;
+ case THTTPEvent::EGotResponseBodyData:
+ event.Set(KGotResponseBodyData);
+ break;
+ case THTTPEvent::EResponseComplete:
+ event.Set(KResponseComplete);
+ break;
+ case THTTPEvent::EGotResponseTrailerHeaders:
+ event.Set(KGotResponseTrailerHeaders);
+ break;
+ case THTTPEvent::ESucceeded:
+ event.Set(KSucceeded);
+ break;
+ case THTTPEvent::EFailed:
+ event.Set(KFailed);
+ break;
+ case THTTPEvent::EUnrecoverableError:
+ event.Set(KUnrecoverableError);
+ break;
+ case THTTPEvent::ETooMuchRequestData:
+ event.Set(KTooMuchRequestData);
+ break;
+ case THTTPEvent::ERedirectRequiresConfirmation:
+ event.Set(KRedirectRequiresConfirmation);
+ break;
+ case THTTPEvent::ENeedTunnel:
+ event.Set(KNeedTunnel);
+ break;
+ case THTTPEvent::EGetCipherSuite:
+ event.Set(KGetCipherSuite);
+ break;
+ default:
+ event.Set(KUnknownEvent);
+ };
+
+ _LIT(KHandled, "Handled");
+ _LIT(KOutgoing,"Outgoing");
+ _LIT(KIncoming,"Incoming");
+ TPtrC direction(KHandled);
+
+ switch (aDirection)
+ {
+ case THTTPEvent::EIncoming:
+ direction.Set(KIncoming);
+ break;
+ case THTTPEvent::EOutgoing:
+ direction.Set(KOutgoing);
+ break;
+ };
+ TBuf<64> filterName;
+ filterName.Copy(aFilterName);
+ TBuf<256> logBuffer; // if its longer than 256 then http logging code will fall over anyway
+ _LIT(KLogFormat, ", Transaction %d, %S, %S, %S");
+ logBuffer.AppendFormat(KLogFormat, aTransactionId, &event, &direction, &filterName);
+ __LOG(logBuffer);
+ }
+
+#endif
+
+
+
+TBool CTransaction::RunOneFilterL()
+ {
+ // The active scheduler is only really used to split up filtering
+ // in order to make things more interactive. By now, the next
+ // filter to run should have been worked out, so run it and work
+ // out what to do next time.
+ RHTTPTransaction t;
+ t.iImplementation = this;
+ TBool processNext = EFalse;
+ iCurrentFilter = iNextFilter;
+ iStatus = EInFilter;
+ __LOGEVENT(iId, CurrentEvent().iEvent,CurrentEvent().iDirection,
+ iSession.Manager().StringPool().StringF(
+ iSession.FilterQueue()[iNextFilter].iName).DesC());
+
+ // Run it.
+
+ MHTTPFilter* filter = iSession.FilterQueue()[iNextFilter].iFilter;
+ THTTPEvent currentEvent = CurrentEvent().iEvent;
+ //I need to keep a copy of the event and if the event is asyncronous I need to keep the event
+
+ if(iProcessingSyncEvent==0 )
+ {
+ iASyncEvent= iEventQueue[0].iEvent;
+ iCurrentASyncFilter=iNextFilter;
+ }
+ if (currentEvent.IsSessionEvent())
+ {
+ filter->MHFSessionRunL(STATIC_CAST(THTTPSessionEvent&, currentEvent));
+ }
+ else
+ filter->MHFRunL(t, currentEvent);
+
+ switch (iStatus.Int())
+ {
+ case EInFilter:
+ processNext = ETrue;
+ iStatus = EFilter;
+ break;
+ case ECancelled:
+ iStatus = EPassive;
+ processNext = ETrue;
+ break;
+ default:
+ break;
+ }
+ return processNext;
+ }
+
+
+
+
+TInt CTransaction::AddEvent(THTTPEvent& aEvent, TInt aDirection,
+ THTTPFilterHandle aStart)
+ {
+ // API for adding new events to the queue.
+ TInt startFilter = 0;
+ // Look at the start filter handle to decide where to start the filter.
+ switch (aStart.iValue)
+ {
+ case THTTPFilterHandle::EUndefined:
+ HTTPPanic::Panic(HTTPPanic::EInvalidFilterHandle);
+ break;
+ case THTTPFilterHandle::EClient:
+ startFilter = iSession.FilterQueue().Count() - 1;
+ __LOGEVENT(iId,aEvent,aDirection,_L8("Client"));
+ break;
+ case THTTPFilterHandle::EProtocolHandler:
+ startFilter = 0;
+ __LOGEVENT(iId,aEvent,aDirection,_L8("Protocol Handler"));
+ break;
+ case THTTPFilterHandle::ECurrentFilter:
+ if (iStatus.Int() != EInFilter && iStatus.Int() != ECancelled && iStatus.Int() != ESuspended)
+ HTTPPanic::Panic(HTTPPanic::EInvalidFilterHandle);
+ startFilter = iCurrentFilter;
+
+ __LOGEVENT(iId,aEvent,aDirection,iSession.Manager().StringPool().StringF(
+ iSession.FilterQueue()[iCurrentFilter].iName).DesC());
+ break;
+ default:
+ // Assume any other value is a filter handle.
+ {
+ TInt i;
+ TInt count = iSession.FilterQueue().Count();
+ // If there aren't any filters, you shouldn't be specifying a
+ // filter handle. Return an error.
+ if (count == 0)
+ HTTPPanic::Panic(HTTPPanic::EInvalidFilterHandle);
+ for (i = 0; i < count; ++i)
+ {
+ if (iSession.FilterQueue()[i].iHandle == aStart.iValue)
+ break;
+ }
+ if (i == count)
+ {
+ // We haven't found that filter. We may as well leave
+ HTTPPanic::Panic(HTTPPanic::EInvalidFilterHandle);
+ }
+ startFilter = i;
+ __LOGEVENT(iId,aEvent,aDirection,iSession.Manager().StringPool().
+ StringF(iSession.FilterQueue()[i].iName).DesC());
+ break;
+ }
+ }
+
+ TEventRegistration r(aEvent, aDirection, startFilter);
+ return AppendEvent(r);
+ }
+
+TInt CTransaction::SendEvent(THTTPEvent& aEvent, TInt aDirection, THTTPFilterHandle aStart)
+ {
+ TInt err = AddEvent(aEvent, aDirection, aStart);
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ // If necessary, process the event having added it to the
+ // queue. It's not necessary if we're inside a MHFRunL (in which
+ // case the event will be processed when we return to
+ // CTransaction::RunL) or if we've already added an event that
+ // hasn't been run yet (in which case we'll be active)
+ if (iStatus.Int() == EPassive)
+ {
+ Activate(FindNextFilter());
+ }
+ return err;
+ }
+
+void CTransaction::SendEventL(THTTPEvent& aEvent, TInt aDirection,
+ THTTPFilterHandle aStart)
+ {
+ User::LeaveIfError(SendEvent(aEvent, aDirection, aStart));
+ }
+
+
+void CTransaction::SynchronousSendEvent(THTTPEvent aEvent, TInt aDirection,
+ THTTPFilterHandle aStart)
+ {
+ // Remember the current async event, in case this is a nested async event.
+ TEventRegistration storedSyncEvent = iCurrentSyncEvent;
+ iProcessingSyncEvent = ETrue;
+
+ // As we're in sync mode, this call must succeed.
+ AddEvent(aEvent, aDirection, aStart);
+
+ TInt s = iStatus.Int();
+ // Remember the current filter so that events fired after a cancel
+ // start from the right place.
+ TInt rememberedCurrentFilter = iCurrentFilter;
+ iStatus = EPassive;
+ do
+ {
+ iStatus = FindNextFilter();
+ if (iStatus.Int() != EPassive)
+ {
+ TRAPD(error, RunOneFilterL());
+ if (error)
+ RunError(error);
+ }
+ }
+ while (iStatus.Int() != EPassive && iStatus.Int() != EExiting);
+
+ iCurrentFilter = rememberedCurrentFilter;
+ if ( iStatus.Int() != EExiting )
+ {
+ iStatus = s;
+ }
+ iProcessingSyncEvent = EFalse;
+ iCurrentSyncEvent = storedSyncEvent;
+ }
+
+CTransaction::TTransactionStates CTransaction::FindNextFilter()
+ {
+ if (!EventAvailable())
+ return EPassive;
+ NextFilter();
+ if (iStatus.Int() == EPassive)
+ {
+ // Starts processing of the event on the front of the queue.
+ // The event starts at the filter after the one that generated it,
+ // in the direction it's going.
+ iNextFilter = CurrentEvent().iStartFilter;
+ NextFilter();
+ }
+ do
+ {
+ // See if there's another filter to process this event.
+ iStatus = FindFilterForThisEvent();
+ // If not, move on to the next event
+ if (iStatus.Int() == EPassive && EventAvailable())
+ {
+ // Starts processing of the event on the front of the queue.
+ // The event starts at the filter after the one that generated it,
+ // in the direction it's going.
+ iNextFilter = CurrentEvent().iStartFilter;
+ NextFilter();
+ // Having identified the first filter to be considered for this
+ // event, identify the first filter that actualy matches the
+ // event.
+ }
+ }
+ while (iStatus.Int() == EPassive && EventAvailable());
+ return static_cast<TTransactionStates>(iStatus.Int());
+ }
+
+CTransaction::TTransactionStates CTransaction::FindFilterForThisEvent()
+ {
+ // Identify the next filter to run.
+ TBool found = EFalse;
+
+ do
+ {
+ // If we've run out of filters, this event is finished.
+ if (iNextFilter < 0 ||
+ iNextFilter >= iSession.FilterQueue().Count())
+ break;
+ if (iNextFilter < iSession.FilterQueue().Count())
+ {
+ THTTPFilterRegistration& filter =
+ iSession.FilterQueue()[iNextFilter];
+ // Is this filter interested in this event?
+ RStringF hdr =
+ iSession.Manager().StringPool().StringF(filter.iHeader);
+
+ // If a header has been specified in the filter, check if
+ // it's present by searching for part 0 of that header.
+ THTTPHdrVal headerValue;
+ TBool hasHeader = hdr == RStringF() ||
+ !iResponse->Headers().GetField(hdr,0,headerValue);
+
+ // Does this filter match the event? That is to say:
+ // The event matches, or the filter matches any event and
+ // The filter matches any status code or the status matches and
+ // The filter matches any header or the header matches.
+
+ // first check UID's
+ if (CurrentEvent().iEvent.iUID == KHTTPMatchAnyEventUid ||
+ filter.iEvent.iUID == KHTTPMatchAnyEventUid ||
+ filter.iEvent.iUID == CurrentEvent().iEvent.iUID)
+ {
+ if (CurrentEvent().iEvent.IsSessionEvent())
+ {
+ if (filter.iEvent.iStatus == THTTPEvent::EAnySessionEvent ||
+ filter.iEvent.iStatus == THTTPEvent::EAll ||
+ filter.iEvent.iStatus == CurrentEvent().iEvent.iStatus)
+ found = ETrue;
+ }
+ else if (
+ (filter.iEvent.iStatus == CurrentEvent().iEvent.iStatus ||
+ filter.iEvent.iStatus == THTTPEvent::EAnyTransactionEvent ||
+ filter.iEvent.iStatus == THTTPEvent::EAll) &&
+ (filter.iStatus == KAnyStatusCode || filter.iStatus == iResponse->Status()) &&
+ (filter.iHeader == RStringF() || hasHeader))
+ found = ETrue;
+ }
+ }
+
+ if (!found)
+ {
+ // If not, move on to the next filter.
+ NextFilter();
+ }
+ }while (!found);
+
+ if (!found)
+ {
+ // We haven't found a filter interested in this message, so we
+ // must have finised processing it. Move on to the next event
+ // if there is one.
+ RemoveEvent();
+ if (EventAvailable())
+ {
+ iNextFilter = CurrentEvent().iStartFilter;
+ NextFilter();
+ }
+ return EPassive;
+ }
+ else
+ {
+ // We have found a filter.
+ return EFilter;
+ }
+ }
+
+// Activate and complete ourself.
+void CTransaction::Activate(TTransactionStates aStatus)
+ {
+ if (aStatus == EPassive)
+ {
+ iStatus = aStatus;
+ return;
+ }
+ SetActive();
+ TRequestStatus* r = &iStatus;
+ User::RequestComplete(r, aStatus);
+ }
+
+
+// This function is not allowed to fail in async mode.
+TInt CTransaction::AppendEvent(TEventRegistration& aEvent)
+ {
+ if (iProcessingSyncEvent)
+ {
+ iCurrentSyncEvent = aEvent;
+ return KErrNone;
+ }
+ else
+ {
+ if(aEvent.iEvent == THTTPEvent::EResume)
+ {
+ TInt pos(0);
+ return iEventQueue.Insert(aEvent,pos);
+ }
+ return iEventQueue.Append(aEvent);
+ }
+ }
+
+RString CTransaction::CipherSuite()
+ {
+ RHTTPTransactionPropertySet properties(PropertySet());
+ RStringPool strPool(Session().StringPool());
+ THTTPHdrVal cipherSuiteValue;
+ TBool foundCipherSuiteProperty = properties.Property(strPool.StringF(HTTP::ECipherSuiteValue,
+ RHTTPSession::GetTable()), cipherSuiteValue);
+
+ if(!foundCipherSuiteProperty)
+ {
+ // Send synchronous event down to Protocol Handler to ask it to query the connection for
+ // the cipher suite used and store it as a property of the transaction.
+ SynchronousSendEvent(THTTPEvent::EGetCipherSuite, THTTPEvent::EOutgoing, THTTPFilterHandle::EClient);
+ foundCipherSuiteProperty = properties.Property(strPool.StringF(HTTP::ECipherSuiteValue,
+ RHTTPSession::GetTable()), cipherSuiteValue);
+ }
+
+ // Make sure property is set to correct type.
+ __ASSERT_DEBUG(foundCipherSuiteProperty && cipherSuiteValue.Type() == THTTPHdrVal::KStrVal,
+ HTTPPanic::Panic(HTTPPanic::EHeaderInvalidType));
+ return cipherSuiteValue.Str();
+ }
+
+void CTransaction::SetupHttpDataOptimiser (MHttpDataOptimiser& aHttpOptimiser)
+ {
+ iHttpDataOptimiser = &aHttpOptimiser;
+ }
+
+MHttpDataOptimiser* CTransaction::HttpDataOptimiser ()
+ {
+ return iHttpDataOptimiser;
+ }
+
+
+