applayerpluginsandutils/httpprotocolplugins/httpclient/chttpclientfilter.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpclient/chttpclientfilter.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,312 @@
+// 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 "chttpclientfilter.h"
+
+#include <httpstringconstants.h>
+#include <http/rhttpheaders.h>
+#include <http/mhttpdatasupplier.h>
+#include <httperr.h>
+
+#include "thttpclientpanic.h"
+
+CHttpClientFilter::~CHttpClientFilter()
+	{
+	// If this object has been destroyed from the cleanup stack during creation
+	// of the object, it might still be loaded - check. Normaly the delete is 
+	// initiated by the 'delete this' in MHFUnload.
+	if( iLoadCount )
+		{
+		// As already in a destructor, MHFUnload must not delete this again.
+		iLoadCount = -1;
+		RStringF filterName = iStringPool.StringF(HTTP::EHttpClientFilter, iStringTable);
+		iSession.FilterCollection().RemoveFilter(filterName);
+		}
+	}
+
+CHttpClientFilter::CHttpClientFilter()
+:CBase(), iStringTable(RHTTPSession::GetTable())
+	{
+	}
+
+void CHttpClientFilter::ConstructL(RHTTPSession aSession)
+	{
+	iSession = aSession;
+	iStringPool = iSession.StringPool();
+
+	// Register the filter for ESubmit to check and alter outgoing headers and
+	// EGotResponseHeaders for altering incoming response headers to add 
+	// defaults if they are missing, e.g. add Content-Type with value 
+	// application/octet-stream if it is missing. Also, register for the two
+	// pipelining error codes - this is to be able to cancel and re-submit those 
+	// transactions.
+	RStringF filterName = iStringPool.StringF(HTTP::EHttpClientFilter, iStringTable);
+	iSession.FilterCollection().AddFilterL(
+										  *this, 
+										  THTTPEvent::ESubmit, 
+										  MHTTPFilter::EProtocolHandler + 20,
+										  filterName
+										  );
+	iSession.FilterCollection().AddFilterL(
+										  *this, 
+										  THTTPEvent::EGotResponseHeaders, 
+										  MHTTPFilter::EProtocolHandler + 20,
+										  filterName
+										  );
+	iSession.FilterCollection().AddFilterL(
+										  *this, 
+										  KErrHttpNonPipeliningError, 
+										  MHTTPFilter::EProtocolHandler + 20,
+										  filterName
+										  );
+	iSession.FilterCollection().AddFilterL(
+										  *this, 
+										  KErrHttpPipeliningError, 
+										  MHTTPFilter::EProtocolHandler + 20,
+										  filterName
+										  );
+	}
+
+void CHttpClientFilter::AlterRequestHeadersL(RHTTPTransaction aTransaction)
+	{
+	AddSessionHeadersL(aTransaction.Request().GetHeaderCollection(), iSession.RequestSessionHeadersL());
+
+	EnsurePathExistsL(aTransaction);
+	EnsureContentLengthL(aTransaction);
+	EnsureNoEndToEndHeadersInConnectionHeaderL(aTransaction);
+//removed the following Expect100 line to fix INC45389 & INC42505 - please do not add back in without checking these defects
+//	AddExpect100ContinueL(aTransaction);
+	}
+
+void CHttpClientFilter::AlterResponseHeadersL(RHTTPTransaction aTransaction)
+	{
+	AddSessionHeadersL(aTransaction.Response().GetHeaderCollection(), iSession.ResponseSessionHeadersL());
+
+	EnsureContentTypePresentL(aTransaction);
+	}
+
+
+void CHttpClientFilter::EnsurePathExistsL(RHTTPTransaction aTransaction)
+	{
+	RHTTPRequest request = aTransaction.Request();
+	TUriC8 originalUri = request.URI();
+
+	// There is alwaya a path in a uri - ensure that it is not empty.
+	const TDesC8& path = originalUri.Extract(EUriPath);
+	TBool pathPresent = (path.Length() > 0);
+	TBool hostPresent = originalUri.IsPresent(EUriHost);
+
+	// If there is no path then make sure the uri
+	// ends in '/' - server root path.
+	if( hostPresent && !pathPresent )
+		{
+		_LIT8(KDefaultUriPath, "/");
+
+		CUri8* uri = CUri8::NewLC(originalUri);
+		uri->SetComponentL(KDefaultUriPath, EUriPath);
+		request.SetURIL(uri->Uri());
+		CleanupStack::PopAndDestroy(uri);
+		}
+	}
+
+void CHttpClientFilter::EnsureContentLengthL(RHTTPTransaction aTransaction)
+	{
+	RHTTPRequest request = aTransaction.Request();
+
+	if( request.HasBody() )
+		{
+		THTTPHdrVal hdrVal;
+		RStringF teName = iStringPool.StringF(HTTP::ETransferEncoding, iStringTable);
+
+		// Get rid of Content-Length header if present
+		// NOTE - cannot use a local variable for the Content-Length as causes a 
+		// compiler bug in thumb builds - grand! : (
+		RHTTPHeaders headers = request.GetHeaderCollection();
+		headers.RemoveField(iStringPool.StringF(HTTP::EContentLength, iStringTable)); 
+		
+		TInt bodySize = request.Body()->OverallDataSize();
+		if( bodySize != KErrNotFound )
+			{
+			// Size is known - set the ContentLength header.
+			headers.SetFieldL(iStringPool.StringF(HTTP::EContentLength, iStringTable), THTTPHdrVal(bodySize));
+			}
+		else if( headers.GetField(teName, 0, hdrVal) == KErrNotFound )
+			{
+			// Size is unknown and there's been no Encoding indicated by the 
+			// client - set the 'TransferEncoding: chunked'
+			hdrVal.SetStrF(iStringPool.StringF(HTTP::EChunked, iStringTable));
+			headers.SetFieldL(teName, hdrVal);
+			}
+		}
+	}
+
+void CHttpClientFilter::EnsureNoEndToEndHeadersInConnectionHeaderL(RHTTPTransaction aTransaction)
+	{
+	RHTTPHeaders headers = aTransaction.Request().GetHeaderCollection();
+	RStringF connection = iStringPool.StringF(HTTP::EConnection,iStringTable);
+	const TInt numConnectionHeaderParts = headers.FieldPartsL(connection);
+
+	for( TInt ii = numConnectionHeaderParts - 1; ii >= 0; --ii ) 
+		{
+		// Examine connection-tokens from back to front so index is always valid. 
+		// Check for an end to end header and remove it as a connection header
+		// must not contain end to end headers.
+		THTTPHdrVal value;
+		TInt ret = headers.GetField(connection, ii, value);
+
+		if( ( ret != KErrNotFound ) && ( value.Type() == THTTPHdrVal::KStrFVal ) )
+			{
+			RStringF valueStrF = value.StrF();
+			if( valueStrF.Index(iStringTable) != HTTP::EClose  && 
+				!IsHopByHopHeader(valueStrF) )
+				{
+				// The connection-token is not 'close' nor is it a end-to-end
+				// header field name - remove it.
+				User::LeaveIfError(headers.RemoveFieldPart(connection, ii)); 
+				}
+			}
+		else
+			{
+			// The connection-token is not a hop-by-hop header field name -
+			// remove it.
+			User::LeaveIfError(headers.RemoveFieldPart(connection, ii));
+			}			
+		}
+	}
+
+TBool CHttpClientFilter::IsHopByHopHeader(RStringF aHeaderName)
+	{
+	switch( aHeaderName.Index(iStringTable) )
+		{
+	case HTTP::EKeepAlive:
+	case HTTP::EProxyAuthorization:
+	case HTTP::EProxyAuthenticate:
+	case HTTP::ETE:
+	case HTTP::ETrailer:
+	case HTTP::ETransferEncoding:
+	case HTTP::EUpgrade:
+		return ETrue;
+	default:
+		// NOT a hop by hop header, at least not as defined in http 1.1
+		return EFalse;
+		};
+	}
+
+void CHttpClientFilter::EnsureContentTypePresentL(RHTTPTransaction aTransaction)
+	{
+	// From RFC 2616 Section 7.2.1 - 
+	//
+	// Any HTTP/1.1 message containing an entity-body SHOULD include a Content-Type
+	// header field defining the media type of that body. If and only if the 
+	// media type is not given by a Content-Type field, the recipient MAY attempt
+	// to guess the media type via inspection of its content and/or the name 
+	// extension(s) of the URI used to identify the resource. If the media type
+	// remains unknown, the recipient SHOULD treat it as type "application/octet-stream".
+
+	RHTTPHeaders headers = aTransaction.Response().GetHeaderCollection();
+	THTTPHdrVal contentType;
+	RStringF contentTypeString = iStringPool.StringF(HTTP::EContentType, iStringTable);
+	if( headers.GetField(contentTypeString, 0, contentType) == KErrNotFound )
+		{
+		// There is no Content-Type header - add it with a value of 
+		// "application/octet-stream".
+		contentType.SetStrF(iStringPool.StringF(HTTP::EApplicationOctetStream, iStringTable));
+		headers.SetFieldL(contentTypeString, contentType);
+		}
+	}
+	 
+
+void CHttpClientFilter::AddSessionHeadersL(RHTTPHeaders aTransactionHeaders, RHTTPHeaders aSessionHeaders)
+	{
+	THTTPHdrFieldIter iter = aSessionHeaders.Fields();
+
+	while( !iter.AtEnd() )
+		{
+		RStringTokenF headerToken = iter();
+		RStringF headerName = iStringPool.StringF(headerToken);
+
+		// See if transaction headers already contains this header.
+		TPtrC8 rawData;
+		if( aTransactionHeaders.GetRawField(headerName, rawData) == KErrNotFound )
+			{
+			aSessionHeaders.GetRawFieldL(headerName,rawData);
+			aTransactionHeaders.SetRawFieldL(headerName, rawData, KFieldSeparator);
+			}
+		++iter;
+		}
+	}
+
+/*
+ *	Methods from MHTTPFilterBase
+ */
+
+void CHttpClientFilter::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
+	{
+	switch( aEvent.iStatus )
+		{
+	case THTTPEvent::ESubmit:
+		{
+		AlterRequestHeadersL(aTransaction);
+		} break;
+	case THTTPEvent::EGotResponseHeaders:
+		{
+		AlterResponseHeadersL(aTransaction);
+		} break;
+	case KErrHttpNonPipeliningError:
+	case KErrHttpPipeliningError:
+		{
+		// This transaction was being pipelined when an error occurred. Need to
+		// cancel and then re-submit.
+		aTransaction.Cancel();
+		aTransaction.SubmitL();	
+		}
+		break;
+	default:
+		break;
+		}
+	}
+
+void CHttpClientFilter::MHFSessionRunL(const THTTPSessionEvent& /*aEvent*/)
+	{
+	}
+
+TInt CHttpClientFilter::MHFRunError(TInt /*aError*/, RHTTPTransaction aTransaction, const THTTPEvent& /*aEvent*/)
+	{
+	// Something has gone wrong - probably no memory. Fail the transaction.
+	aTransaction.Fail();
+	return KErrNone;
+	}
+
+TInt CHttpClientFilter::MHFSessionRunError(TInt aError, const THTTPSessionEvent& /*aEvent*/)
+	{
+	return aError;
+	}
+
+/*
+ *	Methods from MHTTPFilter
+ */
+
+void CHttpClientFilter::MHFLoad(RHTTPSession, THTTPFilterHandle)
+	{
+	++iLoadCount;
+	}
+
+void CHttpClientFilter::MHFUnload(RHTTPSession /*aSession*/, THTTPFilterHandle)
+	{
+	if( --iLoadCount )
+		return;
+
+	delete this;
+	}