upnp/upnpstack/dlnawebserver/src/upnprangeheaderparser.cpp
changeset 0 f5a58ecadc66
--- /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
+