applayerpluginsandutils/httpprotocolplugins/WspProtocolHandler/CWspHeaderUtils.cpp
changeset 0 b16258d2340f
equal deleted inserted replaced
-1:000000000000 0:b16258d2340f
       
     1 // Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 // System includes
       
    17 #include <wspstringconstants.h>
       
    18 #include <wspdecoder.h>
       
    19 #include <wsperror.h>
       
    20 
       
    21 // User includes
       
    22 #include "wsppanic.h"
       
    23 
       
    24 // Class signature
       
    25 #include "cwspheaderutils.h"
       
    26 
       
    27 // Constants used in this file
       
    28 const TInt KDefaultSessionHeadersBufferSize		= 512;
       
    29 const TInt KByteLength							= 1;
       
    30 
       
    31 const TUint8 KNullTerminator					= 0x00;
       
    32 const TUint8 KConvertToShortInt					= 0x80;
       
    33 
       
    34 LOCAL_C void StringArrayCleanup(TAny* aStringArray);
       
    35 
       
    36 CWspHeaderUtils* CWspHeaderUtils::NewL(CWspHeaderCodec& aCodec)
       
    37 	{
       
    38 	return new(ELeave) CWspHeaderUtils(aCodec);
       
    39 	}
       
    40 
       
    41 CWspHeaderUtils::CWspHeaderUtils(CWspHeaderCodec& aCodec)
       
    42 	:iCodec(aCodec)
       
    43 	{
       
    44 	}
       
    45 
       
    46 CWspHeaderUtils::~CWspHeaderUtils()
       
    47 	{
       
    48 	}
       
    49 
       
    50 HBufC8* CWspHeaderUtils::EncodeHeadersL(RStringPool aStringPool, RHTTPHeaders aHeaders)
       
    51 	{
       
    52 	// Create a buffer for the encoded headers
       
    53 	HBufC8* buf = HBufC8::NewL(KDefaultSessionHeadersBufferSize);
       
    54 	CleanupStack::PushL(buf);
       
    55 
       
    56 	// Encode the content-type header if it exists
       
    57 	TBool hasContentType = EncodeContentTypeL(aStringPool, aHeaders, buf);
       
    58 
       
    59 	// Go through the headers
       
    60 	THTTPHdrFieldIter fields = aHeaders.Fields();
       
    61 	fields.First();
       
    62 	while( fields.AtEnd() == EFalse )
       
    63 		{
       
    64 		// Get the field value
       
    65 		RStringF headerField = aStringPool.StringF(fields());
       
    66 
       
    67 		// Was there a content-type header present? Make sure that it is not
       
    68 		// appended a second time.
       
    69 		TInt headerFieldValue = iCodec.EncodeFieldName(headerField);
       
    70 		if( !hasContentType || headerFieldValue != WSP::EContentType )
       
    71 			{
       
    72 			// Encode the header field
       
    73 			EncodeHeaderFieldL(aHeaders, headerField, buf);
       
    74 			}
       
    75 		// Next header field
       
    76 		++fields;
       
    77 		}
       
    78 	// Cleanup...
       
    79 	CleanupStack::Pop(buf);
       
    80 	return buf;
       
    81 	}
       
    82 
       
    83 HBufC8* CWspHeaderUtils::EncodeNoTrailerHeadersL(RStringPool aStringPool, RHTTPHeaders aHeaders, HBufC8*& aTrailerData)
       
    84 	{
       
    85 	// Need an array of RStringFs for the headers in the trailers - need to put 
       
    86 	// on the cleanup stack
       
    87 	RArray<RStringF> trailerHeaders;
       
    88 	TCleanupItem arrayCleaner = TCleanupItem(&StringArrayCleanup, (TAny*) &trailerHeaders);
       
    89 	CleanupStack::PushL(arrayCleaner);
       
    90 
       
    91 	// Need to find out what headers are in the trailers
       
    92 	THTTPHdrVal trailerHeaderVal;
       
    93 	TInt index =0;
       
    94 	while( aHeaders.GetField(
       
    95 							aStringPool.StringF(WSP::ETrailer, WSP::Table),
       
    96 							index,		// Zero index -> first part
       
    97 							trailerHeaderVal
       
    98 							) != KErrNotFound )
       
    99 		{
       
   100 		__ASSERT_DEBUG( trailerHeaderVal.Type() == THTTPHdrVal::KStrFVal, Panic(KWspPanicBadTrailerHeader) );
       
   101 
       
   102 		// Got a trailer header - append to the list
       
   103 		RStringF header = trailerHeaderVal.StrF();
       
   104 		User::LeaveIfError(trailerHeaders.Append(header));
       
   105 
       
   106 		// Need to increment the reference count of this string
       
   107 		header.Copy();
       
   108 
       
   109 		// Next...
       
   110 		++index;
       
   111 		}
       
   112 	__ASSERT_DEBUG( trailerHeaders.Count() > 0, Panic(KWspPanicNoTrailerHeaders) );
       
   113 
       
   114 	// Create a buffer for the encoded headers
       
   115 	HBufC8* bufHdrs = HBufC8::NewL(KDefaultSessionHeadersBufferSize);
       
   116 	CleanupStack::PushL(bufHdrs);
       
   117 
       
   118 	// Encode the content-type header if it exists
       
   119 	TBool hasContentType = EncodeContentTypeL(aStringPool, aHeaders, bufHdrs);
       
   120 
       
   121 	// Ok, now encode all the headers bar the ones in trailerHeaders
       
   122 	// Go through the headers
       
   123 	THTTPHdrFieldIter fields = aHeaders.Fields();
       
   124 	fields.First();
       
   125 	while( fields.AtEnd() == EFalse )
       
   126 		{
       
   127 		// Get the header field 
       
   128 		RStringF headerField = aStringPool.StringF(fields());
       
   129 
       
   130 		// Was there a content-type header present? Make sure that it is not
       
   131 		// appended a second time, and only append if this is a trailer header.
       
   132 		TInt headerFieldValue = iCodec.EncodeFieldName(headerField);
       
   133 		if( !IsTrailerHeader(trailerHeaders, headerField) && 
       
   134 			(!hasContentType || headerFieldValue != WSP::EContentType) )
       
   135 			{
       
   136 			// Encode the field
       
   137 			EncodeHeaderFieldL(aHeaders, headerField, bufHdrs);
       
   138 			}
       
   139 		// Next header field
       
   140 		++fields;
       
   141 		}
       
   142 	// Now, need to encode the trailer headers. Create a buffer for the trailer 
       
   143 	// headers
       
   144 	HBufC8* bufTrls = HBufC8::NewL(KDefaultSessionHeadersBufferSize);
       
   145 	CleanupStack::PushL(bufTrls);
       
   146 
       
   147 	TInt count = trailerHeaders.Count();
       
   148 	for( TInt i=0; i < count; ++i)
       
   149 		{
       
   150 		// Encode the header field
       
   151 		EncodeHeaderFieldL(aHeaders, trailerHeaders[i], bufTrls);
       
   152 		}
       
   153 	// Pass back the encoded trailer headers and other headers
       
   154 	aTrailerData = bufTrls->ReAllocL(bufTrls->Des().Length());
       
   155 
       
   156 	// Cleanup...
       
   157 	CleanupStack::Pop(2, bufHdrs);
       
   158 	CleanupStack::PopAndDestroy(&trailerHeaders);
       
   159 
       
   160 	return bufHdrs;
       
   161 	}
       
   162 
       
   163 void CWspHeaderUtils::DecodeReplyHeadersL(RStringPool aStringPool, const TDesC8& aEncodedData, RHTTPHeaders& aHeaders)
       
   164 	{
       
   165 	// Ok, the data should have a content-type header at the start, but without
       
   166 	// the header field name - straight onto the field value. The type of the 
       
   167 	// first byte will give the length of the field value.
       
   168 	TWspPrimitiveDecoder decoder = TWspPrimitiveDecoder(aEncodedData);
       
   169 
       
   170 	TInt dataLength = 0;
       
   171 	switch( decoder.VarType() )
       
   172 		{
       
   173 	case TWspPrimitiveDecoder::ELengthVal:
       
   174 		{
       
   175 		// The content-type header follows the BNF - 
       
   176 		//
       
   177 		// content-type-value = content-general-form = value-length media-type
       
   178 
       
   179 		// The length of the data has been specified - extra it and include the
       
   180 		// length of value-length
       
   181 		TInt consumed = decoder.LengthVal(dataLength);
       
   182 		dataLength += consumed;
       
   183 		} break;
       
   184 	case TWspPrimitiveDecoder::EString:
       
   185 		{
       
   186 		// The content-type header follows the BNF - 
       
   187 		//
       
   188 		// content-type-value = constrained-media = extension-media = *TEXT end-of-string
       
   189 		
       
   190 		// The end of the string is give by a NULL terminator
       
   191 		TInt endPos = aEncodedData.Locate(KNullTerminator);
       
   192 		if( endPos == KErrNotFound )
       
   193 			{
       
   194 			// The header data is corrupt
       
   195 			User::Leave(KErrCorrupt);
       
   196 			}
       
   197 		dataLength = endPos + 1;
       
   198 		} break;
       
   199 	case TWspPrimitiveDecoder::E7BitVal:
       
   200 		{
       
   201 		// The content-type header follows the BNF - 
       
   202 		//
       
   203 		// content-type-value = constrained-media = short-integer = OCTET
       
   204 
       
   205 		// The header data is a single byte long
       
   206 		dataLength = KByteLength;
       
   207 		} break;
       
   208 	default:
       
   209 		// The header data is corrupt
       
   210 		User::Leave(KErrCorrupt);
       
   211 		break;
       
   212 		}
       
   213 	// Set the content type field value
       
   214 	TPtrC8 contentTypeValue = aEncodedData.Left(dataLength);
       
   215 
       
   216 	// Set the raw data in the header object
       
   217 	TBuf8<KByteLength> contentTypeToken;
       
   218 	contentTypeToken.Append((TUint8)WSP::EContentType);
       
   219 	aHeaders.SetRawFieldL(aStringPool.StringF(WSP::EContentType, WSP::Table), contentTypeValue, contentTypeToken);
       
   220 
       
   221 	// Skip past the content-type field value
       
   222 	TPtrC8 encodedData = aEncodedData.Mid(dataLength);
       
   223 
       
   224 	// Segment the remaining data
       
   225 	DecodeHeadersL(aStringPool, encodedData, aHeaders);
       
   226 	}
       
   227 
       
   228 void CWspHeaderUtils::DecodeHeadersL(RStringPool aStringPool, const TDesC8& aEncodedData, RHTTPHeaders& aHeaders)
       
   229 	{
       
   230 	// Use a segmentor to slice-up the data buffer
       
   231 	TWspHeaderSegmenter segmentor = TWspHeaderSegmenter(aStringPool, WSP::Table, aEncodedData);
       
   232 
       
   233 	// Slice..
       
   234 	TBool done = EFalse;
       
   235 	while( !done )
       
   236 		{
       
   237 		// Get the next field
       
   238 		TWspField field;
       
   239 		TInt error = segmentor.NextL(field);
       
   240 
       
   241 		// Add the field name to the cleanup stack
       
   242 		CleanupClosePushL(field.iHdrName);
       
   243 
       
   244 		// Check for corrupt data
       
   245 		if( error == KErrCorrupt )
       
   246 			{
       
   247 			// Data is corrupt
       
   248 			User::Leave(KErrCorrupt);
       
   249 			}
       
   250 
       
   251 		// Was there a field found?
       
   252 		if( error == KErrNotFound )
       
   253 			{
       
   254 			// No more data - stop
       
   255 			done = ETrue;
       
   256 			}
       
   257 		else
       
   258 			{
       
   259 			// Set-up the field in the headers object
       
   260 			TInt headerToken = field.iHdrName.Index(WSP::Table);
       
   261 			if( headerToken == KErrNotFound )
       
   262 				{
       
   263 				// No token value so add as straight text with a NULL terminator
       
   264 				HBufC8* tokenText = HBufC8::NewLC(field.iHdrName.DesC().Length() + 1);
       
   265 				TPtr8 tokenTextBuffer(tokenText->Des());
       
   266 				tokenTextBuffer.Append(KNullTerminator);
       
   267 				aHeaders.SetRawFieldL(field.iHdrName, field.iValBuffer, *tokenText);
       
   268 				CleanupStack::PopAndDestroy(tokenText);
       
   269 				}
       
   270 			else
       
   271 				{
       
   272 				// Encoded token available so use binary token as separator
       
   273 				headerToken += KConvertToShortInt;
       
   274 				TBuf8<KByteLength> headerTokenDes;
       
   275 				headerTokenDes.Append((TUint8)headerToken);
       
   276 				aHeaders.SetRawFieldL(field.iHdrName, field.iValBuffer, headerTokenDes);
       
   277 				}
       
   278 			}
       
   279 
       
   280 		// Cleanup the field variable
       
   281 		CleanupStack::PopAndDestroy(&field.iHdrName);
       
   282 
       
   283 		}
       
   284 	}
       
   285 
       
   286 TBool CWspHeaderUtils::EncodeContentTypeL(RStringPool aStringPool, RHTTPHeaders aHeaders, HBufC8*& aBuf)
       
   287 	{
       
   288 	// Get the TPtr8 from the buffer
       
   289 	TPtr8 encodedHdrs = aBuf->Des();
       
   290 
       
   291 	// Get the content-type header, if it exists
       
   292 	RStringF headerField = aStringPool.StringF(WSP::EContentType, WSP::Table);
       
   293 	TPtrC8 encodedData;
       
   294 	TInt foundContentType = aHeaders.GetRawField(headerField, encodedData);
       
   295 	TBool hasContentType = foundContentType != KErrNotFound;
       
   296 
       
   297 	// If the content-type header exists, append to the start of the buffer 
       
   298 	// without the field name.
       
   299 	if( hasContentType )
       
   300 		{
       
   301 		// Append the encoded field value - check for space first
       
   302 		while( encodedHdrs.Length() + encodedData.Length() > encodedHdrs.MaxLength() )
       
   303 			{
       
   304 			aBuf = aBuf->ReAllocL(encodedHdrs.MaxLength() + KDefaultSessionHeadersBufferSize);
       
   305 			encodedHdrs = aBuf->Des();
       
   306 			}
       
   307 		// Append the field value
       
   308 		encodedHdrs.Append(encodedData);
       
   309 		}
       
   310 	return hasContentType;
       
   311 	}
       
   312 
       
   313 void CWspHeaderUtils::EncodeHeaderFieldL(RHTTPHeaders aHeaders, RStringF aHeaderField, HBufC8*& aBuf)
       
   314 	{
       
   315 	// Check to see if the header field is well-known
       
   316 	TInt headerFieldValue = iCodec.EncodeFieldName(aHeaderField);
       
   317 
       
   318 	TBool wellKnownHeader = headerFieldValue != KErrNotFound;
       
   319 
       
   320 	// Calculate the header field length
       
   321 	TInt fieldNameLength = KByteLength;
       
   322 	if( !wellKnownHeader )
       
   323 		{
       
   324 		// The header is not well-known - need to encode the header field 
       
   325 		// name as token text, which is NULL-terminated.
       
   326 		fieldNameLength = aHeaderField.DesC().Length() + 1;
       
   327 		}
       
   328 	// Need to add the header field name and then the encoded header value.
       
   329 	// First get the OTA format of the field value
       
   330 	TPtrC8 encodedData;
       
   331 	TInt err = aHeaders.GetRawField(aHeaderField, encodedData);
       
   332 
       
   333 	// Was there any error encoding the header?
       
   334 	if( err != KErrNone )
       
   335 		{
       
   336 		// Header isn't here - something has gone wrong
       
   337 		User::Leave(KWspErrMissingHeader);
       
   338 		}
       
   339 
       
   340 	// Ensure there is enough space in the buffer.
       
   341 	TPtr8 encodedHdrs = aBuf->Des();
       
   342 	while( encodedHdrs.Length() + encodedData.Length() + fieldNameLength > encodedHdrs.MaxLength() )
       
   343 		{
       
   344 		aBuf = aBuf->ReAllocL(encodedHdrs.MaxLength() + KDefaultSessionHeadersBufferSize);
       
   345 		encodedHdrs = aBuf->Des();
       
   346 		}
       
   347 	// Append the field name - check to see if well-known
       
   348 	if( wellKnownHeader )
       
   349 		{
       
   350 		// Append as an encoded byte-value - the top bit must be set, since well-known
       
   351 		// header field names are encoded as short-int ([WSP] Sect. 8.4.2.6 "Header")
       
   352 		encodedHdrs.Append(headerFieldValue | KConvertToShortInt);
       
   353 		}
       
   354 	else
       
   355 		{
       
   356 		// Append as token text
       
   357 		encodedHdrs.Append(aHeaderField.DesC());
       
   358 		encodedHdrs.Append(KNullTerminator);
       
   359 		}
       
   360 	// Append the encoded field value
       
   361 	encodedHdrs.Append(encodedData);
       
   362 	}
       
   363 
       
   364 TBool CWspHeaderUtils::IsTrailerHeader(RArray<RStringF> aTrailerHeaders, RStringF aHeaderField)
       
   365 	{
       
   366 	// Search the list of trailer headers to see if aHeaderField is in it.
       
   367 	TInt index = 0;
       
   368 	TInt count = aTrailerHeaders.Count();
       
   369 	TBool found = EFalse;
       
   370 	while( !found && index < count )
       
   371 		{
       
   372 		// Make the comparison
       
   373 		RStringF header = aTrailerHeaders[index];
       
   374 		found = header == aHeaderField;
       
   375 		++index;
       
   376 		}
       
   377 	return found;
       
   378 	}
       
   379 
       
   380 LOCAL_C void StringArrayCleanup(TAny* aStringArray)
       
   381 	{
       
   382 	RArray<RStringF> array = *REINTERPRET_CAST(RArray<RStringF>*, aStringArray);
       
   383 
       
   384 	TInt count = array.Count();
       
   385 	for( TInt i = 0; i < count; ++i)
       
   386 		{
       
   387 		// Close the string
       
   388 		array[i].Close();
       
   389 		}
       
   390 	array.Close();
       
   391 	}