#include #define INITIAL_HEADERS_COUNT 16 #define HEADERS_INCREMENT 4 #define INVALID_HEADER_INDEX 0xff #define INVALID_HEADER_SLOT 0xFFFFFFFF // // STRESS_BUG_DEBUG - used to catch a specific stress fault, // where we have a corrupted crit sec. // #define CLEAR_DEBUG_CRIT(x) #define IS_DEBUG_CRIT_OK(x) typedef enum { HTTP_METHOD_TYPE_UNKNOWN = -1, HTTP_METHOD_TYPE_FIRST = 0, HTTP_METHOD_TYPE_GET= HTTP_METHOD_TYPE_FIRST, HTTP_METHOD_TYPE_HEAD, HTTP_METHOD_TYPE_POST, HTTP_METHOD_TYPE_PUT, HTTP_METHOD_TYPE_PROPFIND, HTTP_METHOD_TYPE_PROPPATCH, HTTP_METHOD_TYPE_LOCK, HTTP_METHOD_TYPE_UNLOCK, HTTP_METHOD_TYPE_COPY, HTTP_METHOD_TYPE_MOVE, HTTP_METHOD_TYPE_MKCOL, HTTP_METHOD_TYPE_CONNECT, HTTP_METHOD_TYPE_DELETE, HTTP_METHOD_TYPE_LINK, HTTP_METHOD_TYPE_UNLINK, HTTP_METHOD_TYPE_BMOVE, HTTP_METHOD_TYPE_BCOPY, HTTP_METHOD_TYPE_BPROPFIND, HTTP_METHOD_TYPE_BPROPPATCH, HTTP_METHOD_TYPE_BDELETE, HTTP_METHOD_TYPE_SUBSCRIBE, HTTP_METHOD_TYPE_UNSUBSCRIBE, HTTP_METHOD_TYPE_NOTIFY, HTTP_METHOD_TYPE_POLL, HTTP_METHOD_TYPE_CHECKIN, HTTP_METHOD_TYPE_CHECKOUT, HTTP_METHOD_TYPE_INVOKE, HTTP_METHOD_TYPE_SEARCH, HTTP_METHOD_TYPE_PIN, HTTP_METHOD_TYPE_MPOST, HTTP_METHOD_TYPE_LAST = HTTP_METHOD_TYPE_MPOST } HTTP_METHOD_TYPE; // // HTTP_HEADERS - array of pointers to general HTTP header strings. Headers are // stored without line termination. _HeadersLength maintains the cumulative // amount of buffer space required to store all the headers. Accounts for the // missing line termination sequence // #define HTTP_HEADER_SIGNATURE 0x64616548 // "Head" class HTTP_HEADERS { public: // // _bKnownHeaders - array of bytes which index into lpHeaders // the 0 column is an error catcher, so all indexes need to be biased by 1 (++'ed) // BYTE _bKnownHeaders[HTTP_QUERY_MAX+2]; private: #if INET_DEBUG DWORD _Signature; #endif // // _lpHeaders - (growable) array of pointers to headers added by app // HEADER_STRING * _lpHeaders; // // _TotalSlots - number of pointers in (i.e. size of) _lpHeaders // DWORD _TotalSlots; // // _NextOpenSlot - offset where the next array is open for allocation // DWORD _NextOpenSlot; // // _FreeSlots - number of available headers in _lpHeaders // DWORD _FreeSlots; // // _HeadersLength - the amount of buffer space required to store the headers. // For each header, includes +2 for the line termination that must be added // when the header buffer is generated, or when the headers are queried // DWORD _HeadersLength; // // _IsRequestHeaders - TRUE if this HTTP_HEADERS object describes the // request headers // BOOL _IsRequestHeaders; // // _lpszVerb etc. - in the case of request headers, we maintain these // pointers and corresponding lengths to make modifying the request line // easier in the case of a redirect // // N.B. The pointers are just offsets into the request line AND MUST NOT BE // FREED // LPSTR _lpszVerb; DWORD _dwVerbLength; LPSTR _lpszObjectName; DWORD _dwObjectNameLength; LPSTR _lpszVersion; DWORD _dwVersionLength; DWORD _RequestVersionMajor; DWORD _RequestVersionMinor; // // _Error - status code if error // DWORD _Error; // // _CritSec - acquire this when accessing header structure - stops multiple // threads clashing while modifying headers // // Question: why do we allow multi-threaded access to headers on same request? // CCritSec _CritSec; // // private methods // DWORD AllocateHeaders( IN DWORD dwNumberOfHeaders ); public: HTTP_HEADERS() { #if INET_DEBUG _Signature = HTTP_HEADER_SIGNATURE; #endif CLEAR_DEBUG_CRIT(_szCritSecBefore); CLEAR_DEBUG_CRIT(_szCritSecAfter); _CritSec.Init(); Initialize(); } ~HTTP_HEADERS() { DEBUG_ENTER((DBG_OBJECTS, None, "~HTTP_HEADERS", "%#x", this )); #if INET_DEBUG INET_ASSERT(_Signature == HTTP_HEADER_SIGNATURE); #endif FreeHeaders(); DEBUG_LEAVE(0); } VOID Initialize(VOID) { _lpHeaders = NULL; _TotalSlots = 0; _FreeSlots = 0; _HeadersLength = 0; _lpszVerb = NULL; _dwVerbLength = 0; _lpszObjectName = NULL; _dwObjectNameLength = 0; _lpszVersion = NULL; _dwVersionLength = 0; _RequestVersionMajor = 0; _RequestVersionMinor = 0; _NextOpenSlot = 0; memset((void *) _bKnownHeaders, INVALID_HEADER_SLOT, ARRAY_ELEMENTS(_bKnownHeaders)); _Error = AllocateHeaders(INITIAL_HEADERS_COUNT); } BOOL IsHeaderPresent(DWORD dwQueryIndex) const { return (_bKnownHeaders[dwQueryIndex] != INVALID_HEADER_INDEX) ? TRUE : FALSE ; } BOOL LockHeaders(VOID) { return _CritSec.Lock(); } VOID UnlockHeaders(VOID) { _CritSec.Unlock(); } VOID FreeHeaders( VOID ); DWORD CopyHeaders( IN OUT LPSTR * lpBuffer, IN LPSTR lpszObjectName, IN DWORD dwObjectNameLength ); #ifdef COMPRESSED_HEADERS VOID CopyCompressedHeaders( IN OUT LPSTR * lpBuffer ); #endif //COMPRESSED_HEADERS HEADER_STRING * FASTCALL FindFreeSlot( DWORD* piSlot ); HEADER_STRING * GetSlot( DWORD iSlot ) { return &_lpHeaders[iSlot]; } LPSTR GetHeaderPointer (LPBYTE pbBase, DWORD iSlot) { INET_ASSERT (iSlot < _TotalSlots); return _lpHeaders[iSlot].StringAddress((LPSTR) pbBase); } VOID ShrinkHeader( LPBYTE pbBase, DWORD iSlot, DWORD dwOldQueryIndex, DWORD dwNewQueryIndex, DWORD cbNewSize ); DWORD inline FastFind( IN DWORD dwQueryIndex, IN DWORD dwIndex ); DWORD inline FastNukeFind( IN DWORD dwQueryIndex, IN DWORD dwIndex, OUT BYTE **lplpbPrevIndex ); DWORD inline SlowFind( IN LPSTR lpBase, IN LPCSTR lpszHeaderName, IN DWORD dwHeaderNameLength, IN DWORD dwIndex, IN DWORD dwHash, OUT DWORD *lpdwQueryIndex, OUT BYTE **lplpbPrevIndex ); BYTE inline FastAdd( IN DWORD dwQueryIndex, IN DWORD dwSlot ); BOOL inline HeaderMatch( IN DWORD dwHash, IN LPSTR lpszHeaderName, IN DWORD dwHeaderNameLength, OUT DWORD *lpdwQueryIndex ); VOID RemoveAllByIndex( IN DWORD dwQueryIndex ); VOID RemoveHeader(IN DWORD dwIndex, IN DWORD dwQueryIndex, IN BYTE *pbPrevByte) { INET_ASSERT(dwIndex < _TotalSlots); INET_ASSERT(dwIndex != 0); // INET_ASSERT(_HeadersLength > 2); INET_ASSERT(_lpHeaders[dwIndex].StringLength() > 2); INET_ASSERT(_FreeSlots <= _TotalSlots); // // remove the length of the header + 2 for CR-LF from the total headers // length // if (_HeadersLength) { _HeadersLength -= _lpHeaders[dwIndex].StringLength() + (sizeof("\r\n") - 1) ; } // // Update the cached known headers, if this is one. // if ( dwQueryIndex < INVALID_HEADER_INDEX ) { *pbPrevByte = (BYTE) _lpHeaders[dwIndex].GetNextKnownIndex(); } // // set the header string to NULL. Frees the header buffer // _lpHeaders[dwIndex] = (LPSTR)NULL; // // we have freed a slot in the headers array // if ( _FreeSlots == 0 ) { _NextOpenSlot = dwIndex; } ++_FreeSlots; INET_ASSERT(_FreeSlots <= _TotalSlots); } DWORD GetSlotsInUse (void) { return _TotalSlots - _FreeSlots; } DWORD HeadersLength(VOID) const { return _HeadersLength; } DWORD ObjectNameLength(VOID) const { return _dwObjectNameLength; } LPSTR ObjectName(VOID) const { return _lpszObjectName; } DWORD AddHeader( IN LPSTR lpszHeaderName, IN DWORD dwHeaderNameLength, IN LPSTR lpszHeaderValue, IN DWORD dwHeaderValueLength, IN DWORD dwIndex, IN DWORD dwFlags ); DWORD AddHeader( IN DWORD dwQueryIndex, IN LPSTR lpszHeaderValue, IN DWORD dwHeaderValueLength, IN DWORD dwIndex, IN DWORD dwFlags ); DWORD ReplaceHeader( IN LPSTR lpszHeaderName, IN DWORD dwHeaderNameLength, IN LPSTR lpszHeaderValue, IN DWORD dwHeaderValueLength, IN DWORD dwIndex, IN DWORD dwFlags ); DWORD ReplaceHeader( IN DWORD dwQueryIndex, IN LPSTR lpszHeaderValue, IN DWORD dwHeaderValueLength, IN DWORD dwIndex, IN DWORD dwFlags ); DWORD FindHeader( IN LPSTR lpBase, IN LPCSTR lpszHeaderName, IN DWORD dwHeaderNameLength, IN DWORD dwModifiers, OUT LPVOID lpBuffer, IN OUT LPDWORD lpdwBufferLength, IN OUT LPDWORD lpdwIndex ); DWORD FindHeader( IN LPSTR lpBase, IN DWORD dwQueryIndex, IN DWORD dwModifiers, OUT LPVOID lpBuffer, IN OUT LPDWORD lpdwBufferLength, IN OUT LPDWORD lpdwIndex ); DWORD FastFindHeader( IN LPSTR lpBase, IN DWORD dwQueryIndex, OUT LPVOID *lplpBuffer, IN OUT LPDWORD lpdwBufferLength, IN OUT DWORD dwIndex ); DWORD QueryRawHeaders( IN LPSTR lpBase, IN BOOL bCrLfTerminated, IN LPVOID lpBuffer, IN OUT LPDWORD lpdwBufferLength ); DWORD QueryFilteredRawHeaders( IN LPSTR lpBase, IN LPSTR *lplpFilterList, IN DWORD cListElements, IN BOOL fExclude, IN BOOL fSkipVerb, IN BOOL bCrLfTerminated, IN LPVOID lpBuffer, IN OUT LPDWORD lpdwBufferLength ); DWORD AddRequest( IN LPSTR lpszVerb, IN LPSTR lpszObjectName, IN LPSTR lpszVersion ); DWORD ModifyRequest( IN HTTP_METHOD_TYPE tMethod, IN LPSTR lpszObjectName, IN DWORD dwObjectNameLength, IN LPSTR lpszVersion OPTIONAL, IN DWORD dwVersionLength ); HEADER_STRING * GetFirstHeader(VOID) const { INET_ASSERT(_lpHeaders != NULL); return _lpHeaders; } HEADER_STRING * GetEmptyHeader(VOID) const { for (DWORD i = 0; i < _TotalSlots; ++i) { if (_lpHeaders[i].HaveString() && _lpHeaders[i].StringLength() == 0) { return &_lpHeaders[i]; } } return NULL; } VOID SetIsRequestHeaders(BOOL bRequestHeaders) { _IsRequestHeaders = bRequestHeaders; } DWORD GetError(VOID) const { return _Error; } LPSTR GetVerb(LPDWORD lpdwVerbLength) const { *lpdwVerbLength = _dwVerbLength; return _lpszVerb; } //VOID SetRequestVersion(DWORD dwMajor, DWORD dwMinor) { // _RequestVersionMajor = dwMajor; // _RequestVersionMinor = dwMinor; //} VOID SetRequestVersion( VOID ); DWORD MajorVersion(VOID) const { return _RequestVersionMajor; } DWORD MinorVersion(VOID) const { return _RequestVersionMinor; } };