Leaked source code of windows server 2003
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.

1951 lines
55 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. httphdr.cxx
  5. Abstract:
  6. This module defines the functions for handling the dictionary items.
  7. It contains custom implementation of dictionary for HTTP header parsing.
  8. Author:
  9. Murali R. Krishnan ( MuraliK ) 8-Nov-1996
  10. Environment:
  11. User Mode - Win32
  12. Project:
  13. Internet Server DLL
  14. Functions Exported:
  15. Revision History:
  16. --*/
  17. /************************************************************
  18. * Include Headers
  19. ************************************************************/
  20. # include "httphdr.hxx"
  21. # include <iis64.h>
  22. // NYI: Temporary copy for now
  23. struct NAME_COLLECTION {
  24. LPCSTR pszName;
  25. INT cchName;
  26. };
  27. # define HfmHeader( HfmId, HfmString) { HfmString, (sizeof( HfmString) - 1) },
  28. NAME_COLLECTION g_HttpHeaders[] = {
  29. ALL_HTTP_FAST_MAP_HEADERS()
  30. { NULL, NULL}
  31. };
  32. # undef HfmHeader
  33. # define NUM_HTTP_HEADERS (sizeof( g_HttpHeaders)/sizeof( g_HttpHeaders[0]) - 1)
  34. # define _HTTP_HEADER_SIG_CHARS ( 32)
  35. // SigBits avoids the upcase-low-case troubles.
  36. # define SigBits( ch) ( (ch) & 0x1F)
  37. # define SigBit_I ( ('I') & 0x1F) // SigBit of I
  38. # define SigBit_U ( ('U') & 0x1F) // SigBit of U
  39. //
  40. // This header hash is specifically tailored for HTTP Headers in
  41. // ALL_HTTP_FAST_MAP_HEADERS()
  42. //
  43. inline int _HTTP_HEADER_HASH( LPCSTR psz, DWORD cchLen, DWORD sigChar)
  44. {
  45. register DWORD ch = SigBits( (DWORD ) *psz);
  46. return (( ch * (sigChar/2)) +
  47. // ((( SigBits( *psz) == SigBit_I) && (cchLen > 6)) ?
  48. // ((cchLen > 6) ? SigBits(psz[6]) :
  49. ((ch == SigBit_I && cchLen>6) ? SigBits(psz[6]) :
  50. (((ch == SigBit_U)?
  51. (cchLen) :
  52. SigBits( psz[cchLen/2])))
  53. )
  54. );
  55. } // _HTTP_HEADER_HASH()
  56. // extract the case-insetive bits for US-ASCII character set.
  57. # define IcaseBits(ch) ( (ch) & 0xDF)
  58. // emulate stricmp by disregarding bit 5
  59. inline BOOL _HTTP_HEADER_CHAR_I_NOTEQUAL( CHAR ch1, CHAR ch2)
  60. { return ( (IcaseBits(ch1)) != ( IcaseBits(ch2))); }
  61. /*++
  62. I tried using the cached case-insensitive name for comparison
  63. using the _HTTP_HEADER_CHAR_I_NORMAL_1()
  64. but that requires more instructions since the x86 generated
  65. some unwanted instructions for access to memory :(
  66. x86 is smart to execute the above function _HTTP_HEADER_CHAR_I_NOTEQUAL()
  67. very well.
  68. --*/
  69. // same as func _HTTP_HEADER_CHAR_I_NOTEQUAL()
  70. // except that ch2 is already normalized
  71. inline BOOL _HTTP_HEADER_CHAR_I_NOTEQUAL_1( CHAR ch1, CHAR ch2)
  72. { return ( (IcaseBits(ch1)) != ( ch2)); }
  73. #if COMPRESSED_HEADERS
  74. //
  75. // Lookup table for compressed headers.
  76. //
  77. HTTP_FAST_MAP_HEADERS
  78. CHeaderLUT[] =
  79. {
  80. HM_ACC, //#A // Accept:
  81. HM_MAX, //#B
  82. HM_MAX, //#C
  83. HM_MAX, //#D
  84. HM_MAX, //#E
  85. HM_MAX, //#F
  86. HM_MAX, //#G
  87. HM_AUT, //#H // Authorization:
  88. HM_MAX, //#I
  89. HM_CON, //#J // Connection:
  90. HM_MAX, //#K
  91. HM_MAX, //#L
  92. HM_MAX, //#M
  93. HM_CLE, //#N // Content-Length:
  94. HM_MAX, //#O
  95. HM_MAX, //#P
  96. HM_MAX, //#Q
  97. HM_CTY, //#R // Content-Type:
  98. HM_MAX, //#S
  99. HM_MAX, //#T
  100. HM_MAX, //#U
  101. HM_VIA, //#V
  102. HM_HST, //#W // Host:
  103. HM_IMS, //#X // If-Modified-Since:
  104. HM_MAX, //#Y
  105. HM_MAX, //#Z
  106. HM_MAX, //#a
  107. HM_MAX, //#b
  108. HM_MAX, //#c
  109. HM_MAX, //#d
  110. HM_MAX, //#e
  111. HM_MAX, //#f
  112. HM_MAX, //#g
  113. HM_PRA, //#h // Proxy-Authorization:
  114. HM_MAX, //#i
  115. HM_RNG, //#j // Range:
  116. HM_MAX, //#k
  117. HM_MAX, //#l
  118. HM_MAX, //#m
  119. HM_TEC, //#n // Transfer-Encoding:
  120. HM_MAX, //#o
  121. HM_MAX, //#p
  122. HM_MAX, //#q
  123. HM_MAX, //#r
  124. HM_MAX, //#s
  125. HM_MAX, //#t
  126. HM_UMS //#u // Unless-Modified-Since:
  127. };
  128. #endif
  129. /************************************************************
  130. * Functions
  131. ************************************************************/
  132. HTTP_HEADER_MAPPER::~HTTP_HEADER_MAPPER( VOID)
  133. {
  134. if ( NULL != m_rgOnNameMapper) {
  135. delete [] m_rgOnNameMapper;
  136. m_rgOnNameMapper = NULL;
  137. }
  138. } // HTTP_HEADER_MAPPER::~HTTP_HEADER_MAPPER()
  139. BOOL
  140. HTTP_HEADER_MAPPER::Initialize( VOID)
  141. {
  142. DWORD i;
  143. m_rgOnNameMapper = new int[ SizeOfNameMapper()];
  144. if ( NULL == m_rgOnNameMapper) {
  145. IF_DEBUG(ERROR) {
  146. DBGPRINTF(( DBG_CONTEXT,
  147. "Allocation of Name Mapper of size %d failed\n",
  148. SizeOfNameMapper()));
  149. }
  150. return FALSE;
  151. }
  152. // initialize the array of indexes
  153. for ( i = 0 ; i < SizeOfNameMapper() ; ++i ) {
  154. m_rgOnNameMapper[i] = -1; // set to invalid index
  155. }
  156. NAME_COLLECTION * pnc = g_HttpHeaders;
  157. for( pnc = g_HttpHeaders; pnc->pszName != NULL; pnc++) {
  158. int iN = _HTTP_HEADER_HASH(pnc->pszName, pnc->cchName, m_nSigChars);
  159. IF_DEBUG( API_ENTRY) {
  160. DBGPRINTF(( DBG_CONTEXT,
  161. " _HTTP_HEADER_HASH( %s, len=%d, %d) => %d\n",
  162. pnc->pszName, pnc->cchName, m_nSigChars, iN));
  163. }
  164. // We are using a very strict Algorithm for generating the mapping.
  165. // If the following assert fails, then someone has broken the algo's
  166. // assumption, by adding a new entry. We have to find a new algo.
  167. // Algo's assumption: 1st char and next to last char are unique.
  168. // If not, modify the algo to use another pair or a different method
  169. // (different hash function).
  170. if ((iN < 0) ||
  171. (((DWORD ) iN) >= SizeOfNameMapper()) ||
  172. (m_rgOnNameMapper[ iN] != -1)) {
  173. IF_DEBUG( ERROR) {
  174. DBGPRINTF(( DBG_CONTEXT,
  175. " %08x::Initialize() OnName Mapper failed."
  176. " Item (%s) indexes to location %d=>%d with (%s).\n",
  177. this, pnc->pszName, iN,
  178. m_rgOnNameMapper[iN],
  179. g_HttpHeaders[ m_rgOnNameMapper[iN]].pszName
  180. ));
  181. }
  182. // DBG_ASSERT( m_rgOnNameMapper[iN] == -1 );
  183. return ( FALSE);
  184. }
  185. // store the index here
  186. m_rgOnNameMapper[iN] = DIFF(pnc - g_HttpHeaders);
  187. } // for
  188. m_nItems = DIFF(pnc - g_HttpHeaders);
  189. return (TRUE);
  190. } // HTTP_HEADER_MAPPER::Initialize()
  191. BOOL
  192. HTTP_HEADER_MAPPER:: FindOrdinal(
  193. IN LPCSTR pszName,
  194. IN INT cchName,
  195. OUT DWORD * pdwOrdinal) const
  196. {
  197. DBG_ASSERT( m_rgOnNameMapper);
  198. if ( cchName > 2 ) {
  199. #if COMPRESSED_HEADERS
  200. if (*pszHeader == '#')
  201. {
  202. HM_ID i;
  203. CHAR c;
  204. c = pszHeader[1];
  205. if (c >= 'A')
  206. {
  207. i = CHeaderLUT[c - ( !(c & 0x20) ? 'A' : ('a' - ('Z' - 'A') - 1) )];
  208. *FieldIndex = i;
  209. return (i != HM_MAX);
  210. }
  211. return FALSE;
  212. }
  213. #endif
  214. int iHash = _HTTP_HEADER_HASH( pszName, cchName, m_nSigChars);
  215. DBG_ASSERT( iHash >= 0);
  216. if (((DWORD ) iHash) >= SizeOfNameMapper()) {
  217. //
  218. // Out of bounds index value received for the index into our
  219. // lookup table => our hash calculator indicates this is not
  220. // a fast-mappable header => fail the FindOrdinal() call
  221. //
  222. return ( FALSE);
  223. }
  224. int i = m_rgOnNameMapper[iHash];
  225. //
  226. // The value from the m_rgOnNameMapper should be
  227. // -1, if the header is not a fast-map header at all
  228. // < NUM_HTTP_HEADERS if this is a valid fast-map header thus
  229. // giving the index of the header in the header-mapper structure.
  230. //
  231. DBG_ASSERT( (i== -1) || (i < NUM_HTTP_HEADERS));
  232. if ( (i != -1) && (cchName == g_HttpHeaders[i].cchName) ) {
  233. LPCSTR pszFN = g_HttpHeaders[i].pszName;
  234. // let us use stride 2 and be pipeline friendly
  235. if ((cchName & 0x1)) {
  236. // odd length => eliminate the first char
  237. cchName--;
  238. if ( _HTTP_HEADER_CHAR_I_NOTEQUAL(
  239. pszName[cchName],
  240. pszFN[cchName] ) )
  241. {
  242. return FALSE;
  243. }
  244. }
  245. DBG_ASSERT( (cchName % 2) == 0);
  246. while ( (cchName-= 2) >= 0 ) {
  247. if ( _HTTP_HEADER_CHAR_I_NOTEQUAL( pszName[cchName],
  248. pszFN[cchName] ) ||
  249. _HTTP_HEADER_CHAR_I_NOTEQUAL( pszName[cchName + 1],
  250. pszFN[cchName + 1] )
  251. )
  252. {
  253. return FALSE;
  254. }
  255. } // while
  256. *pdwOrdinal = (DWORD ) i;
  257. return TRUE;
  258. }
  259. }
  260. return FALSE;
  261. } // HTTP_HEADER_MAPPER::FindOrdinal()
  262. LPCSTR
  263. HTTP_HEADER_MAPPER::FindName( IN DWORD dwOrdinal) const
  264. {
  265. DBG_ASSERT( dwOrdinal < NUM_HTTP_HEADERS );
  266. return ( g_HttpHeaders[dwOrdinal].pszName);
  267. } // HTTP_HEADER_MAPPER::FindName()
  268. VOID
  269. HTTP_HEADER_MAPPER::PrintToBuffer( IN CHAR * pchBuffer,
  270. IN OUT LPDWORD pcch) const
  271. {
  272. DWORD cb;
  273. DWORD i;
  274. DBG_ASSERT( NULL != pchBuffer);
  275. // 0. Print the location of this object
  276. // 1. Print all the <Name, ordinal> pairs
  277. // 2. Print the OnNameMapper values
  278. cb = wsprintfA( pchBuffer,
  279. "HTTP_HEADER_MAPPER (%08x). NumItems= %d. NameColl= %08x"
  280. " NSigChars= %d\n\n",
  281. this, m_nItems, g_HttpHeaders, m_nSigChars
  282. );
  283. for ( i = 0; i < NUM_HTTP_HEADERS; i++) {
  284. if ( cb + 80 < *pcch) {
  285. cb += wsprintfA( pchBuffer + cb,
  286. " [%2d] @%4d\tLen=%-4d %-25s\n",
  287. i,
  288. _HTTP_HEADER_HASH( g_HttpHeaders[i].pszName,
  289. g_HttpHeaders[i].cchName,
  290. m_nSigChars),
  291. g_HttpHeaders[i].cchName,
  292. g_HttpHeaders[i].pszName
  293. );
  294. } else {
  295. cb += 80;
  296. }
  297. } // for
  298. if ( cb + 60 < *pcch) {
  299. cb += wsprintfA( pchBuffer + cb, "\n Sizeof OnNameMapper = %d\n\n",
  300. SizeOfNameMapper()
  301. );
  302. } else {
  303. cb += 60;
  304. }
  305. if ( NULL != m_rgOnNameMapper) {
  306. for( i = 0; i < SizeOfNameMapper(); i++) {
  307. if ( (i % 20) == 0) {
  308. pchBuffer[cb++] = '\n';
  309. pchBuffer[cb] = '\0';
  310. }
  311. if ( cb + 5 < *pcch) {
  312. cb += wsprintfA( pchBuffer + cb,
  313. "%4d", m_rgOnNameMapper[ i]
  314. );
  315. } else {
  316. cb += 5;
  317. }
  318. } // for
  319. }
  320. if ( cb + 80 < *pcch) {
  321. cb += wsprintfA( pchBuffer+cb,
  322. "\n %d items stored in %d buckets. Density = %5d\n",
  323. NUM_HTTP_HEADERS, SizeOfNameMapper(),
  324. ( 10000 * NUM_HTTP_HEADERS)/SizeOfNameMapper()
  325. );
  326. } else {
  327. cb += 80;
  328. }
  329. *pcch = cb;
  330. return;
  331. } // HTTP_HEADER_MAPPER::PrintToBuffer( )
  332. VOID
  333. HTTP_HEADER_MAPPER::Print( VOID) const
  334. {
  335. CHAR pchBuffer[ 20000];
  336. DWORD cb = sizeof( pchBuffer);
  337. PrintToBuffer( pchBuffer, &cb);
  338. DBG_ASSERT( cb < sizeof(pchBuffer));
  339. DBGDUMP(( DBG_CONTEXT, pchBuffer));
  340. return;
  341. } // HTTP_HEADER_MAPPER::Print()
  342. /************************************************************
  343. * HTTP_HEADERS
  344. ************************************************************/
  345. #ifdef _PRIVATE_HTTP_HEADERS_TEST
  346. HTTP_HEADER_MAPPER *
  347. HTTP_HEADERS::QueryHHMapper(void)
  348. {
  349. return ( &sm_hhm);
  350. } // HTTP_HEADERS::QueryHHMapper()
  351. # endif // _PRIVATE_HTTP_HEADERS_TEST
  352. inline VOID
  353. UpdatePointer( IN OUT LPCSTR * ppsz, IN const CHAR * pchOld,
  354. IN DWORD cchLen, IN const CHAR * pchNew)
  355. {
  356. if ( (*ppsz >= pchOld) &&
  357. (*ppsz < (pchOld + cchLen))
  358. ){
  359. IF_DEBUG( ERROR) {
  360. DBGPRINTF(( DBG_CONTEXT,
  361. " Updating pointer [%08x] from %08x to %08x\n",
  362. ppsz, *ppsz, ((*ppsz - pchOld) + pchNew)));
  363. }
  364. // update the pointer
  365. *ppsz = ((*ppsz - pchOld) + pchNew);
  366. }
  367. }
  368. DWORD
  369. NAME_VALUE_CHUNK::PrintToBuffer( IN CHAR * pchOut, IN OUT LPDWORD pcchMax) const
  370. {
  371. DBG_ASSERT( NULL != pchOut);
  372. DBG_ASSERT( NULL != pcchMax);
  373. NAME_VALUE_PAIR * pnv;
  374. DWORD cch = 0;
  375. if ( m_nPairs == 0) {
  376. *pcchMax = 0;
  377. return ( 0);
  378. }
  379. if ( 80 < *pcchMax) {
  380. cch = wsprintfA( pchOut, " NAME_VALUE_CHUNK: %08x; NumPairs = %d\n",
  381. this, m_nPairs);
  382. } else {
  383. cch = 80;
  384. }
  385. // Iterate over given pairs of name-value items and dump the output
  386. for ( pnv = (NAME_VALUE_PAIR *) m_rgNVP;
  387. pnv < ((NAME_VALUE_PAIR *) m_rgNVP) + m_nPairs;
  388. pnv++) {
  389. if ( pnv->pchName != NULL) {
  390. if ( (cch + pnv->cchName + pnv->cchValue + 3) < *pcchMax ) {
  391. pchOut[cch++] = '\t';
  392. CopyMemory( pchOut + cch, pnv->pchName, pnv->cchName);
  393. cch += pnv->cchName;
  394. pchOut[cch++] = '=';
  395. CopyMemory( pchOut + cch, pnv->pchValue, pnv->cchValue);
  396. cch += pnv->cchValue;
  397. pchOut[cch++] = '\n';
  398. } else {
  399. cch += pnv->cchName + pnv->cchValue + 3;
  400. }
  401. }
  402. } // for
  403. *pcchMax = cch;
  404. return (cch);
  405. } // NAME_VALUE_CHUNK::PrintToBuffer()
  406. BOOL
  407. NAME_VALUE_CHUNK::AddEntry( IN const CHAR * pszHeader, IN DWORD cchHeader,
  408. IN const CHAR * pszValue, IN DWORD cchValue
  409. )
  410. {
  411. NAME_VALUE_PAIR * pnp;
  412. DBG_ASSERT( IsSpaceAvailable());
  413. // Walk the array and pick the first location that is free.
  414. for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
  415. pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
  416. pnp++) {
  417. if ( NULL == pnp->pchName) {
  418. // Found a blank one. Fill up the contents.
  419. // NOTE: We are not making copies of the contents,
  420. // We are just storing the pointers.
  421. pnp->pchName = pszHeader;
  422. pnp->cchName = cchHeader;
  423. pnp->pchValue = pszValue;
  424. pnp->cchValue = cchValue;
  425. return (TRUE);
  426. }
  427. } // for
  428. if ( m_nPairs < MAX_HEADERS_PER_CHUNK) {
  429. // store it at the next available location.
  430. pnp = (NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs;
  431. m_nPairs++;
  432. pnp->pchName = pszHeader;
  433. pnp->cchName = cchHeader;
  434. pnp->pchValue = pszValue;
  435. pnp->cchValue = cchValue;
  436. return ( TRUE);
  437. }
  438. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  439. return (FALSE);
  440. } // NAME_VALUE_CHUNK::AddEntry()
  441. NAME_VALUE_PAIR *
  442. NAME_VALUE_CHUNK::FindEntry( IN const CHAR * pszHeader, IN DWORD cchHeader)
  443. {
  444. NAME_VALUE_PAIR * pnp;
  445. // Walk the array and pick the first location that is free.
  446. for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
  447. pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
  448. pnp++) {
  449. DBG_ASSERT( (cchHeader != pnp->cchName) || (pnp->pchName != NULL) );
  450. if ( (cchHeader == pnp->cchName) &&
  451. !_strnicmp( pszHeader, pnp->pchName, cchHeader)
  452. ) {
  453. return ( pnp);
  454. }
  455. } // for
  456. SetLastError( ERROR_NO_MORE_ITEMS);
  457. return ( NULL);
  458. } // NAME_VALUE_CHUNK::FindEntry()
  459. NAME_VALUE_PAIR *
  460. NAME_VALUE_CHUNK::FindMatchingOrFreeEntry( IN const CHAR * pszHeader,
  461. IN DWORD cchHeader,
  462. IN LPBOOL pfFound )
  463. {
  464. NAME_VALUE_PAIR * pnp;
  465. DBG_ASSERT( pszHeader != NULL);
  466. DBG_ASSERT( pfFound != NULL);
  467. // Walk the array and pick the first location that is free.
  468. for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
  469. pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
  470. pnp++) {
  471. DBG_ASSERT( (cchHeader != pnp->cchName) || (pnp->pchName != NULL) );
  472. if ( (cchHeader == pnp->cchName) &&
  473. !_strnicmp( pszHeader, pnp->pchName, cchHeader)
  474. ) {
  475. *pfFound = TRUE;
  476. return ( pnp);
  477. }
  478. } // for
  479. if ( m_nPairs < MAX_HEADERS_PER_CHUNK) {
  480. // return the free entry
  481. DBG_ASSERT(((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs) == pnp);
  482. *pfFound = FALSE;
  483. return ( pnp);
  484. }
  485. SetLastError( ERROR_NO_MORE_ITEMS);
  486. return ( NULL);
  487. } // NAME_VALUE_CHUNK::FindMatchingOrFreeEntry()
  488. BOOL
  489. NAME_VALUE_CHUNK::UpdatePointers(
  490. IN const CHAR * pchOld,
  491. IN DWORD cchLen,
  492. IN const CHAR * pchNew)
  493. {
  494. if ( m_nPairs > 0) {
  495. NAME_VALUE_PAIR * pnp;
  496. // Walk the array and pick the first location that is free.
  497. for ( pnp = (NAME_VALUE_PAIR * ) m_rgNVP;
  498. pnp < ((NAME_VALUE_PAIR * ) m_rgNVP + m_nPairs);
  499. pnp++) {
  500. UpdatePointer( &pnp->pchName, pchOld, cchLen, pchNew);
  501. UpdatePointer( &pnp->pchValue, pchOld, cchLen, pchNew);
  502. } // for
  503. }
  504. return (TRUE);
  505. } // NAME_VALUE_CHUNK::UpdatePointers()
  506. //
  507. // Declare the header mapper. For the existing set of headers,
  508. // use of 14X14 hash bucket is sufficient.
  509. //
  510. HTTP_HEADER_MAPPER HTTP_HEADERS::sm_hhm(14);
  511. BOOL
  512. HTTP_HEADERS::Initialize( VOID)
  513. {
  514. sm_hhm.SetSigChars( 14);
  515. if ( !sm_hhm.Initialize()) {
  516. IF_DEBUG( ERROR) {
  517. DBGPRINTF(( DBG_CONTEXT,
  518. " HTTP_HEADERS::Initialize() failed. \n"
  519. ));
  520. }
  521. return ( FALSE);
  522. }
  523. return ( TRUE);
  524. } // HTTP_HEADERS::Initialize()
  525. VOID
  526. HTTP_HEADERS::Cleanup( VOID)
  527. {
  528. // Currently there is no function to cleanup sm_hmm :(
  529. } // HTTP_HEADERS::Cleanup()
  530. HTTP_HEADERS::HTTP_HEADERS(VOID)
  531. : m_chNull ( '\0'),
  532. m_buffHeaders (),
  533. m_iMaxFastMapHeader ( 0),
  534. m_cchHeaders ( 0),
  535. m_cchBuffHeaders ( 0)
  536. {
  537. InitializeListHead( &m_ActiveList);
  538. InitializeListHead( &m_FreeList);
  539. IF_DEBUG( INIT_CLEAN) {
  540. DBGPRINTF(( DBG_CONTEXT, "HTTP_HEADERS() => %08x\n", this));
  541. }
  542. Reset();
  543. } // HTTP_HEADERS::HTTP_HEADERS()
  544. HTTP_HEADERS::~HTTP_HEADERS( VOID)
  545. {
  546. NAME_VALUE_CHUNK * pNVC = NULL;
  547. while ( !IsListEmpty( &m_FreeList ) )
  548. {
  549. pNVC = CONTAINING_RECORD( m_FreeList.Flink,
  550. NAME_VALUE_CHUNK,
  551. m_listEntry );
  552. RemoveEntryList( &pNVC->m_listEntry );
  553. delete pNVC;
  554. }
  555. while ( !IsListEmpty( &m_ActiveList ) )
  556. {
  557. pNVC = CONTAINING_RECORD( m_ActiveList.Flink,
  558. NAME_VALUE_CHUNK,
  559. m_listEntry );
  560. RemoveEntryList( &pNVC->m_listEntry );
  561. delete pNVC;
  562. }
  563. IF_DEBUG( INIT_CLEAN) {
  564. DBGPRINTF(( DBG_CONTEXT, "deleted HTTP_HEADERS %08x\n", this));
  565. }
  566. } // HTTP_HEADERS::~HTTP_HEADERS()
  567. VOID
  568. HTTP_HEADERS::Reset( VOID)
  569. {
  570. m_cchHeaders = 0;
  571. m_rcInlinedHeader[0] = '\0';
  572. m_cchBuffHeaders = 0;
  573. m_buffHeaders.Resize( HH_MIN);
  574. m_iMaxFastMapHeader = 0;
  575. ZeroMemory( m_rgpszHeaders, sizeof( m_rgpszHeaders));
  576. // We will skip setting the m_rgpszHeaders to be NULL.
  577. // the iMaxFastMapHeader does the necessary job for the same
  578. //
  579. // Move the Name-Value chunks from active list to the free-list.
  580. //
  581. while ( !IsListEmpty( &m_ActiveList)) {
  582. PLIST_ENTRY pl = m_ActiveList.Flink;
  583. RemoveEntryList( pl);
  584. InsertTailList( &m_FreeList, pl);
  585. } // while
  586. DBG_ASSERT( IsListEmpty( &m_ActiveList));
  587. DBG_CODE( InitializeListHead( &m_ActiveList)); // just paranoid!
  588. return;
  589. } // HTTP_HEADERS::Reset()
  590. VOID
  591. HTTP_HEADERS::CancelHeader( IN LPCSTR pszName)
  592. {
  593. HTTP_FAST_MAP_HEADERS iField;
  594. DWORD cchName = strlen( pszName);
  595. // Find and store the header and value
  596. if ( sm_hhm.FindOrdinal( pszName, cchName, (LPDWORD ) &iField ) ) {
  597. FastMapCancel( iField);
  598. } else {
  599. CancelHeaderInChunks( pszName, cchName);
  600. }
  601. } // HTTP_HEADERS::CancelHeader()
  602. BOOL
  603. HTTP_HEADERS::StoreHeader(IN const CHAR * pszHeader, IN DWORD cchHeader,
  604. IN const CHAR * pszValue, IN DWORD cchValue
  605. )
  606. /*++
  607. This function is used to copy the header values instead of just
  608. storing the pointer values. This function can be used by filters to set/reset
  609. headers.
  610. --*/
  611. {
  612. HTTP_FAST_MAP_HEADERS iField;
  613. // Find and store the header and value
  614. if ( sm_hhm.FindOrdinal( pszHeader, cchHeader, (LPDWORD ) &iField ) ) {
  615. return ( FastMapStoreWithConcat( iField, pszValue, cchValue) );
  616. }
  617. else
  618. {
  619. return ( AddEntryToChunks( pszHeader, cchHeader, pszValue, cchValue, TRUE));
  620. }
  621. } // HTTP_HEADERS::StoreHeader()
  622. BOOL
  623. HTTP_HEADERS::StoreHeader(IN const CHAR * pszHeader,
  624. IN const CHAR * pszValue
  625. )
  626. {
  627. return ( StoreHeader( pszHeader, strlen( pszHeader),
  628. pszValue, strlen( pszValue)
  629. )
  630. );
  631. } // HTTP_HEADERS::StoreHeader()
  632. BOOL
  633. HTTP_HEADERS::FastMapStoreWithConcat( IN HTTP_FAST_MAP_HEADERS hfm,
  634. IN LPCSTR pszValue, IN DWORD cchValue)
  635. {
  636. // NYI: Following storage introduces fragmentation,
  637. // which we do not care about for now :(
  638. return (ConcatToHolder( m_rgpszHeaders + hfm, pszValue, cchValue));
  639. } // FastMapStoreWithConcat()
  640. VOID
  641. HTTP_HEADERS::PrintToBuffer( IN CHAR * pchBuffer, IN OUT LPDWORD pcchMax) const
  642. {
  643. DWORD cb;
  644. PLIST_ENTRY pl;
  645. DBG_ASSERT( pchBuffer != NULL);
  646. DBG_ASSERT( pcchMax != NULL);
  647. // 0. Print the summary of the object
  648. // 1. Print all the Fast Map headers
  649. // 2. Print the rest of the headers
  650. if( 100 < *pcchMax) {
  651. cb = wsprintfA( pchBuffer,
  652. "\nHTTP_HEADERS (%08x). cchHeaders = %d (buff = %d/%d)\n"
  653. " Fast-Map headers: MaxFastMapHeaders = %d\n"
  654. ,
  655. this, m_cchHeaders,
  656. m_cchBuffHeaders, m_buffHeaders.QuerySize(),
  657. FastMapMaxIndex()
  658. );
  659. } else {
  660. cb = 100;
  661. }
  662. for ( DWORD i = 0; i < FastMapMaxIndex(); i++) {
  663. if ( m_rgpszHeaders[i] != NULL) {
  664. if ( cb + 200 < *pcchMax) {
  665. cb += wsprintfA( pchBuffer + cb,
  666. "\t%s = %s\n",
  667. sm_hhm.FindName( i),
  668. m_rgpszHeaders[i]
  669. );
  670. } else {
  671. cb += 200;
  672. }
  673. }
  674. } // for
  675. // Print all other headers starting with the cached 1st chunk
  676. DWORD cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
  677. for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
  678. NAME_VALUE_CHUNK * pnvc =
  679. CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  680. cb1 = (cb < *pcchMax) ? *pcchMax - cb : 0;
  681. cb += pnvc->PrintToBuffer ( pchBuffer + cb, &cb1);
  682. } // for
  683. pchBuffer[cb] = '\0';
  684. //
  685. // Print the Raw header from buffer ... NYI
  686. //
  687. if ( cb + 2 < *pcchMax ) {
  688. lstrcat( pchBuffer + cb, "\n\n");
  689. cb += 2;
  690. } else {
  691. cb += 2;
  692. }
  693. *pcchMax = cb;
  694. return;
  695. } // HTTP_HEADERS::PrintToBuffer()
  696. BOOL
  697. HTTP_HEADERS::UpdatePointers(
  698. IN const CHAR * pchOld,
  699. IN DWORD cchLen,
  700. IN const CHAR * pchNew)
  701. {
  702. // REMOVE
  703. IF_DEBUG( ERROR) {
  704. DBGPRINTF(( DBG_CONTEXT,
  705. "%08x::UpdatePointers( %08x, %d, %08x) - is costly\n",
  706. this, pchOld, cchLen, pchNew));
  707. }
  708. DBG_ASSERT( pchOld != pchNew); // if this is true why call this function?
  709. // 1. Update the fast map pointers.
  710. LPCSTR * ppsz;
  711. for ( ppsz = m_rgpszHeaders;
  712. ppsz < (m_rgpszHeaders + MAX_HTTP_FAST_MAP_HEADERS);
  713. ppsz++) {
  714. UpdatePointer( ppsz, pchOld, cchLen, pchNew);
  715. } // for
  716. // 3. Update pointers in the name-value chunk list
  717. PLIST_ENTRY pl;
  718. for ( pl = m_ActiveList.Flink; pl != &m_ActiveList; pl = pl->Flink) {
  719. NAME_VALUE_CHUNK * pnvc =
  720. CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  721. // REMOVE
  722. DBGPRINTF(( DBG_CONTEXT,
  723. "HH(%08x)::UpdatePointers( %08x, %d, %08x)"
  724. " for the NVC %08x (pl = %08x)\n",
  725. this, pchOld, cchLen, pchNew, pnvc, pl));
  726. pnvc->UpdatePointers( pchOld, cchLen, pchNew);
  727. } // for
  728. return ( TRUE);
  729. } // HTTP_HEADERS::UpdatePointers()
  730. BOOL
  731. HTTP_HEADERS::MakeRoomInBuffer( IN DWORD cchReqd, IN LPCSTR * ppszVal)
  732. {
  733. // REMOVE
  734. DBGPRINTF(( DBG_CONTEXT, "%08x:: MakeRoomInBuffer( %d, %08x). buff=%08x. size=%d\n",
  735. this, cchReqd, ppszVal,&m_buffHeaders, m_buffHeaders.QuerySize()));
  736. //
  737. // Bug 136637 : Because of the way we move our headers around when we get a new header
  738. // value for an existing header, it's really easy to chew up lots of memory rather
  739. // quickly, so we'll artificially limit the size of the buffer to avoid denial-of-service
  740. // attacks.
  741. //
  742. if ( cchReqd > HH_MAX )
  743. {
  744. DBGPRINTF((DBG_CONTEXT,
  745. "Reached max buffer size (%d), refusing request to resize buffer to %d bytes\n",
  746. HH_MAX,
  747. cchReqd));
  748. SetLastError( ERROR_OUTOFMEMORY );
  749. return FALSE;
  750. }
  751. if ( cchReqd > m_buffHeaders.QuerySize()) {
  752. // cache old pointer to update the other pointers properly
  753. LPSTR pszOld = (LPSTR ) m_buffHeaders.QueryPtr();
  754. if ( !m_buffHeaders.Resize( cchReqd, HH_GROW_BY)) {
  755. IF_DEBUG( ERROR) {
  756. DBGPRINTF(( DBG_CONTEXT, "%08x::Unable to allocate %d bytes\n",
  757. this, cchReqd));
  758. }
  759. return ( FALSE);
  760. }
  761. DBG_ASSERT( cchReqd <= m_buffHeaders.QuerySize());
  762. LPSTR pszNew = (LPSTR ) m_buffHeaders.QueryPtr();
  763. if ( pszNew != pszOld) {
  764. // Trouble starts.
  765. // I have to update all the guys pointing inside the old blob
  766. // especially the pointers in
  767. // the range (pszOld to pszOld + m_cchBuffHeaders)
  768. UpdatePointer(ppszVal, pszOld, m_cchBuffHeaders, pszNew);
  769. // REMOVE
  770. DBGPRINTF(( DBG_CONTEXT, "%08x:: MakeRoomInBuffer( %d, %08x). buff=%08x. size=%d\n",
  771. this, cchReqd, ppszVal,&m_buffHeaders, m_buffHeaders.QuerySize()));
  772. return ( UpdatePointers( pszOld, m_cchBuffHeaders, pszNew));
  773. }
  774. // We are just lucky to be able to have reallocated at same place.
  775. }
  776. return ( TRUE);
  777. } // HTTP_HEADERS::MakeRoomInBuffer()
  778. VOID
  779. HTTP_HEADERS::Print( VOID) const
  780. {
  781. CHAR pchBuffer[ 20000];
  782. DWORD cchMax = sizeof( pchBuffer);
  783. PrintToBuffer( pchBuffer, &cchMax);
  784. DBGDUMP(( DBG_CONTEXT, pchBuffer));
  785. } // HTTP_HEADERS::Print()
  786. CHAR *
  787. HTTP_HEADERS::FindValue( IN LPCSTR pszName, OUT LPDWORD pcchValue)
  788. {
  789. DWORD cchName = strlen( pszName);
  790. HTTP_FAST_MAP_HEADERS iField;
  791. //
  792. // 1. Lookup in the fast map for this item
  793. //
  794. if ( sm_hhm.FindOrdinal( pszName, cchName, (LPDWORD ) &iField)) {
  795. // found in the fast-map.
  796. CHAR * pszValue = (CHAR * ) FastMapQueryValue( iField);
  797. if ( pcchValue != NULL) {
  798. *pcchValue = (( pszValue != NULL) ? strlen( pszValue) : 0);
  799. }
  800. return ( pszValue);
  801. }
  802. // 2. Search in the slow list - name-value-chunks
  803. NAME_VALUE_PAIR * pnp = FindValueInChunks( pszName, cchName);
  804. if ( pnp != NULL) {
  805. if ( pcchValue != NULL) {
  806. DBG_ASSERT( pnp->pchValue != NULL);
  807. *pcchValue = pnp->cchValue;
  808. }
  809. return ( (CHAR *) pnp->pchValue);
  810. }
  811. return ( NULL);
  812. } // HTTP_HEADERS::FindValue()
  813. NAME_VALUE_PAIR *
  814. HTTP_HEADERS::FindValueInChunks( IN LPCSTR pszName, IN DWORD cchName)
  815. {
  816. PLIST_ENTRY pl;
  817. NAME_VALUE_PAIR * pnp = NULL;
  818. // find a Name-value-pair/chunk that holds this entry.
  819. for ( pl = m_ActiveList.Flink;
  820. (pl != &m_ActiveList);
  821. pl = pl->Flink) {
  822. NAME_VALUE_CHUNK *
  823. pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  824. pnp = pnc->FindEntry( pszName, cchName);
  825. if ( NULL != pnp) {
  826. // we found the required name-value-pair.stop searching
  827. break;
  828. }
  829. } // for
  830. return ( pnp);
  831. } // HTTP_HEADERS::FindValueInChunks()
  832. VOID
  833. HTTP_HEADERS::CancelHeaderInChunks( IN LPCSTR pszName, IN DWORD cchName)
  834. {
  835. PLIST_ENTRY pl;
  836. NAME_VALUE_PAIR * pnp = NULL;
  837. NAME_VALUE_CHUNK * pnc;
  838. // NYI: This function can benefit from better implementation
  839. // instead of moving memory around.
  840. // Since the freq. of use of this func is less, we will not optimize :(
  841. // find the Name-value-pair/chunk that holds this entry.
  842. for ( pl = m_ActiveList.Flink;
  843. (pnp == NULL) && (pl != &m_ActiveList);
  844. pl = pl->Flink) {
  845. pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  846. pnp = pnc->FindEntry( pszName, cchName);
  847. } // for
  848. if ( pnp != NULL) {
  849. // pnp - current item
  850. // pnc - the current chunk
  851. // to cancel the item, just left-shift the array of
  852. // NAME_VALUE_PAIRS in the chunk and reset the m_nPairs value
  853. DBG_ASSERT( (pnp >= pnc->m_rgNVP) &&
  854. (pnp < pnc->m_rgNVP + pnc->m_nPairs));
  855. DBG_ASSERT( (pnc->m_nPairs - (pnp - pnc->m_rgNVP)) >= 1 );
  856. MoveMemory( pnp, (pnp + 1),
  857. ((pnc->m_nPairs - 1 - (pnp - pnc->m_rgNVP)) *
  858. sizeof( NAME_VALUE_PAIR))
  859. );
  860. pnc->m_nPairs--;
  861. // NYI: if pnc->m_nPairs == 0,
  862. // we can move this away from the active list
  863. }
  864. return;
  865. } // CancelHeaderInChunks()
  866. BOOL
  867. HTTP_HEADERS::NextPair( IN OUT HH_ITERATOR * phi,
  868. OUT NAME_VALUE_PAIR ** ppnp
  869. )
  870. {
  871. DBG_ASSERT( phi );
  872. DBG_ASSERT( ppnp );
  873. if ( phi->dwOrdinal < FastMapMaxIndex()) {
  874. // Iterate over the FastMap headers ...
  875. for ( DWORD i = phi->dwOrdinal; i < FastMapMaxIndex(); i++ ) {
  876. if ( m_rgpszHeaders[i] != NULL) {
  877. NAME_VALUE_PAIR * pnp = &phi->np;
  878. // found a non-NULL value.
  879. pnp->pchValue = m_rgpszHeaders[i];
  880. pnp->pchName = sm_hhm.FindName( i);
  881. // NYI: It will be nice to get the length directly :(
  882. pnp->cchName = strlen( pnp->pchName);
  883. pnp->cchValue= strlen( pnp->pchValue);
  884. phi->dwOrdinal = i + 1;
  885. *ppnp = pnp;
  886. return ( TRUE);
  887. }
  888. } // for
  889. // we exhausted the fast-map. Fall through after updating Ordinal
  890. phi->dwOrdinal = (i);
  891. }
  892. //
  893. // Find the pair in the chunk
  894. //
  895. return ( NextPairInChunks( phi, ppnp));
  896. } // HTTP_HEADERS::NextPair()
  897. BOOL
  898. HTTP_HEADERS::NextPairInChunks( IN OUT HH_ITERATOR * phi,
  899. OUT NAME_VALUE_PAIR ** ppnp
  900. )
  901. {
  902. DBG_ASSERT( phi);
  903. DBG_ASSERT( ppnp);
  904. DBG_ASSERT( phi->dwOrdinal >= FastMapMaxIndex());
  905. PLIST_ENTRY pl;
  906. do {
  907. PLIST_ENTRY pl = (PLIST_ENTRY ) phi->pChunk;
  908. if ( pl == &m_ActiveList) {
  909. break;
  910. }
  911. NAME_VALUE_CHUNK * pnc =
  912. (NAME_VALUE_CHUNK *) CONTAINING_RECORD( pl, NAME_VALUE_CHUNK,
  913. m_listEntry);
  914. while ( phi->dwPair < pnc->m_nPairs) {
  915. // extract the current pair, update pair pointer and return
  916. *ppnp = (NAME_VALUE_PAIR *) (pnc->m_rgNVP + phi->dwPair);
  917. phi->dwPair++;
  918. if ( (*ppnp)->pchName ) {
  919. return ( TRUE);
  920. }
  921. }
  922. // we could not find any in the current chunk. Move to next chunk.
  923. phi->pChunk = (PVOID)pnc->m_listEntry.Flink;
  924. phi->dwPair = 0; // pair # within the chunk
  925. } while ( TRUE);
  926. SetLastError( ERROR_NO_MORE_ITEMS);
  927. return ( FALSE);
  928. } // HTTP_HEADERS::NextPairInChunks()
  929. BOOL
  930. HTTP_HEADERS::AddEntryToChunks(
  931. IN const CHAR * pszHeader,
  932. IN DWORD cchHeader,
  933. IN const CHAR * pszValue,
  934. IN DWORD cchValue,
  935. BOOL fCopyValue
  936. )
  937. /*++
  938. This function stores the <header, value> pair for headers not found
  939. in the fast-map. It checks to see if the header already exists
  940. with some value. If it does, then the new value is just concatenated
  941. to the old one. Else the new value is stored separately in the first
  942. available free chunk.
  943. If there is no free chunk available, this function also allocates a free
  944. chunk and stores the data in the new chunk.
  945. --*/
  946. {
  947. // Store the header that is not part of the Fast Map
  948. PLIST_ENTRY pl;
  949. NAME_VALUE_CHUNK * pnc;
  950. NAME_VALUE_PAIR * pnp;
  951. NAME_VALUE_CHUNK * pncFirst = NULL;
  952. NAME_VALUE_PAIR * pnpFirst = NULL;
  953. BOOL fRet = FALSE;
  954. // find a Name-value-pair/chunk that can hold this entry.
  955. for ( pl = m_ActiveList.Flink;
  956. (pl != &m_ActiveList);
  957. pl = pl->Flink)
  958. {
  959. BOOL fFound = FALSE;
  960. pnc = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  961. pnp = pnc->FindMatchingOrFreeEntry( pszHeader, cchHeader, &fFound);
  962. //
  963. // Found a matching entry, so update
  964. //
  965. if ( fFound )
  966. {
  967. DBG_ASSERT( pnp != NULL);
  968. // pnc points to the chunk containing the matched item
  969. // pnp points to the exact pair that matched up
  970. DBG_ASSERT( (pnp->cchName == cchHeader) &&
  971. (!_strnicmp( pnp->pchName, pszHeader, cchHeader))
  972. );
  973. IF_DEBUG( ERROR)
  974. {
  975. DBGPRINTF(( DBG_CONTEXT, "Match For (%s) found at PNP=%08x\n",
  976. pszHeader, pnp));
  977. }
  978. // Concat the given value to the existing value element.
  979. // Nothing more needs to be done
  980. fRet = ConcatToHolder( &pnp->pchValue, pszValue, cchValue);
  981. if ( fRet)
  982. {
  983. // update the length of the datum.
  984. pnp->cchValue += (1 + cchValue); // 1 for the ',' concat sign.
  985. }
  986. return ( fRet);
  987. }
  988. else if ( pnp != NULL && pncFirst == NULL)
  989. {
  990. // cache it for later use, if header is never found
  991. pncFirst = pnc;
  992. pnpFirst = pnp;
  993. }
  994. } // for
  995. if (pncFirst == NULL )
  996. {
  997. // No match found. No free chunk is available.
  998. // Pull a new one from free list or create one
  999. if ( IsListEmpty( &m_FreeList))
  1000. {
  1001. pncFirst = new NAME_VALUE_CHUNK();
  1002. if ( NULL == pncFirst)
  1003. {
  1004. SetLastError( ERROR_NOT_ENOUGH_MEMORY);
  1005. return ( FALSE);
  1006. }
  1007. }
  1008. else
  1009. {
  1010. // pull one from the free list and use it.
  1011. pl = m_FreeList.Flink;
  1012. RemoveEntryList( pl);
  1013. pncFirst = CONTAINING_RECORD( pl, NAME_VALUE_CHUNK, m_listEntry);
  1014. pncFirst->Reset();
  1015. }
  1016. InsertTailList( &m_ActiveList, &pncFirst->m_listEntry);
  1017. DBG_ASSERT( pncFirst->m_nPairs == 0);
  1018. pnpFirst = ((NAME_VALUE_PAIR * ) pncFirst->m_rgNVP);
  1019. }
  1020. //
  1021. // At this point, we know it's a new header, so store the new <header, value> pair
  1022. // in pnp and increment count of pairs.
  1023. //
  1024. DBG_ASSERT( NULL != pncFirst);
  1025. DBG_ASSERT( NULL != pnpFirst);
  1026. //
  1027. // Sometimes, we need to make a copy of the header eg when the header to be added
  1028. // comes from a filter. At other times, we can just store a copy of the pointer,
  1029. // because we know it'll be valid for the duration of the use of the data structure
  1030. //
  1031. if (fCopyValue)
  1032. {
  1033. //
  1034. // Copy actual values : ConcatToHolder assumes first parameter points to NULL
  1035. // if it's a new value to be stored
  1036. //
  1037. //
  1038. // Copy the header name
  1039. //
  1040. pnpFirst->pchName = NULL;
  1041. fRet = ConcatToHolder(&pnpFirst->pchName, pszHeader, cchHeader);
  1042. if (fRet)
  1043. {
  1044. pnpFirst->cchName = cchHeader;
  1045. }
  1046. else
  1047. {
  1048. return FALSE;
  1049. }
  1050. //
  1051. //Copy the header value
  1052. //
  1053. pnpFirst->pchValue = NULL;
  1054. fRet = ConcatToHolder(&pnpFirst->pchValue, pszValue, cchValue);
  1055. if (fRet)
  1056. {
  1057. pnpFirst->cchValue = cchValue;
  1058. if ( pnpFirst == (NAME_VALUE_PAIR * ) pncFirst->m_rgNVP + pncFirst->m_nPairs )
  1059. {
  1060. pncFirst->m_nPairs++;
  1061. }
  1062. }
  1063. else
  1064. {
  1065. pnpFirst->pchName = NULL;
  1066. return FALSE;
  1067. }
  1068. }
  1069. else
  1070. {
  1071. //
  1072. // Just copy pointer to value
  1073. //
  1074. pnpFirst->pchName = pszHeader;
  1075. pnpFirst->cchName = cchHeader;
  1076. pnpFirst->pchValue = pszValue;
  1077. pnpFirst->cchValue = cchValue;
  1078. if ( pnpFirst == (NAME_VALUE_PAIR * ) pncFirst->m_rgNVP + pncFirst->m_nPairs )
  1079. {
  1080. pncFirst->m_nPairs++;
  1081. }
  1082. fRet = TRUE;
  1083. }
  1084. DBG_ASSERT( pncFirst->m_nPairs <= MAX_HEADERS_PER_CHUNK);
  1085. return ( fRet );
  1086. } // HTTP_HEADERS::AddEntryToChunks()
  1087. BOOL
  1088. HTTP_HEADERS::ConcatToHolder( IN LPCSTR * ppsz,
  1089. IN LPCSTR pszNew,
  1090. IN DWORD cchNew
  1091. )
  1092. /*++
  1093. Given an internal pointer ppsz of the HTTP_HEADERS object,
  1094. this function appends the new value to the old value present
  1095. using ',' as the concatenation character.
  1096. It automatically allocates room and grows buffers, updates pointers, etc
  1097. if need be.
  1098. --*/
  1099. {
  1100. BOOL fRet = TRUE;
  1101. LPCSTR pszOld = *ppsz;
  1102. DWORD cchOld = pszOld ? strlen( pszOld) : 0;
  1103. DWORD cchReqd = cchOld + cchNew + 2;
  1104. // Find if we have enough space in the inlined buffer
  1105. if ( ( m_cchHeaders + cchReqd < sizeof( m_rcInlinedHeader))
  1106. ) {
  1107. // Aha we are lucky. Make a copy at the end and form concatenated result
  1108. *ppsz = m_rcInlinedHeader + m_cchHeaders;
  1109. m_cchHeaders += cchReqd;
  1110. } else {
  1111. // Clearly we do not have room in the Inlined Header,
  1112. // store the stuff in the aux buffer area.
  1113. // Find if space is sufficient.
  1114. // This will automatically alloc and update pointers
  1115. if ( MakeRoomInBuffer( (m_cchBuffHeaders + cchReqd), &pszNew)
  1116. ){
  1117. pszOld = *ppsz; // get the new pointer (since it could have moved)
  1118. LPSTR pszBuf = (LPSTR ) m_buffHeaders.QueryPtr();
  1119. // we have space at the end of the buffer here. Use this space.
  1120. *ppsz = pszBuf + m_cchBuffHeaders;
  1121. m_cchBuffHeaders += cchReqd;
  1122. } else {
  1123. DBGPRINTF(( DBG_CONTEXT,
  1124. "Unable to create room for %d characters \n",
  1125. m_cchBuffHeaders + cchOld + cchNew + 3));
  1126. return ( FALSE);
  1127. }
  1128. }
  1129. if ( !cchOld )
  1130. {
  1131. CopyMemory( (PVOID) (*ppsz), pszNew, cchNew + 1 );
  1132. }
  1133. else
  1134. {
  1135. // Format the value as := <old> ',' <new>
  1136. CopyMemory( (PVOID ) *ppsz, pszOld, cchOld);
  1137. ((CHAR *) *ppsz)[cchOld] = ','; // concat character
  1138. CopyMemory( (PVOID ) (*ppsz + cchOld + 1), pszNew, cchNew + 1);
  1139. }
  1140. DBG_ASSERT( fRet == TRUE);
  1141. return ( fRet);
  1142. } // HTTP_HEADERS::ConcatToHolder()
  1143. /**************************************************
  1144. * PARSER for the HTTP_HEADERS
  1145. **************************************************/
  1146. inline const CHAR *
  1147. SkipLeadingWhiteSpace( IN const CHAR * pchStart, IN DWORD cchLen)
  1148. {
  1149. const CHAR * pchScan;
  1150. for ( pchScan = pchStart;
  1151. ((pchScan < (pchStart + cchLen)) && isspace( (UCHAR)(*pchScan)));
  1152. pchScan++)
  1153. ;
  1154. return ( pchScan);
  1155. } // SkipLeadingWhiteSpace()
  1156. BOOL
  1157. HTTP_HEADERS::ParseHeaderFirstLine( IN const CHAR * pchFirstLine,
  1158. IN CHAR * pchScan,
  1159. IN DWORD cchFirstLine)
  1160. /*++
  1161. Description:
  1162. Extract the HTTP method, URL, & HTTP-version strings from the first
  1163. line of header block.
  1164. Input: <Method> <URL> HTTP/<Version>
  1165. Use <blank> or <tab> as the delimiter.
  1166. NYI: What about other delimiters?
  1167. We will do only forward scans, because
  1168. 1) the header can be malformed
  1169. 2) HTTP/0.9 requests do not contain the version string
  1170. 3) there may be more than 3 parameters on the first line.
  1171. In all the above cases reverse scans will cause trouble.
  1172. Note that a line termination can be by using \r\n or just \n :(
  1173. Arguments:
  1174. pchFirstLine - pointer to character stream containing the
  1175. first char of the line
  1176. pchScan - points to the end of the first line.
  1177. cchFirstLine - count of characters on the first line
  1178. Returns:
  1179. TRUE on success & FALSE for failure.
  1180. Use GetLastError() to get correct Win32 error code on failure.
  1181. --*/
  1182. {
  1183. LPSTR pszScan2;
  1184. pszScan2 = (LPSTR ) memchr ( pchFirstLine, ' ', cchFirstLine);
  1185. if ( NULL != pszScan2) {
  1186. LPSTR pszScan3;
  1187. *pszScan2++ = '\0'; // replace the blank with a null char
  1188. while ( _HTTP_IS_LINEAR_SPACE( *pszScan2 ) )
  1189. {
  1190. ++pszScan2;
  1191. }
  1192. //
  1193. // pszScan2 now points to the URL sent
  1194. //
  1195. DWORD cReq = DIFF(pchScan - pszScan2);
  1196. pszScan3 = (LPSTR ) memchr( pszScan2, ' ', cReq);
  1197. if ( NULL != pszScan3 ) {
  1198. *pszScan3++ = '\0';
  1199. while ( _HTTP_IS_LINEAR_SPACE( *pszScan3 ) )
  1200. {
  1201. ++pszScan3;
  1202. }
  1203. // pszScan3 should be now pointing to start of version info
  1204. // Note that we are not removing spaces between the version
  1205. // and the line delimiter
  1206. pchScan[(( (pchScan > pszScan3) && (pchScan[-1] == '\r')) ?
  1207. -1: 0)] = '\0';
  1208. } else {
  1209. // only 2 parameters. No version is present.
  1210. // Point pszScan3 to the null-part of pszScan2)
  1211. pszScan3 =
  1212. pchScan + ((pchScan[-1] == '\r') ? -1 : 0);
  1213. *pszScan3 = '\0';
  1214. }
  1215. //
  1216. // We know that we are parsing a new request.
  1217. // So we are not checking for concat during fast-map store here.
  1218. //
  1219. FastMapStore( HM_VER, pszScan3);
  1220. FastMapStore( HM_URL, pszScan2 );
  1221. FastMapStore( HM_MET, pchFirstLine);
  1222. } else {
  1223. IF_DEBUG( ERROR) {
  1224. DBGPRINTF(( DBG_CONTEXT,
  1225. " The scan for fields where they are not separated "
  1226. " with space is intentionally left out!\n"
  1227. ));
  1228. }
  1229. SetLastError( ERROR_INVALID_PARAMETER );
  1230. return FALSE;
  1231. }
  1232. //
  1233. // everything is happy here ... return success
  1234. //
  1235. return ( TRUE);
  1236. } // HTTP_HEADERS::ParseHeaderFirstLine()
  1237. BOOL
  1238. HTTP_HEADERS::ParseInput( IN const CHAR * pchHeaderBlob,
  1239. IN DWORD cchLen,
  1240. OUT DWORD * pcbExtraData
  1241. )
  1242. /*++
  1243. Description:
  1244. This function parses the input string and extracts the HTTP headers
  1245. for storage inside the http header structure. Fast-map headers are stored
  1246. in the dedicated fast-map storage area. Non-standard headers (that are
  1247. not part of) fast-map are stored inside the Name-value chunks.
  1248. The Grammar for incoming HTTP headers is:
  1249. <HTTP-verb> <URL> <HTTP-version>
  1250. {<Header-Name>:<Header-Value> { {\r\n} | {\n}}}*
  1251. where,
  1252. <HTTP-verb> == A+
  1253. <URL> == /[A|D]*
  1254. <HTTP-version> == HTTP/D+.D+
  1255. <Header-Name> == A[A|D]*
  1256. <Header-Value> == [A|D]*
  1257. <CRLF> == \r\n
  1258. A == any ascii character except ' ' '\t' '\n' '\r'
  1259. D == 0-9
  1260. Arguments:
  1261. pchHeaderBlob - pointer to header containing the header blob (+ maybe
  1262. the body of the request).
  1263. cchLen - length of the header block alone.
  1264. pcbExtraData - pointer to a DWORD which on return will contain the
  1265. number of extra characters of data present in the
  1266. blob given.
  1267. Returns:
  1268. TRUE on successfully parsing and splitting the headers apart.
  1269. FALSE if there is any failure.
  1270. --*/
  1271. {
  1272. CHAR * pchScan;
  1273. CHAR * pchRequest;
  1274. DWORD cReq;
  1275. IF_DEBUG( INIT_CLEAN)
  1276. {
  1277. DBGPRINTF(( DBG_CONTEXT, "%08x::ParseInput( %08x:, %d) \n"
  1278. "Input Headers:\n%s\n",
  1279. this, pchHeaderBlob, cchLen, pchHeaderBlob));
  1280. }
  1281. //
  1282. // 1. Skip all the leading spaces and ignore them all.
  1283. // We do not need these fields
  1284. //
  1285. pchScan = (CHAR *) SkipLeadingWhiteSpace( pchHeaderBlob, cchLen);
  1286. cchLen -= DIFF(pchScan - pchHeaderBlob);
  1287. //
  1288. // 2. Make a copy of the incoming header blob so that we can own the
  1289. // input headers and munge it in our own fashion
  1290. // NYI: One can optimize this by selectively copying segments that
  1291. // are worth using (values), but that will be costly due to
  1292. // multiple small CopyMemory() operations.
  1293. //
  1294. if ( cchLen < sizeof( m_rcInlinedHeader)) {
  1295. pchRequest = (CHAR * ) m_rcInlinedHeader;
  1296. m_cchHeaders = cchLen;
  1297. } else {
  1298. if ( !m_buffHeaders.Resize( cchLen + 4, HH_GROW_BY)) {
  1299. return ( FALSE);
  1300. }
  1301. pchRequest = (CHAR * ) m_buffHeaders.QueryPtr();
  1302. m_cchBuffHeaders = cchLen;
  1303. }
  1304. // 2a. copy the header to the buffer
  1305. CopyMemory( (PVOID ) pchRequest, pchScan, cchLen);
  1306. //
  1307. // pchRequest points to the local copy of the request headers
  1308. //
  1309. DBG_ASSERT( (pchRequest == m_rcInlinedHeader) ||
  1310. (pchRequest == m_buffHeaders.QueryPtr())
  1311. );
  1312. //
  1313. // 3. Get the first line and extract the method string
  1314. //
  1315. pchScan = (CHAR * ) memchr( pchRequest, '\n', cchLen);
  1316. if ( pchScan == NULL ) {
  1317. SetLastError( ERROR_INVALID_PARAMETER );
  1318. return FALSE;
  1319. }
  1320. //
  1321. // 4. Extract the Method, URL and Version Number
  1322. //
  1323. if ( !ParseHeaderFirstLine( pchRequest, pchScan, DIFF(pchScan - pchRequest))) {
  1324. return ( FALSE);
  1325. }
  1326. //
  1327. // 5. Extract all other headers
  1328. //
  1329. LPSTR pszHeader = pchScan + 1;
  1330. cReq = DIFF(pchRequest + cchLen - pszHeader);
  1331. LPSTR pchEnd = pszHeader + cReq;
  1332. LPSTR pszEol, pszValue;
  1333. //
  1334. // pszHeader ( and thus pszEnd & pszEol ) are relative to pchRequest.
  1335. // This needs to be preserved in this loop.
  1336. //
  1337. for ( ; (*pszHeader != '\r' || (cReq > 1 && *(pszHeader + 1) != '\n')) &&
  1338. (*pszHeader != '\n') &&
  1339. (pszValue = (LPSTR)memchr( pszHeader, ':', cReq ));
  1340. pszHeader = (pszEol + 1), cReq = DIFF(pchEnd - pszHeader)
  1341. )
  1342. {
  1343. UINT chN = (UINT)(*(PBYTE)++pszValue);
  1344. // *pszValue = '\0';
  1345. int cchHeader = DIFF(pszValue - pszHeader);
  1346. pszEol = (LPSTR ) memchr( pszValue, '\n', cReq - cchHeader);
  1347. if ( NULL == pszEol ) {
  1348. //
  1349. // Aha! we found a completely malformed header block here.
  1350. // Let us return a failure to the caller.
  1351. //
  1352. IF_DEBUG( ERROR) {
  1353. DBGPRINTF(( DBG_CONTEXT,
  1354. " The scan for header-value blocks found a line"
  1355. " not properly terminated with \'\\n\'\n"
  1356. ));
  1357. }
  1358. SetLastError( ERROR_INVALID_PARAMETER );
  1359. return FALSE;
  1360. }
  1361. DBG_ASSERT( NULL != pszEol );
  1362. DWORD cchValue;
  1363. DWORD iField;
  1364. // skip spaces if any.
  1365. if ( _HTTP_IS_LINEAR_SPACE( (CHAR)chN ) )
  1366. {
  1367. while ( _HTTP_IS_LINEAR_SPACE( (CHAR)(*(PBYTE)++pszValue) ) )
  1368. ;
  1369. }
  1370. // Terminate the value string
  1371. if ( (pszEol > pszValue) && (pszEol[-1] == '\r') )
  1372. {
  1373. pszEol[-1] = '\0';
  1374. cchValue = DIFF(pszEol - pszValue - 1);
  1375. }
  1376. else
  1377. {
  1378. pszEol[0] = '\0';
  1379. cchValue = DIFF(pszEol - pszValue);
  1380. }
  1381. IF_DEBUG( INIT_CLEAN )
  1382. {
  1383. DBGPRINTF((DBG_CONTEXT, "\t[%s] = %s\n", pszHeader, pszValue ));
  1384. }
  1385. { // Store the header - inline to reduce cost
  1386. HTTP_FAST_MAP_HEADERS iField;
  1387. BOOL fRet = TRUE;
  1388. // Find and store the header and value
  1389. if ( sm_hhm.FindOrdinal( pszHeader, cchHeader,
  1390. (LPDWORD ) &iField ) )
  1391. {
  1392. if ( FastMapQueryValue(iField) == NULL )
  1393. {
  1394. // Store the item and continue scan for next header
  1395. FastMapStore( iField, pszValue);
  1396. continue;
  1397. }
  1398. else
  1399. {
  1400. // FastMapStoreWithConcat can resize the header
  1401. // buffer. If the headers we're scanning are
  1402. // coming out of the header buffer, we'll need
  1403. // to update pszHeader later, so save the current
  1404. // pointer just in case.
  1405. CHAR *pOldBuff = (CHAR *)m_buffHeaders.QueryPtr();
  1406. fRet = FastMapStoreWithConcat( iField, pszValue,
  1407. cchValue);
  1408. if ( !fRet)
  1409. {
  1410. IF_DEBUG( ERROR)
  1411. {
  1412. DBGPRINTF(( DBG_CONTEXT, "Failed to StoreHeader %s"
  1413. "in fast map with concat\n",
  1414. pszHeader));
  1415. }
  1416. return ( FALSE);
  1417. }
  1418. // See if the buffer has changed. If it has,
  1419. // update the end pointer and the eol pointer. pszHeader
  1420. // gets updated at the end of the loop from pszEol. Be
  1421. // careful if you try and use any other pointers between
  1422. // here and the end of the loop.
  1423. // We update these pointers only if they were already pointing
  1424. // in alloced buffer space ( i.e. not using the m_rcInlinedHeader buffer )
  1425. if (pOldBuff != (CHAR *)m_buffHeaders.QueryPtr() &&
  1426. pOldBuff == pchRequest )
  1427. {
  1428. // Buffer got resized, fix up appropriate pointers.
  1429. pchEnd = (CHAR *)m_buffHeaders.QueryPtr() +
  1430. (pchEnd - pchRequest);
  1431. pszEol = (CHAR *)m_buffHeaders.QueryPtr() +
  1432. (pszEol - pchRequest);
  1433. pchRequest = (CHAR *)m_buffHeaders.QueryPtr();
  1434. }
  1435. }
  1436. }
  1437. else
  1438. {
  1439. // AddEntry to chunks also has resizing buffer issues. See
  1440. // comments above.
  1441. CHAR *pOldBuff = (CHAR *)m_buffHeaders.QueryPtr();
  1442. fRet = AddEntryToChunks( pszHeader, cchHeader, pszValue,
  1443. cchValue);
  1444. if ( !fRet)
  1445. {
  1446. IF_DEBUG( ERROR)
  1447. {
  1448. DBGPRINTF(( DBG_CONTEXT,
  1449. "Failed to StoreHeader %s in chunks\n",
  1450. pszHeader));
  1451. }
  1452. return ( FALSE);
  1453. }
  1454. if (pOldBuff != (CHAR *)m_buffHeaders.QueryPtr() &&
  1455. pOldBuff == pchRequest )
  1456. {
  1457. // Buffer got resized, fix up appropriate pointers.
  1458. pchEnd = (CHAR *)m_buffHeaders.QueryPtr() +
  1459. (pchEnd - pchRequest);
  1460. pszEol = (CHAR *)m_buffHeaders.QueryPtr() +
  1461. (pszEol - pchRequest);
  1462. pchRequest = (CHAR *)m_buffHeaders.QueryPtr();
  1463. }
  1464. }
  1465. }
  1466. } // for()
  1467. if ( (*pszHeader == '\r') || (*pszHeader == '\n') )
  1468. {
  1469. // blank header line - end of the parsing
  1470. while ( cReq )
  1471. {
  1472. --cReq;
  1473. if ( *pszHeader++ == '\n' )
  1474. {
  1475. break;
  1476. }
  1477. }
  1478. }
  1479. *pcbExtraData = DIFF(pchEnd - pszHeader);
  1480. return ( TRUE);
  1481. } // HTTP_HEADERS::ParseInput()
  1482. /************************ End of File ***********************/