/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
#include "http_parser.h"
#include "http_parser_internal.h"
#include "oscl_string_utils.h"
#include "oscl_string_containers.h"

// Use default DLL entry point for Symbian
#include "oscl_dll.h"

OSCL_DLL_ENTRY_POINT_DEFAULT()


// three inline functions for multiple class implementation
inline bool isLetter(const char c)
{
    return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c == 45)); // A-Z, or a-z or -
}

inline bool isDigit(const char c)
{
    return (c >= 48 && c <= 57);
}

inline bool isHexDigit(const char c)
{
    return (isDigit(c) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)); // 0-9, A-F or a-f
}


////////////////////////////////////////////////////////////////////////////////////
////// HTTPParser implementation ///////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

OSCL_EXPORT_REF int32 HTTPParser::parse(const OsclRefCounterMemFrag &aInputDataStream, RefCountHTTPEntityUnit &aEntityUnit)
{
    if (!iParserInput->push_back((OsclRefCounterMemFrag &)aInputDataStream)) // not a new data fragment
        return PARSE_NEED_MORE_DATA;

    if (!aEntityUnit.empty()) aEntityUnit.clear();
    if (!iHeader->isParsed()) return iHeader->parse(*iParserInput, aEntityUnit);

    return parseEntityBody(aEntityUnit);
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF int32 HTTPParser::doSanityCheckForResponseHeader()
{
    if (!iHeader->isParsed()) return PARSE_HEADER_NOT_PARSED_YET;
    return iHeader->doSanityCheckForResponseHeader();
}


////////////////////////////////////////////////////////////////////////////////////
// assume aFieldKeyList has enough space to hold all parsed key list, if it is small, it will cause crash
// since we have no way to check the space
OSCL_EXPORT_REF uint32 HTTPParser::getFieldKeyListInHeader(StrPtrLen *&aFieldKeyList)
{
    return iHeader->getKeyValuesStore()->getCurrentKeyList(aFieldKeyList); // iHeader should be created successfully in factory method
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool HTTPParser::getField(const StrCSumPtrLen &aNewFieldName, StrPtrLen &aNewFieldValue, uint32 index)
{
    return iHeader->getField(aNewFieldName, aNewFieldValue, index);
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF uint32 HTTPParser::getNumberOfFieldsByKey(const StrCSumPtrLen &aNewFieldName)
{
    return iHeader->getNumberOfFieldsByKey(aNewFieldName);
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF uint32 HTTPParser::getTotalFieldsInHeader()
{
    return iHeader->getNumFields();
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF uint32 HTTPParser::getHTTPStatusCode()
{
    return iHeader->getStatusCode();
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void HTTPParser::getContentInfo(HTTPContentInfo &aContentInfo)
{
    aContentInfo.clear();
    if (iContentInfo) iContentInfo->get(aContentInfo);
}

////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF uint32 HTTPParser::getHttpVersionNum()
{
    return iHeader->getHttpVersionNum();
}

////////////////////////////////////////////////////////////////////////////////////
//Set maximum line size for multiline response
OSCL_EXPORT_REF void HTTPParser::setMaxLineSizeForMultiLineResponse(int32 &aSizeLimit)
{
    iHeader->setMaxLineSizeForMultiLineResponse(aSizeLimit);
}


////////////////////////////////////////////////////////////////////////////////////
// reset the parser to parse a new HTTP response
OSCL_EXPORT_REF void HTTPParser::reset()
{
    if (iParserInput) iParserInput->clear();
    if (iContentInfo) iContentInfo->clear();
    if (iHeader) iHeader->reset();

    // delete iEntityBody
    if (iEntityBody) OSCL_DELETE(iEntityBody);
    iEntityBody = NULL;
}

////////////////////////////////////////////////////////////////////////////////////
// factory method
OSCL_EXPORT_REF HTTPParser* HTTPParser::create()
{
    HTTPParser *parser = OSCL_NEW(HTTPParser, ());
    if (!parser) return NULL;
    if (!parser->construct())
    {
        OSCL_DELETE(parser);
        return NULL;
    }
    return parser;
}

////////////////////////////////////////////////////////////////////////////////////
bool HTTPParser::construct()
{
    reset();

    // create the component objects
    if ((iContentInfo = OSCL_NEW(HTTPContentInfoInternal, ())) == NULL) return false;
    if ((iParserInput = HTTPParserInput::create()) == NULL) return false;
    if ((iHeader = HTTPParserHeaderObject::create(iContentInfo)) == NULL) return false;
    return true;
}

////////////////////////////////////////////////////////////////////////////////////
// destructor
OSCL_EXPORT_REF HTTPParser::~HTTPParser()
{
    reset();

    // delete iParserInput
    if (iParserInput) OSCL_DELETE(iParserInput);
    iParserInput = NULL;

    // delete iContentInfo
    if (iContentInfo) OSCL_DELETE(iContentInfo);
    iContentInfo = NULL;

    // delete iHeader
    if (iHeader) OSCL_DELETE(iHeader);
    iHeader = NULL;

    // delete iEntityBody
    if (iEntityBody) OSCL_DELETE(iEntityBody);
    iEntityBody = NULL;
}

////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParser::parseEntityBody(RefCountHTTPEntityUnit &aEntityUnit)
{
    if (!iHeader || !iHeader->isParsed()) return PARSE_HEADER_NOT_PARSED_YET;
    if (iHeader->isWholeResponseParsed()) return PARSE_SUCCESS_END_OF_MESSAGE;

    if (!iEntityBody)
    {
        // create iEntityBody
        // After parsing the header, we should get right content type, iContentType
        if (iContentInfo->getContentType() == HTTP_CONTENT_NORMAL)
            iEntityBody = OSCL_NEW(HTTPParserNormalContentObject, (iHeader->getKeyValuesStore(), iHeader->getAllocator(), iContentInfo));
        else if (iContentInfo->getContentType() == HTTP_CONTENT_NULTIPART)
            iEntityBody = OSCL_NEW(HTTPParserMultipartContentObject, (iHeader->getKeyValuesStore(), iHeader->getAllocator(), iContentInfo));
        else if (iContentInfo->getContentType() == HTTP_CONTENT_CHUNKED_TRANSFER_ENCODING)
            iEntityBody = OSCL_NEW(HTTPParserCTEContentObject, (iHeader->getKeyValuesStore(), iHeader->getAllocator(), iContentInfo));

        if (!iEntityBody) return PARSE_MEMORY_ALLOCATION_FAILURE;
    }

    return iEntityBody->parse(*iParserInput, aEntityUnit);
}


/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPContentInfoInternal Implementation ///////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
bool HTTPContentInfoInternal::parseContentInfo(StringKeyValueStore &aKeyValueStore)
{
    // Content-Length
    StrCSumPtrLen contenLengthKey("Content-Length");
    StrPtrLen contentLengthValue;
    if (aKeyValueStore.getValueByKey(contenLengthKey, contentLengthValue))
    {
        // has content length
        PV_atoi(contentLengthValue.c_str(), 'd', iContentLength);
    }

    // Content-Type
    StrCSumPtrLen contentTypeKey("Content-Type");
    StrPtrLen contentTypeValue;
    if (aKeyValueStore.getValueByKey(contentTypeKey, contentTypeValue))
    {
        // has content type
        if (!parseContentType(contentTypeValue)) return false;
    }

    // Content-Range
    StrCSumPtrLen contentRangeKey("Content-Range");
    StrPtrLen contentRangeValue;
    if (aKeyValueStore.getValueByKey(contentRangeKey, contentRangeValue))
    {
        // has content type
        parseContentRange(contentRangeValue);
    }

    // check Chunked Transfer-Encoding, "Transfer-Encoding : chunked"
    StrCSumPtrLen transferEncodingKey("Transfer-Encoding");
    StrPtrLen transferEncodingValue;
    if (aKeyValueStore.getValueByKey(transferEncodingKey, transferEncodingValue))
    {
        // has content type
        verifyTransferEncoding(transferEncodingValue);
    }
    return true;
}

/////////////////////////////////////////////////////////////////////////////////////
void HTTPContentInfoInternal::parseContentRange(const StrPtrLen &aContentRange)
{
    char *ptr = (char *)aContentRange.c_str();
    uint32 len = aContentRange.length();

    while (!isLetter(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    OSCL_FastString bytesString(_STRLIT_CHAR("bytes"));
    if (len <= oscl_strlen(bytesString.get_cstr())) return;

    if (((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'b') &&
            ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'y') &&
            ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e') &&
            ((ptr[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 's'))
    {
        // find "bytes"
        ptr += 5;
        if ((len -= 5) <= 0) return;

        // get the left side of the range
        while (!isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        char *start_ptr = ptr;
        uint32 start_len = len;
        while (isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        PV_atoi(start_ptr, 'd', start_len - len, iContentRangeLeft);

        // get the right side of the range
        while (!isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        start_ptr = ptr;
        start_len = len;
        while (isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        PV_atoi(start_ptr, 'd', start_len - len, iContentRangeRight);

        // get the content length
        while (!isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        start_ptr = ptr;
        start_len = len;
        while (isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        PV_atoi(start_ptr, 'd', start_len - len, iContentLength);
    }
}

/////////////////////////////////////////////////////////////////////////////////////
bool HTTPContentInfoInternal::parseContentType(const StrPtrLen &aContentType)
{
    // identify multipart content type and chunked transfer-encoding
    // Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
    char *ptr = (char *)aContentType.c_str();
    uint32 len = aContentType.length();

    // eat non-letter characters
    while (!isLetter(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    OSCL_FastString typeString(_STRLIT_CHAR("multipart/byteranges"));
    if (len <= oscl_strlen(typeString.get_cstr())) return true;

    if (((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'm') &&
            ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'u') &&
            ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 'l') &&
            ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            ((ptr[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 'i') &&
            ((ptr[5] | OSCL_ASCII_CASE_MAGIC_BIT) == 'p') &&
            ((ptr[6] | OSCL_ASCII_CASE_MAGIC_BIT) == 'a') &&
            ((ptr[7] | OSCL_ASCII_CASE_MAGIC_BIT) == 'r') &&
            ((ptr[8] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            (ptr[9]                               == '/') &&
            ((ptr[10] | OSCL_ASCII_CASE_MAGIC_BIT) == 'b') &&
            ((ptr[11] | OSCL_ASCII_CASE_MAGIC_BIT) == 'y') &&
            ((ptr[12] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            ((ptr[13] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e') &&
            ((ptr[14] | OSCL_ASCII_CASE_MAGIC_BIT) == 'r') &&
            ((ptr[15] | OSCL_ASCII_CASE_MAGIC_BIT) == 'a') &&
            ((ptr[16] | OSCL_ASCII_CASE_MAGIC_BIT) == 'n') &&
            ((ptr[17] | OSCL_ASCII_CASE_MAGIC_BIT) == 'g') &&
            ((ptr[18] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e') &&
            ((ptr[19] | OSCL_ASCII_CASE_MAGIC_BIT) == 's'))
    {
        // find "multipart/byteranges"
        // constinue search "boundary"
        ptr += 20;
        if ((len -= 20) <= 8) return false;

        while (!isLetter(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        if (((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'b') &&
                ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'o') &&
                ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 'u') &&
                ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'n') &&
                ((ptr[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 'd') &&
                ((ptr[5] | OSCL_ASCII_CASE_MAGIC_BIT) == 'a') &&
                ((ptr[6] | OSCL_ASCII_CASE_MAGIC_BIT) == 'r') &&
                ((ptr[7] | OSCL_ASCII_CASE_MAGIC_BIT) == 'y'))
        {
            // find "boundary"
            ptr += 8;
            if ((len -= 8) <= 0) return false;

            // find "="
            while (*ptr != HTTP_CHAR_EQUAL && len > 0)
            {
                ptr++;
                len--;
            }
            if (len <= 0) return false;
            ptr++;

            while ((*ptr == HTTP_CHAR_SPACE || *ptr == HTTP_CHAR_TAB) && len > 0)
            {
                ptr++;
                len--;
            }
            char *boundaryStartPtr = ptr;
            uint32 start_len = (uint32)len;
            while (!(*ptr == HTTP_CHAR_NULL || *ptr == HTTP_CHAR_SPACE || *ptr == HTTP_CHAR_TAB ||
                     *ptr == HTTP_CHAR_CR   || *ptr == HTTP_CHAR_LF) && len > 0)
            {
                ptr++;
                len--;
            }
            iContentType = HTTP_CONTENT_NULTIPART;
            return copyBoundaryString(boundaryStartPtr, start_len - (uint32)len);
        }
    }
    return true;
}

/////////////////////////////////////////////////////////////////////////////////////
void HTTPContentInfoInternal::verifyTransferEncoding(const StrPtrLen &aTransferEncodingValue)
{
    char *ptr = (char *)aTransferEncodingValue.c_str();
    uint32 len = aTransferEncodingValue.length();

    // eat non-letter characters
    while (!isLetter(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    OSCL_FastString chunkedString(_STRLIT_CHAR("chunked"));
    if (len < oscl_strlen(chunkedString.get_cstr())) return;

    if (((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'c') &&
            ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'h') &&
            ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 'u') &&
            ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'n') &&
            ((ptr[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 'k') &&
            ((ptr[5] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e') &&
            ((ptr[6] | OSCL_ASCII_CASE_MAGIC_BIT) == 'd'))
    {
        // find "chunked"
        iContentType = HTTP_CONTENT_CHUNKED_TRANSFER_ENCODING;
    }
}

bool HTTPContentInfoInternal::copyBoundaryString(const char* aBoundaryString, const uint32 aBoundaryStringLength)
{
    // allocate memory for boundary string
    if (!iBoundaryBuffer) iBoundaryBuffer = new char[aBoundaryStringLength+1];
    if (!iBoundaryBuffer) return false;
    if (!aBoundaryString) return false;

    oscl_memcpy(iBoundaryBuffer, aBoundaryString, aBoundaryStringLength);
    iBoundaryBuffer[aBoundaryStringLength] = HTTP_CHAR_NULL;
    iBoundary.setPtrLen(iBoundaryBuffer, aBoundaryStringLength);
    return true;
}

////////////////////////////////////////////////////////////////////////////////////
bool HTTPContentInfoInternal::parseBoudaryLine(HTTPMemoryFragment &aInputLineData, bool &isFinalBoundary)
{
    isFinalBoundary = false;

    // check boundary line : --BOUNDARY STRING
    char *ptr = (char *)aInputLineData.getPtr();
    int32 len = aInputLineData.getAvailableSpace();
    while (*ptr != HTTP_CHAR_MINUS && len > 0)
    {
        ptr++;
        len--;
    }
    if (len <= 0) return false; // this line is not boundary line
    if (*(++ptr) != HTTP_CHAR_MINUS) return false; // not "--"
    ptr++;
    len -= 2;
    if (len < iBoundary.length()) return false;
    char *boundaryString = (char *)iBoundary.c_str();
    int32 i = 0;
    for (i = 0; i < iBoundary.length(); i++)
    {
        if (ptr[i] != boundaryString[i]) return false;
    }

    // check the last "--" as the flag of final boundary string
    ptr += iBoundary.length();
    len -= iBoundary.length();
    if (len >= 2)
    {
        if (*ptr == HTTP_CHAR_MINUS && *(ptr + 1) == HTTP_CHAR_MINUS)
        {
            isFinalBoundary = true;
        }
    }
    return true;
}


/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserInput Implementation ///////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
HTTPParserInput* HTTPParserInput::create()
{
    HTTPParserInput *parserInput = OSCL_NEW(HTTPParserInput, ());
    if (!parserInput) return NULL;
    if (!parserInput->construct())
    {
        OSCL_DELETE(parserInput);
        return NULL;
    }
    return parserInput;
}

bool HTTPParserInput::construct()
{
    // create iLineBuffer
    OsclMemAllocator alloc;
    iLineBuffer = (char *)alloc.allocate(iLineBufferSize);
    if (!iLineBuffer) return false;

    int32 err = 0;
    OSCL_TRY(err,
             iDataInQueue.reserve(DATA_QUEUE_VECTOR_RESERVE_SIZE);
             iDataOutQueue.reserve(DATA_QUEUE_VECTOR_RESERVE_SIZE);
            );
    return (err == 0);

}

HTTPParserInput::~HTTPParserInput()
{
    clear();
    iDataInQueue.clear();
    iDataOutQueue.clear();
    if (iLineBuffer)
    {
        OsclMemAllocator alloc;
        alloc.deallocate(iLineBuffer);
        iLineBuffer = NULL;
    }
}

bool HTTPParserInput::push_back(OsclRefCounterMemFrag &aFrag)
{
    if (!aFrag.getMemFragPtr() || !aFrag.getRefCounter())  // empty fragment
    {
        return (!iDataInQueue.empty()); // true for iDataInQueue not being empty
    }

    // check if this input is same to the previous one
    if (!iDataInQueue.empty())
    {
        if ((uint8*)aFrag.getMemFragPtr() == (uint8*)iDataInQueue[iDataInQueue.size()-1].getMemFragPtr())
        {
            return true; // true for iDataInQueue not being empty
        }
    }

    // push into the data queue
    int32 err = 0;
    OSCL_TRY(err, iDataInQueue.push_back(aFrag););
    if (err) return false;

    return true;
}

/////////////////////////////////////////////////////////////////////////////////////
bool HTTPParserInput::getNextCompleteLine(HTTPMemoryFragment &aHttpFrag, bool aHeaderParsed)
{
    int32 offset = isNextLineAvailable(aHeaderParsed);
    if (offset <= 0) return false;
    aHttpFrag.bind(iHTTPMemFrag.getPtr(), offset);
    return true;
}

// aRequestDataSize==0 means no request size, the function needs to send out whatever amount of data it has,
// but with one input data fragment each time.
// return value: actual size, if aRequestDataSize > 0, actual size <= aRequestDataSize
// actual size = 0, => no data, -1 means error
int32 HTTPParserInput::getData(HTTPMemoryFragment &aHttpFrag, const uint32 aRequestDataSize)
{
    if (iDataInQueue.empty()) return 0;

    uint32 requestSize = (aRequestDataSize > 0 ? aRequestDataSize : 0xffffffff);
    uint32 availableFragSize = iDataInQueue[0].getMemFragSize() - iDataInQueueMemFragOffset;
    uint32 actualSize = OSCL_MIN(requestSize, availableFragSize);

    if (actualSize > 0)
    {
        // create the output fragments
        uint8* fragStartPtr = (uint8*)iDataInQueue[0].getMemFragPtr() + iDataInQueueMemFragOffset;
        aHttpFrag.bind((void *)fragStartPtr, actualSize);
        if (!constructOutputFragment(actualSize, (void *)fragStartPtr, (iDataInQueueMemFragOffset == 0))) return -1;
    }

    // check if iDataInQueue[0] needs to be removed and update iDataInQueueMemFragOffset
    if (availableFragSize <= requestSize)
    {
        iDataInQueue.erase(iDataInQueue.begin());
        iDataInQueueMemFragOffset = 0;
    }
    else
    {
        // updata iDataInQueueMemFragOffset
        iDataInQueueMemFragOffset += actualSize;
    }

    return (int32)actualSize;
}

// This function is for parsing multipart content, specically for the final boundary string like --boundaryString--, which could
// has no "\r\n", so getNextCompleteLine may not work in this case
// In general, if iLineBuffer has data, then send out iLineBuffer, then check if input data queue has data, if it has, then send
// out the first buffer. Return false for no any data (both iLineBuffer and data queue are empty)
// Note that this function doesn't do "get" that means changing internal pointers, instead, only does "view"
bool HTTPParserInput::viewAvailableInputData(HTTPMemoryFragment &aHttpFrag)
{
    if (iLineBufferOccupied == 0 && iDataInQueue.empty()) return false;

    if (iLineBufferOccupied)
    {
        aHttpFrag.bind(iLineBuffer, iLineBufferOccupied);
    }
    else   // iDataInQueue is not empty
    {
        int32 availableFragSize = iDataInQueue[0].getMemFragSize() - iDataInQueueMemFragOffset;
        if (availableFragSize == 0) return false;
        uint8* fragStartPtr = (uint8*)iDataInQueue[0].getMemFragPtr() + iDataInQueueMemFragOffset;
        aHttpFrag.bind(fragStartPtr, availableFragSize);
    }
    return true;
}


/////////////////////////////////////////////////////////////////////////////////////
bool HTTPParserInput::getOutputMemFrag(OsclRefCounterMemFrag &aMemFrag)
{
    if (iDataOutQueue.empty()) return false;
    iDataOutQueue[0].getRefCountMemFrag(aMemFrag);
    iDataOutQueue.erase(iDataOutQueue.begin());
    return true;
}

/////////////////////////////////////////////////////////////////////////////////////
// pass ending CRLF
void HTTPParserInput::skipCRLF()
{
    if (iDataInQueue.empty()) return;
    uint8* fragStartPtr = (uint8*)iDataInQueue[0].getMemFragPtr() + iDataInQueueMemFragOffset;
    int32 availableFragSize = iDataInQueue[0].getMemFragSize() - iDataInQueueMemFragOffset;
    while ((*fragStartPtr == HTTP_CHAR_CR || *fragStartPtr == HTTP_CHAR_LF) && availableFragSize > 0)
    {
        *fragStartPtr++;
        availableFragSize--;
        iDataInQueueMemFragOffset++;
    }
}

/////////////////////////////////////////////////////////////////////////////////////
// pass ending CRLF
void HTTPParserInput::skipCRLFChunkedCoding()
{
    if (iDataInQueue.empty()) return;
    uint8* fragStartPtr = (uint8*)iDataInQueue[0].getMemFragPtr() + iDataInQueueMemFragOffset;
    int32 availableFragSize = iDataInQueue[0].getMemFragSize() - iDataInQueueMemFragOffset;

    while ((*fragStartPtr == HTTP_CHAR_CR && *(fragStartPtr + 1) == HTTP_CHAR_LF) && availableFragSize > 0)
    {
        fragStartPtr += 2;
        availableFragSize -= 2;
        iDataInQueueMemFragOffset += 2;
    }
}

// return value: 0 => not available ; >0 means the offset of the next complete line from the current point
// -1 error
int32 HTTPParserInput::isNextLineAvailable(bool aHeaderParsed)
{
    if (iDataInQueue.empty()) return 0;

    while (iDataInQueue.size() > 0)
    {
        if (iDataInQueueMemFragOffset >= iDataInQueue[0].getMemFragSize())
        {
            // remove iDataInQueue[0], since it is copied to iDataOutQueue
            iDataInQueue.erase(iDataInQueue.begin());
            iDataInQueueMemFragOffset = 0;
            iHTTPMemFrag.clear();
            return 0;
        }

        bool bNewData = (iDataInQueueMemFragOffset == 0);
        iHTTPMemFrag.bind((void *)((uint8*)iDataInQueue[0].getMemFragPtr() + iDataInQueueMemFragOffset),
                          iDataInQueue[0].getMemFragSize() - iDataInQueueMemFragOffset);

        int32 offset = checkNextLine(iHTTPMemFrag);
        iDataInQueueMemFragOffset += offset;
        if (offset > 0 && iLineBufferOccupied == 0)
        {
            // construct an output fragment
            if (!constructOutputFragment(iDataInQueueMemFragOffset, NULL, bNewData)) return -1;
            return offset;
        }

        // copy to iLineBuffer to concatenate the line fragments
        uint32 remaining_bytes = (offset > 0 ? offset : iHTTPMemFrag.getAvailableSpace());
        HTTPMemoryFragment aFrag(iHTTPMemFrag.getPtr(), remaining_bytes);
        if (assemblyLineFragments(aFrag)) return -1;

        if (offset > 0)
        {
            iHTTPMemFrag.bind(iLineBuffer, iLineBufferOccupied);
            if (!constructOutputFragment(iDataInQueueMemFragOffset, NULL, bNewData)) return -1;
            iLineBufferOccupied = 0;
            return iHTTPMemFrag.getCapacity();
        }

        if (!aHeaderParsed)
        {
            // save iDataInQueue[0]
            if (!bNewData && !iDataOutQueue.empty()) iDataOutQueue.erase(&iDataOutQueue.back());
            int32 err = 0;
            OSCL_TRY(err, iDataOutQueue.push_back(iDataInQueue[0]););
            if (err) return -1;
        }
        // remove iDataInQueue[0]
        iDataInQueue.erase(iDataInQueue.begin());
        iDataInQueueMemFragOffset = 0;
        iHTTPMemFrag.clear();
    }

    return 0;
}

int32 HTTPParserInput::assemblyLineFragments(HTTPMemoryFragment &aFrag)
{
    if (aFrag.getCapacity() <= iLineBufferSize - iLineBufferOccupied)
    {
        oscl_memcpy(iLineBuffer + iLineBufferOccupied, (char*)aFrag.getPtr(), aFrag.getCapacity());
    }
    else
    {
        // realloc iLineBuffer
        // aFrag.getCapacity()+iLineBufferOccupied>iLineBufferSize
        iLineBufferSize = (aFrag.getCapacity() + iLineBufferOccupied) << 1;

        OsclMemAllocator alloc;
        char *aNewLineBuffer = (char*)alloc.allocate(iLineBufferSize);
        if (!aNewLineBuffer) return -1;
        if (iLineBufferOccupied) oscl_memcpy(aNewLineBuffer, iLineBuffer, iLineBufferOccupied);
        oscl_memcpy(aNewLineBuffer + iLineBufferOccupied, (char*)aFrag.getPtr(), aFrag.getCapacity());

        // deallocate iLineBuffer
        alloc.deallocate(iLineBuffer);
        iLineBuffer = aNewLineBuffer;
    }
    iLineBufferOccupied += aFrag.getCapacity();
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserInput::checkNextLine(HTTPMemoryFragment &aInputDataStream)
{
    char *ptr = (char *)aInputDataStream.getPtr(), *start_ptr = ptr;
    int32 streamLength = aInputDataStream.getAvailableSpace();
    while (streamLength > 1)
    {
        if (*ptr != HTTP_CHAR_CR && *ptr != HTTP_CHAR_LF)
        {
            ptr++;
            streamLength--;
        }
        else if (*ptr == HTTP_CHAR_CR || *ptr == HTTP_CHAR_LF)
        {
            // Check for header over multiple lines, header will be divided over multiple header if
            // ptr[0] and ptr[1] points either CR or LF and ptr[2] points to either SPACE or TAB,
            // but make sure that the header starts with non-CR or non-LF character
            if ((*start_ptr != HTTP_CHAR_CR && *start_ptr != HTTP_CHAR_LF) &&
                    (streamLength > 2 && (ptr[2] == HTTP_CHAR_SPACE || ptr[2] == HTTP_CHAR_TAB)))
            {
                ptr += 2; // ptr increase by 2, One for CR and second for LF
                streamLength -= 2;
                iCurrentHeaderOverMultipleLine = true; // Since header over muliple header, update data member flag to TRUE
            }
            else
            {
                break;       // break when find complete next line
            }
        }
    }

    if (*ptr == HTTP_CHAR_CR || *ptr == HTTP_CHAR_LF)
    {
        if (streamLength > 1 &&
                (ptr[1] == HTTP_CHAR_CR || ptr[1] == HTTP_CHAR_LF) &&
                ptr[1] != ptr[0]) ptr++;    // avoid double CR or double LF, should treat it as different lines
        // Note that double CR(CRLFCRLF) or double LF (CRLFCRLF, or LFLF) means end of HTTP header
        return ptr - start_ptr + 1;
    }

    return 0; // no complete key-value pair available
}

/////////////////////////////////////////////////////////////////////////////////////
// if aNewMemFragPtr=NULL, no change to memory fragment pointer, existing fragment with larger length
bool HTTPParserInput::constructOutputFragment(const uint32 aNewMemFragLen, const void *aNewMemFragPtr, const bool isNewFrag)
{
    if (isNewFrag || iDataOutQueue.empty())
    {
        RefCounterMemoryFragment refCountMemfrag(iDataInQueue[0].getMemFrag(), iDataInQueue[0].getRefCounter());
        int32 err = 0;
        OSCL_TRY(err, iDataOutQueue.push_back(refCountMemfrag););
        if (err) return false;
    }

    // update ptr and len of the memory fragment
    iDataOutQueue.back().update(aNewMemFragLen);
    if (aNewMemFragPtr) iDataOutQueue.back().update((void*)aNewMemFragPtr);
    return true;
}

bool HTTPParserInput::empty()
{
    if (iDataInQueue.empty()) return true;
    if (iDataInQueue.size() > 1) return false;

    // iDataInQueue.size() = 1
    if (iDataInQueueMemFragOffset == iDataInQueue[0].getMemFragSize())
    {
        iDataInQueue.clear();
        iDataInQueueMemFragOffset = 0;
        return true;
    }
    return false;
}

/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserBaseObject Implementation //////////////////////*/////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserBaseObject::parseHeaderFields(HTTPMemoryFragment &aInputLineData, const bool aReplaceOldValue)
{
    // parse header fields
    char *fieldKey;
    uint32 fieldKeyLength;
    char *fieldValue;
    uint32 fieldValueLength = 0;
    int32 status = getNextFieldKeyValuePair(aInputLineData, fieldKey, fieldKeyLength, fieldValue, fieldValueLength);
    if (status == 1) return HTTPParser::PARSE_HEADER_AVAILABLE; // end of header
    if (status < 0)
    {
        LOGINFO((0, "HTTPParserBaseObject::parseHeaderFields() : Syntax Error founded!!"));
        return HTTPParser::PARSE_SYNTAX_ERROR;     // no divider characters found!
    }

    // exception handling (no key or no value case)
    if (fieldKeyLength == 0) return HTTPParser::PARSE_SUCCESS; // for no key, just ignore it
    char spaceChar = HTTP_CHAR_SPACE;
    if (fieldValueLength == 0)  // for no value, just set ''
    {
        fieldValue = &spaceChar;
        fieldValueLength = 1;
    }
    if (status != 0) return HTTPParser::PARSE_SUCCESS; // just ignore

    // add a key-value pair(fieldKey, fieldValue) to store
    return addKeyValuePairToStore(fieldKey, fieldKeyLength, fieldValue, fieldValueLength, aReplaceOldValue);
}

int32 HTTPParserBaseObject::addKeyValuePairToStore(const char *aFieldKey, const uint32 aFieldKeyLength,
        const char *aFieldValue, const uint32 aFieldValueLength,
        const bool aNeedReplaceOldValue)
{
    if (aFieldKeyLength + aFieldValueLength < iKeyValueStore->getAvailableSize())
    {
        if (iKeyValueStore->addKeyValuePair(aFieldKey, aFieldKeyLength, aFieldValue, aFieldValueLength, aNeedReplaceOldValue) != 0)
        {
            return HTTPParser::PARSE_GENERAL_ERROR;
        }
    }
    else
    {
        // not enough memory
        if (!reallocKeyValueStore(aFieldKeyLength + aFieldValueLength)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;
        // re-add key-value pair to store
        if (iKeyValueStore->addKeyValuePair(aFieldKey, aFieldKeyLength, aFieldValue, aFieldValueLength, aNeedReplaceOldValue) ==
                StringKeyValueStore::StringKeyValueStore_Failure) return HTTPParser::PARSE_GENERAL_ERROR;
    }
    return HTTPParser::PARSE_SUCCESS;
}

int32 HTTPParserBaseObject::addKeyValuePairToStore(const StrCSumPtrLen &aNewKey,
        const StrPtrLen &aNewValue,
        const bool aNeedReplaceOldValue)
{
    return addKeyValuePairToStore((char*)aNewKey.c_str(), aNewKey.length(),
                                  (char*)aNewValue.c_str(), aNewValue.length(),
                                  aNeedReplaceOldValue);
}


bool HTTPParserBaseObject::reallocKeyValueStore(const uint32 aCurrKeyValueSize)
{
    // calculate the new KeyValueStore size
    uint32 miniSize = iKeyValueStore->getCurrentMemoryUsage() + aCurrKeyValueSize;
    uint32 aNewStoreSize = OSCL_MAX(miniSize, iKeyValueStore->getStoreSize()) << 1;

    // create a new store
    StringKeyValueStore *aKeyValueStore = StringKeyValueStore::create(aNewStoreSize);
    if (!aKeyValueStore) return false;
    if (!aKeyValueStore->copy(*iKeyValueStore))
    {
        OSCL_DELETE(aKeyValueStore);
        return false;
    }
    OSCL_DELETE(iKeyValueStore);
    iKeyValueStore = aKeyValueStore;
    return true;

}

bool HTTPParserBaseObject::constructEntityUnit(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    OsclRefCounterMemFrag entityUnit;
    int32 err = 0;
    OSCL_TRY(err, entityUnit = iEntityUnitAlloc->get(););
    if (err) return false;

    // add memory fragments into the entity unit
    HTTPEntityUnit* entityUnitFrag = OSCL_PLACEMENT_NEW(((uint8*) entityUnit.getMemFragPtr()), HTTPEntityUnit());
    OsclRefCounterMemFrag memfrag;
    while (aParserInput.getOutputMemFrag(memfrag))
    {
        if (!entityUnitFrag->addMemFrag(memfrag))
        {
            entityUnitFrag->~HTTPEntityUnit();
            return false;
        }
    }
    //if(entityUnitFrag->getNumFragments() == 0) { entityUnitFrag->~HTTPEntityUnit(); return false; } // no memory fragments
    aEntityUnit = RefCountHTTPEntityUnit(*entityUnitFrag, entityUnit.getRefCounter()); // no memory fragments is possible
    entityUnitFrag->~HTTPEntityUnit(); // destruct entityUnitFrag
    return true;
}

// return value: 0 normal, 1 end of header, 2 ignore, -1 error
int32 HTTPParserBaseObject::getNextFieldKeyValuePair(HTTPMemoryFragment &aInputDataStream, char *&aFieldKey, uint32 &aFieldKeyLength,
        char *&aFieldValue, uint32 &aFieldValueLength)
{
    // get key
    int32 status = parseNextValueItem(aInputDataStream, aFieldKey, aFieldKeyLength, true);
    if (status != 0) return status; // end of header or error

    // get value
    return parseNextValueItem(aInputDataStream, aFieldValue, aFieldValueLength, false);
}

// return value: 0 normal,
//               1 end of header,
//               2 ignore (for CRLF, to handle CRLF split into separate fragments)
//              -1 error
int32 HTTPParserBaseObject::parseNextValueItem(HTTPMemoryFragment &aInputDataStream, char *&valueItemPtr, uint32 &valueItemLength, const bool isKeyItem)
{
    char dividerChar0 = (isKeyItem ? HTTP_CHAR_COLON : HTTP_CHAR_CR);
    char dividerChar1 = (isKeyItem ? HTTP_CHAR_COLON : HTTP_CHAR_LF);

    char *ptr = (char *)aInputDataStream.getPtr();
    int32 len = aInputDataStream.getAvailableSpace();

    // eat all non-letter characters at the beginning
    int32 status = getLineStartPoint(ptr, len, isKeyItem);
    if (status == 2) return status; // ignore
    if (status == 1)
    {
        aInputDataStream.update(ptr);    // Final CRLF, end of HTTP header
        return 1;
    }

    // search divider characters to identify the value item
    valueItemPtr = ptr;
    while (*ptr != dividerChar0 && *ptr != dividerChar1 && len > 0)
    {
        ptr++;    // assuming there is no case like "zzz key :"
        len--;
    }
    if (len <= 0) return -1; // no divider chars

    char *end_ptr = ptr--;
    while (*ptr == HTTP_CHAR_SPACE || *ptr == HTTP_CHAR_TAB) ptr--; // eat space or tab characater
    valueItemLength = (ptr > valueItemPtr ? (ptr - valueItemPtr + 1) : 0); // ptr is the ending pointer for a value item
    if (valueItemLength == 0 && (*valueItemPtr != dividerChar0 || *valueItemPtr != dividerChar1)) valueItemLength = 1;

    ptr = end_ptr;
    if (isKeyItem) ptr++;
    else
    {
        saveEndingCRLF(ptr, len, iPrevCRLF);
        if (len > 0 && (ptr[1] == HTTP_CHAR_CR || ptr[1] == HTTP_CHAR_LF)) ptr++; // pass CRLF at each line
    }

    aInputDataStream.update(ptr);
    return 0;
}

// return value: 0 normal, 1 end of header, 2 ignore
int32 HTTPParserBaseObject::getLineStartPoint(char *&ptr, int32 &len, const bool isKeyItem)
{
    if (isKeyItem)
    {
        while (!isLetter(*ptr) && (*ptr != HTTP_CHAR_CR && *ptr != HTTP_CHAR_LF) && len > 0)
        {
            iPrevCRLF = 0;
            ptr++;
            len--; // eat all non-letter characters except CRLF
        }
        if (*ptr == HTTP_CHAR_CR || *ptr == HTTP_CHAR_LF)  // key guarantees to have very first LETTER character
        {
            if (iPrevCRLF == 0)
            {
                saveEndingCRLF(ptr, len, iPrevCRLF);
                if (len > 0 && (ptr[1] == HTTP_CHAR_CR || ptr[1] == HTTP_CHAR_LF)) ptr++;
                return 2; // save CRLF to iPrevCRLF, and ignore
            }
            else   // iPrevCRLF has something
            {
                uint8 currCRLF = 0;
                saveEndingCRLF(ptr, len, currCRLF);
                if (len > 0 && (ptr[1] == HTTP_CHAR_CR || ptr[1] == HTTP_CHAR_LF)) ptr++;
                if (iPrevCRLF & currCRLF) return 1; // double CR or LF, end of HTTP header
                iPrevCRLF = currCRLF;
                return 2; // ignore
            }
        }
    }
    else
    {
        while ((*ptr == HTTP_CHAR_SPACE || *ptr == HTTP_CHAR_TAB) && len > 0)
        {
            ptr++;
            len--;
        } // eat space or tab character
    }

    return 0;
}

// aNeedReset=1, set aCRLF; aNeedReset=0, update aCRLF
void HTTPParserBaseObject::saveEndingCRLF(char *ptr, uint32 len, uint8& aCRLF, bool aNeedReset)
{
    char *tmpPtr = ptr;
    int32 tmpLen = (int32)len;
    if (aNeedReset) aCRLF = 0;

    // get to CRLF point
    while ((*tmpPtr != HTTP_CHAR_CR && *tmpPtr != HTTP_CHAR_LF) && tmpLen > 0)
    {
        tmpPtr++;
        tmpLen--;
    }

    while ((*tmpPtr == HTTP_CHAR_CR || *tmpPtr == HTTP_CHAR_LF) && tmpLen > 0)
    {
        if (*tmpPtr == HTTP_CHAR_CR) aCRLF |= 0x2; // bit 1 = 1
        if (*tmpPtr == HTTP_CHAR_LF) aCRLF |= 0x1; // bit 0 = 1
        tmpPtr++;
        tmpLen--;
    }
}

/*
This function removes all extra SPACE,TAB,CRLF. It copies outputdata to aHeaderFieldBuffer buffer.
Ex: input = "headeCRLF  rField= headerValCRLF ueCRLF"
    ouput = "headerField= headerValueCRLF"
*/
bool HTTPParserBaseObject::removeUnwantedCharacterHeaderFieldOverMultipleLine(HTTPMemoryFragment &aInputLineData, char *aHeaderFieldBuffer, int32 &headerLength)
{
    char *ptr = (char *)aInputLineData.getPtr();
    bool crlfFlag = false;
    uint32 i = 0;
    uint32 checkHeaderLengthExceed = 0;
    headerLength = 0;
    while (i < aInputLineData.getCapacity() - 2) //2 for CRLF
    {
        if (*ptr == HTTP_CHAR_CR && *(ptr + 1) == HTTP_CHAR_LF)
        {
            i += 2;
            ptr += 2;    //to remove extra CRLF
            crlfFlag = true;
            if (checkHeaderLengthExceed > iMaxLineSizeForMultiLineResponse) //It does not include CR LF
                return false;
            else
                checkHeaderLengthExceed = 0;
        }
        else
        {
            if (crlfFlag && *ptr != HTTP_CHAR_SPACE && *ptr != HTTP_CHAR_TAB)
            {
                crlfFlag = false;
            }

            if (!crlfFlag) //to remove SPACE or TAB just after CRLF
            {
                aHeaderFieldBuffer[headerLength] = *ptr;
                headerLength++;
            }
            ptr++;
            i++;
            checkHeaderLengthExceed++;
        }
    }
    aHeaderFieldBuffer[headerLength++] = *ptr++;  //CR
    aHeaderFieldBuffer[headerLength] = *ptr;      //LF
    return true;
}

bool HTTPParserBaseObject::processHeaderFieldOverMulipleLine(HTTPMemoryFragment &aInputLineData, char *aHeaderFieldBuffer)
{
    OsclMemAllocator alloc;
    int32 headerLength = 0;
    aHeaderFieldBuffer = (char *)alloc.allocate(aInputLineData.getCapacity());
    if (aHeaderFieldBuffer == NULL) return false;
    oscl_memcpy(aHeaderFieldBuffer, "\0", aInputLineData.getCapacity());
    if (false == removeUnwantedCharacterHeaderFieldOverMultipleLine(aInputLineData, aHeaderFieldBuffer, headerLength))
    {
        alloc.deallocate(aHeaderFieldBuffer);
        LOGINFO((0, "HTTPParserHeaderObject::parse() : Header field length exceeds"));
        return false;
    }
    aInputLineData.bind(aHeaderFieldBuffer, headerLength);
    return true;
}
/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserHeaderObject Implementation ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserHeaderObject::parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    HTTPMemoryFragment aInputLineData;
    char *aHeaderFieldBuffer = NULL;
    while (aParserInput.getNextCompleteLine(aInputLineData))
    {
        if (!iHeaderFirstLineParsed)
        {
            // parse the first line : Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
            int32 status = parseFirstLine(aInputLineData);
            if (status < 0) return status; // syntax error
            iHeaderFirstLineParsed = true;
        }
        else
        {
            //setCurrentHeaderNumLine(aParserInput.getCurrentHeaderNumLine());
            if (aParserInput.getHeaderOverMultipleLineFlag())
            {
                if (false == processHeaderFieldOverMulipleLine(aInputLineData, aHeaderFieldBuffer))
                {
                    aParserInput.setHeaderOverMultipleLineFlag(false);
                    return HTTPParser::PARSE_LINESIZE_FOR_MULTILINE_HEADER_EXCEED; //when header is over multiple line check header length
                }

            }
            else if ((aInputLineData.getCapacity() - 2) > iMaxLineSizeForMultiLineResponse)
            {
                LOGINFO((0, "HTTPParserHeaderObject::parse() : Header field length exceeds"));
                return HTTPParser::PARSE_LINESIZE_FOR_MULTILINE_HEADER_EXCEED; //when header is not over multiple line check header length
            }
            int32 status = parseHeaderFields(aInputLineData);
            if (status == HTTPParser::PARSE_HEADER_AVAILABLE)
            {
                iHeaderParsed = true;
                // check content info
                if (!iContentInfo->parseContentInfo(*iKeyValueStore)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;
                // construct output entity unit
                if (!constructEntityUnit(aParserInput, aEntityUnit)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;
                if (!isGoodStatusCode())
                {
                    LOGINFO((0, "HTTPParserHeaderObject::parse() : NOT GOOD STATUS CODE"));
                    return HTTPParser::PARSE_STATUS_LINE_SHOW_NOT_SUCCESSFUL;
                }
                if (checkResponseParsedComplete()) iResponseParsedComplete = true;
                return HTTPParser::PARSE_HEADER_AVAILABLE;
            }
            if (aParserInput.getHeaderOverMultipleLineFlag())
            {
                OsclMemAllocator alloc;
                alloc.deallocate(aHeaderFieldBuffer);
                aParserInput.setHeaderOverMultipleLineFlag(false);
            }
            if (status != HTTPParser::PARSE_SUCCESS) return status;
        }

    } // end of: while(iParserInput->getNextCompleteLine(aInputLineData))

    // check content info
    return HTTPParser::PARSE_NEED_MORE_DATA;
}

// return value: 0 => ok , or HTTPParser enum codes
int32 HTTPParserHeaderObject::parseFirstLine(HTTPMemoryFragment &aInputDataStream)
{
    // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
    char *ptr = (char *)aInputDataStream.getPtr();
    int32 len = (int32)aInputDataStream.getAvailableSpace();

    while (!isLetter(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    if (len < 8) return HTTPParser::PARSE_SYNTAX_ERROR;

    // check HTTP/1.x
    if (((ptr[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'h') &&
            ((ptr[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            ((ptr[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 't') &&
            ((ptr[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'p') &&
            (ptr[4]                              == '/'))
    {
        ptr += 5; // size of "http/"
        if (!checkHTTPVersion(ptr))
        {
            return HTTPParser::PARSE_HTTP_VERSION_NOT_SUPPORTED;
        }

        // ptr should be updated in checkHTTPVersion()
        while (!isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        if (len <= 0) return 0; // no digital status code
        char *start_ptr = ptr;
        uint32 start_len = len;
        while (isDigit(*ptr) && len > 0)
        {
            ptr++;
            len--;
        }
        //if(len <= 0) return 1; // no Reason-Phrase

        // get status code
        PV_atoi(start_ptr, 'd', start_len - len, iStatusCode);
        saveEndingCRLF(ptr, len, iPrevCRLF);
        return 0;
    }

    // add first-line into the key-value store
    StrPtrLen firstLine = "Response-Line";
    addKeyValuePairToStore(firstLine.c_str(), firstLine.length(),
                           (char *)aInputDataStream.getPtr(), aInputDataStream.getAvailableSpace(),
                           true);

    return HTTPParser::PARSE_SYNTAX_ERROR; // looks like the status line has something we don't understand.
}

bool HTTPParserHeaderObject::checkHTTPVersion(char* &aPtr)
{
    if (aPtr[0] == '1' && aPtr[0] == HTTP_CHAR_SPACE)
    {
        // support HTTP/1
        aPtr += 2;
        iHttpVersionNum = 0;
        return true;
    }

    if (aPtr[0] == '1' &&
            aPtr[1] == '.' &&
            (aPtr[2] == '0' || aPtr[2] == '1'))
    {
        iHttpVersionNum = (aPtr[2] == '0' ? 0 : 1);
        aPtr += 3;
        return true;
    }

    return false;
}

// This function gets complicated since a couple of new cases have been added
bool HTTPParserHeaderObject::isGoodStatusCode()
{
    if (iStatusCode < GOOD_HTTP_STATUS_CODE_START_FROM100 ||
            iStatusCode > GOOD_HTTP_STATUS_CODE_END_AT299) return false;

    // check 1xx code, 1xx code is only allowed in Http/1.1
    if (checkGood1xxCode())
        return true;

    // check 2xx code, if 204 (no content) or 2xx code with content-length=0, then we need to error out
    bool goodStatusCode = checkGood2xxCode();
    return goodStatusCode;
}

// check 1xx code, 1xx code is only allowed in Http/1.1
bool HTTPParserHeaderObject::checkGood1xxCode()
{
    if (iHttpVersionNum == 1 &&
            (GOOD_HTTP_STATUS_CODE_START_FROM100 <= iStatusCode && iStatusCode < GOOD_HTTP_STATUS_CODE_START_FROM200))
    {
        return true;
    }
    return false;
}

// check 2xx code, if 2xx code with content-length=0, then we need to error out
bool HTTPParserHeaderObject::checkGood2xxCode()
{
    uint32 goodStatusCodeStart = GOOD_HTTP_STATUS_CODE_START_FROM200;
    if (GOOD_HTTP_STATUS_CODE_START_FROM200 <= iStatusCode && iStatusCode <= GOOD_HTTP_STATUS_CODE_END_AT299)
    {
        // for Http status code 204 (no content), error our right away
        if (iStatusCode == HTTP_STATUS_CODE_204_NO_CONTENT)
        {
            LOGINFO((0, "HTTPParserHeaderObject::checkGood2xxCode() : iStatusCode=HTTP_STATUS_CODE_204_NO_CONTENT"));
            return false;
        }

        // other 2xx code, check the zero or empty content-length
        StrCSumPtrLen contenLengthKey("Content-Length");
        StrPtrLen contentLengthValue;
        if (iKeyValueStore->getValueByKey(contenLengthKey, contentLengthValue))
        {
            // has Content-Length field
            uint32 aContentLength = 0;
            PV_atoi(contentLengthValue.c_str(), 'd', aContentLength);

            // check the empty Content-Length case
            char *ptr = (char *)contentLengthValue.c_str();
            if (aContentLength == 0 || ptr[0] == HTTP_CHAR_SPACE)
            {
                LOGINFO((0, "HTTPParserHeaderObject::checkGood2xxCode() : zero or empty content length for 2xx code"));
                return false;
            }
        }
    }
    return (goodStatusCodeStart <= iStatusCode && iStatusCode <= GOOD_HTTP_STATUS_CODE_END_AT299);
}

int32 HTTPParserHeaderObject::doSanityCheckForResponseHeader()
{
    // check Chunked Transfer Encoding supported for Http/1.1 only
    if (!checkChunkedTransferEncodingSupported()) return HTTPParser::PARSE_TRANSFER_ENCODING_NOT_SUPPORTED;
    return HTTPParser::PARSE_SUCCESS;
}


// check Chunked Transfer Encoding supported by Http/1.1 only
bool HTTPParserHeaderObject::checkChunkedTransferEncodingSupported()
{
    StrCSumPtrLen transferEncodingKey("Transfer-Encoding");
    StrPtrLen transferEncodingValue;
    if (iKeyValueStore->getValueByKey(transferEncodingKey, transferEncodingValue))
    {
        LOGINFO((0, "HTTPParserHeaderObject::checkChunkedTransferEncodingSupported() : has Transfer-encoding field, HttpVersionNum=%d", iHttpVersionNum));
        // has Transfer-encoding field
        if (iHttpVersionNum == 0) return false;
    }
    return true;
}

bool HTTPParserHeaderObject::checkResponseParsedComplete()
{
    // check "Content-Length"
    StrCSumPtrLen contentLengthKey = "Content-Length";
    StrPtrLen contentLengthValue;

    if (!getField(contentLengthKey, contentLengthValue)) return false; // no "Content-Length"

    // get "Content-Length" value
    uint32 contentLength = 0;
    PV_atoi(contentLengthValue.c_str(), 'd', contentLength);
    return (contentLength == 0);
}

HTTPParserHeaderObject *HTTPParserHeaderObject::create(HTTPContentInfoInternal *aContentInfo)
{
    HTTPParserHeaderObject *header = OSCL_NEW(HTTPParserHeaderObject, ());
    if (!header) return NULL;
    if (!header->construct(aContentInfo))
    {
        OSCL_DELETE(header);
        return NULL;
    }
    return header;
}

bool HTTPParserHeaderObject::construct(HTTPContentInfoInternal *aContentInfo)
{
    reset();
    iContentInfo = aContentInfo;

    if ((iKeyValueStore = StringKeyValueStore::create()) == NULL) return false;

    iEntityUnitAlloc = OSCL_NEW(PVMFBufferPoolAllocator, ());
    if (!iEntityUnitAlloc) return false;

    int32 err = 0;
    OSCL_TRY(err, iEntityUnitAlloc->size(HTTP_ENTITY_UNIT_POOLNUM, sizeof(HTTPEntityUnit)));
    if (err) return false;

    return true;
}

HTTPParserHeaderObject::~HTTPParserHeaderObject()
{
    reset();

    // delete iKeyValueStore
    if (iKeyValueStore) OSCL_DELETE(iKeyValueStore);
    iKeyValueStore = NULL;

    // delete iEntityUnitAlloc
    if (iEntityUnitAlloc) OSCL_DELETE(iEntityUnitAlloc);
    iEntityUnitAlloc = NULL;
}


/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserEntityBodyObject Implementation ////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserEntityBodyObject::parseEnityBodyChunkData(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    int32 requestSize = iContentInfo->getContentRangeLength() - iCurrentChunkDataLength;
    HTTPMemoryFragment aFrag;
    int32 actualSize = 0;
    while (requestSize > 0)
    {
        if ((actualSize = aParserInput.getData(aFrag, requestSize)) <= 0) break;
        iCurrentChunkDataLength += actualSize;
        iNumChunks++;
        requestSize -= actualSize;
    }
    if (actualSize < 0) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;
    if (actualSize == 0 && requestSize > 0) return HTTPParser::PARSE_NEED_MORE_DATA;

    // get complete chunk, and then construct output entity unit
    if (!constructEntityUnit(aParserInput, aEntityUnit)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;

    // pass ending CRLF for the chunk data for next chunk parsing
    aParserInput.skipCRLF();
    return HTTPParser::PARSE_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserNormalContentObject Implementation /////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserNormalContentObject::parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    HTTPMemoryFragment aFrag;
    int32 actualSize = 0;


    if (iCurrTotalLengthObtained == 0 && iContentInfo->iContentRangeLeft > 0) iCurrTotalLengthObtained = iContentInfo->iContentRangeLeft;

    // Shoutast sessions do not have Content-Length
    int32 requestSize = (iContentInfo->iContentLength == 0 ? 0 : (int32)iContentInfo->iContentLength - (int32)iCurrTotalLengthObtained);
    if (requestSize <= 0 && iContentInfo->iContentLength > 0)
    {
        if (requestSize == 0) return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
        return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA;
    }

    while ((actualSize = aParserInput.getData(aFrag, requestSize)) > 0)
    {
        iCurrTotalLengthObtained += actualSize;
        if (requestSize > 0)
        {
            if ((requestSize -= actualSize) <= 0) break; // we don't need to process aFrag
        }
    }
    if (actualSize < 0) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;

    // construct output entity unit
    if (!constructEntityUnit(aParserInput, aEntityUnit)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;


    if (iCurrTotalLengthObtained >= iContentInfo->iContentLength && iContentInfo->iContentLength > 0)
    {
        if (iCurrTotalLengthObtained > iContentInfo->iContentLength ||
                !aParserInput.empty()) return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA;
        return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
    }

    if (actualSize == 0 &&
            (iContentInfo->iContentLength > iCurrTotalLengthObtained || iContentInfo->iContentLength == 0))
    {
        return HTTPParser::PARSE_SUCCESS_END_OF_INPUT;
    }

    return HTTPParser::PARSE_SUCCESS;
}


/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserCTEContentObject Implementation /////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserCTEContentObject::parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    // get CTE chunk length
    if (iContentInfo->iContentRangeRight == 0)
    {
        HTTPMemoryFragment aInputLineData;
        int32 chunkLength = -1;
        while (aParserInput.getNextCompleteLine(aInputLineData, true))  // true means header is already parsed
        {
            if (getCTEChunkLength(aInputLineData, chunkLength)) break;
        }
        if (chunkLength == -1) return HTTPParser::PARSE_NEED_MORE_DATA;
        if (chunkLength == 0)
        {
            return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
        }

        iContentInfo->iContentRangeRight = chunkLength - 1;
        iContentInfo->iContentLength += chunkLength;
        aParserInput.clearOutputQueue();
    }

    // get CTE chunk data, should use skipCRLFChunkedCoding, there is problem
    // with skipCRLF() since the \r\n has to come toegether. Ling
    aParserInput.skipCRLFChunkedCoding();
    int32 status = parseEnityBodyChunkData(aParserInput, aEntityUnit);

    if (status != HTTPParser::PARSE_SUCCESS) return status;

    reset(); // for next chunk parsing
    if (aParserInput.empty()) return HTTPParser::PARSE_SUCCESS_END_OF_INPUT;
    return HTTPParser::PARSE_SUCCESS;
}

bool HTTPParserCTEContentObject::getCTEChunkLength(HTTPMemoryFragment &aInputLineData, int32 &aChunkSize)
{
    char *ptr = (char *)aInputLineData.getPtr();
    int32 len = (int32)aInputLineData.getAvailableSpace();
    while (!isHexDigit(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    if (len <= 0) return false;
    char *start_ptr = ptr;
    int32 start_len = len;
    while (isHexDigit(*ptr) && len > 0)
    {
        ptr++;
        len--;
    }
    if (len <= 0) return false;
    uint32 chunkSize;
    PV_atoi(start_ptr, 'x', start_len - len, chunkSize);
    aChunkSize = (int32)chunkSize;
    return true;
}

/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserMultipartContentObject Implementation /////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserMultipartContentObject::parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
    // parse boundary string and chunk header
    int32 status = parseChunkHeader(aParserInput);
    if (status != HTTPParser::PARSE_SUCCESS) return status;

    // get chunk data

    status = parseEnityBodyChunkData(aParserInput, aEntityUnit);
    if (status != HTTPParser::PARSE_SUCCESS) return status;

    reset(); // for next chunk parsing
    if (aParserInput.empty()) return HTTPParser::PARSE_SUCCESS_END_OF_INPUT;
    return HTTPParser::PARSE_SUCCESS;
}

int32 HTTPParserMultipartContentObject::parseChunkHeader(HTTPParserInput &aParserInput)
{
    if (iHeaderInEntityBodyParsed) return HTTPParser::PARSE_SUCCESS;

    // parse boundary line
    if (!iBoudaryLineParsed)
    {
        int32 status = parseChunkBoundaryLine(aParserInput);
        if (status != HTTPParser::PARSE_SUCCESS) return status;
        if (!iBoudaryLineParsed) return HTTPParser::PARSE_NEED_MORE_DATA; // try next time
    }

    // parse chunk header
    HTTPMemoryFragment aInputLineData;
    while (aParserInput.getNextCompleteLine(aInputLineData))
    {
        if (!iBoudaryLineParsed)
        {
            return HTTPParser::PARSE_SYNTAX_ERROR;
        }
        int32 status = parseHeaderFields(aInputLineData, true); // true means replace the old field value with the new one
        if (status == HTTPParser::PARSE_HEADER_AVAILABLE)
        {
            iHeaderInEntityBodyParsed = true;
            iCounter++;
            // update content info
            if (!iContentInfo->parseContentInfo(*iKeyValueStore)) return HTTPParser::PARSE_MEMORY_ALLOCATION_FAILURE;
            aParserInput.clearOutputQueue();
            saveEndingCRLF((char *)aInputLineData.getPtr(), (int32)aInputLineData.getAvailableSpace(), iPrevCRLF);
            break;
        }
        if (status != HTTPParser::PARSE_SUCCESS) return status;
    }
    if (!iHeaderInEntityBodyParsed) return HTTPParser::PARSE_NEED_MORE_DATA;

    // check the extra CRLF
    if (needSkipCRLF()) aParserInput.skipCRLF();
    return HTTPParser::PARSE_SUCCESS;
}


int32 HTTPParserMultipartContentObject::parseChunkBoundaryLine(HTTPParserInput &aParserInput)
{
    HTTPMemoryFragment aInputLineData;
    if (aParserInput.getNextCompleteLine(aInputLineData))
    {

        // parse boundary line : --BOUNDARY STRING
        bool isFinalBoundary = false;
        if (iContentInfo->parseBoudaryLine(aInputLineData, isFinalBoundary)) iBoudaryLineParsed = true;
        if (isFinalBoundary) return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
        saveEndingCRLF((char *)aInputLineData.getPtr(), (int32)aInputLineData.getAvailableSpace(), iPrevCRLF);

        if (!iBoudaryLineParsed)
        {
            if (aParserInput.getNextCompleteLine(aInputLineData))
            {
                bool isFinalBoundary = false;
                if (iContentInfo->parseBoudaryLine(aInputLineData, isFinalBoundary)) iBoudaryLineParsed = true;
            }
        }
    }

    if (!iBoudaryLineParsed)
    {
        // try to see whether it is final boundary line, like --boundaryString-- (no "\r\n")
        HTTPMemoryFragment frag;
        if (!aParserInput.viewAvailableInputData(frag)) return HTTPParser::PARSE_NEED_MORE_DATA;

        bool isFinalBoundary = false;
        iContentInfo->parseBoudaryLine(frag, isFinalBoundary);
        if (isFinalBoundary) return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
    }
    return HTTPParser::PARSE_SUCCESS;
}
