| /* ------------------------------------------------------------------ |
| * 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; |
| } |