diff -r 000000000000 -r b16258d2340f applayerpluginsandutils/httpprotocolplugins/httpclient/chttpclientfilter.cpp --- /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 +#include +#include +#include + +#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; + }