| /* ------------------------------------------------------------------ |
| * 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. |
| * ------------------------------------------------------------------- |
| */ |
| #ifndef HTTP_PARSER_INTERNAL_H_ |
| #define HTTP_PARSER_INTERNAL_H_ |
| |
| #include "oscl_refcounter_memfrag.h" |
| #include "oscl_vector.h" |
| #include "http_parcom_internal.h" |
| #include "http_parser_external.h" |
| |
| #ifndef PVMF_POOL_BUFFER_ALLOCATOR_H_INCLUDED |
| #include "pvmf_pool_buffer_allocator.h" |
| #endif |
| |
| #ifndef PVLOGGER_H_INCLUDED |
| #include "pvlogger.h" |
| #endif |
| |
| #ifndef STRING_KEYVALUE_STORE_H_INCLUDED |
| #include "string_keyvalue_store.h" |
| #endif |
| |
| #define HTTP_ENTITY_UNIT_POOLNUM 4 |
| #define DEFAULT_MAX_LINE_BUFFER_SIZE 512 |
| #define DATA_QUEUE_VECTOR_RESERVE_SIZE 4 |
| #define GOOD_HTTP_STATUS_CODE_START_FROM100 100 |
| #define GOOD_HTTP_STATUS_CODE_START_FROM200 200 |
| #define GOOD_HTTP_STATUS_CODE_END_AT299 299 |
| #define HTTP_STATUS_CODE_204_NO_CONTENT 204 |
| |
| |
| enum HTTPContentType |
| { |
| HTTP_CONTENT_NORMAL = 0, // no chunk header info |
| HTTP_CONTENT_NULTIPART, // for Content-Type : multipart/byteranges |
| HTTP_CONTENT_CHUNKED_TRANSFER_ENCODING // for Transfer-Encoding : chunked |
| }; |
| |
| #if 1 |
| #define LOGINFO(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iLogger,PVLOGMSG_INFO,m); |
| #else |
| #define LOGINFO(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m); |
| #endif |
| |
| |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // HTTPContentInfoInternal object encapsulates content type/length/range infomation parsing happening |
| // on header parsing and entity body parsing |
| struct HTTPContentInfoInternal |
| { |
| uint32 iContentLength; // for "Content-Length" |
| uint32 iContentRangeLeft; // for "Content-Range" |
| uint32 iContentRangeRight; |
| |
| // constructor |
| HTTPContentInfoInternal() : iBoundaryBuffer(NULL) |
| { |
| clear(); |
| } |
| |
| // ~destructor |
| ~HTTPContentInfoInternal() |
| { |
| clear(); |
| if (iBoundaryBuffer) OSCL_ARRAY_DELETE(iBoundaryBuffer); |
| iBoundaryBuffer = NULL; |
| } |
| |
| // clear |
| void clear() |
| { |
| iContentLength = 0; |
| iContentRangeLeft = 0; |
| iContentRangeRight = 0; |
| |
| iContentType = HTTP_CONTENT_NORMAL; |
| iBoundary.setPtrLen("", 0); |
| } |
| |
| void get(HTTPContentInfo &aContentInfo) |
| { |
| aContentInfo.iContentLength = iContentLength; |
| aContentInfo.iContentRangeLeft = iContentRangeLeft; |
| aContentInfo.iContentRangeRight = iContentRangeRight; |
| } |
| |
| // operator "=" |
| HTTPContentInfoInternal &operator=(const HTTPContentInfoInternal& x) |
| { |
| iContentLength = x.iContentLength; |
| iContentRangeLeft = x.iContentRangeLeft; |
| iContentRangeRight = x.iContentRangeRight; |
| iContentType = x.iContentType; |
| return *this; |
| } |
| |
| bool parseContentInfo(StringKeyValueStore &aKeyValueStore); |
| uint32 getContentType() const |
| { |
| return (uint32)iContentType; |
| } |
| // range lengh = range right - range left |
| uint32 getContentRangeLength() const |
| { |
| return (iContentRangeRight == 0 ? 0 : iContentRangeRight - iContentRangeLeft + 1); |
| } |
| |
| bool parseBoudaryLine(HTTPMemoryFragment &aInputLineData, bool &isFinalBoundary); |
| |
| private: |
| void parseContentRange(const StrPtrLen &aContentRange); |
| bool parseContentType(const StrPtrLen &aContentType); |
| void verifyTransferEncoding(const StrPtrLen &aTransferEncodingValue); |
| bool copyBoundaryString(const char* aBoundaryString, const uint32 aBoundaryStringLength); |
| |
| private: |
| HTTPContentType iContentType; |
| char *iBoundaryBuffer; |
| StrPtrLen iBoundary; // for "Content-Type : multipart/byteranges" |
| }; |
| |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // HTTPParserInput object hides fragment concatenation and grouping for header parsing and output generation |
| class HTTPParserInput |
| { |
| |
| public: |
| // factory method |
| static HTTPParserInput *create(); |
| |
| // destructor |
| ~HTTPParserInput(); |
| |
| // clear |
| void clear() |
| { |
| iDataInQueue.clear(); |
| iDataOutQueue.clear(); |
| iHTTPMemFrag.clear(); |
| iDataInQueueMemFragOffset = 0; |
| iLineBufferOccupied = 0; |
| } |
| |
| void clearOutputQueue() |
| { |
| iDataOutQueue.clear(); |
| } |
| |
| // add data |
| bool push_back(OsclRefCounterMemFrag &aFrag); |
| bool getNextCompleteLine(HTTPMemoryFragment &aHttpFrag, bool aHeaderParsed = false); |
| |
| // 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 getData(HTTPMemoryFragment &aHttpFrag, const uint32 aRequestDataSize = 0); |
| bool getOutputMemFrag(OsclRefCounterMemFrag &aMemFrag); |
| |
| // 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" (i.e. not changing internal pointers), instead, only does "view" |
| bool viewAvailableInputData(HTTPMemoryFragment &aHttpFrag); |
| bool empty(); |
| |
| // pass ending CRLF |
| void skipCRLF(); |
| |
| private: |
| |
| // constructor |
| HTTPParserInput() : iLineBufferSize(DEFAULT_MAX_LINE_BUFFER_SIZE) |
| { |
| clear(); |
| } |
| |
| // create iLineBuffer |
| bool construct(); |
| |
| // return value: 0 => not available ; >0 means the offset of the next complete line from the current point |
| // -1 error |
| int32 isNextLineAvailable(bool aHeaderParsed); |
| // called by isNextLineAvailable() |
| int32 assemblyLineFragments(HTTPMemoryFragment &aFrag); |
| |
| // if aNewMemFragPtr=NULL, no change to memory fragment pointer. isNewFrag=false means update the existing fragment |
| // from iDataOutQueue; if isNewFrag=true, get fragment from iDataInQueue |
| bool constructOutputFragment(const uint32 aNewMemFragLen, const void *aNewMemFragPtr = NULL, bool isNewFrag = false); |
| int32 checkNextLine(HTTPMemoryFragment &aInputDataStream); |
| |
| private: |
| Oscl_Vector<OsclRefCounterMemFrag, OsclMemAllocator> iDataInQueue; |
| Oscl_Vector<RefCounterMemoryFragment, OsclMemAllocator> iDataOutQueue; // RefCounterMemoryFragment defined in http_parcom_internal.h |
| uint32 iDataInQueueMemFragOffset; |
| HTTPMemoryFragment iHTTPMemFrag; // hold the input data fragment being processed |
| char *iLineBuffer; // concatenate multiple fragments of each header line |
| uint32 iLineBufferSize; |
| uint32 iLineBufferOccupied; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // HTTPParserBaseObject is the base class for header class and entity body class that still needs some kind of |
| // header parsing inside the entity body |
| |
| #define LOGGER_TAG "datapath.sourcenode.protocolenginenode" // "protocolenginenode.protocolengine" |
| //#define LOGGER_TAG "protocolenginenode.protocolengine" |
| |
| |
| class HTTPParserBaseObject |
| { |
| public: |
| int32 parseHeaderFields(HTTPMemoryFragment &aInputLineData, const bool aReplaceOldValue = false); |
| bool constructEntityUnit(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| void saveEndingCRLF(char *ptr, uint32 len, uint8& aCRLF, bool aNeedReset = true); |
| |
| // constructor |
| HTTPParserBaseObject(StringKeyValueStore *aKeyValueStore = NULL, PVMFBufferPoolAllocator *aEntityUnitAlloc = NULL) : |
| iKeyValueStore(aKeyValueStore), iEntityUnitAlloc(aEntityUnitAlloc), iPrevCRLF(0) |
| { |
| iLogger = PVLogger::GetLoggerObject(LOGGER_TAG); |
| } |
| |
| protected: |
| // due to the fact that Key-Value store may increase size in the fly, create this method to |
| // to wrap up all the details |
| // return code comes from HTTPParser return code |
| int32 addKeyValuePairToStore(const char *aFieldKey, const uint32 aFieldKeyLength, |
| const char *aFieldValue, const uint32 aFieldValueLength, |
| const bool aNeedReplaceOldValue = false); |
| |
| int32 addKeyValuePairToStore(const StrCSumPtrLen &aNewKey, |
| const StrPtrLen &aNewValue, |
| const bool aNeedReplaceOldValue = false); |
| |
| private: |
| // return value: 0 normal, 1 end of header |
| int32 getNextFieldKeyValuePair(HTTPMemoryFragment &aInputDataStream, char *&aFieldKey, uint32 &aFieldKeyLength, |
| char *&aFieldValue, uint32 &aFieldValueLength); |
| // return value: 0 normal, 1 end of header, 2 ignore |
| int32 getLineStartPoint(char *&ptr, int32 &len, const bool isKeyItem); |
| // return value: 0 normal, 1 end of header |
| int32 parseNextValueItem(HTTPMemoryFragment &aInputDataStream, char *&valueItemPtr, uint32 &valueItemLength, const bool isKeyItem); |
| |
| // called by addKeyValuePairToStore, increase the size of the current store |
| // due to not enough memory to hold new key-value pair |
| bool reallocKeyValueStore(const uint32 aCurrKeyValueSize); |
| |
| protected: |
| StringKeyValueStore *iKeyValueStore; |
| PVMFBufferPoolAllocator *iEntityUnitAlloc; |
| uint8 iPrevCRLF; // bit0 -- LF , bit1 -- CR |
| PVLogger *iLogger; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // HTTPParserHeaderObject encapsulates HTTP header parsing, basically parses every field inside the header and save it to |
| // key-value store, and also triggers content info parsing |
| class HTTPParserHeaderObject : public HTTPParserBaseObject |
| { |
| public: |
| int32 parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| bool isParsed() const |
| { |
| return iHeaderParsed; |
| } |
| // query this case: parsing header completely means parsing the whole response completely |
| // in case of no http body with content-length setting to zero |
| bool isWholeResponseParsed() const |
| { |
| return iResponseParsedComplete; |
| } |
| // check if there are some unsupported headers |
| int32 doSanityCheckForResponseHeader(); |
| |
| // The following "get" functions are for "get" functions of parser |
| uint32 getStatusCode() const |
| { |
| return iStatusCode; |
| } |
| uint32 getNumFields() |
| { |
| return (iKeyValueStore == NULL ? 0 : iKeyValueStore->getNumberOfKeyValuePairs()); |
| } |
| uint32 getFieldKeyList(StrPtrLen *&aFieldKeyList) |
| { |
| return (iKeyValueStore == NULL ? 0 : iKeyValueStore->getCurrentKeyList(aFieldKeyList)); |
| } |
| bool getField(const StrCSumPtrLen &aNewFieldKey, StrPtrLen &aNewFieldValue, const uint32 index = 0) |
| { |
| return (iKeyValueStore == NULL ? false : iKeyValueStore->getValueByKey(aNewFieldKey, aNewFieldValue, index)); |
| } |
| uint32 getNumberOfFieldsByKey(const StrCSumPtrLen &aNewFieldName) |
| { |
| return (iKeyValueStore == NULL ? 0 : iKeyValueStore->getNumberOfValuesByKey(aNewFieldName)); |
| } |
| /////////////////////////////////////////////////////////////////////////// |
| |
| // get iKeyValueStore and iEntityUnitAlloc, will be used HTTPParserEntityBodyObject |
| StringKeyValueStore *getKeyValuesStore() const |
| { |
| return iKeyValueStore; |
| } |
| PVMFBufferPoolAllocator *getAllocator() const |
| { |
| return iEntityUnitAlloc; |
| } |
| |
| // constructor |
| HTTPParserHeaderObject() |
| { |
| ; |
| } |
| |
| // destructor |
| ~HTTPParserHeaderObject(); |
| |
| // reset |
| void reset() |
| { |
| iStatusCode = 0; //200; |
| iHttpVersionNum = 0; |
| iHeaderParsed = false; |
| iHeaderFirstLineParsed = false; |
| iResponseParsedComplete = false; |
| if (iKeyValueStore) iKeyValueStore->clear(); |
| iPrevCRLF = 0; |
| } |
| |
| // factory method, create iKeyValueStore and iEntityUnitAlloc |
| static HTTPParserHeaderObject *create(HTTPContentInfoInternal *aContentInfo); |
| |
| private: |
| // return value: 0 => ok , 1 => data not enough -1 => syntax error |
| int32 parseFirstLine(HTTPMemoryFragment &aInputDataStream); |
| bool construct(HTTPContentInfoInternal *aContentInfo); |
| bool isGoodStatusCode(); |
| bool checkGood1xxCode(); |
| bool checkGood2xxCode(); |
| bool checkHTTPVersion(char* &aPtr); |
| bool checkResponseParsedComplete(); |
| bool checkChunkedTransferEncodingSupported(); |
| |
| private: |
| HTTPContentInfoInternal *iContentInfo; |
| uint32 iStatusCode; |
| uint32 iHttpVersionNum; |
| bool iHeaderParsed; |
| bool iHeaderFirstLineParsed; |
| bool iResponseParsedComplete; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // HTTPParserEntityBodyObject encapsulates HTTP entity body parsing. Here we support three types of entity body, |
| // normal body without any internal headers (usually in HTTP 1.0), multipart/byteranges used in pv fasttrack download |
| // and chunked transfer encoding, both having internal headers. To handle these three types of content, the following |
| // classes are designed to remove conditional using polymorphism, or remove type code with class, i.e. derived classes |
| // implement the interface defined in the base class, parse(). |
| class HTTPParserEntityBodyObject : public HTTPParserBaseObject |
| { |
| public: |
| virtual int32 parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit) = 0; |
| |
| // constructor |
| HTTPParserEntityBodyObject(StringKeyValueStore *aKeyValueStore, |
| PVMFBufferPoolAllocator *aEntityUnitAlloc, |
| HTTPContentInfoInternal *aContentInfo) : |
| HTTPParserBaseObject(aKeyValueStore, aEntityUnitAlloc), |
| iContentInfo(aContentInfo), |
| iCurrentChunkDataLength(0), |
| iNumChunks(0), |
| iCounter(0) |
| { |
| ; |
| } |
| |
| virtual ~HTTPParserEntityBodyObject() |
| { |
| ; |
| } |
| |
| protected: |
| // used in HTTPParserCTEContentObject and HTTPParserMultipartContentObject |
| int32 parseEnityBodyChunkData(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| |
| protected: |
| HTTPContentInfoInternal *iContentInfo; |
| uint32 iCurrentChunkDataLength; |
| uint32 iNumChunks; |
| uint32 iCounter; // for debugging purpose |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| class HTTPParserNormalContentObject : public HTTPParserEntityBodyObject |
| { |
| public: |
| int32 parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| |
| // constructor |
| HTTPParserNormalContentObject(StringKeyValueStore *aKeyValueStore, |
| PVMFBufferPoolAllocator *aEntityUnitAlloc, |
| HTTPContentInfoInternal *aContentInfo) : |
| HTTPParserEntityBodyObject(aKeyValueStore, aEntityUnitAlloc, aContentInfo), |
| iCurrTotalLengthObtained(0) |
| { |
| ; |
| } |
| virtual ~HTTPParserNormalContentObject() |
| { |
| ; |
| } |
| |
| private: |
| uint32 iCurrTotalLengthObtained; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| // CTE = Chunked Transfer Encoding |
| class HTTPParserCTEContentObject : public HTTPParserEntityBodyObject |
| { |
| public: |
| int32 parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| |
| // constructor |
| HTTPParserCTEContentObject(StringKeyValueStore *aKeyValueStore, |
| PVMFBufferPoolAllocator *aEntityUnitAlloc, |
| HTTPContentInfoInternal *aContentInfo) : |
| HTTPParserEntityBodyObject(aKeyValueStore, aEntityUnitAlloc, aContentInfo) |
| { |
| ; |
| } |
| |
| virtual ~HTTPParserCTEContentObject() |
| { |
| ; |
| } |
| private: |
| bool getCTEChunkLength(HTTPMemoryFragment &aInputLineData, int32 &aChunkSize); |
| // reset for next chunk parsing |
| void reset() |
| { |
| iCurrentChunkDataLength = 0; |
| if (iContentInfo) |
| { |
| iContentInfo->iContentRangeLeft = 0; |
| iContentInfo->iContentRangeRight = 0; |
| } |
| } |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////// |
| class HTTPParserMultipartContentObject : public HTTPParserEntityBodyObject |
| { |
| public: |
| int32 parse(HTTPParserInput &aParserInput, RefCountHTTPEntityUnit &aEntityUnit); |
| |
| // constructor |
| HTTPParserMultipartContentObject(StringKeyValueStore *aKeyValueStore, |
| PVMFBufferPoolAllocator *aEntityUnitAlloc, |
| HTTPContentInfoInternal *aContentInfo) : |
| HTTPParserEntityBodyObject(aKeyValueStore, aEntityUnitAlloc, aContentInfo) |
| { |
| reset(); |
| } |
| |
| virtual ~HTTPParserMultipartContentObject() |
| { |
| ; |
| } |
| private: |
| void reset() |
| { |
| iBoudaryLineParsed = iHeaderInEntityBodyParsed = false; |
| iCurrentChunkDataLength = 0; |
| iNumChunks = 0; |
| } |
| |
| bool needSkipCRLF() |
| { |
| // iPrevCRLF&0x3!=0x3 means iPrevCRLF hasn't got complete CRLF (i.e.both CR and LF) |
| return ((iNumChunks == 0) && ((iPrevCRLF&0x3) != 0x3)); |
| } |
| |
| int32 parseChunkHeader(HTTPParserInput &aParserInput); |
| int32 parseChunkBoundaryLine(HTTPParserInput &aParserInput); |
| |
| private: |
| bool iBoudaryLineParsed; |
| bool iHeaderInEntityBodyParsed; |
| |
| }; |
| |
| |
| #endif // HTTP_PARSER_INTERNAL_H_ |
| |