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.

2809 lines
69 KiB

  1. #include <wininetp.h>
  2. #include <perfdiag.hxx>
  3. #include "httpp.h"
  4. #include <wininetp.h>
  5. #define DATE_AND_TIME_STRING_BUFFER_LENGTH 128
  6. PRIVATE
  7. BOOL
  8. FMatchList(
  9. LPSTR *lplpList,
  10. DWORD cListLen,
  11. HEADER_STRING *lpHeader,
  12. LPSTR lpBase
  13. )
  14. {
  15. DWORD i;
  16. for (i=0; i < cListLen; ++i) {
  17. if (!lpHeader->Strnicmp(lpBase, lplpList[i], strlen(lplpList[i]))) {
  18. return (TRUE);
  19. }
  20. }
  21. return(FALSE);
  22. }
  23. DWORD
  24. FASTCALL
  25. CalculateHashNoCase(
  26. IN LPCSTR lpszString,
  27. IN DWORD dwStringLength
  28. )
  29. /*++
  30. Routine Description:
  31. Calculate a case-insensitive hash number given a string. Assumes input is
  32. 7-bit ASCII
  33. Arguments:
  34. lpszString - string to hash
  35. dwStringLength - length of lpszString, or -1 if we need to calculate
  36. Return Value:
  37. DWORD - a generated hash value
  38. --*/
  39. {
  40. DWORD dwHash = HEADER_HASH_SEED;
  41. while (dwStringLength != 0) {
  42. CHAR ch = *lpszString;
  43. if ((ch >= 'A') && (ch <= 'Z')) {
  44. ch = MAKE_LOWER(ch);
  45. }
  46. dwHash += (DWORD)(dwHash << 5) + ch; /*+ *pszName++;*/
  47. ++lpszString;
  48. --dwStringLength;
  49. }
  50. return dwHash;
  51. }
  52. //
  53. // methods
  54. //
  55. DWORD
  56. HTTP_HEADERS::AllocateHeaders(
  57. IN DWORD dwNumberOfHeaders
  58. )
  59. /*++
  60. Routine Description:
  61. Allocates or grows the array of header pointers (HEADER_STRING objects)
  62. Arguments:
  63. dwNumberOfHeaders - number of additional header slots to create
  64. Return Value:
  65. DWORD
  66. Success - ERROR_SUCCESS
  67. Failure - ERROR_NOT_ENOUGH_MEMORY
  68. --*/
  69. {
  70. DEBUG_ENTER((DBG_HTTP,
  71. Dword,
  72. "AllocateHeaders",
  73. "%d",
  74. dwNumberOfHeaders
  75. ));
  76. PERF_ENTER(AllocateHeaders);
  77. //
  78. // we really need to be able to realloc an array of HEADER_STRING objects
  79. // (see below)
  80. //
  81. DWORD error;
  82. DWORD slots = _TotalSlots;
  83. if ( (_TotalSlots + dwNumberOfHeaders) > (INVALID_HEADER_INDEX-1))
  84. {
  85. INET_ASSERT(FALSE);
  86. _NextOpenSlot = 0;
  87. _TotalSlots = 0;
  88. _FreeSlots = 0;
  89. error = ERROR_NOT_ENOUGH_MEMORY;
  90. goto quit;
  91. }
  92. _lpHeaders = (HEADER_STRING *)ResizeBuffer((HLOCAL)_lpHeaders,
  93. (_TotalSlots + dwNumberOfHeaders)
  94. * sizeof(HEADER_STRING),
  95. FALSE // not moveable
  96. );
  97. if (_lpHeaders != NULL) {
  98. _NextOpenSlot = _TotalSlots;
  99. _TotalSlots += dwNumberOfHeaders;
  100. _FreeSlots += dwNumberOfHeaders;
  101. //
  102. // this is slightly ugly, but it seems there's no easy C++ way to
  103. // do this - we need to be able to realloc() an array of objects
  104. // created by new(), but so far, it can't be done
  105. //
  106. for (; slots < _TotalSlots; ++slots) {
  107. _lpHeaders[slots].Clear();
  108. }
  109. error = ERROR_SUCCESS;
  110. } else {
  111. INET_ASSERT(FALSE);
  112. _NextOpenSlot = 0;
  113. _TotalSlots = 0;
  114. _FreeSlots = 0;
  115. error = ERROR_NOT_ENOUGH_MEMORY;
  116. }
  117. quit:
  118. INET_ASSERT(_FreeSlots <= _TotalSlots);
  119. PERF_LEAVE(AllocateHeaders);
  120. DEBUG_LEAVE(error);
  121. return error;
  122. }
  123. VOID
  124. HTTP_HEADERS::FreeHeaders(
  125. VOID
  126. )
  127. /*++
  128. Routine Description:
  129. Free the headers strings and the headers array
  130. Arguments:
  131. None.
  132. Return Value:
  133. None.
  134. --*/
  135. {
  136. DEBUG_ENTER((DBG_HTTP,
  137. None,
  138. "FreeHeaders",
  139. NULL
  140. ));
  141. if (!LockHeaders())
  142. {
  143. goto quit;
  144. }
  145. //
  146. // free up each individual entry (free string buffers)
  147. //
  148. for (DWORD i = 0; i < _TotalSlots; ++i) {
  149. _lpHeaders[i] = (LPSTR)NULL;
  150. }
  151. //
  152. // followed by the array itself
  153. //
  154. if (_lpHeaders) {
  155. _lpHeaders = (HEADER_STRING *)FREE_MEMORY((HLOCAL)_lpHeaders);
  156. INET_ASSERT(_lpHeaders == NULL);
  157. }
  158. _TotalSlots = 0;
  159. _FreeSlots = 0;
  160. _HeadersLength = 0;
  161. _lpszVerb = NULL;
  162. _dwVerbLength = 0;
  163. _lpszObjectName = NULL;
  164. _dwObjectNameLength = 0;
  165. _lpszVersion = NULL;
  166. _dwVersionLength = 0;
  167. UnlockHeaders();
  168. quit:
  169. DEBUG_LEAVE(0);
  170. }
  171. DWORD
  172. HTTP_HEADERS::CopyHeaders(
  173. IN OUT LPSTR * lpBuffer,
  174. IN LPSTR lpszObjectName,
  175. IN DWORD dwObjectNameLength
  176. )
  177. /*++
  178. Routine Description:
  179. Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
  180. This method is called to convert the request headers list to a buffer that
  181. we can send to the server
  182. N.B. This function MUST be called with the headers already locked
  183. Arguments:
  184. lpBuffer - pointer to pointer to buffer where headers are
  185. written. We update the pointer
  186. lpszObjectName - optional object name
  187. dwObjectNameLength - optional object name length
  188. Return Value:
  189. DWORD
  190. Success - ERROR_SUCCESS
  191. Failure - ERROR_NOT_ENOUGH_MEMORY
  192. Ran out of memory while trying to synchronize src data access
  193. --*/
  194. {
  195. DEBUG_ENTER((DBG_HTTP,
  196. None,
  197. "CopyHeaders",
  198. "%#x, %#x [%q], %d",
  199. lpBuffer,
  200. lpszObjectName,
  201. lpszObjectName,
  202. dwObjectNameLength
  203. ));
  204. DWORD dwError = ERROR_SUCCESS;
  205. if (!LockHeaders())
  206. {
  207. dwError = ERROR_NOT_ENOUGH_MEMORY;
  208. goto quit;
  209. }
  210. DWORD i = 0;
  211. if (lpszObjectName != NULL) {
  212. memcpy(*lpBuffer, _lpszVerb, _dwVerbLength);
  213. *lpBuffer += _dwVerbLength;
  214. *(*lpBuffer)++ = ' ';
  215. memcpy(*lpBuffer, lpszObjectName, dwObjectNameLength);
  216. *lpBuffer += dwObjectNameLength;
  217. *(*lpBuffer)++ = ' ';
  218. memcpy(*lpBuffer, _lpszVersion, _dwVersionLength);
  219. *lpBuffer += _dwVersionLength;
  220. *(*lpBuffer)++ = '\r';
  221. *(*lpBuffer)++ = '\n';
  222. i = 1;
  223. }
  224. for (; i < _TotalSlots; ++i) {
  225. if (_lpHeaders[i].HaveString()) {
  226. _lpHeaders[i].CopyTo(*lpBuffer);
  227. *lpBuffer += _lpHeaders[i].StringLength();
  228. *(*lpBuffer)++ = '\r';
  229. *(*lpBuffer)++ = '\n';
  230. }
  231. }
  232. UnlockHeaders();
  233. quit:
  234. DEBUG_LEAVE(dwError);
  235. return dwError;
  236. }
  237. HEADER_STRING *
  238. FASTCALL
  239. HTTP_HEADERS::FindFreeSlot(
  240. DWORD* piSlot
  241. )
  242. /*++
  243. Routine Description:
  244. Finds the next free slot in the headers list, or adds some new slots
  245. N.B. This function MUST be called with the headers already locked
  246. Arguments:
  247. piSlot: returns index of slot found
  248. Return Value:
  249. HEADER_STRING * - pointer to next free slot
  250. --*/
  251. {
  252. DEBUG_ENTER((DBG_HTTP,
  253. Pointer,
  254. "FindFreeSlot",
  255. NULL
  256. ));
  257. PERF_ENTER(FindFreeSlot);
  258. DWORD i;
  259. DWORD error;
  260. HEADER_STRING * header = NULL;
  261. //
  262. // if there are no free slots, allocate some more
  263. //
  264. if (_FreeSlots == 0) {
  265. i = _TotalSlots;
  266. error = AllocateHeaders(HEADERS_INCREMENT);
  267. } else {
  268. i = 0;
  269. error = ERROR_SUCCESS;
  270. if (!_lpHeaders[_NextOpenSlot].HaveString())
  271. {
  272. --_FreeSlots;
  273. header = &_lpHeaders[_NextOpenSlot];
  274. *piSlot = _NextOpenSlot;
  275. _NextOpenSlot = (_NextOpenSlot == (_TotalSlots-1)) ? (_TotalSlots-1) : _NextOpenSlot++;
  276. goto quit;
  277. }
  278. }
  279. if (error == ERROR_SUCCESS) {
  280. for (; i < _TotalSlots; ++i) {
  281. if (!_lpHeaders[i].HaveString()) {
  282. --_FreeSlots;
  283. header = &_lpHeaders[i];
  284. *piSlot = i;
  285. _NextOpenSlot = (i == (_TotalSlots-1)) ? (_TotalSlots-1) : (i+1);
  286. break;
  287. }
  288. }
  289. if (header == NULL) {
  290. //
  291. // we would have just allocated extra slots if we didn't have
  292. // any, so we shouldn't be here
  293. //
  294. INET_ASSERT(FALSE);
  295. error = ERROR_WINHTTP_INTERNAL_ERROR;
  296. }
  297. }
  298. quit:
  299. _Error = error;
  300. PERF_LEAVE(FindFreeSlot);
  301. DEBUG_LEAVE(header);
  302. return header;
  303. }
  304. VOID
  305. HTTP_HEADERS::ShrinkHeader(
  306. IN LPBYTE pbBase,
  307. IN DWORD iSlot,
  308. IN DWORD dwOldQueryIndex,
  309. IN DWORD dwNewQueryIndex,
  310. IN DWORD cbNewSize
  311. )
  312. /*++
  313. Routine Description:
  314. Low level function that does a surgical replace of one header with another.
  315. This code updates internal structures such as bKnownHeaders and the stored
  316. hash value for the new Header.
  317. N.B. This function MUST be called with the headers already locked
  318. Arguments:
  319. Return Value:
  320. None.
  321. --*/
  322. {
  323. HEADER_STRING* pHeader = _lpHeaders + iSlot;
  324. INET_ASSERT(_bKnownHeaders[dwOldQueryIndex] == (BYTE) iSlot ||
  325. dwNewQueryIndex == dwOldQueryIndex );
  326. //
  327. // Swap in the new header. Update Length, Hash, and its cached position
  328. // in the known header array.
  329. //
  330. _bKnownHeaders[dwOldQueryIndex] = INVALID_HEADER_INDEX;
  331. _bKnownHeaders[dwNewQueryIndex] = (BYTE) iSlot;
  332. pHeader->SetLength (cbNewSize);
  333. pHeader->SetHash (GlobalKnownHeaders[dwNewQueryIndex].HashVal);
  334. }
  335. DWORD
  336. inline
  337. HTTP_HEADERS::SlowFind(
  338. IN LPSTR lpBase,
  339. IN LPCSTR lpszHeaderName,
  340. IN DWORD dwHeaderNameLength,
  341. IN DWORD dwIndex,
  342. IN DWORD dwHash,
  343. OUT DWORD *lpdwQueryIndex,
  344. OUT BYTE **lplpbPrevIndex
  345. )
  346. /*++
  347. Routine Description:
  348. Finds the next occurance of lpszHeaderName in the header array, uses
  349. a cached table of well known headers to accerlate the search if the
  350. string is a known header.
  351. N.B. This function MUST be called with the headers already locked
  352. Arguments:
  353. Return Value:
  354. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  355. --*/
  356. {
  357. //
  358. // Now see if this is a known header passed in as a string,
  359. // If it is, we save ourselves the loop, and just map it right in to a known header
  360. //
  361. DWORD dwKnownQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  362. *lpdwQueryIndex = INVALID_HEADER_SLOT;
  363. if ( dwKnownQueryIndex != 0 )
  364. {
  365. dwKnownQueryIndex--;
  366. if ( ((int)dwHeaderNameLength >= GlobalKnownHeaders[dwKnownQueryIndex].Length) &&
  367. strnicmp(lpszHeaderName,
  368. GlobalKnownHeaders[dwKnownQueryIndex].Text,
  369. GlobalKnownHeaders[dwKnownQueryIndex].Length) == 0)
  370. {
  371. *lpdwQueryIndex = dwKnownQueryIndex;
  372. INET_ASSERT((int)(dwHeaderNameLength) == GlobalKnownHeaders[dwKnownQueryIndex].Length);
  373. if ( lplpbPrevIndex )
  374. {
  375. return FastNukeFind(
  376. dwKnownQueryIndex,
  377. dwIndex,
  378. lplpbPrevIndex
  379. );
  380. }
  381. else
  382. {
  383. return FastFind(
  384. dwKnownQueryIndex,
  385. dwIndex
  386. );
  387. }
  388. }
  389. }
  390. //
  391. // Otherwise we painfully enumerate the whole array of headers
  392. //
  393. for (DWORD i = 0; i < _TotalSlots; ++i)
  394. {
  395. HEADER_STRING * pString;
  396. pString = &_lpHeaders[i];
  397. if (!pString->HaveString()) {
  398. continue;
  399. }
  400. if (pString->HashStrnicmp(lpBase,
  401. lpszHeaderName,
  402. dwHeaderNameLength,
  403. dwHash) == 0)
  404. {
  405. //
  406. // if we haven't reached the required index yet, continue
  407. //
  408. if (dwIndex != 0) {
  409. --dwIndex;
  410. continue;
  411. }
  412. return i; // found index/slot
  413. }
  414. }
  415. return INVALID_HEADER_SLOT; // not found
  416. }
  417. DWORD
  418. inline
  419. HTTP_HEADERS::FastFind(
  420. IN DWORD dwQueryIndex,
  421. IN DWORD dwIndex
  422. )
  423. /*++
  424. Routine Description:
  425. Finds the next occurance of a known header string in the lpHeaders array.
  426. Since this is a known string, an index is used to refer to it.
  427. A cached table of well known headers is used to accerlate the search.
  428. N.B. This function MUST be called with the headers already locked
  429. Arguments:
  430. Return Value:
  431. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  432. --*/
  433. {
  434. DWORD dwSlot;
  435. dwSlot = _bKnownHeaders[dwQueryIndex];
  436. while ( (dwIndex > 0) && (dwSlot < INVALID_HEADER_INDEX) )
  437. {
  438. HEADER_STRING * pString;
  439. pString = &_lpHeaders[dwSlot];
  440. dwSlot = pString->GetNextKnownIndex();
  441. dwIndex--;
  442. }
  443. if ( dwSlot >= INVALID_HEADER_INDEX)
  444. {
  445. return INVALID_HEADER_SLOT;
  446. }
  447. return dwSlot; // found it.
  448. }
  449. DWORD
  450. inline
  451. HTTP_HEADERS::FastNukeFind(
  452. IN DWORD dwQueryIndex,
  453. IN DWORD dwIndex,
  454. OUT BYTE **lplpbPrevIndex
  455. )
  456. /*++
  457. Routine Description:
  458. Finds the next occurance of a known header string in the lpHeaders array.
  459. Since this is a known string, an index is used to refer to it.
  460. A cached table of well known headers is used to accerlate the search.
  461. Also provides a ptr to ptr to the slot which directs us to the one found.
  462. This is needed for deletion purposes.
  463. N.B. This function MUST be called with the headers already locked
  464. Arguments:
  465. Return Value:
  466. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  467. --*/
  468. {
  469. BYTE *lpbSlot;
  470. *lplpbPrevIndex = lpbSlot = &_bKnownHeaders[dwQueryIndex];
  471. dwIndex++;
  472. while ( (dwIndex > 0) && (*lpbSlot < INVALID_HEADER_INDEX) )
  473. {
  474. HEADER_STRING * pString;
  475. pString = &_lpHeaders[*lpbSlot];
  476. *lplpbPrevIndex = lpbSlot;
  477. lpbSlot = pString->GetNextKnownIndexPtr();
  478. dwIndex--;
  479. }
  480. if ( **lplpbPrevIndex >= INVALID_HEADER_INDEX ||
  481. dwIndex > 0 )
  482. {
  483. return INVALID_HEADER_SLOT;
  484. }
  485. return ((DWORD) **lplpbPrevIndex); // found it.
  486. }
  487. VOID
  488. HTTP_HEADERS::RemoveAllByIndex(
  489. IN DWORD dwQueryIndex
  490. )
  491. /*++
  492. Routine Description:
  493. Removes all Known Headers found in the header array.
  494. N.B. This function MUST be called with the headers already locked
  495. Arguments:
  496. dwQueryIndex - index to known header string to remove from array.
  497. Return Value:
  498. None.
  499. --*/
  500. {
  501. BYTE bSlot;
  502. BYTE bPrevSlot;
  503. bSlot = bPrevSlot = _bKnownHeaders[dwQueryIndex];
  504. while (bSlot < INVALID_HEADER_INDEX)
  505. {
  506. HEADER_STRING * pString;
  507. bPrevSlot = bSlot;
  508. pString = &_lpHeaders[bSlot];
  509. bSlot = (BYTE) pString->GetNextKnownIndex();
  510. RemoveHeader(bPrevSlot, dwQueryIndex, &_bKnownHeaders[dwQueryIndex]);
  511. }
  512. _bKnownHeaders[dwQueryIndex] = INVALID_HEADER_INDEX;
  513. return;
  514. }
  515. BOOL
  516. inline
  517. HTTP_HEADERS::HeaderMatch(
  518. IN DWORD dwHash,
  519. IN LPSTR lpszHeaderName,
  520. IN DWORD dwHeaderNameLength,
  521. OUT DWORD *lpdwQueryIndex
  522. )
  523. /*++
  524. Routine Description:
  525. Looks up a Known HTTP header string using its Hash value and
  526. string contained the name of the header.
  527. Arguments:
  528. dwHash - Hash value of header name string
  529. lpszHeaderName - name of header we are matching
  530. dwHeaderNameLength - length of header name string
  531. lpdwQueryIndex - If found, this is the HTTP_QUERY_* based index to the header.
  532. Return Value:
  533. BOOL
  534. Success - The string and hash matched againsted a known header
  535. Failure - There is no known header for that hash & string pair.
  536. --*/
  537. {
  538. *lpdwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  539. if ( *lpdwQueryIndex != 0 )
  540. {
  541. (*lpdwQueryIndex)--;
  542. if ( ((int)dwHeaderNameLength == GlobalKnownHeaders[*lpdwQueryIndex].Length) &&
  543. strnicmp(lpszHeaderName,
  544. GlobalKnownHeaders[*lpdwQueryIndex].Text,
  545. GlobalKnownHeaders[*lpdwQueryIndex].Length) == 0)
  546. {
  547. return TRUE;
  548. }
  549. }
  550. return FALSE;
  551. }
  552. BYTE
  553. inline
  554. HTTP_HEADERS::FastAdd(
  555. IN DWORD dwQueryIndex,
  556. IN DWORD dwSlot
  557. )
  558. /*++
  559. Routine Description:
  560. Rapidly adds a known string to the header array, this function
  561. is used to matain coherency of the _bKnownHeaders which
  562. contained indexed offsets into the header array for known headers.
  563. Note that this function is used instead of latter listed below
  564. in order to maintain proper order in headers received.
  565. N.B. This function MUST be called with the headers already locked
  566. Arguments:
  567. dwQueryIndex - index to known header string to remove from array.
  568. dwSlot - Slot in which this header is being added.
  569. Return Value:
  570. None.
  571. --*/
  572. {
  573. BYTE *lpbSlot;
  574. lpbSlot = &_bKnownHeaders[dwQueryIndex];
  575. while ( (*lpbSlot < INVALID_HEADER_INDEX) )
  576. {
  577. HEADER_STRING * pString;
  578. pString = &_lpHeaders[*lpbSlot];
  579. lpbSlot = pString->GetNextKnownIndexPtr();
  580. }
  581. INET_ASSERT(*lpbSlot == INVALID_HEADER_INDEX);
  582. *lpbSlot = (BYTE) dwSlot;
  583. return INVALID_HEADER_INDEX;
  584. }
  585. //BYTE
  586. //inline
  587. //HTTP_HEADERS::FastAdd(
  588. // IN DWORD dwQueryIndex,
  589. // IN DWORD dwSlot
  590. // )
  591. //{
  592. // BYTE bOldSlot;
  593. //
  594. // bOldSlot = _bKnownHeaders[dwQueryIndex];
  595. // _bKnownHeaders[dwQueryIndex] = (BYTE) dwSlot;
  596. //
  597. // return bOldSlot;
  598. //}
  599. DWORD
  600. HTTP_HEADERS::AddHeader(
  601. IN LPSTR lpszHeaderName,
  602. IN DWORD dwHeaderNameLength,
  603. IN LPSTR lpszHeaderValue,
  604. IN DWORD dwHeaderValueLength,
  605. IN DWORD dwIndex,
  606. IN DWORD dwFlags
  607. )
  608. /*++
  609. Routine Description:
  610. Adds a single header to the array of headers, given the header name and
  611. value. Called via HttpOpenRequest()
  612. Arguments:
  613. lpszHeaderName - pointer to name of header to add, e.g. "Accept:"
  614. dwHeaderNameLength - length of the header name
  615. lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
  616. dwHeaderValueLength - length of the header value
  617. dwIndex - if coalescing headers, index of header to update
  618. dwFlags - flags controlling function:
  619. COALESCE_HEADER_WITH_COMMA
  620. COALESCE_HEADER_WITH_SEMICOLON
  621. - headers of the same name can be combined
  622. CLEAN_HEADER
  623. - header is supplied by user, so we must ensure
  624. it has correct format
  625. Return Value:
  626. DWORD
  627. Success - ERROR_SUCCESS
  628. Failure - ERROR_NOT_ENOUGH_MEMORY
  629. Ran out of memory allocating string
  630. ERROR_INVALID_PARAMETER
  631. The header value was bad
  632. --*/
  633. {
  634. DEBUG_ENTER((DBG_HTTP,
  635. Dword,
  636. "AddHeader",
  637. "%.*q, %d, %.*q, %d, %d, %#x",
  638. min(dwHeaderNameLength + 1, 80),
  639. lpszHeaderName,
  640. dwHeaderNameLength,
  641. min(dwHeaderValueLength + 1, 80),
  642. lpszHeaderValue,
  643. dwHeaderValueLength,
  644. dwIndex,
  645. dwFlags
  646. ));
  647. PERF_ENTER(AddHeader);
  648. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  649. if (!LockHeaders())
  650. {
  651. error = ERROR_NOT_ENOUGH_MEMORY;
  652. goto quit;
  653. }
  654. INET_ASSERT(lpszHeaderName != NULL);
  655. INET_ASSERT(*lpszHeaderName != '\0');
  656. INET_ASSERT(dwHeaderNameLength != 0);
  657. INET_ASSERT(lpszHeaderValue != NULL);
  658. INET_ASSERT(*lpszHeaderValue != '\0');
  659. INET_ASSERT(dwHeaderValueLength != 0);
  660. INET_ASSERT(_FreeSlots <= _TotalSlots);
  661. //
  662. // we may have been handed a header with a trailing colon. We don't care
  663. // for such nasty imagery
  664. //
  665. if (lpszHeaderName[dwHeaderNameLength - 1] == ':') {
  666. --dwHeaderNameLength;
  667. }
  668. DWORD dwQueryIndex;
  669. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  670. DWORD i = 0;
  671. //
  672. // if we are coalescing headers then find a header with the same name
  673. //
  674. if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
  675. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
  676. {
  677. DWORD dwSlot;
  678. dwSlot = SlowFind(
  679. NULL,
  680. lpszHeaderName,
  681. dwHeaderNameLength,
  682. dwIndex,
  683. dwHash,
  684. &dwQueryIndex,
  685. NULL
  686. );
  687. if (dwSlot != ((DWORD) -1))
  688. {
  689. HEADER_STRING * pString;
  690. pString = &_lpHeaders[dwSlot];
  691. //
  692. // found what we are looking for. Coalesce it
  693. //
  694. pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
  695. pString->Strncat(
  696. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
  697. "; " :
  698. ", ",
  699. 2);
  700. pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
  701. _HeadersLength += 2 + dwHeaderValueLength;
  702. error = ERROR_SUCCESS;
  703. }
  704. }
  705. else
  706. {
  707. //
  708. // Check to verify that the header we're adding is a known header,
  709. // If its a known header we use dwQueryIndex to update the known header array
  710. // otherwise, IF ITS NOT, we make sure to set dwQueryIndex to INVALID_...
  711. //
  712. if (! HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwQueryIndex) )
  713. {
  714. dwQueryIndex = INVALID_HEADER_SLOT;
  715. }
  716. /*
  717. // Perhaps this more efficent ???
  718. dwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  719. if ( dwQueryIndex != 0 )
  720. {
  721. dwQueryIndex--;
  722. if ( ((int)dwHeaderNameLength < GlobalKnownHeaders[dwQueryIndex].Length) ||
  723. strnicmp(lpszHeaderName,
  724. GlobalKnownHeaders[dwQueryIndex].Text,
  725. GlobalKnownHeaders[dwQueryIndex].Length) != 0)
  726. {
  727. dwQueryIndex = INVALID_HEADER_SLOT;
  728. }
  729. }
  730. else
  731. {
  732. dwQueryIndex = INVALID_HEADER_SLOT;
  733. }
  734. */
  735. }
  736. //
  737. // if we didn't find the header value or we are not coalescing then add the
  738. // header
  739. //
  740. if (error == ERROR_HTTP_HEADER_NOT_FOUND)
  741. {
  742. //
  743. // find the next slot for this header
  744. //
  745. HEADER_STRING * freeHeader;
  746. DWORD iSlot;
  747. freeHeader = FindFreeSlot(&iSlot);
  748. if (freeHeader == NULL) {
  749. error = GetError();
  750. INET_ASSERT(error != ERROR_SUCCESS);
  751. goto Cleanup;
  752. }
  753. freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
  754. dwHeaderNameLength,
  755. dwHeaderNameLength
  756. + sizeof(": ") - 1
  757. + dwHeaderValueLength
  758. + 1 // for extra NULL terminator
  759. );
  760. if (freeHeader->IsError()) {
  761. error = ::GetLastError();
  762. INET_ASSERT(error != ERROR_SUCCESS);
  763. goto Cleanup;
  764. }
  765. freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
  766. freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
  767. _HeadersLength += dwHeaderNameLength
  768. + (sizeof(": ") - 1)
  769. + dwHeaderValueLength
  770. + (sizeof("\r\n") - 1)
  771. ;
  772. freeHeader->SetHash(dwHash);
  773. if ( dwQueryIndex != INVALID_HEADER_SLOT )
  774. {
  775. freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
  776. }
  777. error = ERROR_SUCCESS;
  778. }
  779. Cleanup:
  780. UnlockHeaders();
  781. quit:
  782. PERF_LEAVE(AddHeader);
  783. DEBUG_LEAVE(error);
  784. return error;
  785. }
  786. DWORD
  787. HTTP_HEADERS::AddHeader(
  788. IN DWORD dwQueryIndex,
  789. IN LPSTR lpszHeaderValue,
  790. IN DWORD dwHeaderValueLength,
  791. IN DWORD dwIndex,
  792. IN DWORD dwFlags
  793. )
  794. /*++
  795. Routine Description:
  796. Adds a single header to the array of headers, given the header name and
  797. value. Called via HttpOpenRequest()
  798. Arguments:
  799. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  800. lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
  801. dwHeaderValueLength - length of the header value
  802. dwIndex - if coalescing headers, index of header to update
  803. dwFlags - flags controlling function:
  804. COALESCE_HEADER_WITH_COMMA
  805. COALESCE_HEADER_WITH_SEMICOLON
  806. - headers of the same name can be combined
  807. CLEAN_HEADER
  808. - header is supplied by user, so we must ensure
  809. it has correct format
  810. Return Value:
  811. DWORD
  812. Success - ERROR_SUCCESS
  813. Failure - ERROR_NOT_ENOUGH_MEMORY
  814. Ran out of memory allocating string
  815. ERROR_INVALID_PARAMETER
  816. The header value was bad
  817. --*/
  818. {
  819. DEBUG_ENTER((DBG_HTTP,
  820. Dword,
  821. "AddHeader",
  822. "%q, %u, %.*q, %d, %d, %#x",
  823. GlobalKnownHeaders[dwQueryIndex].Text,
  824. dwQueryIndex,
  825. min(dwHeaderValueLength + 1, 80),
  826. lpszHeaderValue,
  827. dwHeaderValueLength,
  828. dwIndex,
  829. dwFlags
  830. ));
  831. PERF_ENTER(AddHeader);
  832. INET_ASSERT(dwQueryIndex <= HTTP_QUERY_MAX);
  833. INET_ASSERT(lpszHeaderValue != NULL);
  834. INET_ASSERT(*lpszHeaderValue != '\0');
  835. INET_ASSERT(dwHeaderValueLength != 0);
  836. INET_ASSERT(_FreeSlots <= _TotalSlots);
  837. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  838. DWORD i = 0;
  839. LPSTR lpszHeaderName;
  840. DWORD dwHeaderNameLength;
  841. DWORD dwHash;
  842. dwHash = GlobalKnownHeaders[dwQueryIndex].HashVal;
  843. lpszHeaderName = GlobalKnownHeaders[dwQueryIndex].Text;
  844. dwHeaderNameLength = GlobalKnownHeaders[dwQueryIndex].Length;
  845. //
  846. // if we are coalescing headers then find a header with the same name
  847. //
  848. if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
  849. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
  850. {
  851. DWORD dwSlot;
  852. dwSlot = FastFind(
  853. dwQueryIndex,
  854. dwIndex
  855. );
  856. if (dwSlot != INVALID_HEADER_SLOT)
  857. {
  858. HEADER_STRING * pString;
  859. pString = &_lpHeaders[dwSlot];
  860. //
  861. // found what we are looking for. Coalesce it
  862. //
  863. pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
  864. pString->Strncat(
  865. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
  866. "; " :
  867. ", ",
  868. 2);
  869. pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
  870. _HeadersLength += 2 + dwHeaderValueLength;
  871. error = ERROR_SUCCESS;
  872. }
  873. }
  874. //
  875. // if we didn't find the header value or we are not coalescing then add the
  876. // header
  877. //
  878. if (error == ERROR_HTTP_HEADER_NOT_FOUND)
  879. {
  880. //
  881. // find the next slot for this header
  882. //
  883. HEADER_STRING * freeHeader;
  884. DWORD iSlot;
  885. freeHeader = FindFreeSlot(&iSlot);
  886. if (freeHeader == NULL) {
  887. error = GetError();
  888. INET_ASSERT(error != ERROR_SUCCESS);
  889. goto quit;
  890. }
  891. freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
  892. dwHeaderNameLength,
  893. dwHeaderNameLength
  894. + sizeof(": ") - 1
  895. + dwHeaderValueLength
  896. + 1 // for extra NULL terminator
  897. );
  898. if (freeHeader->IsError()) {
  899. error = ::GetLastError();
  900. INET_ASSERT(error != ERROR_SUCCESS);
  901. goto quit;
  902. }
  903. freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
  904. freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
  905. _HeadersLength += dwHeaderNameLength
  906. + (sizeof(": ") - 1)
  907. + dwHeaderValueLength
  908. + (sizeof("\r\n") - 1)
  909. ;
  910. freeHeader->SetHash(dwHash);
  911. freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
  912. error = ERROR_SUCCESS;
  913. }
  914. quit:
  915. PERF_LEAVE(AddHeader);
  916. DEBUG_LEAVE(error);
  917. return error;
  918. }
  919. DWORD
  920. HTTP_HEADERS::ReplaceHeader(
  921. IN LPSTR lpszHeaderName,
  922. IN DWORD dwHeaderNameLength,
  923. IN LPSTR lpszHeaderValue,
  924. IN DWORD dwHeaderValueLength,
  925. IN DWORD dwIndex,
  926. IN DWORD dwFlags
  927. )
  928. /*++
  929. Routine Description:
  930. Replaces a HTTP (request) header. The header can be replaced with a NULL
  931. value, meaning that the header is removed
  932. Arguments:
  933. lpszHeaderName - pointer to the header name
  934. dwHeaderNameLength - length of the header name
  935. lpszHeaderValue - pointer to the header value
  936. dwHeaderValueLength - length of the header value
  937. dwIndex - index of header to replace
  938. dwFlags - flags controlling function. Allowed flags are:
  939. COALESCE_HEADER_WITH_COMMA
  940. COALESCE_HEADER_WITH_SEMICOLON
  941. - headers of the same name can be combined
  942. ADD_HEADER
  943. - if the header-name is not found and there is
  944. a valid header-value, then the header is added
  945. ADD_HEADER_IF_NEW
  946. - if the header-name exists then we return an
  947. error, else we add the header-value
  948. Return Value:
  949. DWORD
  950. Success - ERROR_SUCCESS
  951. Failure - ERROR_HTTP_HEADER_NOT_FOUND
  952. The requested header wasn't found
  953. ERROR_HTTP_HEADER_ALREADY_EXISTS
  954. The header already exists, and was not added or replaced
  955. ERROR_NOT_ENOUGH_MEMORY
  956. Ran out of memory trying to acquire lock
  957. --*/
  958. {
  959. DEBUG_ENTER((DBG_HTTP,
  960. Dword,
  961. "ReplaceHeader",
  962. "%.*q, %d, %.*q, %d, %d, %#x",
  963. min(dwHeaderNameLength + 1, 80),
  964. lpszHeaderName,
  965. dwHeaderNameLength,
  966. min(dwHeaderValueLength + 1, 80),
  967. lpszHeaderValue,
  968. dwHeaderValueLength,
  969. dwIndex,
  970. dwFlags
  971. ));
  972. PERF_ENTER(ReplaceHeader);
  973. INET_ASSERT(lpszHeaderName != NULL);
  974. INET_ASSERT(dwHeaderNameLength != 0);
  975. INET_ASSERT(lpszHeaderName[dwHeaderNameLength - 1] != ':');
  976. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  977. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  978. DWORD dwSlot;
  979. DWORD dwQueryIndex;
  980. BYTE *pbPrevByte;
  981. if (!LockHeaders())
  982. {
  983. error = ERROR_NOT_ENOUGH_MEMORY;
  984. goto quit;
  985. }
  986. dwSlot = SlowFind(
  987. NULL,
  988. lpszHeaderName,
  989. dwHeaderNameLength,
  990. dwIndex,
  991. dwHash,
  992. &dwQueryIndex,
  993. &pbPrevByte
  994. );
  995. if ( dwSlot != ((DWORD) -1))
  996. {
  997. //
  998. // if ADD_HEADER_IF_NEW is set, then we already have the header
  999. //
  1000. if (dwFlags & ADD_HEADER_IF_NEW) {
  1001. error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
  1002. goto Cleanup;
  1003. }
  1004. //
  1005. // for both replace and remove operations, we are going to remove
  1006. // the current header
  1007. //
  1008. RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
  1009. //
  1010. // if replacing then add the new header value
  1011. //
  1012. if (dwHeaderValueLength != 0)
  1013. {
  1014. if ( dwQueryIndex != ((DWORD) -1) )
  1015. {
  1016. error = AddHeader(dwQueryIndex,
  1017. lpszHeaderValue,
  1018. dwHeaderValueLength,
  1019. 0,
  1020. dwFlags
  1021. );
  1022. }
  1023. else
  1024. {
  1025. error = AddHeader(lpszHeaderName,
  1026. dwHeaderNameLength,
  1027. lpszHeaderValue,
  1028. dwHeaderValueLength,
  1029. 0,
  1030. dwFlags
  1031. );
  1032. }
  1033. } else {
  1034. error = ERROR_SUCCESS;
  1035. }
  1036. }
  1037. //
  1038. // if we didn't find the header but ADD_HEADER is set then we simply add it
  1039. // but only if the value length is not zero
  1040. //
  1041. if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
  1042. && (dwHeaderValueLength != 0)
  1043. && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
  1044. {
  1045. if ( dwQueryIndex != ((DWORD) -1) )
  1046. {
  1047. error = AddHeader(dwQueryIndex,
  1048. lpszHeaderValue,
  1049. dwHeaderValueLength,
  1050. 0,
  1051. dwFlags
  1052. );
  1053. }
  1054. else
  1055. {
  1056. error = AddHeader(lpszHeaderName,
  1057. dwHeaderNameLength,
  1058. lpszHeaderValue,
  1059. dwHeaderValueLength,
  1060. 0,
  1061. dwFlags
  1062. );
  1063. }
  1064. }
  1065. Cleanup:
  1066. UnlockHeaders();
  1067. quit:
  1068. PERF_LEAVE(ReplaceHeader);
  1069. DEBUG_LEAVE(error);
  1070. return error;
  1071. }
  1072. DWORD
  1073. HTTP_HEADERS::ReplaceHeader(
  1074. IN DWORD dwQueryIndex,
  1075. IN LPSTR lpszHeaderValue,
  1076. IN DWORD dwHeaderValueLength,
  1077. IN DWORD dwIndex,
  1078. IN DWORD dwFlags
  1079. )
  1080. /*++
  1081. Routine Description:
  1082. Replaces a HTTP (request) header. The header can be replaced with a NULL
  1083. value, meaning that the header is removed
  1084. Arguments:
  1085. lpszHeaderValue - pointer to the header value
  1086. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1087. dwHeaderValueLength - length of the header value
  1088. dwIndex - index of header to replace
  1089. dwFlags - flags controlling function. Allowed flags are:
  1090. COALESCE_HEADER_WITH_COMMA
  1091. COALESCE_HEADER_WITH_SEMICOLON
  1092. - headers of the same name can be combined
  1093. ADD_HEADER
  1094. - if the header-name is not found and there is
  1095. a valid header-value, then the header is added
  1096. ADD_HEADER_IF_NEW
  1097. - if the header-name exists then we return an
  1098. error, else we add the header-value
  1099. Return Value:
  1100. DWORD
  1101. Success - ERROR_SUCCESS
  1102. Failure - ERROR_HTTP_HEADER_NOT_FOUND
  1103. The requested header wasn't found
  1104. ERROR_HTTP_HEADER_ALREADY_EXISTS
  1105. The header already exists, and was not added or replaced
  1106. ERROR_NOT_ENOUGH_MEMORY
  1107. Ran out of memory trying to acquire lock
  1108. --*/
  1109. {
  1110. DEBUG_ENTER((DBG_HTTP,
  1111. Dword,
  1112. "ReplaceHeader",
  1113. "%q, %u, %.*q, %d, %d, %#x",
  1114. GlobalKnownHeaders[dwQueryIndex].Text,
  1115. dwQueryIndex,
  1116. min(dwHeaderValueLength + 1, 80),
  1117. lpszHeaderValue,
  1118. dwHeaderValueLength,
  1119. dwIndex,
  1120. dwFlags
  1121. ));
  1122. PERF_ENTER(ReplaceHeader);
  1123. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1124. DWORD dwSlot;
  1125. BYTE *pbPrevByte;
  1126. if (!LockHeaders())
  1127. {
  1128. error = ERROR_NOT_ENOUGH_MEMORY;
  1129. goto quit;
  1130. }
  1131. dwSlot = FastNukeFind(
  1132. dwQueryIndex,
  1133. dwIndex,
  1134. &pbPrevByte
  1135. );
  1136. if ( dwSlot != INVALID_HEADER_SLOT)
  1137. {
  1138. //
  1139. // if ADD_HEADER_IF_NEW is set, then we already have the header
  1140. //
  1141. if (dwFlags & ADD_HEADER_IF_NEW) {
  1142. error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
  1143. goto Cleanup;
  1144. }
  1145. //
  1146. // for both replace and remove operations, we are going to remove
  1147. // the current header
  1148. //
  1149. RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
  1150. //
  1151. // if replacing then add the new header value
  1152. //
  1153. if (dwHeaderValueLength != 0)
  1154. {
  1155. error = AddHeader(dwQueryIndex,
  1156. lpszHeaderValue,
  1157. dwHeaderValueLength,
  1158. 0,
  1159. dwFlags
  1160. );
  1161. } else {
  1162. error = ERROR_SUCCESS;
  1163. }
  1164. }
  1165. //
  1166. // if we didn't find the header but ADD_HEADER is set then we simply add it
  1167. // but only if the value length is not zero
  1168. //
  1169. if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
  1170. && (dwHeaderValueLength != 0)
  1171. && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
  1172. {
  1173. error = AddHeader(dwQueryIndex,
  1174. lpszHeaderValue,
  1175. dwHeaderValueLength,
  1176. 0,
  1177. dwFlags
  1178. );
  1179. }
  1180. Cleanup:
  1181. UnlockHeaders();
  1182. quit:
  1183. PERF_LEAVE(ReplaceHeader);
  1184. DEBUG_LEAVE(error);
  1185. return error;
  1186. }
  1187. DWORD
  1188. HTTP_HEADERS::FindHeader(
  1189. IN LPSTR lpBase,
  1190. IN LPCSTR lpszHeaderName,
  1191. IN DWORD dwHeaderNameLength,
  1192. IN DWORD dwModifiers,
  1193. OUT LPVOID lpBuffer,
  1194. IN OUT LPDWORD lpdwBufferLength,
  1195. IN OUT LPDWORD lpdwIndex
  1196. )
  1197. /*++
  1198. Routine Description:
  1199. Finds a request or response header
  1200. Arguments:
  1201. lpBase - base for offset HEADER_STRINGs
  1202. lpszHeaderName - pointer to header name
  1203. dwHeaderNameLength - length of header name
  1204. dwModifiers - flags which modify returned value
  1205. lpBuffer - pointer to buffer for results
  1206. lpdwBufferLength - IN: length of lpBuffer
  1207. OUT: length of results, or required length of lpBuffer
  1208. lpdwIndex - IN: 0-based index of header to find
  1209. OUT: next header index if success returned
  1210. Return Value:
  1211. DWORD
  1212. Success - ERROR_SUCCESS
  1213. Failure - ERROR_INSUFFICIENT_BUFFER
  1214. *lpdwBufferLength contains the amount required
  1215. ERROR_HTTP_HEADER_NOT_FOUND
  1216. The specified header (or index of header) was not found
  1217. ERROR_NOT_ENOUGH_MEMORY
  1218. Ran out of memory trying to acquire lock
  1219. --*/
  1220. {
  1221. DEBUG_ENTER((DBG_HTTP,
  1222. Dword,
  1223. "HTTP_HEADERS::FindHeader",
  1224. "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
  1225. lpszHeaderName,
  1226. min(dwHeaderNameLength + 1, 80),
  1227. lpszHeaderName,
  1228. dwHeaderNameLength,
  1229. lpBuffer,
  1230. lpdwBufferLength,
  1231. *lpdwBufferLength,
  1232. dwModifiers,
  1233. lpdwIndex,
  1234. *lpdwIndex
  1235. ));
  1236. PERF_ENTER(FindHeader);
  1237. INET_ASSERT(lpdwIndex != NULL);
  1238. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1239. DWORD dwSlot;
  1240. HEADER_STRING * pString;
  1241. DWORD dwQueryIndex;
  1242. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  1243. if (!LockHeaders())
  1244. {
  1245. error = ERROR_NOT_ENOUGH_MEMORY;
  1246. goto quit;
  1247. }
  1248. dwSlot = SlowFind(
  1249. lpBase,
  1250. lpszHeaderName,
  1251. dwHeaderNameLength,
  1252. *lpdwIndex,
  1253. dwHash,
  1254. &dwQueryIndex,
  1255. NULL
  1256. );
  1257. if ( dwSlot != ((DWORD) -1) )
  1258. {
  1259. pString = &_lpHeaders[dwSlot];
  1260. //
  1261. // found the header - get to the value
  1262. //
  1263. DWORD stringLen;
  1264. LPSTR value;
  1265. stringLen = pString->StringLength();
  1266. INET_ASSERT(stringLen > dwHeaderNameLength);
  1267. //
  1268. // get a pointer to the value string
  1269. //
  1270. value = pString->StringAddress(lpBase) + dwHeaderNameLength;
  1271. stringLen -= dwHeaderNameLength;
  1272. //
  1273. // the input string could be a substring of a different header
  1274. //
  1275. //INET_ASSERT(*value != ':');
  1276. //
  1277. // find the first non-space character in the value.
  1278. //
  1279. // N.B.: Servers can return empty headers, so we may end up with a
  1280. // zero length string
  1281. //
  1282. do {
  1283. ++value;
  1284. --stringLen;
  1285. } while ((stringLen > 0) && (*value == ' '));
  1286. //
  1287. // get the data in the format requested by the app
  1288. //
  1289. LPVOID lpData;
  1290. DWORD dwDataSize;
  1291. DWORD dwRequiredSize;
  1292. SYSTEMTIME systemTime;
  1293. DWORD number;
  1294. //
  1295. // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
  1296. // really be success either...
  1297. //
  1298. error = ERROR_SUCCESS;
  1299. if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME) {
  1300. char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
  1301. if (stringLen < sizeof(buf)) {
  1302. //
  1303. // value probably does not point at a zero-terminated string
  1304. // which HttpDateToSystemTime() expects, so we make a copy
  1305. // and terminate it
  1306. //
  1307. memcpy((LPVOID)buf, (LPVOID)value, stringLen);
  1308. buf[stringLen] = '\0';
  1309. if (HttpDateToSystemTime(buf, &systemTime)) {
  1310. lpData = (LPVOID)&systemTime;
  1311. dwRequiredSize = dwDataSize = sizeof(systemTime);
  1312. } else {
  1313. //
  1314. // couldn't convert date/time. Presume header must be bogus
  1315. //
  1316. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1317. DEBUG_PRINT(HTTP,
  1318. ERROR,
  1319. ("cannot convert %.40q to SYSTEMTIME\n",
  1320. value
  1321. ));
  1322. }
  1323. } else {
  1324. //
  1325. // we would break the date/time buffer!
  1326. //
  1327. error = ERROR_WINHTTP_INTERNAL_ERROR;
  1328. }
  1329. } else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
  1330. if (isdigit(*value)) {
  1331. number = 0;
  1332. for (int i = 0;
  1333. (stringLen > 0) && isdigit(value[i]);
  1334. ++i, --stringLen) {
  1335. number = number * 10 + (DWORD)(value[i] - '0');
  1336. }
  1337. lpData = (LPVOID)&number;
  1338. dwRequiredSize = dwDataSize = sizeof(number);
  1339. } else {
  1340. //
  1341. // not a numeric field. Request must be bogus for this header
  1342. //
  1343. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1344. DEBUG_PRINT(HTTP,
  1345. ERROR,
  1346. ("cannot convert %.20q to NUMBER\n",
  1347. value
  1348. ));
  1349. }
  1350. } else {
  1351. lpData = (LPVOID)value;
  1352. dwDataSize = stringLen;
  1353. dwRequiredSize = dwDataSize + 1;
  1354. }
  1355. //
  1356. // if error == ERROR_SUCCESS then we can attempt to copy the data
  1357. //
  1358. if (error == ERROR_SUCCESS) {
  1359. if (*lpdwBufferLength < dwRequiredSize) {
  1360. *lpdwBufferLength = dwRequiredSize;
  1361. error = ERROR_INSUFFICIENT_BUFFER;
  1362. } else {
  1363. memcpy(lpBuffer, lpData, dwDataSize);
  1364. *lpdwBufferLength = dwDataSize;
  1365. //
  1366. // if dwRequiredSize > dwDataSize, then this is a variable-
  1367. // length item (i.e. a STRING!) so we add a terminating '\0'
  1368. //
  1369. if (dwRequiredSize > dwDataSize) {
  1370. INET_ASSERT(dwRequiredSize - dwDataSize == 1);
  1371. ((LPSTR)lpBuffer)[dwDataSize] = '\0';
  1372. }
  1373. //
  1374. // successfully retrieved the requested header - bump the
  1375. // index
  1376. //
  1377. ++*lpdwIndex;
  1378. }
  1379. }
  1380. }
  1381. UnlockHeaders();
  1382. quit:
  1383. PERF_LEAVE(FindHeader);
  1384. DEBUG_LEAVE(error);
  1385. return error;
  1386. }
  1387. DWORD
  1388. HTTP_HEADERS::FindHeader(
  1389. IN LPSTR lpBase,
  1390. IN DWORD dwQueryIndex,
  1391. IN DWORD dwModifiers,
  1392. OUT LPVOID lpBuffer,
  1393. IN OUT LPDWORD lpdwBufferLength,
  1394. IN OUT LPDWORD lpdwIndex
  1395. )
  1396. /*++
  1397. Routine Description:
  1398. Finds a request or response header, based on index to the header name we are searching for.
  1399. Arguments:
  1400. lpBase - base for offset HEADER_STRINGs
  1401. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1402. dwModifiers - flags which modify returned value
  1403. lpBuffer - pointer to buffer for results
  1404. lpdwBufferLength - IN: length of lpBuffer
  1405. OUT: length of results, or required length of lpBuffer
  1406. lpdwIndex - IN: 0-based index of header to find
  1407. OUT: next header index if success returned
  1408. Return Value:
  1409. DWORD
  1410. Success - ERROR_SUCCESS
  1411. Failure - ERROR_INSUFFICIENT_BUFFER
  1412. *lpdwBufferLength contains the amount required
  1413. ERROR_HTTP_HEADER_NOT_FOUND
  1414. The specified header (or index of header) was not found
  1415. --*/
  1416. {
  1417. DWORD error;
  1418. LPSTR lpData;
  1419. DWORD dwDataSize = 0;
  1420. DWORD dwRequiredSize;
  1421. SYSTEMTIME systemTime;
  1422. DWORD number;
  1423. error = FastFindHeader(
  1424. lpBase,
  1425. dwQueryIndex,
  1426. (LPVOID *)&lpData,
  1427. &dwDataSize,
  1428. *lpdwIndex
  1429. );
  1430. if ( error != ERROR_SUCCESS )
  1431. {
  1432. goto quit;
  1433. }
  1434. //
  1435. // get the data in the format requested by the app
  1436. //
  1437. if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME)
  1438. {
  1439. char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
  1440. if (dwDataSize < sizeof(buf))
  1441. {
  1442. //
  1443. // value probably does not point at a zero-terminated string
  1444. // which HttpDateToSystemTime() expects, so we make a copy
  1445. // and terminate it
  1446. //
  1447. memcpy((LPVOID)buf, (LPVOID)lpData, dwDataSize);
  1448. buf[dwDataSize] = '\0';
  1449. if (HttpDateToSystemTime(buf, &systemTime)) {
  1450. lpData = (LPSTR)&systemTime;
  1451. dwRequiredSize = dwDataSize = sizeof(systemTime);
  1452. } else {
  1453. //
  1454. // couldn't convert date/time. Presume header must be bogus
  1455. //
  1456. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1457. DEBUG_PRINT(HTTP,
  1458. ERROR,
  1459. ("cannot convert %.40q to SYSTEMTIME\n",
  1460. lpData
  1461. ));
  1462. }
  1463. }
  1464. else
  1465. {
  1466. //
  1467. // we would break the date/time buffer!
  1468. //
  1469. error = ERROR_WINHTTP_INTERNAL_ERROR;
  1470. }
  1471. }
  1472. else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER)
  1473. {
  1474. if (isdigit(*lpData)) {
  1475. number = 0;
  1476. for (int i = 0;
  1477. (dwDataSize > 0) && isdigit(lpData[i]);
  1478. ++i, --dwDataSize) {
  1479. number = number * 10 + (DWORD)(lpData[i] - '0');
  1480. }
  1481. lpData = (LPSTR)&number;
  1482. dwRequiredSize = dwDataSize = sizeof(number);
  1483. } else {
  1484. //
  1485. // not a numeric field. Request must be bogus for this header
  1486. //
  1487. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1488. DEBUG_PRINT(HTTP,
  1489. ERROR,
  1490. ("cannot convert %.20q to NUMBER\n",
  1491. lpData
  1492. ));
  1493. }
  1494. }
  1495. else
  1496. {
  1497. dwRequiredSize = dwDataSize + 1;
  1498. }
  1499. //
  1500. // if error == ERROR_SUCCESS then we can attempt to copy the data
  1501. //
  1502. if (error == ERROR_SUCCESS)
  1503. {
  1504. if (*lpdwBufferLength < dwRequiredSize)
  1505. {
  1506. *lpdwBufferLength = dwRequiredSize;
  1507. error = ERROR_INSUFFICIENT_BUFFER;
  1508. }
  1509. else
  1510. {
  1511. memcpy(lpBuffer, lpData, dwDataSize);
  1512. *lpdwBufferLength = dwDataSize;
  1513. //
  1514. // if dwRequiredSize > dwDataSize, then this is a variable-
  1515. // length item (i.e. a STRING!) so we add a terminating '\0'
  1516. //
  1517. if (dwRequiredSize > dwDataSize)
  1518. {
  1519. INET_ASSERT(dwRequiredSize - dwDataSize == 1);
  1520. ((LPSTR)lpBuffer)[dwDataSize] = '\0';
  1521. }
  1522. //
  1523. // successfully retrieved the requested header - bump the
  1524. // index
  1525. //
  1526. ++*lpdwIndex;
  1527. }
  1528. }
  1529. quit:
  1530. return error;
  1531. }
  1532. DWORD
  1533. HTTP_HEADERS::FastFindHeader(
  1534. IN LPSTR lpBase,
  1535. IN DWORD dwQueryIndex,
  1536. OUT LPVOID *lplpBuffer,
  1537. IN OUT LPDWORD lpdwBufferLength,
  1538. IN DWORD dwIndex
  1539. )
  1540. /*++
  1541. Routine Description:
  1542. Finds a request or response header slightly quicker than its higher level
  1543. cousin, FindHeader. Unlike FindHeader this function simply returns
  1544. a pointer and length, and does not copy header data.
  1545. lpBase - base address of strings
  1546. dwQueryIndex - a index into a array known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1547. lplpBuffer - pointer to pointer of the actual header to be returned in.
  1548. lpdwBufferLength - OUT: if successful, length of output buffer, minus 1
  1549. for any trailing EOS, or if the buffer is not
  1550. large enough, the size required
  1551. dwIndex - a index of which header we're asking for, as there can be multiple headers
  1552. under the same name.
  1553. Arguments:
  1554. lpBase - base for offset HEADER_STRINGs
  1555. lpszHeaderName - pointer to header name
  1556. dwHeaderNameLength - length of header name
  1557. dwModifiers - flags which modify returned value
  1558. lpBuffer - pointer to buffer for results
  1559. lpdwBufferLength - IN: length of lpBuffer
  1560. OUT: length of results, or required length of lpBuffer
  1561. lpdwIndex - IN: 0-based index of header to find
  1562. OUT: next header index if success returned
  1563. Return Value:
  1564. DWORD
  1565. Success - ERROR_SUCCESS
  1566. Failure - ERROR_INSUFFICIENT_BUFFER
  1567. *lpdwBufferLength contains the amount required
  1568. ERROR_HTTP_HEADER_NOT_FOUND
  1569. The specified header (or index of header) was not found
  1570. --*/
  1571. {
  1572. DEBUG_ENTER((DBG_HTTP,
  1573. Dword,
  1574. "HTTP_HEADERS::FastFindHeader",
  1575. "%q, %#x, %#x [%#x], %u",
  1576. GlobalKnownHeaders[dwQueryIndex].Text,
  1577. lplpBuffer,
  1578. lpdwBufferLength,
  1579. *lpdwBufferLength,
  1580. dwIndex
  1581. ));
  1582. PERF_ENTER(FindHeader);
  1583. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1584. HEADER_STRING * curHeader;
  1585. DWORD dwSlot;
  1586. dwSlot = FastFind(dwQueryIndex, dwIndex);
  1587. if ( dwSlot != INVALID_HEADER_SLOT)
  1588. {
  1589. //
  1590. // found the header - get to the value
  1591. //
  1592. DWORD stringLen;
  1593. LPSTR value;
  1594. curHeader = GetSlot(dwSlot);
  1595. //
  1596. // get a pointer to the value string
  1597. //
  1598. value = curHeader->StringAddress(lpBase) + (GlobalKnownHeaders[dwQueryIndex].Length+1);
  1599. stringLen = curHeader->StringLength() - (GlobalKnownHeaders[dwQueryIndex].Length+1);
  1600. //
  1601. // find the first non-space character in the value.
  1602. //
  1603. // N.B.: Servers can return empty headers, so we may end up with a
  1604. // zero length string
  1605. //
  1606. while ((stringLen > 0) && (*value == ' '))
  1607. {
  1608. ++value;
  1609. --stringLen;
  1610. }
  1611. //
  1612. // get the data in the format requested by the app
  1613. //
  1614. //
  1615. // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
  1616. // really be success either...
  1617. //
  1618. error = ERROR_SUCCESS;
  1619. *lplpBuffer = (LPVOID)value;
  1620. *lpdwBufferLength = stringLen;
  1621. }
  1622. PERF_LEAVE(FindHeader);
  1623. DEBUG_LEAVE(error);
  1624. return error;
  1625. }
  1626. DWORD
  1627. HTTP_HEADERS::QueryRawHeaders(
  1628. IN LPSTR lpBase,
  1629. IN BOOL bCrLfTerminated,
  1630. IN LPVOID lpBuffer,
  1631. IN OUT LPDWORD lpdwBufferLength
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. Returns all the request or response headers in a single buffer. The headers
  1636. can be returned as ASCIIZ strings, or CR-LF terminated strings
  1637. Arguments:
  1638. lpBase - base address of strings
  1639. bCrLfTerminated - TRUE if each string is terminated with CR-LF
  1640. lpBuffer - pointer to buffer to write headers
  1641. lpdwBufferLength - IN: length of lpBuffer
  1642. OUT: if successful, length of output buffer, minus 1
  1643. for any trailing EOS, or if the buffer is not
  1644. large enough, the size required
  1645. Return Value:
  1646. DWORD
  1647. Success - ERROR_SUCCESS
  1648. Failure - ERROR_INSUFFICIENT_BUFFER
  1649. ERROR_NOT_ENOUGH_MEMORY
  1650. Ran out of memory trying to acquire lock
  1651. --*/
  1652. {
  1653. PERF_ENTER(QueryRawHeaders);
  1654. DWORD error = ERROR_SUCCESS;
  1655. DWORD requiredLength = 0;
  1656. LPSTR lpszBuffer = (LPSTR)lpBuffer;
  1657. if (!LockHeaders())
  1658. {
  1659. error = ERROR_NOT_ENOUGH_MEMORY;
  1660. goto quit;
  1661. }
  1662. for (DWORD i = 0; i < _TotalSlots; ++i) {
  1663. if (_lpHeaders[i].HaveString()) {
  1664. DWORD length;
  1665. length = _lpHeaders[i].StringLength();
  1666. requiredLength += length + (bCrLfTerminated ? 2 : 1);
  1667. if (*lpdwBufferLength > requiredLength) {
  1668. _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
  1669. lpszBuffer += length;
  1670. if (bCrLfTerminated) {
  1671. *lpszBuffer++ = '\r';
  1672. *lpszBuffer++ = '\n';
  1673. } else {
  1674. *lpszBuffer++ = '\0';
  1675. }
  1676. }
  1677. }
  1678. }
  1679. if (bCrLfTerminated)
  1680. {
  1681. requiredLength += 2;
  1682. if (*lpdwBufferLength > requiredLength)
  1683. {
  1684. *lpszBuffer++ = '\r';
  1685. *lpszBuffer++ = '\n';
  1686. }
  1687. }
  1688. UnlockHeaders();
  1689. ++requiredLength;
  1690. if (*lpdwBufferLength < requiredLength) {
  1691. error = ERROR_INSUFFICIENT_BUFFER;
  1692. } else {
  1693. *lpszBuffer = '\0';
  1694. --requiredLength; // remove 1 for trailing '\0'
  1695. }
  1696. *lpdwBufferLength = requiredLength;
  1697. quit:
  1698. PERF_LEAVE(QueryRawHeaders);
  1699. return error;
  1700. }
  1701. DWORD
  1702. HTTP_HEADERS::QueryFilteredRawHeaders(
  1703. IN LPSTR lpBase,
  1704. IN LPSTR *lplpFilterList,
  1705. IN DWORD cListElements,
  1706. IN BOOL fExclude,
  1707. IN BOOL fSkipVerb,
  1708. IN BOOL bCrLfTerminated,
  1709. IN LPVOID lpBuffer,
  1710. IN OUT LPDWORD lpdwBufferLength
  1711. )
  1712. /*++
  1713. Routine Description:
  1714. Returns all the request or response headers in a single buffer. The headers
  1715. can be returned as ASCIIZ strings, or CR-LF terminated strings
  1716. Arguments:
  1717. lpBase - base address of strings
  1718. bCrLfTerminated - TRUE if each string is terminated with CR-LF
  1719. lpBuffer - pointer to buffer to write headers
  1720. lpdwBufferLength - IN: length of lpBuffer
  1721. OUT: if successful, length of output buffer, minus 1
  1722. for any trailing EOS, or if the buffer is not
  1723. large enough, the size required
  1724. Return Value:
  1725. DWORD
  1726. Success - ERROR_SUCCESS
  1727. Failure - ERROR_INSUFFICIENT_BUFFER
  1728. --*/
  1729. {
  1730. DWORD error = ERROR_NOT_SUPPORTED;
  1731. DWORD requiredLength = 0;
  1732. LPSTR lpszBuffer = (LPSTR)lpBuffer;
  1733. BOOL fCopy;
  1734. DWORD i = fSkipVerb ? 1 : 0;
  1735. for (; i < _TotalSlots; ++i) {
  1736. if (_lpHeaders[i].HaveString()) {
  1737. fCopy = TRUE;
  1738. if (lplpFilterList
  1739. && FMatchList(lplpFilterList, cListElements, _lpHeaders+i, lpBase)) {
  1740. fCopy = fExclude?FALSE:TRUE;
  1741. }
  1742. if (fCopy) {
  1743. DWORD length;
  1744. length = _lpHeaders[i].StringLength();
  1745. requiredLength += length + (bCrLfTerminated ? 2 : 1);
  1746. if (*lpdwBufferLength > requiredLength) {
  1747. _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
  1748. lpszBuffer += length;
  1749. if (bCrLfTerminated) {
  1750. *lpszBuffer++ = '\r';
  1751. *lpszBuffer++ = '\n';
  1752. } else {
  1753. *lpszBuffer++ = '\0';
  1754. }
  1755. }
  1756. }
  1757. }
  1758. }
  1759. if (bCrLfTerminated)
  1760. {
  1761. requiredLength += 2;
  1762. if (*lpdwBufferLength > requiredLength)
  1763. {
  1764. *lpszBuffer++ = '\r';
  1765. *lpszBuffer++ = '\n';
  1766. }
  1767. }
  1768. ++requiredLength;
  1769. if (*lpdwBufferLength < requiredLength) {
  1770. error = ERROR_INSUFFICIENT_BUFFER;
  1771. } else {
  1772. *lpszBuffer = '\0';
  1773. --requiredLength; // remove 1 for trailing '\0'
  1774. error = ERROR_SUCCESS;
  1775. }
  1776. *lpdwBufferLength = requiredLength;
  1777. return error;
  1778. }
  1779. DWORD
  1780. HTTP_HEADERS::AddRequest(
  1781. IN LPSTR lpszVerb,
  1782. IN LPSTR lpszObject,
  1783. IN LPSTR lpszVersion
  1784. )
  1785. /*++
  1786. Routine Description:
  1787. Builds the request line from its constituent parts. The request line is the
  1788. first (0th) header in the request headers
  1789. Assumes: 1. This is the one-and-only call to this method
  1790. 2. lpszObject must already be escaped if necessary
  1791. Arguments:
  1792. lpszVerb - pointer to HTTP verb, e.g. "GET"
  1793. lpszObject - pointer to HTTP object name, e.g. "/users/albert/~emc2.htm".
  1794. lpszVersion - pointer to HTTP version string, e.g. "HTTP/1.0"
  1795. Return Value:
  1796. DWORD
  1797. Success - ERROR_SUCCESS
  1798. Failure - ERROR_NOT_ENOUGH_MEMORY
  1799. --*/
  1800. {
  1801. PERF_ENTER(AddRequest);
  1802. //
  1803. // there must not be a header when this method is called
  1804. //
  1805. INET_ASSERT(_HeadersLength == 0);
  1806. DWORD error = ERROR_SUCCESS;
  1807. int verbLen = lstrlen(lpszVerb);
  1808. int objectLen = lstrlen(lpszObject);
  1809. int versionLen = lstrlen(lpszVersion);
  1810. int len = verbLen // "GET"
  1811. + 1 // ' '
  1812. + objectLen // "/users/albert/~emc2.htm"
  1813. + 1 // ' '
  1814. + versionLen // "HTTP/1.0"
  1815. + 1 // '\0'
  1816. ;
  1817. //
  1818. // we are about to start updating the headers for the current
  1819. // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
  1820. //
  1821. HEADER_STRING * pRequest = GetFirstHeader();
  1822. HEADER_STRING & request = *pRequest;
  1823. if (pRequest == NULL) {
  1824. error = ERROR_NOT_ENOUGH_MEMORY;
  1825. goto quit;
  1826. }
  1827. INET_ASSERT(!request.HaveString());
  1828. _lpszVerb = NULL;
  1829. _dwVerbLength = 0;
  1830. _lpszObjectName = NULL;
  1831. _dwObjectNameLength = 0;
  1832. _lpszVersion = NULL;
  1833. _dwVersionLength = 0;
  1834. request.CreateStringBuffer((LPVOID)lpszVerb, verbLen, len);
  1835. if (request.IsError()) {
  1836. error = GetLastError();
  1837. INET_ASSERT(error != ERROR_SUCCESS);
  1838. } else {
  1839. request += ' ';
  1840. request.Strncat((LPVOID)lpszObject, objectLen);
  1841. request += ' ';
  1842. request.Strncat((LPVOID)lpszVersion, versionLen);
  1843. _HeadersLength = len - 1 + (sizeof("\r\n") - 1);
  1844. //
  1845. // we have used the first free slot in the headers array
  1846. //
  1847. --_FreeSlots;
  1848. //
  1849. // update the component variables in case of a ModifyRequest()
  1850. //
  1851. _lpszVerb = request.StringAddress();
  1852. _dwVerbLength = verbLen;
  1853. _lpszObjectName = _lpszVerb + verbLen + 1;
  1854. _dwObjectNameLength = objectLen;
  1855. _lpszVersion = _lpszObjectName + objectLen + 1;
  1856. _dwVersionLength = versionLen;
  1857. SetRequestVersion();
  1858. error = request.IsError() ? ::GetLastError() : ERROR_SUCCESS;
  1859. }
  1860. quit:
  1861. PERF_LEAVE(AddRequest);
  1862. return error;
  1863. }
  1864. DWORD
  1865. HTTP_HEADERS::ModifyRequest(
  1866. IN HTTP_METHOD_TYPE tMethod,
  1867. IN LPSTR lpszObjectName,
  1868. IN DWORD dwObjectNameLength,
  1869. IN LPSTR lpszVersion OPTIONAL,
  1870. IN DWORD dwVersionLength
  1871. )
  1872. /*++
  1873. Routine Description:
  1874. Updates the request line. Used in redirection
  1875. Arguments:
  1876. tMethod - type of new method
  1877. lpszObjectName - pointer to new object name
  1878. dwObjectNameLength - length of new object name
  1879. lpszVersion - optional pointer to version string
  1880. dwVersionLength - length of lpszVersion string if present
  1881. Return Value:
  1882. DWORD
  1883. Success - ERROR_SUCCESS
  1884. Failure - ERROR_NOT_ENOUGH_MEMORY
  1885. --*/
  1886. {
  1887. DEBUG_ENTER((DBG_HTTP,
  1888. Dword,
  1889. "ModifyRequest",
  1890. "%s, %q, %d, %q, %d",
  1891. MapHttpMethodType(tMethod),
  1892. lpszObjectName,
  1893. dwObjectNameLength,
  1894. lpszVersion,
  1895. dwVersionLength
  1896. ));
  1897. PERF_ENTER(ModifyRequest);
  1898. INET_ASSERT(lpszObjectName != NULL);
  1899. INET_ASSERT(dwObjectNameLength != 0);
  1900. //
  1901. // there must already be a header when this method is called
  1902. //
  1903. INET_ASSERT(_HeadersLength != 0);
  1904. //
  1905. // we are about to start updating the headers for the current
  1906. // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
  1907. //
  1908. //
  1909. // BUGBUG [arthurbi] using two HEADER_STRINGs here causes an extra
  1910. // ReAlloc when use the Copy operator between the two.
  1911. //
  1912. HEADER_STRING * pRequest = GetFirstHeader();
  1913. HEADER_STRING & request = *pRequest;
  1914. HEADER_STRING newRequest;
  1915. LPCSTR lpcszVerb;
  1916. DWORD verbLength;
  1917. DWORD error = ERROR_SUCCESS;
  1918. DWORD length;
  1919. //
  1920. // there must already be a request line
  1921. //
  1922. if (pRequest == NULL) {
  1923. error = ERROR_NOT_ENOUGH_MEMORY;
  1924. goto quit;
  1925. }
  1926. INET_ASSERT(request.HaveString());
  1927. //
  1928. // get the verb/method to use.
  1929. //
  1930. if (tMethod == HTTP_METHOD_TYPE_UNKNOWN) {
  1931. //
  1932. // the method is unknown, read the old one out of the string
  1933. // and save off, basically we're reusing the previous one.
  1934. //
  1935. lpcszVerb = request.StringAddress();
  1936. for (DWORD i = 0; i < request.StringLength(); i++) {
  1937. if (lpcszVerb[i] == ' ') {
  1938. break;
  1939. }
  1940. }
  1941. INET_ASSERT((i > 0) && (i < (DWORD)request.StringLength()));
  1942. verbLength = (DWORD)i;
  1943. } else {
  1944. //
  1945. // its one of the normal kind, just map it.
  1946. //
  1947. verbLength = MapHttpMethodType(tMethod, &lpcszVerb);
  1948. }
  1949. if (lpszVersion == NULL) {
  1950. lpszVersion = _lpszVersion;
  1951. dwVersionLength = _dwVersionLength;
  1952. }
  1953. _lpszVerb = NULL;
  1954. _dwVerbLength = 0;
  1955. _lpszObjectName = NULL;
  1956. _dwObjectNameLength = 0;
  1957. _lpszVersion = NULL;
  1958. _dwVersionLength = 0;
  1959. //
  1960. // calculate the new length from the component lengths we originally set
  1961. // in AddRequest(), and the new object name
  1962. //
  1963. length = verbLength + 1 + dwObjectNameLength + 1 + dwVersionLength + 1;
  1964. //
  1965. // create a new request line
  1966. //
  1967. newRequest.CreateStringBuffer((LPVOID)lpcszVerb, verbLength, length);
  1968. if (newRequest.IsError()) {
  1969. error = GetLastError();
  1970. } else {
  1971. newRequest += ' ';
  1972. newRequest.Strncat((LPVOID)lpszObjectName, dwObjectNameLength);
  1973. newRequest += ' ';
  1974. newRequest.Strncat((LPVOID)lpszVersion, dwVersionLength);
  1975. //
  1976. // remove the current request line length from the header buffer
  1977. // aggregate
  1978. //
  1979. _HeadersLength -= request.StringLength();
  1980. //
  1981. // make the current request line the new one
  1982. //
  1983. request = newRequest.StringAddress();
  1984. //
  1985. // and update the address and length variables (version length is the
  1986. // only thing that stays the same)
  1987. //
  1988. if (!request.IsError()) {
  1989. _lpszVerb = request.StringAddress();
  1990. _dwVerbLength = verbLength;
  1991. _lpszObjectName = _lpszVerb + verbLength + 1;
  1992. _dwObjectNameLength = dwObjectNameLength;
  1993. _lpszVersion = _lpszObjectName + dwObjectNameLength + 1;
  1994. _dwVersionLength = dwVersionLength;
  1995. SetRequestVersion();
  1996. //
  1997. // and the new request line length to the aggregate header length
  1998. //
  1999. _HeadersLength += request.StringLength();
  2000. } else {
  2001. error = GetLastError();
  2002. }
  2003. }
  2004. quit:
  2005. PERF_LEAVE(ModifyRequest);
  2006. DEBUG_LEAVE(error);
  2007. return error;
  2008. }
  2009. VOID
  2010. HTTP_HEADERS::SetRequestVersion(
  2011. VOID
  2012. )
  2013. /*++
  2014. Routine Description:
  2015. Set _RequestVersionMajor and _RequestVersionMinor based on the HTTP
  2016. version string
  2017. Arguments:
  2018. None.
  2019. Return Value:
  2020. None.
  2021. --*/
  2022. {
  2023. DEBUG_ENTER((DBG_HTTP,
  2024. None,
  2025. "HTTP_HEADERS::SetRequestVersion",
  2026. NULL
  2027. ));
  2028. INET_ASSERT(_lpszVersion != NULL);
  2029. _RequestVersionMajor = 0;
  2030. _RequestVersionMinor = 0;
  2031. if (strncmp(_lpszVersion, "HTTP/", sizeof("HTTP/") - 1) == 0) {
  2032. LPSTR pNum = _lpszVersion + sizeof("HTTP/") - 1;
  2033. ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMajor);
  2034. while (!isdigit(*pNum) && (*pNum != '\0')) {
  2035. ++pNum;
  2036. }
  2037. ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMinor);
  2038. DEBUG_PRINT(HTTP,
  2039. INFO,
  2040. ("request version = %d.%d\n",
  2041. _RequestVersionMajor,
  2042. _RequestVersionMinor
  2043. ));
  2044. } else {
  2045. DEBUG_PRINT(HTTP,
  2046. WARNING,
  2047. ("\"HTTP/\" not found in %q\n",
  2048. _lpszVersion
  2049. ));
  2050. }
  2051. DEBUG_LEAVE(0);
  2052. }