windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/irtl/httphdr/httphdr.cxx
2020-09-26 16:20:57 +08:00

1952 lines
53 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995-1996 Microsoft Corporation
Module Name :
httphdr.cxx
Abstract:
This module defines the functions for handling the dictionary items.
It contains custom implementation of dictionary for HTTP header parsing.
Author:
Murali R. Krishnan ( MuraliK ) 8-Nov-1996
Environment:
User Mode - Win32
Project:
Internet Server DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
# include "httphdr.hxx"
# include <iis64.h>
// NYI: Temporary copy for now
struct NAME_COLLECTION {
LPCSTR pszName;
INT cchName;
};
# define HfmHeader( HfmId, HfmString) { HfmString, (sizeof( HfmString) - 1) },
NAME_COLLECTION g_HttpHeaders[] = {
ALL_HTTP_FAST_MAP_HEADERS()
{ NULL, NULL}
};
# undef HfmHeader
# define NUM_HTTP_HEADERS (sizeof( g_HttpHeaders)/sizeof( g_HttpHeaders[0]) - 1)
# define _HTTP_HEADER_SIG_CHARS ( 32)
// SigBits avoids the upcase-low-case troubles.
# define SigBits( ch) ( (ch) & 0x1F)
# define SigBit_I ( ('I') & 0x1F) // SigBit of I
# define SigBit_U ( ('U') & 0x1F) // SigBit of U
//
// This header hash is specifically tailored for HTTP Headers in
// ALL_HTTP_FAST_MAP_HEADERS()
//
inline int _HTTP_HEADER_HASH( LPCSTR psz, DWORD cchLen, DWORD sigChar)
{
register DWORD ch = SigBits( (DWORD ) *psz);
return (( ch * (sigChar/2)) +
// ((( SigBits( *psz) == SigBit_I) && (cchLen > 6)) ?
// ((cchLen > 6) ? SigBits(psz[6]) :
((ch == SigBit_I && cchLen>6) ? SigBits(psz[6]) :
(((ch == SigBit_U)?
(cchLen) :
SigBits( psz[cchLen/2])))
)
);
} // _HTTP_HEADER_HASH()
// extract the case-insetive bits for US-ASCII character set.
# define IcaseBits(ch) ( (ch) & 0xDF)
// emulate stricmp by disregarding bit 5
inline BOOL _HTTP_HEADER_CHAR_I_NOTEQUAL( CHAR ch1, CHAR ch2)
{ return ( (IcaseBits(ch1)) != ( IcaseBits(ch2))); }
/*++
I tried using the cached case-insensitive name for comparison
using the _HTTP_HEADER_CHAR_I_NORMAL_1()
but that requires more instructions since the x86 generated
some unwanted instructions for access to memory :(
x86 is smart to execute the above function _HTTP_HEADER_CHAR_I_NOTEQUAL()
very well.
--*/
// same as func _HTTP_HEADER_CHAR_I_NOTEQUAL()
// except that ch2 is already normalized
inline BOOL _HTTP_HEADER_CHAR_I_NOTEQUAL_1( CHAR ch1, CHAR ch2)
{ return ( (IcaseBits(ch1)) != ( ch2)); }
#if COMPRESSED_HEADERS
//
// Lookup table for compressed headers.
//
HTTP_FAST_MAP_HEADERS
CHeaderLUT[] =
{
HM_ACC, //#A // Accept:
HM_MAX, //#B
HM_MAX, //#C
HM_MAX, //#D
HM_MAX, //#E
HM_MAX, //#F
HM_MAX, //#G
HM_AUT, //#H // Authorization:
HM_MAX, //#I
HM_CON, //#J // Connection:
HM_MAX, //#K
HM_MAX, //#L
HM_MAX, //#M
HM_CLE, //#N // Content-Length:
HM_MAX, //#O
HM_MAX, //#P
HM_MAX, //#Q
HM_CTY, //#R // Content-Type:
HM_MAX, //#S
HM_MAX, //#T
HM_MAX, //#U
HM_VIA, //#V
HM_HST, //#W // Host:
HM_IMS, //#X // If-Modified-Since:
HM_MAX, //#Y
HM_MAX, //#Z
HM_MAX, //#a
HM_MAX, //#b
HM_MAX, //#c
HM_MAX, //#d
HM_MAX, //#e
HM_MAX, //#f
HM_MAX, //#g
HM_PRA, //#h // Proxy-Authorization:
HM_MAX, //#i
HM_RNG, //#j // Range:
HM_MAX, //#k
HM_MAX, //#l
HM_MAX, //#m
HM_TEC, //#n // Transfer-Encoding:
HM_MAX, //#o
HM_MAX, //#p
HM_MAX, //#q
HM_MAX, //#r
HM_MAX, //#s
HM_MAX, //#t
HM_UMS //#u // Unless-Modified-Since:
};
#endif
/************************************************************
* Functions
************************************************************/
HTTP_HEADER_MAPPER::~HTTP_HEADER_MAPPER( VOID)
{
if ( NULL != m_rgOnNameMapper) {
delete [] m_rgOnNameMapper;
m_rgOnNameMapper = NULL;
}
} // HTTP_HEADER_MAPPER::~HTTP_HEADER_MAPPER()
BOOL
HTTP_HEADER_MAPPER::Initialize( VOID)
{
DWORD i;
m_rgOnNameMapper = new int[ SizeOfNameMapper()];
if ( NULL == m_rgOnNameMapper) {
IF_DEBUG(ERROR) {
DBGPRINTF(( DBG_CONTEXT,
"Allocation of Name Mapper of size %d failed\n",
SizeOfNameMapper()));
}
return FALSE;
}
// initialize the array of indexes
for ( i = 0 ; i < SizeOfNameMapper() ; ++i ) {
m_rgOnNameMapper[i] = -1; // set to invalid index
}
NAME_COLLECTION * pnc = g_HttpHeaders;
for( pnc = g_HttpHeaders; pnc->pszName != NULL; pnc++) {
int iN = _HTTP_HEADER_HASH(pnc->pszName, pnc->cchName, m_nSigChars);
IF_DEBUG( API_ENTRY) {
DBGPRINTF(( DBG_CONTEXT,
" _HTTP_HEADER_HASH( %s, len=%d, %d) => %d\n",
pnc->pszName, pnc->cchName, m_nSigChars, iN));
}
// We are using a very strict Algorithm for generating the mapping.
// If the following assert fails, then someone has broken the algo's
// assumption, by adding a new entry. We have to find a new algo.
// Algo's assumption: 1st char and next to last char are unique.
// If not, modify the algo to use another pair or a different method
// (different hash function).
if ((iN < 0) ||
(((DWORD ) iN) >= SizeOfNameMapper()) ||
(m_rgOnNameMapper[ iN] != -1)) {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" %08x::Initialize() OnName Mapper failed."
" Item (%s) indexes to location %d=>%d with (%s).\n",
this, pnc->pszName, iN,
m_rgOnNameMapper[iN],
g_HttpHeaders[ m_rgOnNameMapper[iN]].pszName
));
}
// DBG_ASSERT( m_rgOnNameMapper[iN] == -1 );
return ( FALSE);
}
// store the index here
m_rgOnNameMapper[iN] = DIFF(pnc - g_HttpHeaders);
} // for
m_nItems = DIFF(pnc - g_HttpHeaders);
return (TRUE);
} // HTTP_HEADER_MAPPER::Initialize()
BOOL
HTTP_HEADER_MAPPER:: FindOrdinal(
IN LPCSTR pszName,
IN INT cchName,
OUT DWORD * pdwOrdinal) const
{
DBG_ASSERT( m_rgOnNameMapper);
if ( cchName > 2 ) {
#if COMPRESSED_HEADERS
if (*pszHeader == '#')
{
HM_ID i;
CHAR c;
c = pszHeader[1];
if (c >= 'A')
{
i = CHeaderLUT[c - ( !(c & 0x20) ? 'A' : ('a' - ('Z' - 'A') - 1) )];
*FieldIndex = i;
return (i != HM_MAX);
}
return FALSE;
}
#endif
int iHash = _HTTP_HEADER_HASH( pszName, cchName, m_nSigChars);
DBG_ASSERT( iHash >= 0);
if (((DWORD ) iHash) >= SizeOfNameMapper()) {
//
// Out of bounds index value received for the index into our
// lookup table => our hash calculator indicates this is not
// a fast-mappable header => fail the FindOrdinal() call
//
return ( FALSE);
}
int i = m_rgOnNameMapper[iHash];
//
// The value from the m_rgOnNameMapper should be
// -1, if the header is not a fast-map header at all
// < NUM_HTTP_HEADERS if this is a valid fast-map header thus
// giving the index of the header in the header-mapper structure.
//
DBG_ASSERT( (i== -1) || (i < NUM_HTTP_HEADERS));
if ( (i != -1) && (cchName == g_HttpHeaders[i].cchName) ) {
LPCSTR pszFN = g_HttpHeaders[i].pszName;
// let us use stride 2 and be pipeline friendly
if ((cchName & 0x1)) {
// odd length => eliminate the first char
cchName--;
if ( _HTTP_HEADER_CHAR_I_NOTEQUAL(
pszName[cchName],
pszFN[cchName] ) )
{
return FALSE;
}
}
DBG_ASSERT( (cchName % 2) == 0);
while ( (cchName-= 2) >= 0 ) {
if ( _HTTP_HEADER_CHAR_I_NOTEQUAL( pszName[cchName],
pszFN[cchName] ) ||
_HTTP_HEADER_CHAR_I_NOTEQUAL( pszName[cchName + 1],
pszFN[cchName + 1] )
)
{
return FALSE;
}
} // while
*pdwOrdinal = (DWORD ) i;
return TRUE;
}
}
return FALSE;
} // HTTP_HEADER_MAPPER::FindOrdinal()
LPCSTR
HTTP_HEADER_MAPPER::FindName( IN DWORD dwOrdinal) const
{
DBG_ASSERT( dwOrdinal < NUM_HTTP_HEADERS );
return ( g_HttpHeaders[dwOrdinal].pszName);
} // HTTP_HEADER_MAPPER::FindName()
VOID
HTTP_HEADER_MAPPER::PrintToBuffer( IN CHAR * pchBuffer,
IN OUT LPDWORD pcch) const
{
DWORD cb;
DWORD i;
DBG_ASSERT( NULL != pchBuffer);
// 0. Print the location of this object
// 1. Print all the <Name, ordinal> pairs
// 2. Print the OnNameMapper values
cb = wsprintfA( pchBuffer,
"HTTP_HEADER_MAPPER (%08x). NumItems= %d. NameColl= %08x"
" NSigChars= %d\n\n",
this, m_nItems, g_HttpHeaders, m_nSigChars
);
for ( i = 0; i < NUM_HTTP_HEADERS; i++) {
if ( cb + 80 < *pcch) {
cb += wsprintfA( pchBuffer + cb,
" [%2d] @%4d\tLen=%-4d %-25s\n",
i,
_HTTP_HEADER_HASH( g_HttpHeaders[i].pszName,
g_HttpHeaders[i].cchName,
m_nSigChars),
g_HttpHeaders[i].cchName,
g_HttpHeaders[i].pszName
);
} else {
cb += 80;
}
} // for
if ( cb + 60 < *pcch) {
cb += wsprintfA( pchBuffer + cb, "\n Sizeof OnNameMapper = %d\n\n",
SizeOfNameMapper()
);
} else {
cb += 60;
}
if ( NULL != m_rgOnNameMapper) {
for( i = 0; i < SizeOfNameMapper(); i++) {
if ( (i % 20) == 0) {
pchBuffer[cb++] = '\n';
pchBuffer[cb] = '\0';
}
if ( cb + 5 < *pcch) {
cb += wsprintfA( pchBuffer + cb,
"%4d", m_rgOnNameMapper[ i]
);
} else {
cb += 5;
}
} // for
}
if ( cb + 80 < *pcch) {
cb += wsprintfA( pchBuffer+cb,
"\n %d items stored in %d buckets. Density = %5d\n",
NUM_HTTP_HEADERS, SizeOfNameMapper(),
( 10000 * NUM_HTTP_HEADERS)/SizeOfNameMapper()
);
} else {
cb += 80;
}
*pcch = cb;
return;
} // HTTP_HEADER_MAPPER::PrintToBuffer( )
VOID
HTTP_HEADER_MAPPER::Print( VOID) const
{
CHAR pchBuffer[ 20000];
DWORD cb = sizeof( pchBuffer);
PrintToBuffer( pchBuffer, &cb);
DBG_ASSERT( cb < sizeof(pchBuffer));
DBGDUMP(( DBG_CONTEXT, pchBuffer));
return;
} // HTTP_HEADER_MAPPER::Print()
/************************************************************
* HTTP_HEADERS
************************************************************/
#ifdef _PRIVATE_HTTP_HEADERS_TEST
HTTP_HEADER_MAPPER *
HTTP_HEADERS::QueryHHMapper(void)
{
return ( &sm_hhm);
} // HTTP_HEADERS::QueryHHMapper()
# endif // _PRIVATE_HTTP_HEADERS_TEST
inline VOID
UpdatePointer( IN OUT LPCSTR * ppsz, IN const CHAR * pchOld,
IN DWORD cchLen, IN const CHAR * pchNew)
{
if ( (*ppsz >= pchOld) &&
(*ppsz < (pchOld + cchLen))
){
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" Updating pointer [%08x] from %08x to %08x\n",
ppsz, *ppsz, ((*ppsz - pchOld) + pchNew)));
}
// update the pointer
*ppsz = ((*ppsz - pchOld) + pchNew);
}
}
DWORD
NAME_VALUE_CHUNK::PrintToBuffer( IN CHAR * pchOut, IN OUT LPDWORD pcchMax) const
{
DBG_ASSERT( NULL != pchOut);
DBG_ASSERT( NULL != pcchMax);
NAME_VALUE_PAIR * pnv;
DWORD cch = 0;
if ( m_nPairs == 0) {
*pcchMax = 0;
return ( 0);
}
if ( 80 < *pcchMax) {
cch = wsprintfA( pchOut, " NAME_VALUE_CHUNK: %08x; NumPairs = %d\n",
this, m_nPairs);
} else {
cch = 80;
}
// Iterate over given pairs of name-value items and dump the output
for ( pnv = (NAME_VALUE_PAIR *) m_rgNVP;
pnv < ((NAME_VALUE_PAIR *) m_rgNVP) + m_nPairs;
pnv++) {
if ( pnv->pchName != NULL) {
if ( (cch + pnv->cchName + pnv->cchValue + 3) < *pcchMax ) {
pchOut[cch++] = '\t';
CopyMemory( pchOut + cch, pnv->pchName, pnv->cchName);
cch += pnv->cchName;
pchOut[cch++] = '=';
CopyMemory( pchOut + cch, pnv->pchValue, pnv->cchValue);
cch += pnv->cchValue;
pchOut[cch++] = '\n';
} else {
cch += pnv->cchName + pnv->cchValue + 3;
}
}
} // for
*pcchMax = cch;
return (cch);
} // NAME_VALUE_CHUNK::PrintToBuffer()
BOOL
NAME_VALUE_CHUNK::AddEntry( IN const CHAR * pszHeader, IN DWORD cchHeader,
IN const CHAR * pszValue, IN DWORD cchValue
)
{
NAME_VALUE_PAIR * pnp;
DBG_ASSERT( IsSpaceAvailable());
// Walk the array and pick the first location that is free.
for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
pnp++) {
if ( NULL == pnp->pchName) {
// Found a blank one. Fill up the contents.
// NOTE: We are not making copies of the contents,
// We are just storing the pointers.
pnp->pchName = pszHeader;
pnp->cchName = cchHeader;
pnp->pchValue = pszValue;
pnp->cchValue = cchValue;
return (TRUE);
}
} // for
if ( m_nPairs < MAX_HEADERS_PER_CHUNK) {
// store it at the next available location.
pnp = (NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs;
m_nPairs++;
pnp->pchName = pszHeader;
pnp->cchName = cchHeader;
pnp->pchValue = pszValue;
pnp->cchValue = cchValue;
return ( TRUE);
}
SetLastError( ERROR_INSUFFICIENT_BUFFER);
return (FALSE);
} // NAME_VALUE_CHUNK::AddEntry()
NAME_VALUE_PAIR *
NAME_VALUE_CHUNK::FindEntry( IN const CHAR * pszHeader, IN DWORD cchHeader)
{
NAME_VALUE_PAIR * pnp;
// Walk the array and pick the first location that is free.
for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
pnp++) {
DBG_ASSERT( (cchHeader != pnp->cchName) || (pnp->pchName != NULL) );
if ( (cchHeader == pnp->cchName) &&
!_strnicmp( pszHeader, pnp->pchName, cchHeader)
) {
return ( pnp);
}
} // for
SetLastError( ERROR_NO_MORE_ITEMS);
return ( NULL);
} // NAME_VALUE_CHUNK::FindEntry()
NAME_VALUE_PAIR *
NAME_VALUE_CHUNK::FindMatchingOrFreeEntry( IN const CHAR * pszHeader,
IN DWORD cchHeader,
IN LPBOOL pfFound )
{
NAME_VALUE_PAIR * pnp;
DBG_ASSERT( pszHeader != NULL);
DBG_ASSERT( pfFound != NULL);
// Walk the array and pick the first location that is free.
for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
pnp++) {
DBG_ASSERT( (cchHeader != pnp->cchName) || (pnp->pchName != NULL) );
if ( (cchHeader == pnp->cchName) &&
!_strnicmp( pszHeader, pnp->pchName, cchHeader)
) {
*pfFound = TRUE;
return ( pnp);
}
} // for
if ( m_nPairs < MAX_HEADERS_PER_CHUNK) {
// return the free entry
DBG_ASSERT(((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs) == pnp);
*pfFound = FALSE;
return ( pnp);
}
SetLastError( ERROR_NO_MORE_ITEMS);
return ( NULL);
} // NAME_VALUE_CHUNK::FindMatchingOrFreeEntry()
BOOL
NAME_VALUE_CHUNK::UpdatePointers(
IN const CHAR * pchOld,
IN DWORD cchLen,
IN const CHAR * pchNew)
{
if ( m_nPairs > 0) {
NAME_VALUE_PAIR * pnp;
// Walk the array and pick the first location that is free.
for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
pnp++) {
UpdatePointer( &pnp->pchName, pchOld, cchLen, pchNew);
UpdatePointer( &pnp->pchValue, pchOld, cchLen, pchNew);
} // for
}
return (TRUE);
} // NAME_VALUE_CHUNK::UpdatePointers()
//
// Declare the header mapper. For the existing set of headers,
// use of 14X14 hash bucket is sufficient.
//
HTTP_HEADER_MAPPER HTTP_HEADERS::sm_hhm(14);
BOOL
HTTP_HEADERS::Initialize( VOID)
{
sm_hhm.SetSigChars( 14);
if ( !sm_hhm.Initialize()) {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" HTTP_HEADERS::Initialize() failed. \n"
));
}
return ( FALSE);
}
return ( TRUE);
} // HTTP_HEADERS::Initialize()
VOID
HTTP_HEADERS::Cleanup( VOID)
{
// Currently there is no function to cleanup sm_hmm :(
} // HTTP_HEADERS::Cleanup()
HTTP_HEADERS::HTTP_HEADERS(VOID)
: m_chNull ( '\0'),
m_buffHeaders (),
m_iMaxFastMapHeader ( 0),
m_cchHeaders ( 0),
m_cchBuffHeaders ( 0)
{
InitializeListHead( &m_ActiveList);
InitializeListHead( &m_FreeList);
IF_DEBUG( INIT_CLEAN) {
DBGPRINTF(( DBG_CONTEXT, "HTTP_HEADERS() => %08x\n", this));
}
Reset();
} // HTTP_HEADERS::HTTP_HEADERS()
HTTP_HEADERS::~HTTP_HEADERS( VOID)
{
NAME_VALUE_CHUNK * pNVC = NULL;
while ( !IsListEmpty( &m_FreeList ) )
{
pNVC = CONTAINING_RECORD( m_FreeList.Flink,
NAME_VALUE_CHUNK,
m_listEntry );
RemoveEntryList( &pNVC->m_listEntry );
delete pNVC;
}
while ( !IsListEmpty( &m_ActiveList ) )
{
pNVC = CONTAINING_RECORD( m_ActiveList.Flink,
NAME_VALUE_CHUNK,
m_listEntry );
RemoveEntryList( &pNVC->m_listEntry );
delete pNVC;
}
IF_DEBUG( INIT_CLEAN) {
DBGPRINTF(( DBG_CONTEXT, "deleted HTTP_HEADERS %08x\n", this));
}
} // HTTP_HEADERS::~HTTP_HEADERS()
VOID
HTTP_HEADERS::Reset( VOID)
{
m_cchHeaders = 0;
m_rcInlinedHeader[0] = '\0';
m_cchBuffHeaders = 0;
m_buffHeaders.Resize( HH_MIN);
m_iMaxFastMapHeader = 0;
ZeroMemory( m_rgpszHeaders, sizeof( m_rgpszHeaders));
// We will skip setting the m_rgpszHeaders to be NULL.
// the iMaxFastMapHeader does the necessary job for the same
//
// Move the Name-Value chunks from active list to the free-list.
//
while ( !IsListEmpty( &m_ActiveList)) {
PLIST_ENTRY pl = m_ActiveList.Flink;
RemoveEntryList( pl);
InsertTailList( &m_FreeList, pl);
} // while
DBG_ASSERT( IsListEmpty( &m_ActiveList));
DBG_CODE( InitializeListHead( &m_ActiveList)); // just paranoid!
return;
} // HTTP_HEADERS::Reset()
VOID
HTTP_HEADERS::CancelHeader( IN LPCSTR pszName)
{
HTTP_FAST_MAP_HEADERS iField;
DWORD cchName = strlen( pszName);
// Find and store the header and value
if ( sm_hhm.FindOrdinal( pszName, cchName, (LPDWORD ) &iField ) ) {
FastMapCancel( iField);
} else {
CancelHeaderInChunks( pszName, cchName);
}
} // HTTP_HEADERS::CancelHeader()
BOOL
HTTP_HEADERS::StoreHeader(IN const CHAR * pszHeader, IN DWORD cchHeader,
IN const CHAR * pszValue, IN DWORD cchValue
)
/*++
This function is used to copy the header values instead of just
storing the pointer values. This function can be used by filters to set/reset
headers.
--*/
{
HTTP_FAST_MAP_HEADERS iField;
// Find and store the header and value
if ( sm_hhm.FindOrdinal( pszHeader, cchHeader, (LPDWORD ) &iField ) ) {
return ( FastMapStoreWithConcat( iField, pszValue, cchValue) );
}
else
{
return ( AddEntryToChunks( pszHeader, cchHeader, pszValue, cchValue, TRUE));
}
} // HTTP_HEADERS::StoreHeader()
BOOL
HTTP_HEADERS::StoreHeader(IN const CHAR * pszHeader,
IN const CHAR * pszValue
)
{
return ( StoreHeader( pszHeader, strlen( pszHeader),
pszValue, strlen( pszValue)
)
);
} // HTTP_HEADERS::StoreHeader()
BOOL
HTTP_HEADERS::FastMapStoreWithConcat( IN HTTP_FAST_MAP_HEADERS hfm,
IN LPCSTR pszValue, IN DWORD cchValue)
{
// NYI: Following storage introduces fragmentation,
// which we do not care about for now :(
return (ConcatToHolder( m_rgpszHeaders + hfm, pszValue, cchValue));
} // FastMapStoreWithConcat()
VOID
HTTP_HEADERS::PrintToBuffer( IN CHAR * pchBuffer, IN OUT LPDWORD pcchMax) const
{
DWORD cb;
PLIST_ENTRY pl;
DBG_ASSERT( pchBuffer != NULL);
DBG_ASSERT( pcchMax != NULL);
// 0. Print the summary of the object
// 1. Print all the Fast Map headers
// 2. Print the rest of the headers
if( 100 < *pcchMax) {
cb = wsprintfA( pchBuffer,
"\nHTTP_HEADERS (%08x). cchHeaders = %d (buff = %d/%d)\n"
" Fast-Map headers: MaxFastMapHeaders = %d\n"
,
this, m_cchHeaders,
m_cchBuffHeaders, m_buffHeaders.QuerySize(),
FastMapMaxIndex()
);
} else {
cb = 100;
}
for ( DWORD i = 0; i < FastMapMaxIndex(); i++) {
if ( m_rgpszHeaders[i] != NULL) {
if ( cb + 200 < *pcchMax) {
cb += wsprintfA( pchBuffer + cb,
"\t%s = %s\n",
sm_hhm.FindName( i),
m_rgpszHeaders[i]
);
} else {
cb += 200;
}
}
} // for
// Print all other headers starting with the cached 1st chunk
DWORD cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
NAME_VALUE_CHUNK * pnvc =
CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
cb += pnvc->PrintToBuffer ( pchBuffer + cb, &cb1);
} // for
pchBuffer[cb] = '\0';
//
// Print the Raw header from buffer ... NYI
//
if ( cb + 2 < *pcchMax ) {
lstrcat( pchBuffer + cb, "\n\n");
cb += 2;
} else {
cb += 2;
}
*pcchMax = cb;
return;
} // HTTP_HEADERS::PrintToBuffer()
BOOL
HTTP_HEADERS::UpdatePointers(
IN const CHAR * pchOld,
IN DWORD cchLen,
IN const CHAR * pchNew)
{
// REMOVE
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
"%08x::UpdatePointers( %08x, %d, %08x) - is costly\n",
this, pchOld, cchLen, pchNew));
}
DBG_ASSERT( pchOld != pchNew); // if this is true why call this function?
// 1. Update the fast map pointers.
LPCSTR * ppsz;
for ( ppsz = m_rgpszHeaders;
ppsz < (m_rgpszHeaders + MAX_HTTP_FAST_MAP_HEADERS);
ppsz++) {
UpdatePointer( ppsz, pchOld, cchLen, pchNew);
} // for
// 3. Update pointers in the name-value chunk list
PLIST_ENTRY pl;
for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
NAME_VALUE_CHUNK * pnvc =
CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
// REMOVE
DBGPRINTF(( DBG_CONTEXT,
"HH(%08x)::UpdatePointers( %08x, %d, %08x)"
" for the NVC %08x (pl = %08x)\n",
this, pchOld, cchLen, pchNew, pnvc, pl));
pnvc->UpdatePointers( pchOld, cchLen, pchNew);
} // for
return ( TRUE);
} // HTTP_HEADERS::UpdatePointers()
BOOL
HTTP_HEADERS::MakeRoomInBuffer( IN DWORD cchReqd, IN LPCSTR * ppszVal)
{
// REMOVE
DBGPRINTF(( DBG_CONTEXT, "%08x:: MakeRoomInBuffer( %d, %08x). buff=%08x. size=%d\n",
this, cchReqd, ppszVal,&m_buffHeaders, m_buffHeaders.QuerySize()));
//
// Bug 136637 : Because of the way we move our headers around when we get a new header
// value for an existing header, it's really easy to chew up lots of memory rather
// quickly, so we'll artificially limit the size of the buffer to avoid denial-of-service
// attacks.
//
if ( cchReqd > HH_MAX )
{
DBGPRINTF((DBG_CONTEXT,
"Reached max buffer size (%d), refusing request to resize buffer to %d bytes\n",
HH_MAX,
cchReqd));
SetLastError( ERROR_OUTOFMEMORY );
return FALSE;
}
if ( cchReqd > m_buffHeaders.QuerySize()) {
// cache old pointer to update the other pointers properly
LPSTR pszOld = (LPSTR ) m_buffHeaders.QueryPtr();
if ( !m_buffHeaders.Resize( cchReqd, HH_GROW_BY)) {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT, "%08x::Unable to allocate %d bytes\n",
this, cchReqd));
}
return ( FALSE);
}
DBG_ASSERT( cchReqd <= m_buffHeaders.QuerySize());
LPSTR pszNew = (LPSTR ) m_buffHeaders.QueryPtr();
if ( pszNew != pszOld) {
// Trouble starts.
// I have to update all the guys pointing inside the old blob
// especially the pointers in
// the range (pszOld to pszOld + m_cchBuffHeaders)
UpdatePointer(ppszVal, pszOld, m_cchBuffHeaders, pszNew);
// REMOVE
DBGPRINTF(( DBG_CONTEXT, "%08x:: MakeRoomInBuffer( %d, %08x). buff=%08x. size=%d\n",
this, cchReqd, ppszVal,&m_buffHeaders, m_buffHeaders.QuerySize()));
return ( UpdatePointers( pszOld, m_cchBuffHeaders, pszNew));
}
// We are just lucky to be able to have reallocated at same place.
}
return ( TRUE);
} // HTTP_HEADERS::MakeRoomInBuffer()
VOID
HTTP_HEADERS::Print( VOID) const
{
CHAR pchBuffer[ 20000];
DWORD cchMax = sizeof( pchBuffer);
PrintToBuffer( pchBuffer, &cchMax);
DBGDUMP(( DBG_CONTEXT, pchBuffer));
} // HTTP_HEADERS::Print()
CHAR *
HTTP_HEADERS::FindValue( IN LPCSTR pszName, OUT LPDWORD pcchValue)
{
DWORD cchName = strlen( pszName);
HTTP_FAST_MAP_HEADERS iField;
//
// 1. Lookup in the fast map for this item
//
if ( sm_hhm.FindOrdinal( pszName, cchName, (LPDWORD ) &iField)) {
// found in the fast-map.
CHAR * pszValue = (CHAR * ) FastMapQueryValue( iField);
if ( pcchValue != NULL) {
*pcchValue = (( pszValue != NULL) ? strlen( pszValue) : 0);
}
return ( pszValue);
}
// 2. Search in the slow list - name-value-chunks
NAME_VALUE_PAIR * pnp = FindValueInChunks( pszName, cchName);
if ( pnp != NULL) {
if ( pcchValue != NULL) {
DBG_ASSERT( pnp->pchValue != NULL);
*pcchValue = pnp->cchValue;
}
return ( (CHAR *) pnp->pchValue);
}
return ( NULL);
} // HTTP_HEADERS::FindValue()
NAME_VALUE_PAIR *
HTTP_HEADERS::FindValueInChunks( IN LPCSTR pszName, IN DWORD cchName)
{
PLIST_ENTRY pl;
NAME_VALUE_PAIR * pnp = NULL;
// find a Name-value-pair/chunk that holds this entry.
for ( pl = m_ActiveList.Flink;
(pl != &m_ActiveList);
pl = pl->Flink) {
NAME_VALUE_CHUNK *
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindEntry( pszName, cchName);
if ( NULL != pnp) {
// we found the required name-value-pair.stop searching
break;
}
} // for
return ( pnp);
} // HTTP_HEADERS::FindValueInChunks()
VOID
HTTP_HEADERS::CancelHeaderInChunks( IN LPCSTR pszName, IN DWORD cchName)
{
PLIST_ENTRY pl;
NAME_VALUE_PAIR * pnp = NULL;
NAME_VALUE_CHUNK * pnc;
// NYI: This function can benefit from better implementation
// instead of moving memory around.
// Since the freq. of use of this func is less, we will not optimize :(
// find the Name-value-pair/chunk that holds this entry.
for ( pl = m_ActiveList.Flink;
(pnp == NULL) && (pl != &m_ActiveList);
pl = pl->Flink) {
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindEntry( pszName, cchName);
} // for
if ( pnp != NULL) {
// pnp - current item
// pnc - the current chunk
// to cancel the item, just left-shift the array of
// NAME_VALUE_PAIRS in the chunk and reset the m_nPairs value
DBG_ASSERT( (pnp >= pnc->m_rgNVP) &&
(pnp < pnc->m_rgNVP + pnc->m_nPairs));
DBG_ASSERT( (pnc->m_nPairs - (pnp - pnc->m_rgNVP)) >= 1 );
MoveMemory( pnp, (pnp + 1),
((pnc->m_nPairs - 1 - (pnp - pnc->m_rgNVP)) *
sizeof( NAME_VALUE_PAIR))
);
pnc->m_nPairs--;
// NYI: if pnc->m_nPairs == 0,
// we can move this away from the active list
}
return;
} // CancelHeaderInChunks()
BOOL
HTTP_HEADERS::NextPair( IN OUT HH_ITERATOR * phi,
OUT NAME_VALUE_PAIR ** ppnp
)
{
DBG_ASSERT( phi );
DBG_ASSERT( ppnp );
if ( phi->dwOrdinal < FastMapMaxIndex()) {
// Iterate over the FastMap headers ...
for ( DWORD i = phi->dwOrdinal; i < FastMapMaxIndex(); i++ ) {
if ( m_rgpszHeaders[i] != NULL) {
NAME_VALUE_PAIR * pnp = &phi->np;
// found a non-NULL value.
pnp->pchValue = m_rgpszHeaders[i];
pnp->pchName = sm_hhm.FindName( i);
// NYI: It will be nice to get the length directly :(
pnp->cchName = strlen( pnp->pchName);
pnp->cchValue= strlen( pnp->pchValue);
phi->dwOrdinal = i + 1;
*ppnp = pnp;
return ( TRUE);
}
} // for
// we exhausted the fast-map. Fall through after updating Ordinal
phi->dwOrdinal = (i);
}
//
// Find the pair in the chunk
//
return ( NextPairInChunks( phi, ppnp));
} // HTTP_HEADERS::NextPair()
BOOL
HTTP_HEADERS::NextPairInChunks( IN OUT HH_ITERATOR * phi,
OUT NAME_VALUE_PAIR ** ppnp
)
{
DBG_ASSERT( phi);
DBG_ASSERT( ppnp);
DBG_ASSERT( phi->dwOrdinal >= FastMapMaxIndex());
PLIST_ENTRY pl;
do {
PLIST_ENTRY pl = (PLIST_ENTRY ) phi->pChunk;
if ( pl == &m_ActiveList) {
break;
}
NAME_VALUE_CHUNK * pnc =
(NAME_VALUE_CHUNK *) CONTAINING_RECORD( pl, NAME_VALUE_CHUNK,
m_listEntry);
while ( phi->dwPair < pnc->m_nPairs) {
// extract the current pair, update pair pointer and return
*ppnp = (NAME_VALUE_PAIR *) (pnc->m_rgNVP + phi->dwPair);
phi->dwPair++;
if ( (*ppnp)->pchName ) {
return ( TRUE);
}
}
// we could not find any in the current chunk. Move to next chunk.
phi->pChunk = (PVOID)pnc->m_listEntry.Flink;
phi->dwPair = 0; // pair # within the chunk
} while ( TRUE);
SetLastError( ERROR_NO_MORE_ITEMS);
return ( FALSE);
} // HTTP_HEADERS::NextPairInChunks()
BOOL
HTTP_HEADERS::AddEntryToChunks(
IN const CHAR * pszHeader,
IN DWORD cchHeader,
IN const CHAR * pszValue,
IN DWORD cchValue,
BOOL fCopyValue
)
/*++
This function stores the <header, value> pair for headers not found
in the fast-map. It checks to see if the header already exists
with some value. If it does, then the new value is just concatenated
to the old one. Else the new value is stored separately in the first
available free chunk.
If there is no free chunk available, this function also allocates a free
chunk and stores the data in the new chunk.
--*/
{
// Store the header that is not part of the Fast Map
PLIST_ENTRY pl;
NAME_VALUE_CHUNK * pnc;
NAME_VALUE_PAIR * pnp;
NAME_VALUE_CHUNK * pncFirst = NULL;
NAME_VALUE_PAIR * pnpFirst = NULL;
BOOL fRet = FALSE;
// find a Name-value-pair/chunk that can hold this entry.
for ( pl = m_ActiveList.Flink;
(pl != &m_ActiveList);
pl = pl->Flink)
{
BOOL fFound = FALSE;
pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pnp = pnc->FindMatchingOrFreeEntry( pszHeader, cchHeader, &fFound);
//
// Found a matching entry, so update
//
if ( fFound )
{
DBG_ASSERT( pnp != NULL);
// pnc points to the chunk containing the matched item
// pnp points to the exact pair that matched up
DBG_ASSERT( (pnp->cchName == cchHeader) &&
(!_strnicmp( pnp->pchName, pszHeader, cchHeader))
);
IF_DEBUG( ERROR)
{
DBGPRINTF(( DBG_CONTEXT, "Match For (%s) found at PNP=%08x\n",
pszHeader, pnp));
}
// Concat the given value to the existing value element.
// Nothing more needs to be done
fRet = ConcatToHolder( &pnp->pchValue, pszValue, cchValue);
if ( fRet)
{
// update the length of the datum.
pnp->cchValue += (1 + cchValue); // 1 for the ',' concat sign.
}
return ( fRet);
}
else if ( pnp != NULL && pncFirst == NULL)
{
// cache it for later use, if header is never found
pncFirst = pnc;
pnpFirst = pnp;
}
} // for
if (pncFirst == NULL )
{
// No match found. No free chunk is available.
// Pull a new one from free list or create one
if ( IsListEmpty( &m_FreeList))
{
pncFirst = new NAME_VALUE_CHUNK();
if ( NULL == pncFirst)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
return ( FALSE);
}
}
else
{
// pull one from the free list and use it.
pl = m_FreeList.Flink;
RemoveEntryList( pl);
pncFirst = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
pncFirst->Reset();
}
InsertTailList( &m_ActiveList, &pncFirst->m_listEntry);
DBG_ASSERT( pncFirst->m_nPairs == 0);
pnpFirst = ((NAME_VALUE_PAIR * ) pncFirst->m_rgNVP);
}
//
// At this point, we know it's a new header, so store the new <header, value> pair
// in pnp and increment count of pairs.
//
DBG_ASSERT( NULL != pncFirst);
DBG_ASSERT( NULL != pnpFirst);
//
// Sometimes, we need to make a copy of the header eg when the header to be added
// comes from a filter. At other times, we can just store a copy of the pointer,
// because we know it'll be valid for the duration of the use of the data structure
//
if (fCopyValue)
{
//
// Copy actual values : ConcatToHolder assumes first parameter points to NULL
// if it's a new value to be stored
//
//
// Copy the header name
//
pnpFirst->pchName = NULL;
fRet = ConcatToHolder(&pnpFirst->pchName, pszHeader, cchHeader);
if (fRet)
{
pnpFirst->cchName = cchHeader;
}
else
{
return FALSE;
}
//
//Copy the header value
//
pnpFirst->pchValue = NULL;
fRet = ConcatToHolder(&pnpFirst->pchValue, pszValue, cchValue);
if (fRet)
{
pnpFirst->cchValue = cchValue;
if ( pnpFirst == (NAME_VALUE_PAIR * ) pncFirst->m_rgNVP + pncFirst->m_nPairs )
{
pncFirst->m_nPairs++;
}
}
else
{
pnpFirst->pchName = NULL;
return FALSE;
}
}
else
{
//
// Just copy pointer to value
//
pnpFirst->pchName = pszHeader;
pnpFirst->cchName = cchHeader;
pnpFirst->pchValue = pszValue;
pnpFirst->cchValue = cchValue;
if ( pnpFirst == (NAME_VALUE_PAIR * ) pncFirst->m_rgNVP + pncFirst->m_nPairs )
{
pncFirst->m_nPairs++;
}
fRet = TRUE;
}
DBG_ASSERT( pncFirst->m_nPairs <= MAX_HEADERS_PER_CHUNK);
return ( fRet );
} // HTTP_HEADERS::AddEntryToChunks()
BOOL
HTTP_HEADERS::ConcatToHolder( IN LPCSTR * ppsz,
IN LPCSTR pszNew,
IN DWORD cchNew
)
/*++
Given an internal pointer ppsz of the HTTP_HEADERS object,
this function appends the new value to the old value present
using ',' as the concatenation character.
It automatically allocates room and grows buffers, updates pointers, etc
if need be.
--*/
{
BOOL fRet = TRUE;
LPCSTR pszOld = *ppsz;
DWORD cchOld = pszOld ? strlen( pszOld) : 0;
DWORD cchReqd = cchOld + cchNew + 2;
// Find if we have enough space in the inlined buffer
if ( ( m_cchHeaders + cchReqd < sizeof( m_rcInlinedHeader))
) {
// Aha we are lucky. Make a copy at the end and form concatenated result
*ppsz = m_rcInlinedHeader + m_cchHeaders;
m_cchHeaders += cchReqd;
} else {
// Clearly we do not have room in the Inlined Header,
// store the stuff in the aux buffer area.
// Find if space is sufficient.
// This will automatically alloc and update pointers
if ( MakeRoomInBuffer( (m_cchBuffHeaders + cchReqd), &pszNew)
){
pszOld = *ppsz; // get the new pointer (since it could have moved)
LPSTR pszBuf = (LPSTR ) m_buffHeaders.QueryPtr();
// we have space at the end of the buffer here. Use this space.
*ppsz = pszBuf + m_cchBuffHeaders;
m_cchBuffHeaders += cchReqd;
} else {
DBGPRINTF(( DBG_CONTEXT,
"Unable to create room for %d characters \n",
m_cchBuffHeaders + cchOld + cchNew + 3));
return ( FALSE);
}
}
if ( !cchOld )
{
CopyMemory( (PVOID) (*ppsz), pszNew, cchNew + 1 );
}
else
{
// Format the value as := <old> ',' <new>
CopyMemory( (PVOID ) *ppsz, pszOld, cchOld);
((CHAR *) *ppsz)[cchOld] = ','; // concat character
CopyMemory( (PVOID ) (*ppsz + cchOld + 1), pszNew, cchNew + 1);
}
DBG_ASSERT( fRet == TRUE);
return ( fRet);
} // HTTP_HEADERS::ConcatToHolder()
/**************************************************
* PARSER for the HTTP_HEADERS
**************************************************/
inline const CHAR *
SkipLeadingWhiteSpace( IN const CHAR * pchStart, IN DWORD cchLen)
{
const CHAR * pchScan;
for ( pchScan = pchStart;
((pchScan < (pchStart + cchLen)) && isspace( (UCHAR)(*pchScan)));
pchScan++)
;
return ( pchScan);
} // SkipLeadingWhiteSpace()
BOOL
HTTP_HEADERS::ParseHeaderFirstLine( IN const CHAR * pchFirstLine,
IN CHAR * pchScan,
IN DWORD cchFirstLine)
/*++
Description:
Extract the HTTP method, URL, & HTTP-version strings from the first
line of header block.
Input: <Method> <URL> HTTP/<Version>
Use <blank> or <tab> as the delimiter.
NYI: What about other delimiters?
We will do only forward scans, because
1) the header can be malformed
2) HTTP/0.9 requests do not contain the version string
3) there may be more than 3 parameters on the first line.
In all the above cases reverse scans will cause trouble.
Note that a line termination can be by using \r\n or just \n :(
Arguments:
pchFirstLine - pointer to character stream containing the
first char of the line
pchScan - points to the end of the first line.
cchFirstLine - count of characters on the first line
Returns:
TRUE on success & FALSE for failure.
Use GetLastError() to get correct Win32 error code on failure.
--*/
{
LPSTR pszScan2;
pszScan2 = (LPSTR ) memchr ( pchFirstLine, ' ', cchFirstLine);
if ( NULL != pszScan2) {
LPSTR pszScan3;
*pszScan2++ = '\0'; // replace the blank with a null char
while ( _HTTP_IS_LINEAR_SPACE( *pszScan2 ) )
{
++pszScan2;
}
//
// pszScan2 now points to the URL sent
//
DWORD cReq = DIFF(pchScan - pszScan2);
pszScan3 = (LPSTR ) memchr( pszScan2, ' ', cReq);
if ( NULL != pszScan3 ) {
*pszScan3++ = '\0';
while ( _HTTP_IS_LINEAR_SPACE( *pszScan3 ) )
{
++pszScan3;
}
// pszScan3 should be now pointing to start of version info
// Note that we are not removing spaces between the version
// and the line delimiter
pchScan[(( (pchScan > pszScan3) && (pchScan[-1] == '\r')) ?
-1: 0)] = '\0';
} else {
// only 2 parameters. No version is present.
// Point pszScan3 to the null-part of pszScan2)
pszScan3 =
pchScan + ((pchScan[-1] == '\r') ? -1 : 0);
*pszScan3 = '\0';
}
//
// We know that we are parsing a new request.
// So we are not checking for concat during fast-map store here.
//
FastMapStore( HM_VER, pszScan3);
FastMapStore( HM_URL, pszScan2 );
FastMapStore( HM_MET, pchFirstLine);
} else {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" The scan for fields where they are not separated "
" with space is intentionally left out!\n"
));
}
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
//
// everything is happy here ... return success
//
return ( TRUE);
} // HTTP_HEADERS::ParseHeaderFirstLine()
BOOL
HTTP_HEADERS::ParseInput( IN const CHAR * pchHeaderBlob,
IN DWORD cchLen,
OUT DWORD * pcbExtraData
)
/*++
Description:
This function parses the input string and extracts the HTTP headers
for storage inside the http header structure. Fast-map headers are stored
in the dedicated fast-map storage area. Non-standard headers (that are
not part of) fast-map are stored inside the Name-value chunks.
The Grammar for incoming HTTP headers is:
<HTTP-verb> <URL> <HTTP-version>
{<Header-Name>:<Header-Value> { {\r\n} | {\n}}}*
where,
<HTTP-verb> == A+
<URL> == /[A|D]*
<HTTP-version> == HTTP/D+.D+
<Header-Name> == A[A|D]*
<Header-Value> == [A|D]*
<CRLF> == \r\n
A == any ascii character except ' ' '\t' '\n' '\r'
D == 0-9
Arguments:
pchHeaderBlob - pointer to header containing the header blob (+ maybe
the body of the request).
cchLen - length of the header block alone.
pcbExtraData - pointer to a DWORD which on return will contain the
number of extra characters of data present in the
blob given.
Returns:
TRUE on successfully parsing and splitting the headers apart.
FALSE if there is any failure.
--*/
{
CHAR * pchScan;
CHAR * pchRequest;
DWORD cReq;
IF_DEBUG( INIT_CLEAN)
{
DBGPRINTF(( DBG_CONTEXT, "%08x::ParseInput( %08x:, %d) \n"
"Input Headers:\n%s\n",
this, pchHeaderBlob, cchLen, pchHeaderBlob));
}
//
// 1. Skip all the leading spaces and ignore them all.
// We do not need these fields
//
pchScan = (CHAR *) SkipLeadingWhiteSpace( pchHeaderBlob, cchLen);
cchLen -= DIFF(pchScan - pchHeaderBlob);
//
// 2. Make a copy of the incoming header blob so that we can own the
// input headers and munge it in our own fashion
// NYI: One can optimize this by selectively copying segments that
// are worth using (values), but that will be costly due to
// multiple small CopyMemory() operations.
//
if ( cchLen < sizeof( m_rcInlinedHeader)) {
pchRequest = (CHAR * ) m_rcInlinedHeader;
m_cchHeaders = cchLen;
} else {
if ( !m_buffHeaders.Resize( cchLen + 4, HH_GROW_BY)) {
return ( FALSE);
}
pchRequest = (CHAR * ) m_buffHeaders.QueryPtr();
m_cchBuffHeaders = cchLen;
}
// 2a. copy the header to the buffer
CopyMemory( (PVOID ) pchRequest, pchScan, cchLen);
//
// pchRequest points to the local copy of the request headers
//
DBG_ASSERT( (pchRequest == m_rcInlinedHeader) ||
(pchRequest == m_buffHeaders.QueryPtr())
);
//
// 3. Get the first line and extract the method string
//
pchScan = (CHAR * ) memchr( pchRequest, '\n', cchLen);
if ( pchScan == NULL ) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
//
// 4. Extract the Method, URL and Version Number
//
if ( !ParseHeaderFirstLine( pchRequest, pchScan, DIFF(pchScan - pchRequest))) {
return ( FALSE);
}
//
// 5. Extract all other headers
//
LPSTR pszHeader = pchScan + 1;
cReq = DIFF(pchRequest + cchLen - pszHeader);
LPSTR pchEnd = pszHeader + cReq;
LPSTR pszEol, pszValue;
//
// pszHeader ( and thus pszEnd & pszEol ) are relative to pchRequest.
// This needs to be preserved in this loop.
//
for ( ; (*pszHeader != '\r' || (cReq > 1 && *(pszHeader + 1) != '\n')) &&
(*pszHeader != '\n') &&
(pszValue = (LPSTR)memchr( pszHeader, ':', cReq ));
pszHeader = (pszEol + 1), cReq = DIFF(pchEnd - pszHeader)
)
{
UINT chN = (UINT)(*(PBYTE)++pszValue);
// *pszValue = '\0';
int cchHeader = DIFF(pszValue - pszHeader);
pszEol = (LPSTR ) memchr( pszValue, '\n', cReq - cchHeader);
if ( NULL == pszEol ) {
//
// Aha! we found a completely malformed header block here.
// Let us return a failure to the caller.
//
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" The scan for header-value blocks found a line"
" not properly terminated with \'\\n\'\n"
));
}
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
DBG_ASSERT( NULL != pszEol );
DWORD cchValue;
DWORD iField;
// skip spaces if any.
if ( _HTTP_IS_LINEAR_SPACE( (CHAR)chN ) )
{
while ( _HTTP_IS_LINEAR_SPACE( (CHAR)(*(PBYTE)++pszValue) ) )
;
}
// Terminate the value string
if ( (pszEol > pszValue) && (pszEol[-1] == '\r') )
{
pszEol[-1] = '\0';
cchValue = DIFF(pszEol - pszValue - 1);
}
else
{
pszEol[0] = '\0';
cchValue = DIFF(pszEol - pszValue);
}
IF_DEBUG( INIT_CLEAN )
{
DBGPRINTF((DBG_CONTEXT, "\t[%s] = %s\n", pszHeader, pszValue ));
}
{ // Store the header - inline to reduce cost
HTTP_FAST_MAP_HEADERS iField;
BOOL fRet = TRUE;
// Find and store the header and value
if ( sm_hhm.FindOrdinal( pszHeader, cchHeader,
(LPDWORD ) &iField ) )
{
if ( FastMapQueryValue(iField) == NULL )
{
// Store the item and continue scan for next header
FastMapStore( iField, pszValue);
continue;
}
else
{
// FastMapStoreWithConcat can resize the header
// buffer. If the headers we're scanning are
// coming out of the header buffer, we'll need
// to update pszHeader later, so save the current
// pointer just in case.
CHAR *pOldBuff = (CHAR *)m_buffHeaders.QueryPtr();
fRet = FastMapStoreWithConcat( iField, pszValue,
cchValue);
if ( !fRet)
{
IF_DEBUG( ERROR)
{
DBGPRINTF(( DBG_CONTEXT, "Failed to StoreHeader %s"
"in fast map with concat\n",
pszHeader));
}
return ( FALSE);
}
// See if the buffer has changed. If it has,
// update the end pointer and the eol pointer. pszHeader
// gets updated at the end of the loop from pszEol. Be
// careful if you try and use any other pointers between
// here and the end of the loop.
// We update these pointers only if they were already pointing
// in alloced buffer space ( i.e. not using the m_rcInlinedHeader buffer )
if (pOldBuff != (CHAR *)m_buffHeaders.QueryPtr() &&
pOldBuff == pchRequest )
{
// Buffer got resized, fix up appropriate pointers.
pchEnd = (CHAR *)m_buffHeaders.QueryPtr() +
(pchEnd - pchRequest);
pszEol = (CHAR *)m_buffHeaders.QueryPtr() +
(pszEol - pchRequest);
pchRequest = (CHAR *)m_buffHeaders.QueryPtr();
}
}
}
else
{
// AddEntry to chunks also has resizing buffer issues. See
// comments above.
CHAR *pOldBuff = (CHAR *)m_buffHeaders.QueryPtr();
fRet = AddEntryToChunks( pszHeader, cchHeader, pszValue,
cchValue);
if ( !fRet)
{
IF_DEBUG( ERROR)
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to StoreHeader %s in chunks\n",
pszHeader));
}
return ( FALSE);
}
if (pOldBuff != (CHAR *)m_buffHeaders.QueryPtr() &&
pOldBuff == pchRequest )
{
// Buffer got resized, fix up appropriate pointers.
pchEnd = (CHAR *)m_buffHeaders.QueryPtr() +
(pchEnd - pchRequest);
pszEol = (CHAR *)m_buffHeaders.QueryPtr() +
(pszEol - pchRequest);
pchRequest = (CHAR *)m_buffHeaders.QueryPtr();
}
}
}
} // for()
if ( (*pszHeader == '\r') || (*pszHeader == '\n') )
{
// blank header line - end of the parsing
while ( cReq )
{
--cReq;
if ( *pszHeader++ == '\n' )
{
break;
}
}
}
*pcbExtraData = DIFF(pchEnd - pszHeader);
return ( TRUE);
} // HTTP_HEADERS::ParseInput()
/************************ End of File ***********************/