diff -r 000000000000 -r f5a58ecadc66 upnp/upnpstack/dlnawebserver/src/upnprangeheaderparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upnp/upnpstack/dlnawebserver/src/upnprangeheaderparser.cpp Tue Feb 02 01:12:20 2010 +0200 @@ -0,0 +1,463 @@ +/** @file +* Copyright (c) 2005-2006 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: Declares CUpnpRangeHeaderParser class. +* +*/ + +#include "upnphttpservertransactioncreator.h" +#include "upnphttpmessage.h" +#include "upnprangeheaderparser.h" +#include "upnphttpservertransaction.h" +#include "upnphttpservertransaction.h" + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::NewL +// Factory method. +// --------------------------------------------------------------------------- +// +CUpnpRangeHeaderParser* CUpnpRangeHeaderParser::NewL( + CUpnpHttpServerTransaction &aTransaction, TInt& aStartPos, TInt& aEndPos ) + { + CUpnpRangeHeaderParser* self = + CUpnpRangeHeaderParser::NewLC( aTransaction, aStartPos, aEndPos ); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::NewLC +// Factory method. +// --------------------------------------------------------------------------- +// +CUpnpRangeHeaderParser* CUpnpRangeHeaderParser::NewLC( + CUpnpHttpServerTransaction &aTransaction, TInt& aStartPos, TInt& aEndPos ) + { + CUpnpRangeHeaderParser* self = + new( ELeave ) CUpnpRangeHeaderParser( aTransaction, aStartPos, aEndPos ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::CUpnpRangeHeaderParser +// C++ constructor. +// --------------------------------------------------------------------------- +// +CUpnpRangeHeaderParser::CUpnpRangeHeaderParser( + CUpnpHttpServerTransaction &aTransaction, TInt& aStartPos, TInt& aEndPos ) + :iTransaction(aTransaction), iStartPos( aStartPos ), iEndPos( aEndPos ) + { + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::ConstructL +// Two-phased constructor +// --------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::ConstructL() + { + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::~CUpnpRangeHeaderParser +// Destructor. +// --------------------------------------------------------------------------- +// +CUpnpRangeHeaderParser::~CUpnpRangeHeaderParser() + { + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::ParseRangeL +// Function returns parsinfg status. +// --------------------------------------------------------------------------- +// +TInt CUpnpRangeHeaderParser::ParseRangeL( CUpnpHttpMessage* aMsg, TInt aFileSize ) + { + //checks if Range header exist and is correct + TInt rangeStatus = CheckRangeHeaderSyntax( aMsg ); + if ( rangeStatus != EHttpPartialContent ) + { + return rangeStatus; + } + + //checks Range values + rangeStatus = CheckRangeValues( aFileSize ); + if ( rangeStatus < 0 ) + { + return rangeStatus; + } + + // creates response if Range header occurs + if ( rangeStatus==EHttpPartialContent ) + { + //HTTP/1.1 206 Partial Content + CreatePartialContentResponseL( aFileSize, rangeStatus ); + } + else if ( rangeStatus==EHttpNoContent ) + { + //HTTP/1.1 204 No Content + CreateNoContentResponseL( aFileSize, rangeStatus ); //HTTP/1.1 204 No Content + } + + return rangeStatus; + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::CheckRangeHeaderSyntax +// Checks range header syntax. +// --------------------------------------------------------------------------- +// +TInt CUpnpRangeHeaderParser::CheckRangeHeaderSyntax( CUpnpHttpMessage* aMsg ) + { + // checks if range header exist + TBool rangeHeaderExist = EFalse; + aMsg->IsHeader( (TDesC8&) UpnpHTTP::KHdrRange, rangeHeaderExist ); + if( !rangeHeaderExist ) + { + return EHttpOk; //Range header doesn't exist + } + + // checks if Range value exist + if( !(aMsg->GetHeaderValue( (TDesC8&) UpnpHTTP::KHdrRange ).Length() > 0 ) ) + { + return -EHttpBadRequest; //Range value doesn't exist + } + + // checks syntax of Range header + if( !CUpnpRangeHeaderParser::ParseRangeHeader( aMsg->GetHeaderValue( + (TDesC8&) UpnpHTTP::KHdrRange ), iStartPos, iEndPos ) ) + { + return -EHttpBadRequest; //error in syntax + } + + return EHttpPartialContent; //Range header is proper + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::CheckRangeValue +// Checks range values. +// --------------------------------------------------------------------------- +// +TInt CUpnpRangeHeaderParser::CheckRangeValues( TInt aFileSize ) + { + + // empty file + if ( aFileSize == 0 ) + { + if ( ( iStartPos == 0 ) || ( iStartPos == iEndPos ) + || ( iStartPos > 0 && iEndPos == KErrNotFound ) + || ( iStartPos == KErrNotFound && iEndPos > 0 ) ) + { + return EHttpNoContent; + } + else + { + return -EHttpBadRequest; + } + } + else + { + // final bytes request + // e.g. Range: bytes=-70 + if ( ( iStartPos == KErrNotFound && iEndPos > 0 && iEndPos <= aFileSize ) ) + { + iStartPos = aFileSize - iEndPos; + iEndPos = aFileSize-1; + return EHttpPartialContent; + } + // bad posision dependece + // e.g. Range: bytes=70-20 + // e.g requested final bytes bigger than file size + else if ( ( iStartPos == KErrNotFound && iEndPos == 0 ) + || ( iStartPos == KErrNotFound && iEndPos > 0 && iEndPos > aFileSize ) + || ( iStartPos > iEndPos && iEndPos != KErrNotFound ) ) + { + return -EHttpBadRequest; + } + // start position larger than file size + else if ( ( iStartPos >= aFileSize + && ( iStartPos <= iEndPos || iEndPos == KErrNotFound ) ) ) + { + return -EHttpRequestedRangeNotSatisfiable; + } + } + + // updates end position if it is out of file size + if ( iEndPos == KErrNotFound || iEndPos > aFileSize-1 ) + { + iEndPos = aFileSize-1; + } + + return EHttpPartialContent; + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::AppendContentLengthToResponseL +// Appends Content-Length header to response. +// --------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::AppendContentLengthToResponseL( + TInt aFileSize, TInt rangeStatus ) + { + TInt size( 0 ); + if ( rangeStatus == EHttpPartialContent ) + { + size = iEndPos - iStartPos + 1; + } + else + { + size = aFileSize; + } + iSizeBuffer.Num( size ); + iTransaction.AddResponseHeaderL( UpnpHTTP::KHdrContentLength(), iSizeBuffer ); + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::AppendBytesToResponseL +// Appends Bytes header to response. +// --------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::AppendBytesToResponseL( TInt aFileSize ) + { + HBufC8* buffer = HBufC8::NewLC( sizeof(UpnpHTTP::KBytes) + + KIntegerOccurance*KMaxIntegerLength + KSlashSpaceAndMinusSize ); + + //bytes + buffer->Des().Append( UpnpHTTP::KBytes() ); + buffer->Des().Append( UpnpString::KSpace ); + //x + iSizeBuffer.Num( iStartPos ); + buffer->Des().Append( iSizeBuffer ); + buffer->Des().Append( UpnpString::KMinus ); + //y + iSizeBuffer.Num( iEndPos ); + buffer->Des().Append( iSizeBuffer ); + buffer->Des().Append( UpnpString::KSlash ); + //total + iSizeBuffer.Num( aFileSize ); + buffer->Des().Append( iSizeBuffer ); + + iTransaction.AddResponseHeaderL( UpnpHTTP::KHdrContentRange(), *buffer ); + + CleanupStack::PopAndDestroy( buffer ); + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::CreatePartialContentResponseL +// Creates response 206 Partial Content. +// --------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::CreatePartialContentResponseL( + TInt aFileSize, TInt aHttpCode ) + { + // append Partial Content To Response - "206 Partial Content" + iTransaction.AddResponseHeaderL( KNullDesC8(), UpnpHTTP::KHTTPPartialContent() ); + AppendContentLengthToResponseL( aFileSize, aHttpCode ); + + AppendBytesToResponseL( aFileSize ); + } + +// --------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::CreateNoContentResponseL +// Creates response for 204 No Content. +// --------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::CreateNoContentResponseL( + TInt aFileSize, TInt aHttpCode ) + { + // append No Content To Response - "204 No Content" + iTransaction.AddResponseHeaderL( KNullDesC8(), UpnpHTTP::KHTTPNoContent() ); + + AppendContentLengthToResponseL( aFileSize, aHttpCode ); + + // Append No Content Fields To Response; + iTransaction.AddResponseHeaderL( UpnpHTTP::KHdrPragma(), UpnpHTTP::KNoCache() ); + iTransaction.AddResponseHeaderL( UpnpHTTP::KHdrCacheControl(), UpnpHTTP::KNoCache() ); + + + } + +// ----------------------------------------------------------------------------- +// CUpnpHttpSession::ParseRangeHeader +// Parse range values +// ----------------------------------------------------------------------------- +// +TBool CUpnpRangeHeaderParser::ParseRangeHeader( TDesC8& aRangeHeader, TInt& aStartPos, + TInt& aEndPos ) + { + // Check header's syntax: "bytes=x-y" where y i optional + TInt pos( KErrNotFound ); + + // 1 string has to contain "=" + pos = aRangeHeader.FindC( UpnpString::KEqual() ); + if( pos == KErrNotFound ) + return EFalse; + + // 2 "bytes" has to be at the beginnig + pos = aRangeHeader.FindC( UpnpHTTP::KBytes() ); + if( pos == KErrNotFound || pos != 0 ) + return EFalse; + + // 3 Sets position to after bytes + pos = UpnpHTTP::KBytes().Length(); + + // 4 If there any space or tab after "bytes" - move pos after it + CUpnpRangeHeaderParser::MovePosition( aRangeHeader, pos ); + + // 5 "=" - has to be at this position (after "bytes" + spaces or tabs) + if( aRangeHeader[ pos ] != UpnpString::KEqual()[0] ) + return EFalse; + + // 6 Sets position to after "=" + pos++; + + // 7 If there any space or tab after "=" - move pos after it + CUpnpRangeHeaderParser::MovePosition( aRangeHeader, pos ); + + // 8 extract x-y. -1 stands for '=' length + TPtrC8 byteRange = aRangeHeader.Right( aRangeHeader.Length() - pos ); + + // 9 There can't be any comas because multi-range is not allowed + if( byteRange.Find( UpnpString::KComa() ) != KErrNotFound ) + return EFalse; + + // 10 "-" delimiter must occure and it cant't be first char, because notation as follows: "-y" is not allowed + pos = byteRange.Find( UpnpString::KMinus() ); + if( pos == KErrNotFound ) + return EFalse; + + // 11 Checks if it is a final bytes request + // e.g. Range: bytes= -20 + if( pos == 0 ) + { + // If there any space or tab after "-" - move pos after it + CUpnpRangeHeaderParser::MovePosition( byteRange.Right( byteRange.Length()-1 ), pos ); + // if pos equal 0 should be 1 to avoid "-" in getting number from string operation + pos = pos == 0 ? 1 : pos; + TLex8 endMinus( byteRange.Right( byteRange.Length() - pos ) ); + endMinus.SkipSpace(); + TInt error = endMinus.Val( aEndPos ); + if ( !CUpnpRangeHeaderParser::HandleConversionException( endMinus, aEndPos, error ) ) + { + return EFalse; + } + + // We have to check if something else than space or tab leaves after conversion - unless for example 11a will be correct but it is not + if ( CUpnpRangeHeaderParser::HasImproperChars( endMinus ) ) + return EFalse; + + aStartPos = KErrNotFound; + return ETrue; + } + + // 12 All looks fine, so now parse it and get x and y + TLex8 start( byteRange.Left( pos ) ); + start.SkipSpace(); + + // 13 If conversion fails - return error + TInt error = start.Val( aStartPos ); + if ( !CUpnpRangeHeaderParser::HandleConversionException( start, aStartPos, error ) ) + { + return EFalse; + } + // 14 We have to check if something else than space or tab leaves after conversion - unless for example 11a will be correct but it is not + if ( CUpnpRangeHeaderParser::HasImproperChars( start ) ) + return EFalse; + + // y is optional + if( ( byteRange.Length() - pos - 1 ) > 0 ) + { + TLex8 end( byteRange.Right( byteRange.Length() - pos - 1 ) ); + end.SkipSpace(); + + error = end.Val( aEndPos ); + if ( !CUpnpRangeHeaderParser::HandleConversionException( end, aEndPos, error ) ) + { + return EFalse; + } + + // We have to check if something else than space or tab leaves after conversion - unless for example 11a will be correct but it is not + if ( CUpnpRangeHeaderParser::HasImproperChars( end ) ) + return EFalse; + + } + else + { + aEndPos = KErrNotFound; + } + + return ETrue; + } + +// ----------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::HasImproperChars +// Checks number format +// ----------------------------------------------------------------------------- +// +TBool CUpnpRangeHeaderParser::HasImproperChars( const TLex8& aNumber ) + { + TPtrC8 rem_ptr = aNumber.Remainder(); + for( TInt index = 0; index < rem_ptr.Length(); index++ ) + { + if( rem_ptr[ index ] != UpnpString::KSpace()[0] && + rem_ptr[ index ] != UpnpString::KTab()[0] ) + return ETrue; + } + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::MovePosition +// Moves parser position to first not white space +// ----------------------------------------------------------------------------- +// +void CUpnpRangeHeaderParser::MovePosition( const TDesC8& aString, TInt& aPos ) + { + while ( aPos < aString.Length() && + ( aString[ aPos ] == UpnpString::KSpace()[0] || aString[ aPos ] == + UpnpString::KTab()[0] ) + ) + { + aPos++; + } + } + +// ----------------------------------------------------------------------------- +// CUpnpRangeHeaderParser::HandleConversionException +// Handles conversion exceptions +// ----------------------------------------------------------------------------- +// +TBool CUpnpRangeHeaderParser::HandleConversionException( TLex8& aNumber, TInt& aRangePos, + TInt aError ) + { + if( aError == KErrOverflow ) + { + aRangePos = KMaxTInt; + while ( aNumber.Peek().IsDigit() ) + { + aNumber.Inc(); + } + } + else if ( aError != KErrNone ) + { + return EFalse; + } + + return ETrue; + } + +// End Of File +