applayerpluginsandutils/httpprotocolplugins/httpheadercodec/chttpclientheaderwriter.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpheadercodec/chttpclientheaderwriter.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,570 @@
+// Copyright (c) 2002-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 "chttpclientheaderwriter.h"
+
+#include <httpstringconstants.h>
+#include <http/rhttpsession.h>
+#include <httperr.h>
+#include <inetprottextutils.h>
+
+#include "CHeaderField.h"
+
+_LIT8(KSemiSpaceSep,"; ");
+_LIT8(KCodecSpace, " ");
+
+const TInt KMaxNumPortDigits			= 5;
+const TInt KMaxNumPortValue = 0xffff;	// RFC 793
+
+const TInt KNormalAcceptHeaderLen = 96;
+const TInt KNormalAcceptCharsetHeaderLen = 64;
+const TInt KNormalAcceptEncodingHeaderLen = 64;
+const TInt KNormalAcceptLanguageHeaderLen = 64;
+CHttpClientHeaderWriter* CHttpClientHeaderWriter::NewL(RStringPool aStringPool)
+/** 
+	Factory constructor.
+	@internalComponent
+	@param		aStringPool	The current string pool.
+	@return		A pointer to a fully initialised object.
+	@leave		KErrNoMemory	Not enough memory to create object.
+*/
+	{
+	return new (ELeave) CHttpClientHeaderWriter(aStringPool);
+	}
+
+CHttpClientHeaderWriter::~CHttpClientHeaderWriter()
+/**
+	Destructor
+	@internalComponent
+*/
+	{
+	}
+
+CHttpClientHeaderWriter::CHttpClientHeaderWriter(RStringPool aStringPool)
+: CHttpHeaderWriter(aStringPool)
+/**
+	Constructor
+	@internalComponent
+	@param		aStringPool	The current string pool.
+*/	{
+	}
+
+void CHttpClientHeaderWriter::EncodeAcceptL(RHeaderField& aHeader) const
+/**
+	Encodes the accept header. RFC2616 section 14.1 - 
+  
+		Accept			   =	"Accept" ":" #( media-range [ accept-params ] )
+							
+		media-range			=	( "*" "/" "*"
+							|	( type "/" "*" )
+							|	( type "/" subtype )
+							)	*( ";" parameter )
+
+		accept-params		=	";" "q" "=" qvalue *( accept-extension )
+		accept-extension	=	";" token [ "=" ( token | quoted-string ) ]
+
+	The accept header value is a comma separated list of values that may be
+	empty.
+	@internalComponent
+	@param		aHeader	The accept header field to encode.
+	@leave		CHttpWriter::DoGeneralAcceptHeaderL
+	@todo		Is this function needed at all - go straight to DoGeneralAcceptHeaderL?
+*/
+	{
+	DoGeneralAcceptHeaderL( KNormalAcceptHeaderLen,
+						  aHeader, 
+						  iStringPool.StringF(HTTP::EAccept, iStringTable),
+						  KErrHttpEncodeAccept
+						  );
+	}
+
+void CHttpClientHeaderWriter::EncodeAcceptCharsetL(RHeaderField& aHeader) const
+/**
+	Encodes the accept-charset header. RFC2616 section 14.2 - 
+	
+		Accept-Chartset		=	"Accept-Charset" ":" 1#( ( charset | "*" ) [ ";" "q" "=" qvalue ] )
+
+	The accept-charset header value is a comma separated list of values that has
+	at least one value.
+	@internalComponent
+	@param		aHeader	The accept-charset header field to encode.
+	@leave		CHttpWriter::DoGeneralAcceptHeaderL
+	@todo		Is this function needed at all - go straight to DoGeneralAcceptHeaderL?
+*/
+	{
+	DoGeneralAcceptHeaderL( KNormalAcceptCharsetHeaderLen,
+						  aHeader,
+						  iStringPool.StringF(HTTP::EAcceptCharset,iStringTable),
+						  KErrHttpEncodeAcceptCharset
+						  );
+	}
+
+void CHttpClientHeaderWriter::EncodeAcceptLanguageL(RHeaderField& aHeader) const
+/**
+	Encodes the accept-language header. RFC2616 section 14.4 - 
+	
+		Accept-Language		=	"Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] )
+
+		language-range		= ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) ! "*" )
+
+	The accept-language header value is a comma separated list of values that has
+	at least one value.
+	@internalComponent
+	@param		aHeader	The accept-language header field to encode.
+	@leave		CHttpWriter::DoGeneralAcceptHeaderL
+	@todo		Is this function needed at all - go straight to DoGeneralAcceptHeaderL?
+*/
+	{
+	DoGeneralAcceptHeaderL(KNormalAcceptLanguageHeaderLen,
+						  aHeader,
+						  iStringPool.StringF(HTTP::EAcceptLanguage,iStringTable),
+						  KErrHttpEncodeAcceptLanguage
+						  );
+	}
+
+void CHttpClientHeaderWriter::EncodeAuthorizationL(RHeaderField& aHeader) const
+/**
+	Encodes the authorization header. RFC2616 section 14.4 - 
+	
+		Authorization		=	"Authorization" ":" credentials
+		credentials			=	auth-scheme #auth-param
+							|	"Digest" digest-response
+							|	"Basic" basic-credentials
+
+		basic-credentials	=	base64-user-pass
+		base64-user-pass	=	<base64 [4] encoding of user-pass, except not limited to 76 char/line>
+		user-pass			=	userid ":" password
+		userid				=	*<TEXT excluding ":">
+		password			=	*TEXT
+		
+		digest-response		=	1#( username | realm | nonce | digest-uri |	response |
+								[ algorithm ] | [ cnonce ] |
+								[ opaque ] | [ message-qop ] |
+								[ nonce-count ] | [ auth-param ] )
+
+		username			=	"username" "=" username-value
+		username-value		=	quoted-string
+		digest-uri			=	"uri" "=" digest-uri-value
+		digest-uri-value	=	request-uri ; As specified by HTTP/1.1
+		message-qop			=	"qop" "=" qop-value
+		cnonce				=	"cnonce" "=" cnonce-value
+		cnonce-value		=	nonce-value
+		nonce-count			=	"nc" "=" nc-value
+		nc-value			=	8LHEX
+		response			=	"response" "=" request-digest
+		request-digest		=	<"> 32LHEX <">
+		LHEX				=	"0" | "1" | "2" | "3" |
+								"4" | "5" | "6" | "7" |
+								"8" | "9" | "a" | "b" |
+								"c" | "d" | "e" | "f"
+	
+	Encoding here is interpreted very simply. Parts are encoded with no 
+	punctuation after them and parameters are comma separated with the value in
+	quotes. This means that for basic authentication, part 1 must be 'Basic' and
+	part 2 is the credentials. For digest, part1 is 'Digest', and the 
+	digest-response is stored in parameters.
+	@internalComponent
+	@param		aHeader	The authorization header field to encode.
+	@leave		RHeaderField::BeginRawDataL
+	@leave		RHeaderField::PartsL
+	@leave		RHeaderField::WriteRawDataL
+	@leave		KErrHttpEncodeAuthorization	There were no parts, or the part
+											value type or the parameter value 
+											type was not a string or a folded 
+											string.
+*/
+	{
+	__START_PERFORMANCE_LOGGER();
+	// Check part 1
+	aHeader.BeginRawDataL();
+	THeaderFieldPartIter iter = aHeader.PartsL();
+	for( iter.First(); !iter.AtEnd(); )
+		{
+		const CHeaderFieldPart* part = iter();
+		if( part == NULL )
+			{
+			// No parts!!
+			User::Leave(KErrHttpEncodeAuthorization);
+			}
+		THTTPHdrVal ptVal = part->Value();
+		switch( ptVal.Type() )
+			{
+		case THTTPHdrVal::KStrFVal:
+			{
+			aHeader.WriteRawDataL(ptVal.StrF().DesC());
+			} break;
+		case THTTPHdrVal::KStrVal:
+			{
+			aHeader.WriteRawDataL(ptVal.Str().DesC());
+			} break;			
+		default:
+			User::Leave(KErrHttpEncodeAuthorization);
+			break;
+			}
+
+		// Now output the params
+		THeaderFieldParamIter paramIter = part->Parameters();
+		for( paramIter.First(); !paramIter.AtEnd(); )
+			{
+			const  TInt param = paramIter()->Name().Index(iStringTable);
+
+			const TBool quoted = (param != HTTP::ENc && param != HTTP::EAlgorithm  && param != HTTP::EUri  && param != HTTP::EStale);
+			aHeader.WriteRawDataL(' ');
+			aHeader.WriteRawDataL(paramIter()->Name().DesC());
+			aHeader.WriteRawDataL('=');
+			
+			if( quoted ) 
+                {
+                aHeader.WriteRawDataL('"');
+                }
+
+			THTTPHdrVal paramVal = paramIter()->Value();
+			switch (paramVal.Type())
+				{
+			case THTTPHdrVal::KStrFVal:
+				{
+				aHeader.WriteRawDataL(paramVal.StrF().DesC());
+				} break;				
+			case THTTPHdrVal::KStrVal:
+				{
+				aHeader.WriteRawDataL(paramVal.Str().DesC());
+				} break;				
+			default:
+				User::Leave(KErrHttpEncodeAuthorization);
+				break;
+				}
+			if( quoted )
+				{
+				aHeader.WriteRawDataL('"');
+				}
+
+			
+			++paramIter;
+			if (!paramIter.AtEnd())
+				{
+				// This is not the last param so add a comma
+				aHeader.WriteRawDataL(',');
+				}
+			}
+		++iter;
+		if( !iter.AtEnd() )
+			{
+			// This is not the last part so add a space.
+			aHeader.WriteRawDataL(' ');
+			}
+		}
+	aHeader.CommitRawData();
+	__END_PERFORMANCE_LOGGER(_L(",CHttpClientHeaderWriter::EncodeAuthorizationL()"));
+	}
+
+void CHttpClientHeaderWriter::EncodeHostL(RHeaderField& aHeader) const
+/**
+	Encodes the host header. RFC2616 section 14.23
+
+		Host			=	"Host" ":" host [ ":" port ]
+
+	The host header value may be empty.
+	@internalComponent
+	@param		aHeader	The host header field to encode.
+	@leave		RHeaderField::BeginRawDataL
+	@leave		RHeaderField::PartsL
+	@leave		RHeaderField::WriteRawDataL
+	@leave		KErrHttpEncodeHost	There were no parts, or the part value type
+									or the parameter value type was not a string
+									or a folded string.
+*/
+	{
+	__START_PERFORMANCE_LOGGER();
+	// Our convention will be that a string called HTTP::EPort will be used to
+	// set a parameter holding the integer port number
+	// Check part 1
+	THeaderFieldPartIter iter1 = aHeader.PartsL();
+	iter1.First();
+	if( iter1.AtEnd() )
+		{
+		User::Leave(KErrHttpEncodeHost);
+		}
+	const CHeaderFieldPart* part = iter1();
+	if( part == NULL)
+		{
+		User::Leave(KErrHttpEncodeHost);
+		}
+	THTTPHdrVal pt1val = part->Value();
+	if( pt1val.Type() != THTTPHdrVal::KStrFVal )
+		{
+		User::Leave(KErrHttpEncodeHost);
+		}
+	// Write the host string
+	RStringF hostStr = pt1val.StrF();
+	aHeader.BeginRawDataL();
+	aHeader.WriteRawDataL(hostStr.DesC());
+
+	// Check for a port number
+	THeaderFieldParamIter iter2 = part->Parameters();
+	iter2.First();
+	if( !iter2.AtEnd() )
+		{
+		// Got a parameter - if its the port, check then write it
+		const CHeaderFieldParam* param = iter2();
+		if( !param )
+			{
+			User::Leave(KErrHttpEncodeHostPort);
+			}
+		if( param->Name() == iStringPool.StringF(HTTP::EPort,iStringTable) )
+			{
+			// Get the port value
+			THTTPHdrVal portVal = param->Value();
+			if (portVal.Type() != THTTPHdrVal::KTIntVal)
+				{
+				User::Leave(KErrHttpEncodeHostPort);
+				}
+			TBuf8<KMaxNumPortDigits> portDesC;
+			const TInt portValue = portVal.Int();
+			if(portValue > KMaxNumPortValue) 
+				{
+				User::Leave(KErrHttpEncodeHostPort);
+				}
+			portDesC.Num(portValue);
+			aHeader.WriteRawDataL(':');
+			aHeader.WriteRawDataL(portDesC);
+			}
+		}
+	aHeader.CommitRawData();
+	__END_PERFORMANCE_LOGGER(_L(",CHttpClientHeaderWriter::EncodeHostL()"));
+	}
+
+void CHttpClientHeaderWriter::EncodeTEL(RHeaderField& aHeader) const
+/**
+	Encode the TE header. RFC2616 section 14.39.
+
+		TE			=	"TE" ":" #( t-codings )
+		t-codings	=	"trailers" | ( transfer-extension [ accept-params ] )
+	The TE header field value is comma separated list that may be empty.
+	@internalComponent
+	@param		aHeader	The host header field to encode.
+	@leave		CHttpWriter::DoGeneralAcceptHeaderL
+*/
+	{       
+	DoGeneralAcceptHeaderL(KHttpDefaultRawChunkSize, aHeader, iStringPool.StringF(HTTP::ETE,iStringTable), KErrHttpEncodeTE);
+	}
+
+void CHttpClientHeaderWriter::EncodeUserAgentL(RHeaderField& aHeader) const
+	{
+	// User-Agent     = "User-Agent" ":" 1*( product | comment )
+	//  Example: User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+	DoTokenListHeaderL(aHeader, KCodecSpace, KErrHttpEncodeUserAgent);
+	}
+
+void CHttpClientHeaderWriter::EncodeCookieL(RHeaderField& aHeader) const
+	{
+		__START_PERFORMANCE_LOGGER();
+	// NETSCAPE Persistent Client State HTTP Cookies (http://www.netscape.com/newsref/std/cookie_spec.html)
+	// Cookie Header Line = "Cookie: " 1*COOKIE
+	// COOKIE = NAME=VALUE
+
+	// RFC 2965 HTTP State Management Mechanism
+	// cookie = "Cookie" cookie-version 1* ((";" | ",") cookie-value)
+	// cookie-value = NAME "=" VALUE [";" path] [";" domain] [";" port]
+	// NAME = attr
+	// VALUE = value
+	// path = "$Path" "=" value
+	// domain = "$Domain" "=" value
+	// port = "$Port" ["=" <"> value <">]
+	
+
+	// cookies are stored internally as parts in a header (the cookie header);
+	// each header part has a number of parameters which indicate parts of the cookie header. Netscape style cookies do not have
+	// a version parameter. If we get a netscape style cookie then only the name and value get written out.
+
+	// Otherwise the cookie will be either a RFC 2109 or RFC 2965 style cookie which can be treated identically
+	
+	THeaderFieldPartIter iter = aHeader.PartsL();
+	iter.First();
+	if (iter.AtEnd())
+		User::Leave(KErrHttpEncodeCookie);
+
+	TBool usingNetscapeStyleCookies = ETrue;
+	TBool setVersion = EFalse;
+	
+	aHeader.BeginRawDataL();
+	while(!iter.AtEnd())
+		{
+		// Write out one cookie
+		const CHeaderFieldPart* part = iter();
+		if (part == NULL)
+			User::Leave(KErrHttpEncodeCookie);
+		
+		if (!setVersion)
+			{
+			setVersion = ETrue;
+			RStringF version = iStringPool.StringF(HTTP::EVersion,iStringTable);
+			THeaderFieldParamIter paramIter = part->Parameters();
+			paramIter.First();
+			while (!paramIter.AtEnd())
+				{
+				const CHeaderFieldParam* param = paramIter();
+				if (param->Name() == version)
+					{
+					usingNetscapeStyleCookies = EFalse;
+					EncodeOneCookieAttributeL(aHeader, *param);
+					aHeader.WriteRawDataL(KSemiSpaceSep);
+					break;
+					}
+				++paramIter;
+				}
+			}
+		EncodeOneCookieL(aHeader, *part, usingNetscapeStyleCookies);
+
+		++iter;
+		if (!iter.AtEnd())
+			aHeader.WriteRawDataL(KSemiSpaceSep);
+		}
+	aHeader.CommitRawData();
+	__END_PERFORMANCE_LOGGER(_L(",CHttpClientHeaderWriter::EncodeCookieL()"));
+	}
+
+
+void CHttpClientHeaderWriter::EncodeOneCookieAttributeL(RHeaderField& aHeader, const CHeaderFieldParam& aAttribute) const
+	{
+	const TBool quoted = ( aAttribute.Name().Index(iStringTable) == HTTP::ECookiePort );
+	aHeader.WriteRawDataL( '$' );
+	aHeader.WriteRawDataL( aAttribute.Name().DesC() );
+	aHeader.WriteRawDataL( '=' );
+	if( quoted )
+		{
+		aHeader.WriteRawDataL( '"' );
+		}
+	aHeader.WriteRawDataL( aAttribute.Value().StrF().DesC() );
+	if( quoted )
+		{
+		aHeader.WriteRawDataL( '"' );
+		}
+	}
+
+void CHttpClientHeaderWriter::EncodeOneCookieL(RHeaderField& aHeader, const CHeaderFieldPart& aCookieFieldPart, TBool aIsNetscapeStyleCookie) const
+	{
+	// Always write out the name and value attributes which should come first and second. Only write out the other attributes 
+	// if they are one of Path, Domain, Port
+
+	THeaderFieldParamIter paramIter = aCookieFieldPart.Parameters();
+	paramIter.First();
+
+	TBool separatorNeeded = EFalse;
+	while( !paramIter.AtEnd() )
+		{
+		const CHeaderFieldParam* param = paramIter();
+
+		switch( param->Name().Index(iStringTable) )
+			{
+		case HTTP::EDomain:
+		case HTTP::EPath:
+		case HTTP::ECookiePort:
+			{
+			if( !aIsNetscapeStyleCookie )
+				{
+				aHeader.WriteRawDataL(KSemiSpaceSep);
+				EncodeOneCookieAttributeL(aHeader, *param);
+				}
+			} break;
+		case HTTP::ECookieName:
+			{
+			if( separatorNeeded )
+				aHeader.WriteRawDataL(KSemiSpaceSep);
+			aHeader.WriteRawDataL(param->Value().Str().DesC());
+			} break;
+		case HTTP::ECookieValue:
+			{
+			aHeader.WriteRawDataL('=');
+			aHeader.WriteRawDataL(param->Value().Str().DesC());
+			separatorNeeded = ETrue;
+			} break;
+		default:
+			break;
+			}
+		++paramIter;
+		}
+	}
+
+		
+/*
+ *	Methods from CHeaderWriter
+ */
+
+void CHttpClientHeaderWriter::EncodeHeaderL(RHeaderField& aHeader)
+	{
+	RStringF fieldStr = iStringPool.StringF(aHeader.Name());
+	switch( fieldStr.Index(iStringTable) )
+		{
+	case HTTP::EAccept:
+		{
+		EncodeAcceptL(aHeader);
+		} break;
+	case HTTP::EAcceptCharset:
+		{
+		EncodeAcceptCharsetL(aHeader);
+		} break;
+	case HTTP::EAuthorization:
+		{
+		EncodeAuthorizationL(aHeader);
+		} break;
+	case HTTP::EAcceptLanguage:
+		{
+		EncodeAcceptLanguageL(aHeader);
+		} break;
+	case HTTP::EAcceptEncoding:
+		{
+		DoGeneralAcceptHeaderL(KNormalAcceptEncodingHeaderLen, aHeader, iStringPool.StringF(HTTP::EAcceptEncoding,iStringTable), KErrHttpEncodeAcceptEncoding);
+		} break;
+	case HTTP::EHost:
+		{
+		EncodeHostL(aHeader);
+		} break;
+	case HTTP::EUserAgent:
+		{
+		EncodeUserAgentL(aHeader);
+		} break;
+	case HTTP::ECookie:
+		{
+		EncodeCookieL(aHeader);
+		} break;
+	case HTTP::EIfMatch:
+		{
+		DoTokenCsvListHeaderL(aHeader, KErrHttpEncodeIfMatch);
+		} break;
+	case HTTP::EIfNoneMatch:
+		{
+		DoTokenCsvListHeaderL(aHeader, KErrHttpEncodeIfMatch);
+		} break;
+	case HTTP::EIfModifiedSince:
+		{
+		EncodeGenericDateL(aHeader, KErrHttpEncodeIfModifiedSince);
+		} break;
+	case HTTP::EIfUnmodifiedSince:
+		{
+		EncodeGenericDateL(aHeader, KErrHttpEncodeIfUnmodifiedSince);
+		} break;
+	case HTTP::ECookie2:
+		{
+		EncodeGenericNumberHeaderL(aHeader, KErrHttpEncodeCookie2);
+		} break;
+	case HTTP::ETE:
+		{
+		EncodeTEL(aHeader);
+		} break;
+	default:
+		User::Leave(KErrNotSupported);
+		break;
+		}
+	}