diff -r 000000000000 -r b16258d2340f applayerpluginsandutils/httpprotocolplugins/httpheadercodec/chttpclientheaderwriter.cpp --- /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 +#include +#include +#include + +#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 = + user-pass = userid ":" password + userid = * + 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 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; + } + }