You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2809 lines
69 KiB
2809 lines
69 KiB
#include <wininetp.h>
|
|
#include <perfdiag.hxx>
|
|
#include "httpp.h"
|
|
|
|
#include <wininetp.h>
|
|
|
|
#define DATE_AND_TIME_STRING_BUFFER_LENGTH 128
|
|
|
|
PRIVATE
|
|
BOOL
|
|
FMatchList(
|
|
LPSTR *lplpList,
|
|
DWORD cListLen,
|
|
HEADER_STRING *lpHeader,
|
|
LPSTR lpBase
|
|
)
|
|
{
|
|
DWORD i;
|
|
for (i=0; i < cListLen; ++i) {
|
|
if (!lpHeader->Strnicmp(lpBase, lplpList[i], strlen(lplpList[i]))) {
|
|
return (TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
FASTCALL
|
|
CalculateHashNoCase(
|
|
IN LPCSTR lpszString,
|
|
IN DWORD dwStringLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate a case-insensitive hash number given a string. Assumes input is
|
|
7-bit ASCII
|
|
|
|
Arguments:
|
|
|
|
lpszString - string to hash
|
|
|
|
dwStringLength - length of lpszString, or -1 if we need to calculate
|
|
|
|
Return Value:
|
|
|
|
DWORD - a generated hash value
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwHash = HEADER_HASH_SEED;
|
|
|
|
while (dwStringLength != 0) {
|
|
CHAR ch = *lpszString;
|
|
|
|
if ((ch >= 'A') && (ch <= 'Z')) {
|
|
ch = MAKE_LOWER(ch);
|
|
}
|
|
dwHash += (DWORD)(dwHash << 5) + ch; /*+ *pszName++;*/
|
|
|
|
++lpszString;
|
|
--dwStringLength;
|
|
}
|
|
return dwHash;
|
|
}
|
|
|
|
//
|
|
// methods
|
|
//
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::AllocateHeaders(
|
|
IN DWORD dwNumberOfHeaders
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates or grows the array of header pointers (HEADER_STRING objects)
|
|
|
|
Arguments:
|
|
|
|
dwNumberOfHeaders - number of additional header slots to create
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"AllocateHeaders",
|
|
"%d",
|
|
dwNumberOfHeaders
|
|
));
|
|
|
|
PERF_ENTER(AllocateHeaders);
|
|
|
|
//
|
|
// we really need to be able to realloc an array of HEADER_STRING objects
|
|
// (see below)
|
|
//
|
|
|
|
DWORD error;
|
|
DWORD slots = _TotalSlots;
|
|
|
|
|
|
if ( (_TotalSlots + dwNumberOfHeaders) > (INVALID_HEADER_INDEX-1))
|
|
{
|
|
INET_ASSERT(FALSE);
|
|
_NextOpenSlot = 0;
|
|
_TotalSlots = 0;
|
|
_FreeSlots = 0;
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
|
|
_lpHeaders = (HEADER_STRING *)ResizeBuffer((HLOCAL)_lpHeaders,
|
|
(_TotalSlots + dwNumberOfHeaders)
|
|
* sizeof(HEADER_STRING),
|
|
FALSE // not moveable
|
|
);
|
|
if (_lpHeaders != NULL) {
|
|
_NextOpenSlot = _TotalSlots;
|
|
_TotalSlots += dwNumberOfHeaders;
|
|
_FreeSlots += dwNumberOfHeaders;
|
|
|
|
//
|
|
// this is slightly ugly, but it seems there's no easy C++ way to
|
|
// do this - we need to be able to realloc() an array of objects
|
|
// created by new(), but so far, it can't be done
|
|
//
|
|
|
|
for (; slots < _TotalSlots; ++slots) {
|
|
_lpHeaders[slots].Clear();
|
|
}
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
|
|
INET_ASSERT(FALSE);
|
|
_NextOpenSlot = 0;
|
|
_TotalSlots = 0;
|
|
_FreeSlots = 0;
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
quit:
|
|
|
|
INET_ASSERT(_FreeSlots <= _TotalSlots);
|
|
|
|
PERF_LEAVE(AllocateHeaders);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
VOID
|
|
HTTP_HEADERS::FreeHeaders(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the headers strings and the headers array
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
None,
|
|
"FreeHeaders",
|
|
NULL
|
|
));
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// free up each individual entry (free string buffers)
|
|
//
|
|
|
|
for (DWORD i = 0; i < _TotalSlots; ++i) {
|
|
_lpHeaders[i] = (LPSTR)NULL;
|
|
}
|
|
|
|
//
|
|
// followed by the array itself
|
|
//
|
|
|
|
if (_lpHeaders) {
|
|
_lpHeaders = (HEADER_STRING *)FREE_MEMORY((HLOCAL)_lpHeaders);
|
|
|
|
INET_ASSERT(_lpHeaders == NULL);
|
|
}
|
|
|
|
_TotalSlots = 0;
|
|
_FreeSlots = 0;
|
|
_HeadersLength = 0;
|
|
_lpszVerb = NULL;
|
|
_dwVerbLength = 0;
|
|
_lpszObjectName = NULL;
|
|
_dwObjectNameLength = 0;
|
|
_lpszVersion = NULL;
|
|
_dwVersionLength = 0;
|
|
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::CopyHeaders(
|
|
IN OUT LPSTR * lpBuffer,
|
|
IN LPSTR lpszObjectName,
|
|
IN DWORD dwObjectNameLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
|
|
This method is called to convert the request headers list to a buffer that
|
|
we can send to the server
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - pointer to pointer to buffer where headers are
|
|
written. We update the pointer
|
|
|
|
lpszObjectName - optional object name
|
|
|
|
dwObjectNameLength - optional object name length
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory while trying to synchronize src data access
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
None,
|
|
"CopyHeaders",
|
|
"%#x, %#x [%q], %d",
|
|
lpBuffer,
|
|
lpszObjectName,
|
|
lpszObjectName,
|
|
dwObjectNameLength
|
|
));
|
|
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
if (!LockHeaders())
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
DWORD i = 0;
|
|
|
|
if (lpszObjectName != NULL) {
|
|
memcpy(*lpBuffer, _lpszVerb, _dwVerbLength);
|
|
*lpBuffer += _dwVerbLength;
|
|
*(*lpBuffer)++ = ' ';
|
|
memcpy(*lpBuffer, lpszObjectName, dwObjectNameLength);
|
|
*lpBuffer += dwObjectNameLength;
|
|
*(*lpBuffer)++ = ' ';
|
|
memcpy(*lpBuffer, _lpszVersion, _dwVersionLength);
|
|
*lpBuffer += _dwVersionLength;
|
|
*(*lpBuffer)++ = '\r';
|
|
*(*lpBuffer)++ = '\n';
|
|
i = 1;
|
|
}
|
|
for (; i < _TotalSlots; ++i) {
|
|
if (_lpHeaders[i].HaveString()) {
|
|
_lpHeaders[i].CopyTo(*lpBuffer);
|
|
*lpBuffer += _lpHeaders[i].StringLength();
|
|
*(*lpBuffer)++ = '\r';
|
|
*(*lpBuffer)++ = '\n';
|
|
}
|
|
}
|
|
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
DEBUG_LEAVE(dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
HEADER_STRING *
|
|
FASTCALL
|
|
HTTP_HEADERS::FindFreeSlot(
|
|
DWORD* piSlot
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the next free slot in the headers list, or adds some new slots
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
piSlot: returns index of slot found
|
|
|
|
Return Value:
|
|
|
|
HEADER_STRING * - pointer to next free slot
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Pointer,
|
|
"FindFreeSlot",
|
|
NULL
|
|
));
|
|
|
|
PERF_ENTER(FindFreeSlot);
|
|
|
|
DWORD i;
|
|
DWORD error;
|
|
HEADER_STRING * header = NULL;
|
|
|
|
//
|
|
// if there are no free slots, allocate some more
|
|
//
|
|
|
|
if (_FreeSlots == 0) {
|
|
i = _TotalSlots;
|
|
error = AllocateHeaders(HEADERS_INCREMENT);
|
|
} else {
|
|
i = 0;
|
|
error = ERROR_SUCCESS;
|
|
if (!_lpHeaders[_NextOpenSlot].HaveString())
|
|
{
|
|
--_FreeSlots;
|
|
header = &_lpHeaders[_NextOpenSlot];
|
|
*piSlot = _NextOpenSlot;
|
|
_NextOpenSlot = (_NextOpenSlot == (_TotalSlots-1)) ? (_TotalSlots-1) : _NextOpenSlot++;
|
|
goto quit;
|
|
}
|
|
}
|
|
if (error == ERROR_SUCCESS) {
|
|
for (; i < _TotalSlots; ++i) {
|
|
if (!_lpHeaders[i].HaveString()) {
|
|
--_FreeSlots;
|
|
header = &_lpHeaders[i];
|
|
*piSlot = i;
|
|
_NextOpenSlot = (i == (_TotalSlots-1)) ? (_TotalSlots-1) : (i+1);
|
|
break;
|
|
}
|
|
}
|
|
if (header == NULL) {
|
|
|
|
//
|
|
// we would have just allocated extra slots if we didn't have
|
|
// any, so we shouldn't be here
|
|
//
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
quit:
|
|
_Error = error;
|
|
|
|
PERF_LEAVE(FindFreeSlot);
|
|
|
|
DEBUG_LEAVE(header);
|
|
|
|
return header;
|
|
}
|
|
|
|
|
|
VOID
|
|
HTTP_HEADERS::ShrinkHeader(
|
|
IN LPBYTE pbBase,
|
|
IN DWORD iSlot,
|
|
IN DWORD dwOldQueryIndex,
|
|
IN DWORD dwNewQueryIndex,
|
|
IN DWORD cbNewSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Low level function that does a surgical replace of one header with another.
|
|
This code updates internal structures such as bKnownHeaders and the stored
|
|
hash value for the new Header.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HEADER_STRING* pHeader = _lpHeaders + iSlot;
|
|
|
|
INET_ASSERT(_bKnownHeaders[dwOldQueryIndex] == (BYTE) iSlot ||
|
|
dwNewQueryIndex == dwOldQueryIndex );
|
|
|
|
//
|
|
// Swap in the new header. Update Length, Hash, and its cached position
|
|
// in the known header array.
|
|
//
|
|
|
|
_bKnownHeaders[dwOldQueryIndex] = INVALID_HEADER_INDEX;
|
|
_bKnownHeaders[dwNewQueryIndex] = (BYTE) iSlot;
|
|
|
|
pHeader->SetLength (cbNewSize);
|
|
pHeader->SetHash (GlobalKnownHeaders[dwNewQueryIndex].HashVal);
|
|
}
|
|
|
|
DWORD
|
|
inline
|
|
HTTP_HEADERS::SlowFind(
|
|
IN LPSTR lpBase,
|
|
IN LPCSTR lpszHeaderName,
|
|
IN DWORD dwHeaderNameLength,
|
|
IN DWORD dwIndex,
|
|
IN DWORD dwHash,
|
|
OUT DWORD *lpdwQueryIndex,
|
|
OUT BYTE **lplpbPrevIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the next occurance of lpszHeaderName in the header array, uses
|
|
a cached table of well known headers to accerlate the search if the
|
|
string is a known header.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Now see if this is a known header passed in as a string,
|
|
// If it is, we save ourselves the loop, and just map it right in to a known header
|
|
//
|
|
|
|
DWORD dwKnownQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
|
|
|
|
*lpdwQueryIndex = INVALID_HEADER_SLOT;
|
|
|
|
if ( dwKnownQueryIndex != 0 )
|
|
{
|
|
dwKnownQueryIndex--;
|
|
|
|
if ( ((int)dwHeaderNameLength >= GlobalKnownHeaders[dwKnownQueryIndex].Length) &&
|
|
strnicmp(lpszHeaderName,
|
|
GlobalKnownHeaders[dwKnownQueryIndex].Text,
|
|
GlobalKnownHeaders[dwKnownQueryIndex].Length) == 0)
|
|
{
|
|
*lpdwQueryIndex = dwKnownQueryIndex;
|
|
|
|
INET_ASSERT((int)(dwHeaderNameLength) == GlobalKnownHeaders[dwKnownQueryIndex].Length);
|
|
|
|
if ( lplpbPrevIndex )
|
|
{
|
|
return FastNukeFind(
|
|
dwKnownQueryIndex,
|
|
dwIndex,
|
|
lplpbPrevIndex
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return FastFind(
|
|
dwKnownQueryIndex,
|
|
dwIndex
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Otherwise we painfully enumerate the whole array of headers
|
|
//
|
|
|
|
for (DWORD i = 0; i < _TotalSlots; ++i)
|
|
{
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[i];
|
|
|
|
if (!pString->HaveString()) {
|
|
continue;
|
|
}
|
|
|
|
if (pString->HashStrnicmp(lpBase,
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
dwHash) == 0)
|
|
{
|
|
|
|
//
|
|
// if we haven't reached the required index yet, continue
|
|
//
|
|
|
|
if (dwIndex != 0) {
|
|
--dwIndex;
|
|
continue;
|
|
}
|
|
|
|
return i; // found index/slot
|
|
}
|
|
}
|
|
|
|
return INVALID_HEADER_SLOT; // not found
|
|
}
|
|
|
|
|
|
DWORD
|
|
inline
|
|
HTTP_HEADERS::FastFind(
|
|
IN DWORD dwQueryIndex,
|
|
IN DWORD dwIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the next occurance of a known header string in the lpHeaders array.
|
|
Since this is a known string, an index is used to refer to it.
|
|
A cached table of well known headers is used to accerlate the search.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwSlot;
|
|
|
|
dwSlot = _bKnownHeaders[dwQueryIndex];
|
|
|
|
while ( (dwIndex > 0) && (dwSlot < INVALID_HEADER_INDEX) )
|
|
{
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[dwSlot];
|
|
dwSlot = pString->GetNextKnownIndex();
|
|
|
|
dwIndex--;
|
|
}
|
|
|
|
if ( dwSlot >= INVALID_HEADER_INDEX)
|
|
{
|
|
return INVALID_HEADER_SLOT;
|
|
}
|
|
|
|
return dwSlot; // found it.
|
|
}
|
|
|
|
|
|
DWORD
|
|
inline
|
|
HTTP_HEADERS::FastNukeFind(
|
|
IN DWORD dwQueryIndex,
|
|
IN DWORD dwIndex,
|
|
OUT BYTE **lplpbPrevIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the next occurance of a known header string in the lpHeaders array.
|
|
Since this is a known string, an index is used to refer to it.
|
|
A cached table of well known headers is used to accerlate the search.
|
|
Also provides a ptr to ptr to the slot which directs us to the one found.
|
|
This is needed for deletion purposes.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
BYTE *lpbSlot;
|
|
|
|
*lplpbPrevIndex = lpbSlot = &_bKnownHeaders[dwQueryIndex];
|
|
dwIndex++;
|
|
|
|
while ( (dwIndex > 0) && (*lpbSlot < INVALID_HEADER_INDEX) )
|
|
{
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[*lpbSlot];
|
|
*lplpbPrevIndex = lpbSlot;
|
|
lpbSlot = pString->GetNextKnownIndexPtr();
|
|
|
|
dwIndex--;
|
|
}
|
|
|
|
if ( **lplpbPrevIndex >= INVALID_HEADER_INDEX ||
|
|
dwIndex > 0 )
|
|
{
|
|
return INVALID_HEADER_SLOT;
|
|
}
|
|
|
|
return ((DWORD) **lplpbPrevIndex); // found it.
|
|
}
|
|
|
|
VOID
|
|
HTTP_HEADERS::RemoveAllByIndex(
|
|
IN DWORD dwQueryIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes all Known Headers found in the header array.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
dwQueryIndex - index to known header string to remove from array.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
BYTE bSlot;
|
|
BYTE bPrevSlot;
|
|
|
|
bSlot = bPrevSlot = _bKnownHeaders[dwQueryIndex];
|
|
|
|
while (bSlot < INVALID_HEADER_INDEX)
|
|
{
|
|
HEADER_STRING * pString;
|
|
|
|
bPrevSlot = bSlot;
|
|
pString = &_lpHeaders[bSlot];
|
|
bSlot = (BYTE) pString->GetNextKnownIndex();
|
|
|
|
RemoveHeader(bPrevSlot, dwQueryIndex, &_bKnownHeaders[dwQueryIndex]);
|
|
}
|
|
|
|
_bKnownHeaders[dwQueryIndex] = INVALID_HEADER_INDEX;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
inline
|
|
HTTP_HEADERS::HeaderMatch(
|
|
IN DWORD dwHash,
|
|
IN LPSTR lpszHeaderName,
|
|
IN DWORD dwHeaderNameLength,
|
|
OUT DWORD *lpdwQueryIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up a Known HTTP header string using its Hash value and
|
|
string contained the name of the header.
|
|
|
|
Arguments:
|
|
|
|
dwHash - Hash value of header name string
|
|
|
|
lpszHeaderName - name of header we are matching
|
|
|
|
dwHeaderNameLength - length of header name string
|
|
|
|
lpdwQueryIndex - If found, this is the HTTP_QUERY_* based index to the header.
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
Success - The string and hash matched againsted a known header
|
|
|
|
Failure - There is no known header for that hash & string pair.
|
|
|
|
--*/
|
|
|
|
{
|
|
*lpdwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
|
|
|
|
if ( *lpdwQueryIndex != 0 )
|
|
{
|
|
(*lpdwQueryIndex)--;
|
|
|
|
if ( ((int)dwHeaderNameLength == GlobalKnownHeaders[*lpdwQueryIndex].Length) &&
|
|
strnicmp(lpszHeaderName,
|
|
GlobalKnownHeaders[*lpdwQueryIndex].Text,
|
|
GlobalKnownHeaders[*lpdwQueryIndex].Length) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BYTE
|
|
inline
|
|
HTTP_HEADERS::FastAdd(
|
|
IN DWORD dwQueryIndex,
|
|
IN DWORD dwSlot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rapidly adds a known string to the header array, this function
|
|
is used to matain coherency of the _bKnownHeaders which
|
|
contained indexed offsets into the header array for known headers.
|
|
|
|
Note that this function is used instead of latter listed below
|
|
in order to maintain proper order in headers received.
|
|
|
|
N.B. This function MUST be called with the headers already locked
|
|
|
|
Arguments:
|
|
|
|
dwQueryIndex - index to known header string to remove from array.
|
|
|
|
dwSlot - Slot in which this header is being added.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
BYTE *lpbSlot;
|
|
|
|
lpbSlot = &_bKnownHeaders[dwQueryIndex];
|
|
|
|
while ( (*lpbSlot < INVALID_HEADER_INDEX) )
|
|
{
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[*lpbSlot];
|
|
lpbSlot = pString->GetNextKnownIndexPtr();
|
|
}
|
|
|
|
INET_ASSERT(*lpbSlot == INVALID_HEADER_INDEX);
|
|
|
|
*lpbSlot = (BYTE) dwSlot;
|
|
return INVALID_HEADER_INDEX;
|
|
}
|
|
|
|
|
|
//BYTE
|
|
//inline
|
|
//HTTP_HEADERS::FastAdd(
|
|
// IN DWORD dwQueryIndex,
|
|
// IN DWORD dwSlot
|
|
// )
|
|
//{
|
|
// BYTE bOldSlot;
|
|
//
|
|
// bOldSlot = _bKnownHeaders[dwQueryIndex];
|
|
// _bKnownHeaders[dwQueryIndex] = (BYTE) dwSlot;
|
|
//
|
|
// return bOldSlot;
|
|
//}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::AddHeader(
|
|
IN LPSTR lpszHeaderName,
|
|
IN DWORD dwHeaderNameLength,
|
|
IN LPSTR lpszHeaderValue,
|
|
IN DWORD dwHeaderValueLength,
|
|
IN DWORD dwIndex,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a single header to the array of headers, given the header name and
|
|
value. Called via HttpOpenRequest()
|
|
|
|
Arguments:
|
|
|
|
lpszHeaderName - pointer to name of header to add, e.g. "Accept:"
|
|
|
|
dwHeaderNameLength - length of the header name
|
|
|
|
lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
|
|
|
|
dwHeaderValueLength - length of the header value
|
|
|
|
dwIndex - if coalescing headers, index of header to update
|
|
|
|
dwFlags - flags controlling function:
|
|
|
|
COALESCE_HEADER_WITH_COMMA
|
|
COALESCE_HEADER_WITH_SEMICOLON
|
|
- headers of the same name can be combined
|
|
|
|
CLEAN_HEADER
|
|
- header is supplied by user, so we must ensure
|
|
it has correct format
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory allocating string
|
|
|
|
ERROR_INVALID_PARAMETER
|
|
The header value was bad
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"AddHeader",
|
|
"%.*q, %d, %.*q, %d, %d, %#x",
|
|
min(dwHeaderNameLength + 1, 80),
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
min(dwHeaderValueLength + 1, 80),
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
dwIndex,
|
|
dwFlags
|
|
));
|
|
|
|
PERF_ENTER(AddHeader);
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
INET_ASSERT(lpszHeaderName != NULL);
|
|
INET_ASSERT(*lpszHeaderName != '\0');
|
|
INET_ASSERT(dwHeaderNameLength != 0);
|
|
INET_ASSERT(lpszHeaderValue != NULL);
|
|
INET_ASSERT(*lpszHeaderValue != '\0');
|
|
INET_ASSERT(dwHeaderValueLength != 0);
|
|
INET_ASSERT(_FreeSlots <= _TotalSlots);
|
|
|
|
//
|
|
// we may have been handed a header with a trailing colon. We don't care
|
|
// for such nasty imagery
|
|
//
|
|
|
|
if (lpszHeaderName[dwHeaderNameLength - 1] == ':') {
|
|
--dwHeaderNameLength;
|
|
}
|
|
|
|
DWORD dwQueryIndex;
|
|
DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
|
|
|
|
DWORD i = 0;
|
|
|
|
//
|
|
// if we are coalescing headers then find a header with the same name
|
|
//
|
|
|
|
if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
|
|
(dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
|
|
{
|
|
DWORD dwSlot;
|
|
|
|
dwSlot = SlowFind(
|
|
NULL,
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
dwIndex,
|
|
dwHash,
|
|
&dwQueryIndex,
|
|
NULL
|
|
);
|
|
|
|
if (dwSlot != ((DWORD) -1))
|
|
{
|
|
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[dwSlot];
|
|
|
|
//
|
|
// found what we are looking for. Coalesce it
|
|
//
|
|
|
|
pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
|
|
|
|
pString->Strncat(
|
|
(dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
|
|
"; " :
|
|
", ",
|
|
2);
|
|
|
|
pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
|
|
_HeadersLength += 2 + dwHeaderValueLength;
|
|
error = ERROR_SUCCESS;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Check to verify that the header we're adding is a known header,
|
|
// If its a known header we use dwQueryIndex to update the known header array
|
|
// otherwise, IF ITS NOT, we make sure to set dwQueryIndex to INVALID_...
|
|
//
|
|
|
|
if (! HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwQueryIndex) )
|
|
{
|
|
dwQueryIndex = INVALID_HEADER_SLOT;
|
|
}
|
|
|
|
/*
|
|
// Perhaps this more efficent ???
|
|
dwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
|
|
|
|
if ( dwQueryIndex != 0 )
|
|
{
|
|
dwQueryIndex--;
|
|
|
|
if ( ((int)dwHeaderNameLength < GlobalKnownHeaders[dwQueryIndex].Length) ||
|
|
strnicmp(lpszHeaderName,
|
|
GlobalKnownHeaders[dwQueryIndex].Text,
|
|
GlobalKnownHeaders[dwQueryIndex].Length) != 0)
|
|
{
|
|
dwQueryIndex = INVALID_HEADER_SLOT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwQueryIndex = INVALID_HEADER_SLOT;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
//
|
|
// if we didn't find the header value or we are not coalescing then add the
|
|
// header
|
|
//
|
|
|
|
if (error == ERROR_HTTP_HEADER_NOT_FOUND)
|
|
{
|
|
//
|
|
// find the next slot for this header
|
|
//
|
|
|
|
HEADER_STRING * freeHeader;
|
|
DWORD iSlot;
|
|
|
|
freeHeader = FindFreeSlot(&iSlot);
|
|
if (freeHeader == NULL) {
|
|
error = GetError();
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
dwHeaderNameLength
|
|
+ sizeof(": ") - 1
|
|
+ dwHeaderValueLength
|
|
+ 1 // for extra NULL terminator
|
|
);
|
|
if (freeHeader->IsError()) {
|
|
error = ::GetLastError();
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
goto Cleanup;
|
|
}
|
|
freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
|
|
freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
|
|
_HeadersLength += dwHeaderNameLength
|
|
+ (sizeof(": ") - 1)
|
|
+ dwHeaderValueLength
|
|
+ (sizeof("\r\n") - 1)
|
|
;
|
|
freeHeader->SetHash(dwHash);
|
|
|
|
if ( dwQueryIndex != INVALID_HEADER_SLOT )
|
|
{
|
|
freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
|
|
}
|
|
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
Cleanup:
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
PERF_LEAVE(AddHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::AddHeader(
|
|
IN DWORD dwQueryIndex,
|
|
IN LPSTR lpszHeaderValue,
|
|
IN DWORD dwHeaderValueLength,
|
|
IN DWORD dwIndex,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a single header to the array of headers, given the header name and
|
|
value. Called via HttpOpenRequest()
|
|
|
|
Arguments:
|
|
|
|
dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
|
|
|
|
lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
|
|
|
|
dwHeaderValueLength - length of the header value
|
|
|
|
dwIndex - if coalescing headers, index of header to update
|
|
|
|
dwFlags - flags controlling function:
|
|
|
|
COALESCE_HEADER_WITH_COMMA
|
|
COALESCE_HEADER_WITH_SEMICOLON
|
|
- headers of the same name can be combined
|
|
|
|
CLEAN_HEADER
|
|
- header is supplied by user, so we must ensure
|
|
it has correct format
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory allocating string
|
|
|
|
ERROR_INVALID_PARAMETER
|
|
The header value was bad
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"AddHeader",
|
|
"%q, %u, %.*q, %d, %d, %#x",
|
|
GlobalKnownHeaders[dwQueryIndex].Text,
|
|
dwQueryIndex,
|
|
min(dwHeaderValueLength + 1, 80),
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
dwIndex,
|
|
dwFlags
|
|
));
|
|
|
|
PERF_ENTER(AddHeader);
|
|
|
|
INET_ASSERT(dwQueryIndex <= HTTP_QUERY_MAX);
|
|
INET_ASSERT(lpszHeaderValue != NULL);
|
|
INET_ASSERT(*lpszHeaderValue != '\0');
|
|
INET_ASSERT(dwHeaderValueLength != 0);
|
|
INET_ASSERT(_FreeSlots <= _TotalSlots);
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
DWORD i = 0;
|
|
LPSTR lpszHeaderName;
|
|
DWORD dwHeaderNameLength;
|
|
DWORD dwHash;
|
|
|
|
dwHash = GlobalKnownHeaders[dwQueryIndex].HashVal;
|
|
lpszHeaderName = GlobalKnownHeaders[dwQueryIndex].Text;
|
|
dwHeaderNameLength = GlobalKnownHeaders[dwQueryIndex].Length;
|
|
|
|
//
|
|
// if we are coalescing headers then find a header with the same name
|
|
//
|
|
|
|
if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
|
|
(dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
|
|
{
|
|
DWORD dwSlot;
|
|
|
|
dwSlot = FastFind(
|
|
dwQueryIndex,
|
|
dwIndex
|
|
);
|
|
|
|
if (dwSlot != INVALID_HEADER_SLOT)
|
|
{
|
|
|
|
HEADER_STRING * pString;
|
|
|
|
pString = &_lpHeaders[dwSlot];
|
|
|
|
//
|
|
// found what we are looking for. Coalesce it
|
|
//
|
|
|
|
pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
|
|
|
|
pString->Strncat(
|
|
(dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
|
|
"; " :
|
|
", ",
|
|
2);
|
|
|
|
pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
|
|
_HeadersLength += 2 + dwHeaderValueLength;
|
|
error = ERROR_SUCCESS;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if we didn't find the header value or we are not coalescing then add the
|
|
// header
|
|
//
|
|
|
|
if (error == ERROR_HTTP_HEADER_NOT_FOUND)
|
|
{
|
|
//
|
|
// find the next slot for this header
|
|
//
|
|
|
|
HEADER_STRING * freeHeader;
|
|
DWORD iSlot;
|
|
|
|
freeHeader = FindFreeSlot(&iSlot);
|
|
if (freeHeader == NULL) {
|
|
error = GetError();
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
|
|
freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
dwHeaderNameLength
|
|
+ sizeof(": ") - 1
|
|
+ dwHeaderValueLength
|
|
+ 1 // for extra NULL terminator
|
|
);
|
|
if (freeHeader->IsError()) {
|
|
error = ::GetLastError();
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
goto quit;
|
|
}
|
|
freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
|
|
freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
|
|
_HeadersLength += dwHeaderNameLength
|
|
+ (sizeof(": ") - 1)
|
|
+ dwHeaderValueLength
|
|
+ (sizeof("\r\n") - 1)
|
|
;
|
|
freeHeader->SetHash(dwHash);
|
|
freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
|
|
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
quit:
|
|
|
|
PERF_LEAVE(AddHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::ReplaceHeader(
|
|
IN LPSTR lpszHeaderName,
|
|
IN DWORD dwHeaderNameLength,
|
|
IN LPSTR lpszHeaderValue,
|
|
IN DWORD dwHeaderValueLength,
|
|
IN DWORD dwIndex,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replaces a HTTP (request) header. The header can be replaced with a NULL
|
|
value, meaning that the header is removed
|
|
|
|
Arguments:
|
|
|
|
lpszHeaderName - pointer to the header name
|
|
|
|
dwHeaderNameLength - length of the header name
|
|
|
|
lpszHeaderValue - pointer to the header value
|
|
|
|
dwHeaderValueLength - length of the header value
|
|
|
|
dwIndex - index of header to replace
|
|
|
|
dwFlags - flags controlling function. Allowed flags are:
|
|
|
|
COALESCE_HEADER_WITH_COMMA
|
|
COALESCE_HEADER_WITH_SEMICOLON
|
|
- headers of the same name can be combined
|
|
|
|
ADD_HEADER
|
|
- if the header-name is not found and there is
|
|
a valid header-value, then the header is added
|
|
|
|
ADD_HEADER_IF_NEW
|
|
- if the header-name exists then we return an
|
|
error, else we add the header-value
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_HTTP_HEADER_NOT_FOUND
|
|
The requested header wasn't found
|
|
|
|
ERROR_HTTP_HEADER_ALREADY_EXISTS
|
|
The header already exists, and was not added or replaced
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory trying to acquire lock
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"ReplaceHeader",
|
|
"%.*q, %d, %.*q, %d, %d, %#x",
|
|
min(dwHeaderNameLength + 1, 80),
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
min(dwHeaderValueLength + 1, 80),
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
dwIndex,
|
|
dwFlags
|
|
));
|
|
|
|
PERF_ENTER(ReplaceHeader);
|
|
|
|
INET_ASSERT(lpszHeaderName != NULL);
|
|
INET_ASSERT(dwHeaderNameLength != 0);
|
|
INET_ASSERT(lpszHeaderName[dwHeaderNameLength - 1] != ':');
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
|
|
DWORD dwSlot;
|
|
DWORD dwQueryIndex;
|
|
BYTE *pbPrevByte;
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
dwSlot = SlowFind(
|
|
NULL,
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
dwIndex,
|
|
dwHash,
|
|
&dwQueryIndex,
|
|
&pbPrevByte
|
|
);
|
|
|
|
if ( dwSlot != ((DWORD) -1))
|
|
{
|
|
//
|
|
// if ADD_HEADER_IF_NEW is set, then we already have the header
|
|
//
|
|
|
|
if (dwFlags & ADD_HEADER_IF_NEW) {
|
|
error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// for both replace and remove operations, we are going to remove
|
|
// the current header
|
|
//
|
|
|
|
RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
|
|
|
|
//
|
|
// if replacing then add the new header value
|
|
//
|
|
|
|
if (dwHeaderValueLength != 0)
|
|
{
|
|
if ( dwQueryIndex != ((DWORD) -1) )
|
|
{
|
|
error = AddHeader(dwQueryIndex,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
}
|
|
else
|
|
{
|
|
error = AddHeader(lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
}
|
|
|
|
|
|
} else {
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we didn't find the header but ADD_HEADER is set then we simply add it
|
|
// but only if the value length is not zero
|
|
//
|
|
|
|
if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
|
|
&& (dwHeaderValueLength != 0)
|
|
&& (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
|
|
{
|
|
if ( dwQueryIndex != ((DWORD) -1) )
|
|
{
|
|
error = AddHeader(dwQueryIndex,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
}
|
|
else
|
|
{
|
|
error = AddHeader(lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
PERF_LEAVE(ReplaceHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::ReplaceHeader(
|
|
IN DWORD dwQueryIndex,
|
|
IN LPSTR lpszHeaderValue,
|
|
IN DWORD dwHeaderValueLength,
|
|
IN DWORD dwIndex,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replaces a HTTP (request) header. The header can be replaced with a NULL
|
|
value, meaning that the header is removed
|
|
|
|
Arguments:
|
|
|
|
lpszHeaderValue - pointer to the header value
|
|
|
|
dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
|
|
|
|
dwHeaderValueLength - length of the header value
|
|
|
|
dwIndex - index of header to replace
|
|
|
|
dwFlags - flags controlling function. Allowed flags are:
|
|
|
|
COALESCE_HEADER_WITH_COMMA
|
|
COALESCE_HEADER_WITH_SEMICOLON
|
|
- headers of the same name can be combined
|
|
|
|
ADD_HEADER
|
|
- if the header-name is not found and there is
|
|
a valid header-value, then the header is added
|
|
|
|
ADD_HEADER_IF_NEW
|
|
- if the header-name exists then we return an
|
|
error, else we add the header-value
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_HTTP_HEADER_NOT_FOUND
|
|
The requested header wasn't found
|
|
|
|
ERROR_HTTP_HEADER_ALREADY_EXISTS
|
|
The header already exists, and was not added or replaced
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory trying to acquire lock
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"ReplaceHeader",
|
|
"%q, %u, %.*q, %d, %d, %#x",
|
|
GlobalKnownHeaders[dwQueryIndex].Text,
|
|
dwQueryIndex,
|
|
min(dwHeaderValueLength + 1, 80),
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
dwIndex,
|
|
dwFlags
|
|
));
|
|
|
|
PERF_ENTER(ReplaceHeader);
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
DWORD dwSlot;
|
|
BYTE *pbPrevByte;
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
dwSlot = FastNukeFind(
|
|
dwQueryIndex,
|
|
dwIndex,
|
|
&pbPrevByte
|
|
);
|
|
|
|
if ( dwSlot != INVALID_HEADER_SLOT)
|
|
{
|
|
//
|
|
// if ADD_HEADER_IF_NEW is set, then we already have the header
|
|
//
|
|
|
|
if (dwFlags & ADD_HEADER_IF_NEW) {
|
|
error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// for both replace and remove operations, we are going to remove
|
|
// the current header
|
|
//
|
|
|
|
RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
|
|
|
|
//
|
|
// if replacing then add the new header value
|
|
//
|
|
|
|
if (dwHeaderValueLength != 0)
|
|
{
|
|
error = AddHeader(dwQueryIndex,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
} else {
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we didn't find the header but ADD_HEADER is set then we simply add it
|
|
// but only if the value length is not zero
|
|
//
|
|
|
|
if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
|
|
&& (dwHeaderValueLength != 0)
|
|
&& (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
|
|
{
|
|
error = AddHeader(dwQueryIndex,
|
|
lpszHeaderValue,
|
|
dwHeaderValueLength,
|
|
0,
|
|
dwFlags
|
|
);
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
PERF_LEAVE(ReplaceHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::FindHeader(
|
|
IN LPSTR lpBase,
|
|
IN LPCSTR lpszHeaderName,
|
|
IN DWORD dwHeaderNameLength,
|
|
IN DWORD dwModifiers,
|
|
OUT LPVOID lpBuffer,
|
|
IN OUT LPDWORD lpdwBufferLength,
|
|
IN OUT LPDWORD lpdwIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds a request or response header
|
|
|
|
Arguments:
|
|
|
|
lpBase - base for offset HEADER_STRINGs
|
|
|
|
lpszHeaderName - pointer to header name
|
|
|
|
dwHeaderNameLength - length of header name
|
|
|
|
dwModifiers - flags which modify returned value
|
|
|
|
lpBuffer - pointer to buffer for results
|
|
|
|
lpdwBufferLength - IN: length of lpBuffer
|
|
OUT: length of results, or required length of lpBuffer
|
|
|
|
lpdwIndex - IN: 0-based index of header to find
|
|
OUT: next header index if success returned
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INSUFFICIENT_BUFFER
|
|
*lpdwBufferLength contains the amount required
|
|
|
|
ERROR_HTTP_HEADER_NOT_FOUND
|
|
The specified header (or index of header) was not found
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory trying to acquire lock
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_HEADERS::FindHeader",
|
|
"%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
|
|
lpszHeaderName,
|
|
min(dwHeaderNameLength + 1, 80),
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
lpBuffer,
|
|
lpdwBufferLength,
|
|
*lpdwBufferLength,
|
|
dwModifiers,
|
|
lpdwIndex,
|
|
*lpdwIndex
|
|
));
|
|
|
|
|
|
PERF_ENTER(FindHeader);
|
|
|
|
|
|
|
|
INET_ASSERT(lpdwIndex != NULL);
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
DWORD dwSlot;
|
|
HEADER_STRING * pString;
|
|
DWORD dwQueryIndex;
|
|
DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
dwSlot = SlowFind(
|
|
lpBase,
|
|
lpszHeaderName,
|
|
dwHeaderNameLength,
|
|
*lpdwIndex,
|
|
dwHash,
|
|
&dwQueryIndex,
|
|
NULL
|
|
);
|
|
|
|
if ( dwSlot != ((DWORD) -1) )
|
|
{
|
|
pString = &_lpHeaders[dwSlot];
|
|
|
|
//
|
|
// found the header - get to the value
|
|
//
|
|
|
|
DWORD stringLen;
|
|
LPSTR value;
|
|
|
|
stringLen = pString->StringLength();
|
|
|
|
INET_ASSERT(stringLen > dwHeaderNameLength);
|
|
|
|
//
|
|
// get a pointer to the value string
|
|
//
|
|
|
|
value = pString->StringAddress(lpBase) + dwHeaderNameLength;
|
|
stringLen -= dwHeaderNameLength;
|
|
|
|
//
|
|
// the input string could be a substring of a different header
|
|
//
|
|
|
|
//INET_ASSERT(*value != ':');
|
|
|
|
//
|
|
// find the first non-space character in the value.
|
|
//
|
|
// N.B.: Servers can return empty headers, so we may end up with a
|
|
// zero length string
|
|
//
|
|
|
|
do {
|
|
++value;
|
|
--stringLen;
|
|
} while ((stringLen > 0) && (*value == ' '));
|
|
|
|
//
|
|
// get the data in the format requested by the app
|
|
//
|
|
|
|
LPVOID lpData;
|
|
DWORD dwDataSize;
|
|
DWORD dwRequiredSize;
|
|
SYSTEMTIME systemTime;
|
|
DWORD number;
|
|
|
|
//
|
|
// error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
|
|
// really be success either...
|
|
//
|
|
|
|
error = ERROR_SUCCESS;
|
|
|
|
if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME) {
|
|
|
|
char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
|
|
|
|
if (stringLen < sizeof(buf)) {
|
|
|
|
//
|
|
// value probably does not point at a zero-terminated string
|
|
// which HttpDateToSystemTime() expects, so we make a copy
|
|
// and terminate it
|
|
//
|
|
|
|
memcpy((LPVOID)buf, (LPVOID)value, stringLen);
|
|
buf[stringLen] = '\0';
|
|
if (HttpDateToSystemTime(buf, &systemTime)) {
|
|
lpData = (LPVOID)&systemTime;
|
|
dwRequiredSize = dwDataSize = sizeof(systemTime);
|
|
} else {
|
|
|
|
//
|
|
// couldn't convert date/time. Presume header must be bogus
|
|
//
|
|
|
|
error = ERROR_HTTP_INVALID_QUERY_REQUEST;
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
ERROR,
|
|
("cannot convert %.40q to SYSTEMTIME\n",
|
|
value
|
|
));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// we would break the date/time buffer!
|
|
//
|
|
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
}
|
|
} else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
|
|
if (isdigit(*value)) {
|
|
number = 0;
|
|
for (int i = 0;
|
|
(stringLen > 0) && isdigit(value[i]);
|
|
++i, --stringLen) {
|
|
|
|
number = number * 10 + (DWORD)(value[i] - '0');
|
|
}
|
|
lpData = (LPVOID)&number;
|
|
dwRequiredSize = dwDataSize = sizeof(number);
|
|
} else {
|
|
|
|
//
|
|
// not a numeric field. Request must be bogus for this header
|
|
//
|
|
|
|
error = ERROR_HTTP_INVALID_QUERY_REQUEST;
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
ERROR,
|
|
("cannot convert %.20q to NUMBER\n",
|
|
value
|
|
));
|
|
|
|
}
|
|
} else {
|
|
lpData = (LPVOID)value;
|
|
dwDataSize = stringLen;
|
|
dwRequiredSize = dwDataSize + 1;
|
|
}
|
|
|
|
//
|
|
// if error == ERROR_SUCCESS then we can attempt to copy the data
|
|
//
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
if (*lpdwBufferLength < dwRequiredSize) {
|
|
*lpdwBufferLength = dwRequiredSize;
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
memcpy(lpBuffer, lpData, dwDataSize);
|
|
*lpdwBufferLength = dwDataSize;
|
|
|
|
//
|
|
// if dwRequiredSize > dwDataSize, then this is a variable-
|
|
// length item (i.e. a STRING!) so we add a terminating '\0'
|
|
//
|
|
|
|
if (dwRequiredSize > dwDataSize) {
|
|
|
|
INET_ASSERT(dwRequiredSize - dwDataSize == 1);
|
|
|
|
((LPSTR)lpBuffer)[dwDataSize] = '\0';
|
|
}
|
|
|
|
//
|
|
// successfully retrieved the requested header - bump the
|
|
// index
|
|
//
|
|
|
|
++*lpdwIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
UnlockHeaders();
|
|
|
|
quit:
|
|
PERF_LEAVE(FindHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::FindHeader(
|
|
IN LPSTR lpBase,
|
|
IN DWORD dwQueryIndex,
|
|
IN DWORD dwModifiers,
|
|
OUT LPVOID lpBuffer,
|
|
IN OUT LPDWORD lpdwBufferLength,
|
|
IN OUT LPDWORD lpdwIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds a request or response header, based on index to the header name we are searching for.
|
|
|
|
Arguments:
|
|
|
|
lpBase - base for offset HEADER_STRINGs
|
|
|
|
dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
|
|
|
|
dwModifiers - flags which modify returned value
|
|
|
|
lpBuffer - pointer to buffer for results
|
|
|
|
lpdwBufferLength - IN: length of lpBuffer
|
|
OUT: length of results, or required length of lpBuffer
|
|
|
|
lpdwIndex - IN: 0-based index of header to find
|
|
OUT: next header index if success returned
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INSUFFICIENT_BUFFER
|
|
*lpdwBufferLength contains the amount required
|
|
|
|
ERROR_HTTP_HEADER_NOT_FOUND
|
|
The specified header (or index of header) was not found
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD error;
|
|
LPSTR lpData;
|
|
DWORD dwDataSize = 0;
|
|
DWORD dwRequiredSize;
|
|
SYSTEMTIME systemTime;
|
|
DWORD number;
|
|
|
|
error = FastFindHeader(
|
|
lpBase,
|
|
dwQueryIndex,
|
|
(LPVOID *)&lpData,
|
|
&dwDataSize,
|
|
*lpdwIndex
|
|
);
|
|
|
|
if ( error != ERROR_SUCCESS )
|
|
{
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// get the data in the format requested by the app
|
|
//
|
|
|
|
if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME)
|
|
{
|
|
char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
|
|
|
|
if (dwDataSize < sizeof(buf))
|
|
{
|
|
|
|
//
|
|
// value probably does not point at a zero-terminated string
|
|
// which HttpDateToSystemTime() expects, so we make a copy
|
|
// and terminate it
|
|
//
|
|
|
|
memcpy((LPVOID)buf, (LPVOID)lpData, dwDataSize);
|
|
buf[dwDataSize] = '\0';
|
|
if (HttpDateToSystemTime(buf, &systemTime)) {
|
|
lpData = (LPSTR)&systemTime;
|
|
dwRequiredSize = dwDataSize = sizeof(systemTime);
|
|
} else {
|
|
|
|
//
|
|
// couldn't convert date/time. Presume header must be bogus
|
|
//
|
|
|
|
error = ERROR_HTTP_INVALID_QUERY_REQUEST;
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
ERROR,
|
|
("cannot convert %.40q to SYSTEMTIME\n",
|
|
lpData
|
|
));
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// we would break the date/time buffer!
|
|
//
|
|
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER)
|
|
{
|
|
if (isdigit(*lpData)) {
|
|
number = 0;
|
|
for (int i = 0;
|
|
(dwDataSize > 0) && isdigit(lpData[i]);
|
|
++i, --dwDataSize) {
|
|
|
|
number = number * 10 + (DWORD)(lpData[i] - '0');
|
|
}
|
|
lpData = (LPSTR)&number;
|
|
dwRequiredSize = dwDataSize = sizeof(number);
|
|
} else {
|
|
|
|
//
|
|
// not a numeric field. Request must be bogus for this header
|
|
//
|
|
|
|
error = ERROR_HTTP_INVALID_QUERY_REQUEST;
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
ERROR,
|
|
("cannot convert %.20q to NUMBER\n",
|
|
lpData
|
|
));
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwRequiredSize = dwDataSize + 1;
|
|
}
|
|
|
|
//
|
|
// if error == ERROR_SUCCESS then we can attempt to copy the data
|
|
//
|
|
|
|
if (error == ERROR_SUCCESS)
|
|
{
|
|
if (*lpdwBufferLength < dwRequiredSize)
|
|
{
|
|
*lpdwBufferLength = dwRequiredSize;
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
memcpy(lpBuffer, lpData, dwDataSize);
|
|
*lpdwBufferLength = dwDataSize;
|
|
|
|
//
|
|
// if dwRequiredSize > dwDataSize, then this is a variable-
|
|
// length item (i.e. a STRING!) so we add a terminating '\0'
|
|
//
|
|
|
|
if (dwRequiredSize > dwDataSize)
|
|
{
|
|
INET_ASSERT(dwRequiredSize - dwDataSize == 1);
|
|
|
|
((LPSTR)lpBuffer)[dwDataSize] = '\0';
|
|
}
|
|
|
|
//
|
|
// successfully retrieved the requested header - bump the
|
|
// index
|
|
//
|
|
|
|
++*lpdwIndex;
|
|
}
|
|
}
|
|
quit:
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::FastFindHeader(
|
|
IN LPSTR lpBase,
|
|
IN DWORD dwQueryIndex,
|
|
OUT LPVOID *lplpBuffer,
|
|
IN OUT LPDWORD lpdwBufferLength,
|
|
IN DWORD dwIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds a request or response header slightly quicker than its higher level
|
|
cousin, FindHeader. Unlike FindHeader this function simply returns
|
|
a pointer and length, and does not copy header data.
|
|
|
|
|
|
lpBase - base address of strings
|
|
|
|
dwQueryIndex - a index into a array known HTTP headers, see wininet.h HTTP_QUERY_* codes
|
|
|
|
lplpBuffer - pointer to pointer of the actual header to be returned in.
|
|
|
|
lpdwBufferLength - OUT: if successful, length of output buffer, minus 1
|
|
for any trailing EOS, or if the buffer is not
|
|
large enough, the size required
|
|
|
|
dwIndex - a index of which header we're asking for, as there can be multiple headers
|
|
under the same name.
|
|
|
|
Arguments:
|
|
|
|
lpBase - base for offset HEADER_STRINGs
|
|
|
|
lpszHeaderName - pointer to header name
|
|
|
|
dwHeaderNameLength - length of header name
|
|
|
|
dwModifiers - flags which modify returned value
|
|
|
|
lpBuffer - pointer to buffer for results
|
|
|
|
lpdwBufferLength - IN: length of lpBuffer
|
|
OUT: length of results, or required length of lpBuffer
|
|
|
|
lpdwIndex - IN: 0-based index of header to find
|
|
OUT: next header index if success returned
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INSUFFICIENT_BUFFER
|
|
*lpdwBufferLength contains the amount required
|
|
|
|
ERROR_HTTP_HEADER_NOT_FOUND
|
|
The specified header (or index of header) was not found
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_HEADERS::FastFindHeader",
|
|
"%q, %#x, %#x [%#x], %u",
|
|
GlobalKnownHeaders[dwQueryIndex].Text,
|
|
lplpBuffer,
|
|
lpdwBufferLength,
|
|
*lpdwBufferLength,
|
|
dwIndex
|
|
));
|
|
|
|
PERF_ENTER(FindHeader);
|
|
|
|
DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
|
|
|
|
HEADER_STRING * curHeader;
|
|
DWORD dwSlot;
|
|
|
|
dwSlot = FastFind(dwQueryIndex, dwIndex);
|
|
|
|
if ( dwSlot != INVALID_HEADER_SLOT)
|
|
{
|
|
//
|
|
// found the header - get to the value
|
|
//
|
|
|
|
DWORD stringLen;
|
|
LPSTR value;
|
|
|
|
curHeader = GetSlot(dwSlot);
|
|
|
|
//
|
|
// get a pointer to the value string
|
|
//
|
|
|
|
value = curHeader->StringAddress(lpBase) + (GlobalKnownHeaders[dwQueryIndex].Length+1);
|
|
stringLen = curHeader->StringLength() - (GlobalKnownHeaders[dwQueryIndex].Length+1);
|
|
|
|
//
|
|
// find the first non-space character in the value.
|
|
//
|
|
// N.B.: Servers can return empty headers, so we may end up with a
|
|
// zero length string
|
|
//
|
|
|
|
while ((stringLen > 0) && (*value == ' '))
|
|
{
|
|
++value;
|
|
--stringLen;
|
|
}
|
|
|
|
//
|
|
// get the data in the format requested by the app
|
|
//
|
|
|
|
//
|
|
// error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
|
|
// really be success either...
|
|
//
|
|
|
|
error = ERROR_SUCCESS;
|
|
|
|
*lplpBuffer = (LPVOID)value;
|
|
*lpdwBufferLength = stringLen;
|
|
}
|
|
|
|
PERF_LEAVE(FindHeader);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::QueryRawHeaders(
|
|
IN LPSTR lpBase,
|
|
IN BOOL bCrLfTerminated,
|
|
IN LPVOID lpBuffer,
|
|
IN OUT LPDWORD lpdwBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns all the request or response headers in a single buffer. The headers
|
|
can be returned as ASCIIZ strings, or CR-LF terminated strings
|
|
|
|
Arguments:
|
|
|
|
lpBase - base address of strings
|
|
|
|
bCrLfTerminated - TRUE if each string is terminated with CR-LF
|
|
|
|
lpBuffer - pointer to buffer to write headers
|
|
|
|
lpdwBufferLength - IN: length of lpBuffer
|
|
OUT: if successful, length of output buffer, minus 1
|
|
for any trailing EOS, or if the buffer is not
|
|
large enough, the size required
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INSUFFICIENT_BUFFER
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory trying to acquire lock
|
|
--*/
|
|
|
|
{
|
|
PERF_ENTER(QueryRawHeaders);
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
DWORD requiredLength = 0;
|
|
LPSTR lpszBuffer = (LPSTR)lpBuffer;
|
|
|
|
if (!LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
for (DWORD i = 0; i < _TotalSlots; ++i) {
|
|
if (_lpHeaders[i].HaveString()) {
|
|
|
|
DWORD length;
|
|
|
|
length = _lpHeaders[i].StringLength();
|
|
|
|
requiredLength += length + (bCrLfTerminated ? 2 : 1);
|
|
if (*lpdwBufferLength > requiredLength) {
|
|
_lpHeaders[i].CopyTo(lpBase, lpszBuffer);
|
|
lpszBuffer += length;
|
|
if (bCrLfTerminated) {
|
|
*lpszBuffer++ = '\r';
|
|
*lpszBuffer++ = '\n';
|
|
} else {
|
|
*lpszBuffer++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bCrLfTerminated)
|
|
{
|
|
requiredLength += 2;
|
|
if (*lpdwBufferLength > requiredLength)
|
|
{
|
|
*lpszBuffer++ = '\r';
|
|
*lpszBuffer++ = '\n';
|
|
}
|
|
}
|
|
|
|
UnlockHeaders();
|
|
|
|
++requiredLength;
|
|
|
|
if (*lpdwBufferLength < requiredLength) {
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
*lpszBuffer = '\0';
|
|
--requiredLength; // remove 1 for trailing '\0'
|
|
}
|
|
*lpdwBufferLength = requiredLength;
|
|
|
|
quit:
|
|
PERF_LEAVE(QueryRawHeaders);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns all the request or response headers in a single buffer. The headers
|
|
can be returned as ASCIIZ strings, or CR-LF terminated strings
|
|
|
|
Arguments:
|
|
|
|
lpBase - base address of strings
|
|
|
|
bCrLfTerminated - TRUE if each string is terminated with CR-LF
|
|
|
|
lpBuffer - pointer to buffer to write headers
|
|
|
|
lpdwBufferLength - IN: length of lpBuffer
|
|
OUT: if successful, length of output buffer, minus 1
|
|
for any trailing EOS, or if the buffer is not
|
|
large enough, the size required
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INSUFFICIENT_BUFFER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error = ERROR_NOT_SUPPORTED;
|
|
|
|
DWORD requiredLength = 0;
|
|
LPSTR lpszBuffer = (LPSTR)lpBuffer;
|
|
BOOL fCopy;
|
|
|
|
DWORD i = fSkipVerb ? 1 : 0;
|
|
for (; i < _TotalSlots; ++i) {
|
|
if (_lpHeaders[i].HaveString()) {
|
|
fCopy = TRUE;
|
|
if (lplpFilterList
|
|
&& FMatchList(lplpFilterList, cListElements, _lpHeaders+i, lpBase)) {
|
|
fCopy = fExclude?FALSE:TRUE;
|
|
}
|
|
if (fCopy) {
|
|
DWORD length;
|
|
|
|
length = _lpHeaders[i].StringLength();
|
|
requiredLength += length + (bCrLfTerminated ? 2 : 1);
|
|
if (*lpdwBufferLength > requiredLength) {
|
|
_lpHeaders[i].CopyTo(lpBase, lpszBuffer);
|
|
lpszBuffer += length;
|
|
if (bCrLfTerminated) {
|
|
*lpszBuffer++ = '\r';
|
|
*lpszBuffer++ = '\n';
|
|
} else {
|
|
*lpszBuffer++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bCrLfTerminated)
|
|
{
|
|
requiredLength += 2;
|
|
if (*lpdwBufferLength > requiredLength)
|
|
{
|
|
*lpszBuffer++ = '\r';
|
|
*lpszBuffer++ = '\n';
|
|
}
|
|
}
|
|
|
|
++requiredLength;
|
|
|
|
|
|
if (*lpdwBufferLength < requiredLength) {
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
*lpszBuffer = '\0';
|
|
--requiredLength; // remove 1 for trailing '\0'
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
*lpdwBufferLength = requiredLength;
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::AddRequest(
|
|
IN LPSTR lpszVerb,
|
|
IN LPSTR lpszObject,
|
|
IN LPSTR lpszVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds the request line from its constituent parts. The request line is the
|
|
first (0th) header in the request headers
|
|
|
|
Assumes: 1. This is the one-and-only call to this method
|
|
2. lpszObject must already be escaped if necessary
|
|
|
|
Arguments:
|
|
|
|
lpszVerb - pointer to HTTP verb, e.g. "GET"
|
|
|
|
lpszObject - pointer to HTTP object name, e.g. "/users/albert/~emc2.htm".
|
|
|
|
lpszVersion - pointer to HTTP version string, e.g. "HTTP/1.0"
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
PERF_ENTER(AddRequest);
|
|
|
|
//
|
|
// there must not be a header when this method is called
|
|
//
|
|
|
|
INET_ASSERT(_HeadersLength == 0);
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
int verbLen = lstrlen(lpszVerb);
|
|
int objectLen = lstrlen(lpszObject);
|
|
int versionLen = lstrlen(lpszVersion);
|
|
int len = verbLen // "GET"
|
|
+ 1 // ' '
|
|
+ objectLen // "/users/albert/~emc2.htm"
|
|
+ 1 // ' '
|
|
+ versionLen // "HTTP/1.0"
|
|
+ 1 // '\0'
|
|
;
|
|
|
|
//
|
|
// we are about to start updating the headers for the current
|
|
// HTTP_REQUEST_HANDLE_OBJECT. Serialize access
|
|
//
|
|
|
|
HEADER_STRING * pRequest = GetFirstHeader();
|
|
HEADER_STRING & request = *pRequest;
|
|
|
|
if (pRequest == NULL) {
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
INET_ASSERT(!request.HaveString());
|
|
|
|
_lpszVerb = NULL;
|
|
_dwVerbLength = 0;
|
|
_lpszObjectName = NULL;
|
|
_dwObjectNameLength = 0;
|
|
_lpszVersion = NULL;
|
|
_dwVersionLength = 0;
|
|
|
|
request.CreateStringBuffer((LPVOID)lpszVerb, verbLen, len);
|
|
if (request.IsError()) {
|
|
error = GetLastError();
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
} else {
|
|
request += ' ';
|
|
request.Strncat((LPVOID)lpszObject, objectLen);
|
|
request += ' ';
|
|
request.Strncat((LPVOID)lpszVersion, versionLen);
|
|
|
|
_HeadersLength = len - 1 + (sizeof("\r\n") - 1);
|
|
|
|
//
|
|
// we have used the first free slot in the headers array
|
|
//
|
|
|
|
--_FreeSlots;
|
|
|
|
//
|
|
// update the component variables in case of a ModifyRequest()
|
|
//
|
|
|
|
_lpszVerb = request.StringAddress();
|
|
_dwVerbLength = verbLen;
|
|
_lpszObjectName = _lpszVerb + verbLen + 1;
|
|
_dwObjectNameLength = objectLen;
|
|
_lpszVersion = _lpszObjectName + objectLen + 1;
|
|
_dwVersionLength = versionLen;
|
|
SetRequestVersion();
|
|
error = request.IsError() ? ::GetLastError() : ERROR_SUCCESS;
|
|
}
|
|
|
|
quit:
|
|
|
|
PERF_LEAVE(AddRequest);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_HEADERS::ModifyRequest(
|
|
IN HTTP_METHOD_TYPE tMethod,
|
|
IN LPSTR lpszObjectName,
|
|
IN DWORD dwObjectNameLength,
|
|
IN LPSTR lpszVersion OPTIONAL,
|
|
IN DWORD dwVersionLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the request line. Used in redirection
|
|
|
|
Arguments:
|
|
|
|
tMethod - type of new method
|
|
|
|
lpszObjectName - pointer to new object name
|
|
|
|
dwObjectNameLength - length of new object name
|
|
|
|
lpszVersion - optional pointer to version string
|
|
|
|
dwVersionLength - length of lpszVersion string if present
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"ModifyRequest",
|
|
"%s, %q, %d, %q, %d",
|
|
MapHttpMethodType(tMethod),
|
|
lpszObjectName,
|
|
dwObjectNameLength,
|
|
lpszVersion,
|
|
dwVersionLength
|
|
));
|
|
|
|
PERF_ENTER(ModifyRequest);
|
|
|
|
INET_ASSERT(lpszObjectName != NULL);
|
|
INET_ASSERT(dwObjectNameLength != 0);
|
|
|
|
//
|
|
// there must already be a header when this method is called
|
|
//
|
|
|
|
INET_ASSERT(_HeadersLength != 0);
|
|
|
|
//
|
|
// we are about to start updating the headers for the current
|
|
// HTTP_REQUEST_HANDLE_OBJECT. Serialize access
|
|
//
|
|
|
|
//
|
|
// BUGBUG [arthurbi] using two HEADER_STRINGs here causes an extra
|
|
// ReAlloc when use the Copy operator between the two.
|
|
//
|
|
|
|
HEADER_STRING * pRequest = GetFirstHeader();
|
|
HEADER_STRING & request = *pRequest;
|
|
HEADER_STRING newRequest;
|
|
LPCSTR lpcszVerb;
|
|
DWORD verbLength;
|
|
DWORD error = ERROR_SUCCESS;
|
|
DWORD length;
|
|
|
|
//
|
|
// there must already be a request line
|
|
//
|
|
|
|
if (pRequest == NULL) {
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
INET_ASSERT(request.HaveString());
|
|
|
|
//
|
|
// get the verb/method to use.
|
|
//
|
|
|
|
if (tMethod == HTTP_METHOD_TYPE_UNKNOWN) {
|
|
|
|
//
|
|
// the method is unknown, read the old one out of the string
|
|
// and save off, basically we're reusing the previous one.
|
|
//
|
|
|
|
lpcszVerb = request.StringAddress();
|
|
|
|
for (DWORD i = 0; i < request.StringLength(); i++) {
|
|
if (lpcszVerb[i] == ' ') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
INET_ASSERT((i > 0) && (i < (DWORD)request.StringLength()));
|
|
|
|
verbLength = (DWORD)i;
|
|
} else {
|
|
|
|
//
|
|
// its one of the normal kind, just map it.
|
|
//
|
|
|
|
verbLength = MapHttpMethodType(tMethod, &lpcszVerb);
|
|
}
|
|
if (lpszVersion == NULL) {
|
|
lpszVersion = _lpszVersion;
|
|
dwVersionLength = _dwVersionLength;
|
|
}
|
|
|
|
_lpszVerb = NULL;
|
|
_dwVerbLength = 0;
|
|
_lpszObjectName = NULL;
|
|
_dwObjectNameLength = 0;
|
|
_lpszVersion = NULL;
|
|
_dwVersionLength = 0;
|
|
|
|
//
|
|
// calculate the new length from the component lengths we originally set
|
|
// in AddRequest(), and the new object name
|
|
//
|
|
|
|
length = verbLength + 1 + dwObjectNameLength + 1 + dwVersionLength + 1;
|
|
|
|
//
|
|
// create a new request line
|
|
//
|
|
|
|
newRequest.CreateStringBuffer((LPVOID)lpcszVerb, verbLength, length);
|
|
if (newRequest.IsError()) {
|
|
error = GetLastError();
|
|
} else {
|
|
newRequest += ' ';
|
|
newRequest.Strncat((LPVOID)lpszObjectName, dwObjectNameLength);
|
|
newRequest += ' ';
|
|
newRequest.Strncat((LPVOID)lpszVersion, dwVersionLength);
|
|
|
|
//
|
|
// remove the current request line length from the header buffer
|
|
// aggregate
|
|
//
|
|
|
|
_HeadersLength -= request.StringLength();
|
|
|
|
//
|
|
// make the current request line the new one
|
|
//
|
|
|
|
request = newRequest.StringAddress();
|
|
|
|
//
|
|
// and update the address and length variables (version length is the
|
|
// only thing that stays the same)
|
|
//
|
|
|
|
if (!request.IsError()) {
|
|
_lpszVerb = request.StringAddress();
|
|
_dwVerbLength = verbLength;
|
|
_lpszObjectName = _lpszVerb + verbLength + 1;
|
|
_dwObjectNameLength = dwObjectNameLength;
|
|
_lpszVersion = _lpszObjectName + dwObjectNameLength + 1;
|
|
_dwVersionLength = dwVersionLength;
|
|
SetRequestVersion();
|
|
|
|
//
|
|
// and the new request line length to the aggregate header length
|
|
//
|
|
|
|
_HeadersLength += request.StringLength();
|
|
} else {
|
|
error = GetLastError();
|
|
}
|
|
}
|
|
|
|
quit:
|
|
|
|
PERF_LEAVE(ModifyRequest);
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
VOID
|
|
HTTP_HEADERS::SetRequestVersion(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set _RequestVersionMajor and _RequestVersionMinor based on the HTTP
|
|
version string
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
None,
|
|
"HTTP_HEADERS::SetRequestVersion",
|
|
NULL
|
|
));
|
|
|
|
INET_ASSERT(_lpszVersion != NULL);
|
|
|
|
_RequestVersionMajor = 0;
|
|
_RequestVersionMinor = 0;
|
|
if (strncmp(_lpszVersion, "HTTP/", sizeof("HTTP/") - 1) == 0) {
|
|
|
|
LPSTR pNum = _lpszVersion + sizeof("HTTP/") - 1;
|
|
|
|
ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMajor);
|
|
while (!isdigit(*pNum) && (*pNum != '\0')) {
|
|
++pNum;
|
|
}
|
|
ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMinor);
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
INFO,
|
|
("request version = %d.%d\n",
|
|
_RequestVersionMajor,
|
|
_RequestVersionMinor
|
|
));
|
|
|
|
} else {
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
WARNING,
|
|
("\"HTTP/\" not found in %q\n",
|
|
_lpszVersion
|
|
));
|
|
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|