browserutilities/feedsengine/FeedsServer/UrlHandler/src/HttpHandler.cpp
changeset 0 dd21522fd290
child 13 10e98eab6f85
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browserutilities/feedsengine/FeedsServer/UrlHandler/src/HttpHandler.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,468 @@
+/*
+* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "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:  A class that fetches resources via HTTP 1.1.
+*
+*/
+
+
+#include <EscapeUtils.h>
+#include <http.h>
+#include <http/mhttpdatasupplier.h>
+#include <HttpFilterCommonStringsAddition.h>
+
+#include "HttpConnection.h"
+#include "HttpHandler.h"
+#include "LeakTracker.h"
+#include "Logger.h"
+
+#include "CUserAgent.h"
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::NewL
+//
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CHttpHandler* CHttpHandler::NewL(CHttpConnection& aHttpConnection)
+    {
+    CHttpHandler* self = new (ELeave) CHttpHandler(aHttpConnection);
+    
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop();
+
+    return self;
+    }
+
+        
+// -----------------------------------------------------------------------------
+// CHttpHandler::CHttpHandler
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CHttpHandler::CHttpHandler(CHttpConnection& aHttpConnection):
+        KTimerPeriod(40000000), iLeakTracker(CLeakTracker::EHttpHandler),
+        iHttpConnection(&aHttpConnection), 
+        iSession(aHttpConnection.Session()), iLastActivity(0), iStatusCode(KErrNone)
+    {
+    iTimerState.iFunction = TimerCallback;
+    iTimerState.iPtr = static_cast<TAny*>(this);
+    }
+        
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::ConstructL()
+    {
+    // Get the http connection.
+    if (!iHttpConnection->IsConnected())
+        {
+        // Set this instance as the connection observer.
+        
+        // TODO: In 3.1 when multiple tasks are supported this needs to be
+        //      changed to AddObserver as multiple tasks may need to be waken.
+        iHttpConnection->SetObserver(this);
+        iIsConnectionObserver = ETrue;
+        }
+
+    iTimer = CPeriodic::NewL(EPriorityHigh);
+    
+    iStringPool = iSession.StringPool();
+    }        
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::~CHttpHandler
+// Deconstructor.
+// -----------------------------------------------------------------------------
+//
+CHttpHandler::~CHttpHandler()
+    {
+    // If this happened to be the instances that is observing the connection 
+    // then clear the connection's observer...
+    if (iIsConnectionObserver)
+        {
+	        if(iHttpConnection)
+	        {
+	        	iHttpConnection->SetObserver(NULL);	
+	        }
+        
+        }
+	RHTTPTransaction temp;
+	if (iTransaction != temp)
+	{
+		// Close the transaction.
+	    iTransaction.Close();	
+	}
+    delete iResponseBuffer;    
+    delete iTimer;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::LoadUrl
+// 
+// Loads the given url -- asynchronously
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::LoadUrlL(const TDesC& aUrl, MLoadObserver& aObserver)
+    {
+    TUriParser8  uriParser;
+    HBufC8*      url;
+
+    // Set the observer.
+    iObserver = &aObserver;
+
+    // Parse the url.
+    url = HBufC8::NewLC(aUrl.Length());
+    url->Des().Copy(aUrl);
+    User::LeaveIfError(uriParser.Parse(*url));
+
+    // Create the transaction.
+    iTransaction = iSession.OpenTransactionL(uriParser, *this,
+		iSession.StringPool().StringF(HTTP::EGET, RHTTPSession::GetTable()));
+
+    // TODO: Set the headers if any.
+	//++PK Add UA header code
+	
+	iUserAgentHeader = iTransaction.Request().GetHeaderCollection();
+	
+	CUserAgent* tWebUtilsStandardUA = CUserAgent::NewL();
+    CleanupStack::PushL(tWebUtilsStandardUA);
+	
+	HBufC8* tWebUtilsStandardUAHeaderValue = tWebUtilsStandardUA->UserAgentL();	
+	
+	RStringF tStringValue = iSession.StringPool().OpenFStringL(*tWebUtilsStandardUAHeaderValue);	
+	
+	THTTPHdrVal tHeaderValue( tStringValue );
+	
+	RStringF tStringUA = iSession.StringPool().StringF(HTTP::EUserAgent, RHTTPSession::GetTable());
+	
+	iUserAgentHeader.SetFieldL( tStringUA, tStringValue );
+	
+	//++PK
+    // Submit the request.
+	iTransaction.SubmitL();
+
+    // If the connection is available it is safe to tell to the observer
+    // to display progress indicators (i.e. a wait-dialog).
+    if (iHttpConnection->IsConnected())
+        {
+        iObserver->StartWait();
+        
+        // Start the inactive connection timer as well.
+        iTimer->Start(KTimerPeriod, KTimerPeriod, iTimerState);
+        iLastActivity.HomeTime();
+        }
+
+    CleanupStack::PopAndDestroy(tWebUtilsStandardUA);
+    CleanupStack::PopAndDestroy(url);
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::ConnectionAvailable
+// 
+// Notifies the observer that the connection is available.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::ConnectionAvailable()
+    {
+    // Now that the connection is available it is safe to tell to the observer
+    // to display progress indicators (i.e. a wait-dialog).
+    iObserver->StartWait();
+
+    // Start the inactive connection timer.
+    iTimer->Start(KTimerPeriod, KTimerPeriod, iTimerState);
+    iLastActivity.HomeTime();
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::ConnectionFailed
+// 
+// Notifies the observer that the establishment of the connection failed.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::ConnectionFailed(TInt aStatus)
+    {
+    // Cancel the transaction.
+    iTransaction.Cancel();
+		aStatus = -aStatus;
+    // Notify the observer.
+    LoadCompleted(aStatus, NULL, KNullDesC, KNullDesC);  
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::MHFRunL
+// 
+// Called when the filter's registration conditions are satisfied for events that
+// occur on a transaction.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
+	{
+    // Some kind of connection activity occurred so update iLastActivity.
+    iLastActivity.HomeTime();
+
+	switch (aEvent.iStatus)
+		{
+		case THTTPEvent::EGotResponseHeaders:
+			{
+			RHTTPResponse  resp = aTransaction.Response();
+
+            // Get the headers
+            iRespHeaders = resp.GetHeaderCollection();
+			}
+			break;
+
+		case THTTPEvent::EGotResponseBodyData:
+			{
+			MHTTPDataSupplier*  dataSupplier;
+			TPtrC8              ptr;
+
+			// Get the data.
+            dataSupplier = aTransaction.Response().Body();
+			dataSupplier->GetNextDataPart(ptr);
+            CleanupStack::PushL(TCleanupItem(&CleanupDataSupplier, dataSupplier));
+
+			// Append to iResponseBuffer
+			if (iResponseBuffer == NULL)
+				{
+				iResponseBuffer = ptr.AllocL();
+				}
+			else
+				{
+				iResponseBuffer = iResponseBuffer->ReAllocL(iResponseBuffer->Length() + ptr.Length());
+				iResponseBuffer->Des().Append(ptr);
+				}
+
+			// Release the body data.
+            CleanupStack::PopAndDestroy(/*dataSupplier*/);
+			}
+			break;
+
+		case THTTPEvent::ESucceeded:
+			{
+			TDesC*   contentType = NULL;
+            TDesC*   charSet = NULL;
+            
+            // Get the content-type and char-set.
+            GetContentTypeL(contentType, charSet);
+
+            // Pass the buffer to the observer.
+			LoadCompleted(KErrNone, iResponseBuffer, *contentType, *charSet);
+            iResponseBuffer = NULL;
+
+            delete contentType;
+            contentType = NULL;
+            delete charSet;
+            charSet = NULL;
+			}
+			break;
+
+        case THTTPEvent::EFailed:
+        case THTTPEvent::EUnrecoverableError:
+        	{
+        	// Notify the observer.
+        	RHTTPResponse  resp = aTransaction.Response();
+        	TInt statusCode = resp.StatusCode();
+        	if(iStatusCode == KErrNone)
+        		{
+        		iStatusCode = (statusCode + 20000); // Web server HTTP status code are lesser than -20000
+        		}
+        	if(iStatusCode == -(KErrNoMemory)) //KErrNoMemory is not HTTP error so it should be -ve
+        		{
+        		iStatusCode = KErrNoMemory;
+        		}
+            LoadCompleted( iStatusCode, NULL, KNullDesC, KNullDesC );  
+
+            // Clean up the response buffer.
+            delete iResponseBuffer;
+            iResponseBuffer = NULL;
+        	}
+            break;
+
+        default:
+            FEED_LOG1(_L("Feeds"), _L("Feeds_Errors.log"), 
+                    EFileLoggingModeAppend, _L("CHttpHandler::MHFRunL - Failure: default %d."), aEvent.iStatus);
+            if(aEvent.iStatus < KErrNone)
+            	{
+            	// HTTP errors should always be +ve
+            	iStatusCode = -(aEvent.iStatus); 
+            	}
+			break;
+		}
+	}
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::MHFRunError
+// 
+// Called when RunL leaves from a transaction event. This works in the same
+// way as CActve::RunError; return KErrNone if you have handled the error.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpHandler::MHFRunError(TInt aError, RHTTPTransaction /*aTransaction*/, 
+        const THTTPEvent& /*aEvent*/)
+	{
+    FEED_LOG1(_L("Feeds"), _L("Feeds_Errors.log"), 
+            EFileLoggingModeAppend, _L("CHttpHandler::MHFRunError: %d."), aError);
+
+    // Notify the observer.
+	LoadCompleted(aError, NULL, KNullDesC, KNullDesC);
+
+    // Clean up the response buffer.
+    delete iResponseBuffer;
+    iResponseBuffer = NULL;
+
+    return KErrNone;
+	}
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::CleanupDataSupplier
+// 
+// Cleanup stack callback method to release a DataSupplier.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::CleanupDataSupplier(TAny *aPtr)
+    {
+    MHTTPDataSupplier*  supplier = static_cast<MHTTPDataSupplier*>(aPtr);
+
+    // Release it
+	supplier->ReleaseData();
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::GetContentTypeL
+// 
+// Get the content-type and char-encoding from the response header.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::GetContentTypeL(TDesC*& aContentType, TDesC*& aCharSet)
+    {
+    RStringF     fieldName;
+    RStringF     fieldParam;
+    THTTPHdrVal  value;
+
+    // Get the content-type.
+    fieldName = iStringPool.StringF(HTTP::EContentType, RHTTPSession::GetTable());
+    if (iRespHeaders.GetField(fieldName, 0, value) == KErrNone)
+        {
+        aContentType = EscapeUtils::ConvertToUnicodeFromUtf8L(value.StrF().DesC());
+        CleanupStack::PushL(aContentType);
+        }
+
+    // Get the char-encoding.
+    fieldParam = iStringPool.StringF(HTTP::ECharset, RHTTPSession::GetTable());
+    if (iRespHeaders.GetParam(fieldName, fieldParam, value) == KErrNone)
+        {
+        aCharSet = EscapeUtils::ConvertToUnicodeFromUtf8L(value.StrF().DesC());
+        CleanupStack::PushL(aCharSet);
+        }
+
+    if (aContentType == NULL)
+        {
+        aContentType = KNullDesC().AllocL();
+        CleanupStack::PushL(aContentType);
+        }
+
+    if (aCharSet == NULL)
+        {
+        aCharSet = KNullDesC().AllocL();
+        CleanupStack::PushL(aCharSet);
+        }
+
+    CleanupStack::Pop(2);
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::LoadComplete
+// 
+// Passes the status code and responseBody to the observer.  The observer
+// adopts aResponseBody.
+// -----------------------------------------------------------------------------
+//
+void CHttpHandler::LoadCompleted(TInt aStatusCode, TDesC8* aResponseBody,
+        const TDesC& aContentType, const TDesC& aCharSet)
+    {
+    delete iTimer;
+    iTimer = NULL;
+
+    RHTTPTransaction temp;
+    if (iTransaction != temp)
+        {
+		    // Close the transaction.
+        iTransaction.Close();	
+        }
+    if (iIsConnectionObserver)
+        {
+        if(iHttpConnection)
+            {
+       	    iHttpConnection->SetObserver(NULL);	
+            }
+        iIsConnectionObserver = EFalse;
+        }
+
+    // Pass the buffer to the observer.
+    iObserver->LoadCompleted(aStatusCode, aResponseBody, aContentType, aCharSet);
+
+    if (aStatusCode != KErrNone)
+        {
+        FEED_LOG1(_L("Feeds"), _L("Feeds_Errors.log"), 
+                EFileLoggingModeAppend, _L("CHttpHandler::LoadCompleted: %d."), aStatusCode);
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpHandler::TimerCallback
+// 
+// The timer's callback used to abort connections that stop responding.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpHandler::TimerCallback(TAny* aPtr)
+    {
+    CHttpHandler*  self = static_cast<CHttpHandler*>(aPtr);
+    TTime          now;
+
+    // Get the current time.
+    now.HomeTime();
+
+    // If the connection is inactive then delete the timer and cancel the load.
+    if ((self->iLastActivity + self->KTimerPeriod) < now)
+        {
+        delete self->iTimer;
+        self->iTimer = NULL;
+
+        FEED_LOG(_L("Feeds"), _L("Feeds_Errors.log"), 
+                EFileLoggingModeAppend, _L("CHttpHandler::TimerCallback. TIMEDOUT"));
+
+        // Cancel the transaction.
+        self->iTransaction.Cancel();
+
+        // Notify the observer.
+        self->LoadCompleted(KErrTimedOut, NULL, KNullDesC, KNullDesC);  
+        }
+
+    return KErrNone;
+    }