blob: 5f523ad5a474f0ad3cb4664a593d463775188312 [file] [log] [blame]
/* ------------------------------------------------------------------
* 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);
}
////////////////////////////////////////////////////////////////////////////////////
// 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++;
}
}
// 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 && (*ptr != HTTP_CHAR_CR && *ptr != HTTP_CHAR_LF))
{
ptr++;
streamLength--;
}
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()) 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
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--;
}
}
/////////////////////////////////////////////////////////////////////////////////////
////////// HTTPParserHeaderObject Implementation ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
int32 HTTPParserHeaderObject::parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit)
{
HTTPMemoryFragment aInputLineData;
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
{
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 (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
bool goodStatusCode = checkGood1xxCode();
if (!goodStatusCode) return false;
// check 2xx code, if 204 (no content) or 2xx code with content-length=0, then we need to error out
goodStatusCode = checkGood2xxCode();
return goodStatusCode;
}
// check 1xx code, 1xx code is only allowed in Http/1.1
bool HTTPParserHeaderObject::checkGood1xxCode()
{
if (iHttpVersionNum == 0 &&
(GOOD_HTTP_STATUS_CODE_START_FROM100 <= iStatusCode && iStatusCode < GOOD_HTTP_STATUS_CODE_START_FROM200))
{
return false;
}
return true;
}
// 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 (iContentInfo->iContentLength == 0) iContentInfo->iContentLength = 0x7fffffff; // 0=>7fff ffff
if (iCurrTotalLengthObtained == 0 && iContentInfo->iContentRangeLeft > 0) iCurrTotalLengthObtained = iContentInfo->iContentRangeLeft;
int32 requestSize = (int32)iContentInfo->iContentLength - (int32)iCurrTotalLengthObtained;
if (requestSize <= 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)
{
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) 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)
{
if (!aParserInput.empty()) return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA;
return HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE;
}
iContentInfo->iContentRangeRight = chunkLength - 1;
iContentInfo->iContentLength += chunkLength;
aParserInput.clearOutputQueue();
}
// get CTE chunk data
aParserInput.skipCRLF();
int32 status = parseEnityBodyChunkData(aParserInput, aEntityUnit);
if (status == HTTPParser::PARSE_SUCCESS) reset(); // for next chunk parsing
return status;
}
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;
}