internetradio2.0/irnetworkcontroller/src/irdatatransfertracker.cpp
changeset 14 896e9dbc5f19
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/internetradio2.0/irnetworkcontroller/src/irdatatransfertracker.cpp	Wed Aug 18 09:40:26 2010 +0300
@@ -0,0 +1,348 @@
+/*
+* Copyright (c) 2007 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:  Tracks data transmitted through a network connection.
+*
+*/
+
+
+#include <httpstringconstants.h>
+#include <mhttpdatasupplier.h>
+#include <rhttpheaders.h>
+
+#include "irdebug.h"
+#include "irdatatransfertracker.h"
+
+// The threshold in bytes after which notifications are sent to observers.
+// 0.1  MB = 104857.6 bytes ( rounded )
+const TInt KIRDataTransferTrackerThreshold = 104858;
+// 0.01 MB = 1048.57 bytes ( rounded )
+const TInt KIRDataTransferTrackerMiniThreshold = 1049;
+// The overhead in bytes per one HTTP header.
+const TInt KVRBytesPerHeaderOverhead = 2; // "\r\n"
+// The overhead in bytes per one header field in a request.
+const TInt KVRBytesPerHeaderFieldOverhead = 4; // ": \r\n"
+// The overhead in bytes per one header sent.
+const TInt KVRBytesPerHeaderSentOverhead = 4; // "  \r\n"
+// The overhead in bytes per one header received.
+const TInt KVRBytesPerHeaderReceivedOverhead = 3; // " \r\n"
+
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CIRDataTransferTracker* CIRDataTransferTracker::NewL()
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::NewL - Entering" );
+    CIRDataTransferTracker* self = new ( ELeave ) CIRDataTransferTracker;
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    IRLOG_DEBUG( "CIRDataTransferTracker::NewL - Exiting" );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// Constructor.
+// ---------------------------------------------------------------------------
+//
+CIRDataTransferTracker::CIRDataTransferTracker()
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::CIRDataTransferTracker - Entering" );
+    bThresholdCrossed = EFalse;
+    IRLOG_DEBUG( "CIRDataTransferTracker::CIRDataTransferTracker - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Second-phase constructor.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::ConstructL() const
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::ConstructL" );
+    return;
+    }
+
+// ---------------------------------------------------------------------------
+// Destructor.
+// ---------------------------------------------------------------------------
+//
+CIRDataTransferTracker::~CIRDataTransferTracker()
+    {
+        IRLOG_DEBUG( "CIRDataTransferTracker::~CIRDataTransferTracker - Entering" );
+        iObserver = NULL;
+        IRLOG_DEBUG( "CIRDataTransferTracker::~CIRDataTransferTracker - Exiting" );
+
+    }
+
+// ---------------------------------------------------------------------------
+// From class MIRDataTransferTracker.
+// Sets the observer.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::SetObserver( MIRDataTransferObserver* aObserver )
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::SetObserver - Entering" );
+    iObserver = aObserver;
+    IRLOG_DEBUG( "CIRDataTransferTracker::SetObserver - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// From class MIRDataTransferTracker.
+// Returns the current transferred data amounts.
+// ---------------------------------------------------------------------------
+//
+const MIRDataTransferTracker::TIRDataTransferPckg& CIRDataTransferTracker::TransferredData() const
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::TIRDataTransferPckg" );
+    return iPckg;
+    }
+
+// ---------------------------------------------------------------------------
+// From class MIRDataTransferTracker.
+// Binds this to track the supplied HTTP session.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::BindL( RHTTPSession aSession, TIRTransferCategory aCategory )
+    {
+    IRLOG_DEBUG2( "CIRDataTransferTracker::BindL() - aCategory = %d", static_cast<TInt>( aCategory ) );
+
+    aSession.ConnectionInfo().SetPropertyL(
+        aSession.StringPool().StringF( HTTP::ESessionId, aSession.GetTable() ), 
+            THTTPHdrVal( aCategory ) );
+    aSession.FilterCollection().AddFilterL(
+        *this, THTTPEvent::EAnyTransactionEvent, MHTTPFilter::EClientFilters, RStringF() );
+    IRLOG_DEBUG( "CIRDataTransferTracker::BindL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// From class MIRDataTransferTracker.
+// Handles raw data transfers.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::RawDataTransferredL( TInt aBytesSent, TInt aBytesReceived, 
+                                                  TIRTransferCategory aCategory )
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::RawDataTransferredL - Entering" );
+    ProcessDataL( aBytesSent, aBytesReceived, aCategory  );
+    IRLOG_DEBUG( "CIRDataTransferTracker::RawDataTransferredL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// From class MHTTPFilter.
+// Invoked when HTTP transactions are sent or received.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::MHFRunL( const RHTTPTransaction aTransaction, const THTTPEvent& aEvent )
+    {
+    IRLOG_DEBUG2( "CIRDataTransferTracker::MHFRunL() - aEvent.iStatus = %d", aEvent.iStatus );
+
+    RHTTPSession session = aTransaction.Session();
+
+    THTTPHdrVal categoryVal;
+
+    if ( session.ConnectionInfo().Property(
+        session.StringPool().StringF( HTTP::ESessionId, session.GetTable() ), categoryVal ) )
+        {
+        TIRTransferCategory category = static_cast<TIRTransferCategory>( categoryVal.Int() );
+
+        switch ( aEvent.iStatus )
+            {
+            case THTTPEvent::EGotResponseHeaders:
+                ParseHeaderSizeL( aTransaction, category );
+                break;
+            case THTTPEvent::EGotResponseBodyData:
+                ParseBodySizeL( aTransaction, category );
+                break;
+            default:
+                break;
+            }
+        }
+    IRLOG_DEBUG( "CIRDataTransferTracker::MHFRunL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Calculates the header field sizes in bytes from the specified headers.
+// ---------------------------------------------------------------------------
+//
+TInt CIRDataTransferTracker::HeaderSize( const RHTTPTransaction aTransaction, 
+                                         RHTTPHeaders aHeaders )
+    {
+    IRLOG_DEBUG( "CIRDataTransferTracker::HeaderSize - Entering" );
+    TInt size = KVRBytesPerHeaderOverhead;
+
+    RStringPool stringPool = aTransaction.Session().StringPool();
+
+    THTTPHdrFieldIter it = aHeaders.Fields();
+    it.First();
+
+    while ( !it.AtEnd() )
+        {
+        RStringF key = stringPool.StringF( it() );
+
+        TPtrC8 ptr;
+        aHeaders.GetRawField( key, ptr );
+
+        size += key.DesC().Size();
+        size += ptr.Size();
+        size += KVRBytesPerHeaderFieldOverhead;
+
+        ++it;
+        }
+    IRLOG_DEBUG( "CIRDataTransferTracker::HeaderSize - Exiting" );
+    return size;
+
+    }
+
+// ---------------------------------------------------------------------------
+// Parses the header sizes ( both request and response ) of the supplied HTTP
+// transaction, logging it under the specified category.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::ParseHeaderSizeL( const RHTTPTransaction aTransaction, 
+                                                  TIRTransferCategory aCategory )
+    {
+    IRLOG_DEBUG2( "CIRDataTransferTracker::ParseHeaderSizeL() - aCategory = %d", static_cast<TInt>( aCategory ) );
+
+    RHTTPSession session = aTransaction.Session();
+
+    // Parse the request size first.
+
+    RHTTPRequest request = aTransaction.Request();
+    TInt sent = HeaderSize( aTransaction, request.GetHeaderCollection() );
+
+    // Now that the actual header fields are parsed we have to manually extract the original request.
+    // The format of the request is "%S %S %S\r\n", where the first string is the method ( GET or POST ),
+    // the second is the full URI of the request, and the third the HTTP protocol version in use.
+
+    RStringF http = session.StringPool().StringF( HTTP::EHttp11, session.GetTable() );
+
+    sent += request.Method().DesC().Size();
+    sent += request.URI().UriDes().Size();
+    sent += http.DesC().Size();
+    sent += KVRBytesPerHeaderSentOverhead;
+
+    // Proceed to parse the response size.
+
+    RHTTPResponse response = aTransaction.Response();
+    TInt received = HeaderSize( aTransaction, response.GetHeaderCollection() );
+
+    // Now that the actual header fields of the response are parsed, we have to manually extract the
+    // original response. The format of the response is "%S %d %S\r\n", where the first string is the HTTP
+    // protocol version, the first integer is the HTTP status code, and the second string is the
+    // status text.
+
+    TBuf8<KDefaultRealWidth> responseCode;
+    responseCode.AppendNum( static_cast<TInt64>( response.StatusCode() ) );
+
+    received += session.StringPool().StringF( HTTP::EHttp11, session.GetTable() ).DesC().Size();
+    received += responseCode.Size(); // Contains the byte for the trailing space as is, so need not add it as overhead below.
+    received += response.StatusText().DesC().Size();
+    received += KVRBytesPerHeaderReceivedOverhead;
+
+    ProcessDataL( sent, received, aCategory );
+    IRLOG_DEBUG( "CIRDataTransferTracker::ParseHeaderSizeL - Exiting" );
+
+    }
+
+// ---------------------------------------------------------------------------
+// Parses the body size of the supplied HTTP transaction, logging it under the specified category.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::ParseBodySizeL(const RHTTPTransaction aTransaction, 
+                                            TIRTransferCategory aCategory )
+    {
+    IRLOG_DEBUG2( "CIRDataTransferTracker::ParseBodySizeL() - aCategory = %d", static_cast<TInt>( aCategory ) );
+
+    ASSERT( aTransaction.Response().HasBody() );
+
+    TPtrC8 ptr;
+    aTransaction.Response().Body()->GetNextDataPart( ptr );
+
+    ProcessDataL( 0, ptr.Size(), aCategory );
+    IRLOG_DEBUG( "CIRDataTransferTracker::ParseBodySizeL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Processes raw data sent and/or received via the connection.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::ProcessDataL( TInt aBytesSent, TInt aBytesReceived, 
+                                           TIRTransferCategory aCategory )
+    {
+    IRLOG_DEBUG4( "CIRDataTransferTracker::ProcessDataL() - aBytesSent = %d, aBytesReceived = %d, aCategory = %d",
+        aBytesSent, aBytesReceived, static_cast<TInt>( aCategory ) );
+
+    switch ( aCategory )
+        {
+        case MIRDataTransferTracker::EIRTransferCategoryIsds:
+            iPckg.iBytesSentIsds += aBytesSent;
+            iPckg.iBytesReceivedIsds += aBytesReceived;
+            break;
+        case MIRDataTransferTracker::EIRTransferCategoryAudio:
+            iPckg.iBytesSentAudio += aBytesSent;
+            iPckg.iBytesReceivedAudio += aBytesReceived;
+            break;
+        default:
+            iPckg.iBytesSentUnknown += aBytesSent;
+            iPckg.iBytesReceivedUnknown += aBytesReceived;
+            break;
+        }
+
+    iPckg.iBytesSentTotal += aBytesSent;
+    iPckg.iBytesReceivedTotal += aBytesReceived;
+
+    iBytesSinceLastNotification += aBytesSent + aBytesReceived;
+
+    DispatchNotificationL();
+    IRLOG_DEBUG( "CIRDataTransferTracker::ProcessDataL() - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Dispatches the notification of the data transfer conditionally.
+// ---------------------------------------------------------------------------
+//
+void CIRDataTransferTracker::DispatchNotificationL()
+    {
+    IRLOG_DEBUG2( "CIRDataTransferTracker::DispatchNotificationL() - iBytesSinceLastNotif = %d",
+        iBytesSinceLastNotification );
+
+    // Set flag when Byte Counter crosses first threshold 0.1 MB
+    if ( bThresholdCrossed == EFalse && 
+        ( iPckg.iBytesSentTotal+iPckg.iBytesReceivedTotal ) >= KIRDataTransferTrackerThreshold )
+        bThresholdCrossed = ETrue;
+
+    // Smooth updates to each 0.01 MB
+    if ( /*bThresholdCrossed == EFalse &&*/ iBytesSinceLastNotification >= KIRDataTransferTrackerMiniThreshold)
+        {
+        if ( iObserver )
+            {
+            iObserver->HandleDataTransferEventL( iPckg );
+            }
+        iBytesSinceLastNotification = 0;
+        }
+
+
+    // Streaming updates to each 0.1 MB afterwards
+    else if ( iBytesSinceLastNotification >= KIRDataTransferTrackerThreshold )
+        {
+        if ( iObserver )
+            {
+            iObserver->HandleDataTransferEventL( iPckg );
+            }
+        iBytesSinceLastNotification = 0;
+        }
+    IRLOG_DEBUG( "CIRDataTransferTracker::DispatchNotificationL() - Exiting" );
+
+    }