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.

6333 lines
166 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. headers.cxx
  5. Abstract:
  6. Methods for HTTP_HEADERS (..\inc\http.hxx) classes
  7. Contents:
  8. HEADER_STRING::CreateHash
  9. HTTP_HEADERS::HeaderMatch
  10. HTTP_HEADERS::AllocateHeaders
  11. HTTP_HEADERS::FreeHeaders
  12. HTTP_HEADERS::CopyHeaders
  13. HTTP_HEADERS::FindFreeSlot
  14. HTTP_HEADERS::AddHeader
  15. HTTP_HEADERS::ReplaceHeader
  16. HTTP_HEADERS::FindHeader
  17. HTTP_HEADERS::QueryRawHeaders
  18. HTTP_HEADERS::AddRequest
  19. HTTP_HEADERS::ModifyRequest
  20. HTTP_HEADERS::SetRequestVersion
  21. HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer
  22. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader
  23. HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader
  24. HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders
  25. HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders
  26. HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion
  27. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode
  28. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText
  29. HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders
  30. HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName
  31. HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders
  32. MapHttpMethodType
  33. CreateEscapedUrlPath
  34. (CalculateHashNoCase)
  35. Author:
  36. Richard L Firth (rfirth) 20-Dec-1995
  37. Revision History:
  38. 20-Dec-1995 rfirth
  39. Created
  40. --*/
  41. #include <wininetp.h>
  42. #include <perfdiag.hxx>
  43. #include "httpp.h"
  44. //
  45. // private manifests
  46. //
  47. #define DATE_AND_TIME_STRING_BUFFER_LENGTH 128
  48. #ifdef COMPRESSED_HEADERS
  49. typedef struct tagHEADER_MAP {
  50. LPSTR lpszLongHeader;
  51. DWORD dwLenLongHeader;
  52. LPSTR lpszShortHeader;
  53. DWORD dwLenShortHeader;
  54. }
  55. HEADER_MAP;
  56. // Sorted Compression map
  57. HEADER_MAP rgsHeaderMap[] = {
  58. {"", 0, "", 0 },
  59. {"Accept", sizeof("Accept")-1, "#A", sizeof("#A")-1},
  60. {"Accept-Charset", sizeof("Accept-Charset")-1, "#B", sizeof("#B")-1},
  61. {"Accept-Encoding", sizeof("Accept-Encoding")-1, "#C", sizeof("#C")-1},
  62. {"Accept-Language", sizeof("Accept-Language")-1, "#D", sizeof("#D")-1},
  63. {"Accept-Ranges", sizeof("Accept-Ranges")-1, "#E", sizeof("#E")-1},
  64. {"Age", sizeof("Age")-1, "#F", sizeof("#F")-1},
  65. {"Allow", sizeof("Allow")-1, "#G", sizeof("#G")-1},
  66. {"Authorization", sizeof("Authorization")-1, "#H", sizeof("#H")-1},
  67. {"Cache-Control", sizeof("Cache-Control")-1, "#I", sizeof("#I")-1},
  68. {"Connection", sizeof("Connection")-1, "#J", sizeof("#J")-1},
  69. {"Content-Base", sizeof("Content-Base")-1, "#K", sizeof("#K")-1},
  70. {"Content-Encoding", sizeof("Content-Encoding")-1, "#L", sizeof("#L")-1},
  71. {"Content-Language", sizeof("Content-Language")-1, "#M", sizeof("#M")-1},
  72. {"Content-Length", sizeof("Content-Length")-1, "#N", sizeof("#N")-1},
  73. {"Content-Location", sizeof("Content-Location")-1, "#O", sizeof("#O")-1},
  74. {"Content-MD5", sizeof("Content-MD5")-1, "#P", sizeof("#P")-1},
  75. {"Content-Range", sizeof("Content-Range")-1, "#Q", sizeof("#Q")-1},
  76. {"Content-Type", sizeof("Content-Type")-1, "#R", sizeof("#R")-1},
  77. {"Cookie", sizeof("Cookie")-1, "#5", sizeof("#5")-1},
  78. {"Date", sizeof("Date")-1, "#S", sizeof("#S")-1},
  79. {"ETag", sizeof("ETag")-1, "#T", sizeof("#T")-1},
  80. {"Expires", sizeof("Expires")-1, "#U", sizeof("#U")-1},
  81. {"From", sizeof("From")-1, "#V", sizeof("#V")-1},
  82. {"Host", sizeof("Host")-1, "#W", sizeof("#W")-1},
  83. {"If-Modified-Since", sizeof("If-Modified-Since")-1, "#X", sizeof("#X")-1},
  84. {"If-Match", sizeof("If-Match")-1, "#Y", sizeof("#Y")-1},
  85. {"If-None-Match", sizeof("If-None-Match")-1, "#Z", sizeof("#Z")-1},
  86. {"If-Range", sizeof("If-Range")-1, "#a", sizeof("#a")-1},
  87. {"If-Unmodified-Since",sizeof("If-Unmodified-Since")-1, "#b", sizeof("#b")-1},
  88. {"Last-Modified", sizeof("Last-Modified")-1, "#c", sizeof("#c")-1},
  89. {"Location", sizeof("Location")-1, "#d", sizeof("#d")-1},
  90. {"Max-Forwards", sizeof("Max-Forwards")-1, "#e", sizeof("#e")-1},
  91. {"Pragma", sizeof("Pragma")-1, "#f", sizeof("#f")-1},
  92. {"Proxy-Authenticate", sizeof("Proxy-Authenticate")-1, "#g", sizeof("#g")-1},
  93. {"Proxy-Authorization",sizeof("Proxy-Authorization")-1, "#h", sizeof("#h")-1},
  94. {"Public", sizeof("Public")-1, "#I", sizeof("#I")-1},
  95. {"Range", sizeof("Range")-1, "#j", sizeof("#j")-1},
  96. {"Referer", sizeof("Referer")-1, "#k", sizeof("#k")-1},
  97. {"Retry-After", sizeof("Retry-After")-1, "#l", sizeof("#l")-1},
  98. {"Server", sizeof("Server")-1, "#m", sizeof("#m")-1},
  99. {"Transfer-Encoding", sizeof("Transfer-Encoding")-1, "#n", sizeof("#n")-1},
  100. {"UA-color", sizeof("UA-color")-1, "#1", sizeof("#1")-1},
  101. {"UA-cpu", sizeof("UA-cpu")-1, "#2", sizeof("#2")-1},
  102. {"UA-OS", sizeof("UA-OS")-1, "#3", sizeof("#3")-1},
  103. {"UA-pixels", sizeof("UA-pixels")-1, "#4", sizeof("#4")-1},
  104. {"Upgrade", sizeof("Upgrade")-1, "#o", sizeof("#o")-1},
  105. {"User-Agent", sizeof("User-Agent")-1, "#p", sizeof("#p")-1},
  106. {"Vary", sizeof("Vary")-1, "#q", sizeof("#q")-1},
  107. {"Via", sizeof("Via")-1, "#r", sizeof("#r")-1},
  108. {"Warning", sizeof("Warning")-1, "#s", sizeof("#s")-1},
  109. {"WWW-Authenticate", sizeof("WWW-Authenticate")-1, "#t", sizeof("#t")-1}
  110. };
  111. #endif //COMPRESSED_HEADERS
  112. //
  113. // Private functions
  114. //
  115. PRIVATE
  116. BOOL
  117. FMatchList(
  118. LPSTR *lplpList,
  119. DWORD cListLen,
  120. HEADER_STRING *lpHeader,
  121. LPSTR lpBase
  122. );
  123. //
  124. // external functions
  125. //
  126. extern
  127. BOOL
  128. HttpDateToSystemTime(
  129. IN LPSTR lpszHttpDate,
  130. OUT LPSYSTEMTIME lpSystemTime
  131. );
  132. #ifdef COMPRESSED_HEADERS
  133. DWORD
  134. LookupHeadermap(
  135. LPSTR lpszHeader
  136. );
  137. extern BOOL vfCompressedHeaders;
  138. #endif //COMPRESSED_HEADERS
  139. DWORD
  140. FASTCALL
  141. CalculateHashNoCase(
  142. IN LPSTR lpszString,
  143. IN DWORD dwStringLength
  144. )
  145. /*++
  146. Routine Description:
  147. Calculate a case-insensitive hash number given a string. Assumes input is
  148. 7-bit ASCII
  149. Arguments:
  150. lpszString - string to hash
  151. dwStringLength - length of lpszString, or -1 if we need to calculate
  152. Return Value:
  153. DWORD - a generated hash value
  154. --*/
  155. {
  156. DWORD dwHash = HEADER_HASH_SEED;
  157. while (dwStringLength != 0) {
  158. CHAR ch = *lpszString;
  159. if ((ch >= 'A') && (ch <= 'Z')) {
  160. ch = MAKE_LOWER(ch);
  161. }
  162. dwHash += (DWORD)(dwHash << 5) + ch; /*+ *pszName++;*/
  163. ++lpszString;
  164. --dwStringLength;
  165. }
  166. return dwHash;
  167. }
  168. //
  169. // methods
  170. //
  171. VOID
  172. inline
  173. HEADER_STRING::CreateHash(
  174. LPSTR lpszBase
  175. )
  176. {
  177. DWORD i = 0;
  178. LPSTR string = StringAddress(lpszBase);
  179. while ((i < (DWORD)StringLength())
  180. && !((string[i] == ':')
  181. || (string[i] == ' ')
  182. || (string[i] == '\r')
  183. || (string[i] == '\n'))) {
  184. ++i;
  185. }
  186. m_Hash = CalculateHashNoCase(string, i);
  187. }
  188. DWORD
  189. HTTP_HEADERS::AllocateHeaders(
  190. IN DWORD dwNumberOfHeaders
  191. )
  192. /*++
  193. Routine Description:
  194. Allocates or grows the array of header pointers (HEADER_STRING objects)
  195. Arguments:
  196. dwNumberOfHeaders - number of additional header slots to create
  197. Return Value:
  198. DWORD
  199. Success - ERROR_SUCCESS
  200. Failure - ERROR_NOT_ENOUGH_MEMORY
  201. --*/
  202. {
  203. DEBUG_ENTER((DBG_HTTP,
  204. Dword,
  205. "AllocateHeaders",
  206. "%d",
  207. dwNumberOfHeaders
  208. ));
  209. PERF_ENTER(AllocateHeaders);
  210. //
  211. // we really need to be able to realloc an array of HEADER_STRING objects
  212. // (see below)
  213. //
  214. DWORD error;
  215. DWORD slots = _TotalSlots;
  216. if ( (_TotalSlots + dwNumberOfHeaders) > (INVALID_HEADER_INDEX-1))
  217. {
  218. INET_ASSERT(FALSE);
  219. _NextOpenSlot = 0;
  220. _TotalSlots = 0;
  221. _FreeSlots = 0;
  222. error = ERROR_NOT_ENOUGH_MEMORY;
  223. goto quit;
  224. }
  225. _lpHeaders = (HEADER_STRING *)ResizeBuffer((HLOCAL)_lpHeaders,
  226. (_TotalSlots + dwNumberOfHeaders)
  227. * sizeof(HEADER_STRING),
  228. FALSE // not moveable
  229. );
  230. if (_lpHeaders != NULL) {
  231. _NextOpenSlot = _TotalSlots;
  232. _TotalSlots += dwNumberOfHeaders;
  233. _FreeSlots += dwNumberOfHeaders;
  234. //
  235. // this is slightly ugly, but it seems there's no easy C++ way to
  236. // do this - we need to be able to realloc() an array of objects
  237. // created by new(), but so far, it can't be done
  238. //
  239. for (; slots < _TotalSlots; ++slots) {
  240. _lpHeaders[slots].Clear();
  241. }
  242. error = ERROR_SUCCESS;
  243. } else {
  244. INET_ASSERT(FALSE);
  245. _NextOpenSlot = 0;
  246. _TotalSlots = 0;
  247. _FreeSlots = 0;
  248. error = ERROR_NOT_ENOUGH_MEMORY;
  249. }
  250. quit:
  251. INET_ASSERT(_FreeSlots <= _TotalSlots);
  252. PERF_LEAVE(AllocateHeaders);
  253. DEBUG_LEAVE(error);
  254. return error;
  255. }
  256. VOID
  257. HTTP_HEADERS::FreeHeaders(
  258. VOID
  259. )
  260. /*++
  261. Routine Description:
  262. Free the headers strings and the headers array
  263. Arguments:
  264. None.
  265. Return Value:
  266. None.
  267. --*/
  268. {
  269. DEBUG_ENTER((DBG_HTTP,
  270. None,
  271. "FreeHeaders",
  272. NULL
  273. ));
  274. LockHeaders();
  275. //
  276. // free up each individual entry (free string buffers)
  277. //
  278. for (DWORD i = 0; i < _TotalSlots; ++i) {
  279. _lpHeaders[i] = (LPSTR)NULL;
  280. }
  281. //
  282. // followed by the array itself
  283. //
  284. if (_lpHeaders) {
  285. _lpHeaders = (HEADER_STRING *)FREE_MEMORY((HLOCAL)_lpHeaders);
  286. INET_ASSERT(_lpHeaders == NULL);
  287. }
  288. _TotalSlots = 0;
  289. _FreeSlots = 0;
  290. _HeadersLength = 0;
  291. _lpszVerb = NULL;
  292. _dwVerbLength = 0;
  293. _lpszObjectName = NULL;
  294. _dwObjectNameLength = 0;
  295. _lpszVersion = NULL;
  296. _dwVersionLength = 0;
  297. UnlockHeaders();
  298. DEBUG_LEAVE(0);
  299. }
  300. VOID
  301. HTTP_HEADERS::CopyHeaders(
  302. IN OUT LPSTR * lpBuffer,
  303. IN LPSTR lpszObjectName,
  304. IN DWORD dwObjectNameLength
  305. )
  306. /*++
  307. Routine Description:
  308. Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
  309. This method is called to convert the request headers list to a buffer that
  310. we can send to the server
  311. N.B. This function MUST be called with the headers already locked
  312. Arguments:
  313. lpBuffer - pointer to pointer to buffer where headers are
  314. written. We update the pointer
  315. lpszObjectName - optional object name
  316. dwObjectNameLength - optional object name length
  317. Return Value:
  318. None.
  319. --*/
  320. {
  321. DEBUG_ENTER((DBG_HTTP,
  322. None,
  323. "CopyHeaders",
  324. "%#x, %#x [%q], %d",
  325. lpBuffer,
  326. lpszObjectName,
  327. lpszObjectName,
  328. dwObjectNameLength
  329. ));
  330. LockHeaders();
  331. DWORD i = 0;
  332. if (lpszObjectName != NULL) {
  333. memcpy(*lpBuffer, _lpszVerb, _dwVerbLength);
  334. *lpBuffer += _dwVerbLength;
  335. *(*lpBuffer)++ = ' ';
  336. memcpy(*lpBuffer, lpszObjectName, dwObjectNameLength);
  337. *lpBuffer += dwObjectNameLength;
  338. *(*lpBuffer)++ = ' ';
  339. memcpy(*lpBuffer, _lpszVersion, _dwVersionLength);
  340. *lpBuffer += _dwVersionLength;
  341. *(*lpBuffer)++ = '\r';
  342. *(*lpBuffer)++ = '\n';
  343. i = 1;
  344. }
  345. for (; i < _TotalSlots; ++i) {
  346. if (_lpHeaders[i].HaveString()) {
  347. _lpHeaders[i].CopyTo(*lpBuffer);
  348. *lpBuffer += _lpHeaders[i].StringLength();
  349. *(*lpBuffer)++ = '\r';
  350. *(*lpBuffer)++ = '\n';
  351. }
  352. }
  353. UnlockHeaders();
  354. DEBUG_LEAVE(0);
  355. }
  356. #ifdef COMPRESSED_HEADERS
  357. VOID
  358. HTTP_HEADERS::CopyCompressedHeaders(
  359. IN OUT LPSTR * lpBuffer
  360. )
  361. /*++
  362. Routine Description:
  363. Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
  364. This method is called to convert the request headers list to a buffer that
  365. we can send to the server
  366. N.B. This function MUST be called with the headers already locked
  367. Arguments:
  368. lpBuffer - pointer to pointer to buffer where headers are written. We
  369. update the pointer
  370. Return Value:
  371. None.
  372. --*/
  373. {
  374. DEBUG_ENTER((DBG_HTTP,
  375. None,
  376. "CopyCompressedHeaders",
  377. "%#x",
  378. lpBuffer
  379. ));
  380. LPSTR lpszHeaderString;
  381. DWORD j;
  382. LockHeaders();
  383. for (DWORD i = 0; i < _TotalSlots; ++i) {
  384. if (_lpHeaders[i].HaveString()) {
  385. lpszHeaderString = _lpHeaders[i].StringAddress(NULL);
  386. j = LookupHeadermap(lpszHeaderString);
  387. if (j) {
  388. // copy the corresponding short header and bump the pointer
  389. // ahead
  390. DEBUG_PRINT(
  391. HTTP,
  392. INFO,
  393. ("Compressed:%s using %s\n",
  394. lpszHeaderString,
  395. rgsHeaderMap[j].lpszShortHeader
  396. )
  397. );
  398. strcpy(*lpBuffer, rgsHeaderMap[j].lpszShortHeader);
  399. *lpBuffer += rgsHeaderMap[j].dwLenShortHeader;
  400. // then copy the value of the header
  401. // and bump the destination further by the right amount
  402. strncpy(*lpBuffer,
  403. lpszHeaderString + rgsHeaderMap[j].dwLenLongHeader,
  404. _lpHeaders[i].StringLength() - rgsHeaderMap[j].dwLenLongHeader
  405. );
  406. *lpBuffer += (_lpHeaders[i].StringLength() - rgsHeaderMap[j].dwLenLongHeader);
  407. }
  408. else {
  409. // No match found, must be a header we don't know about
  410. DEBUG_PRINT(
  411. HTTP,
  412. INFO,
  413. ("Couldn't compress header for %s\n",
  414. lpszHeaderString
  415. )
  416. );
  417. _lpHeaders[i].CopyTo(*lpBuffer);
  418. *lpBuffer += _lpHeaders[i].StringLength();
  419. }
  420. *(*lpBuffer)++ = '\r';
  421. *(*lpBuffer)++ = '\n';
  422. }
  423. }
  424. UnlockHeaders();
  425. DEBUG_LEAVE(0);
  426. }
  427. #endif //COMPRESSED_HEADERS
  428. HEADER_STRING *
  429. FASTCALL
  430. HTTP_HEADERS::FindFreeSlot(
  431. DWORD* piSlot
  432. )
  433. /*++
  434. Routine Description:
  435. Finds the next free slot in the headers list, or adds some new slots
  436. N.B. This function MUST be called with the headers already locked
  437. Arguments:
  438. piSlot: returns index of slot found
  439. Return Value:
  440. HEADER_STRING * - pointer to next free slot
  441. --*/
  442. {
  443. DEBUG_ENTER((DBG_HTTP,
  444. Pointer,
  445. "FindFreeSlot",
  446. NULL
  447. ));
  448. PERF_ENTER(FindFreeSlot);
  449. DWORD i;
  450. DWORD error;
  451. HEADER_STRING * header = NULL;
  452. //
  453. // if there are no free slots, allocate some more
  454. //
  455. if (_FreeSlots == 0) {
  456. i = _TotalSlots;
  457. error = AllocateHeaders(HEADERS_INCREMENT);
  458. } else {
  459. i = 0;
  460. error = ERROR_SUCCESS;
  461. if (!_lpHeaders[_NextOpenSlot].HaveString())
  462. {
  463. --_FreeSlots;
  464. header = &_lpHeaders[_NextOpenSlot];
  465. *piSlot = _NextOpenSlot;
  466. _NextOpenSlot = (_NextOpenSlot == (_TotalSlots-1)) ? (_TotalSlots-1) : _NextOpenSlot++;
  467. goto quit;
  468. }
  469. }
  470. if (error == ERROR_SUCCESS) {
  471. for (; i < _TotalSlots; ++i) {
  472. if (!_lpHeaders[i].HaveString()) {
  473. --_FreeSlots;
  474. header = &_lpHeaders[i];
  475. *piSlot = i;
  476. _NextOpenSlot = (i == (_TotalSlots-1)) ? (_TotalSlots-1) : (i+1);
  477. break;
  478. }
  479. }
  480. if (header == NULL) {
  481. //
  482. // we would have just allocated extra slots if we didn't have
  483. // any, so we shouldn't be here
  484. //
  485. INET_ASSERT(FALSE);
  486. error = ERROR_INTERNET_INTERNAL_ERROR;
  487. }
  488. }
  489. quit:
  490. _Error = error;
  491. PERF_LEAVE(FindFreeSlot);
  492. DEBUG_LEAVE(header);
  493. return header;
  494. }
  495. VOID
  496. HTTP_HEADERS::ShrinkHeader(
  497. IN LPBYTE pbBase,
  498. IN DWORD iSlot,
  499. IN DWORD dwOldQueryIndex,
  500. IN DWORD dwNewQueryIndex,
  501. IN DWORD cbNewSize
  502. )
  503. /*++
  504. Routine Description:
  505. Low level function that does a surgical replace of one header with another.
  506. This code updates internal structures such as bKnownHeaders and the stored
  507. hash value for the new Header.
  508. N.B. This function MUST be called with the headers already locked
  509. Arguments:
  510. Return Value:
  511. None.
  512. --*/
  513. {
  514. HEADER_STRING* pHeader = _lpHeaders + iSlot;
  515. INET_ASSERT(_bKnownHeaders[dwOldQueryIndex] == (BYTE) iSlot ||
  516. dwNewQueryIndex == dwOldQueryIndex );
  517. //
  518. // Swap in the new header. Update Length, Hash, and its cached position
  519. // in the known header array.
  520. //
  521. _bKnownHeaders[dwOldQueryIndex] = INVALID_HEADER_INDEX;
  522. _bKnownHeaders[dwNewQueryIndex] = (BYTE) iSlot;
  523. pHeader->SetLength (cbNewSize);
  524. pHeader->SetHash (GlobalKnownHeaders[dwNewQueryIndex].HashVal);
  525. }
  526. DWORD
  527. inline
  528. HTTP_HEADERS::SlowFind(
  529. IN LPSTR lpBase,
  530. IN LPSTR lpszHeaderName,
  531. IN DWORD dwHeaderNameLength,
  532. IN DWORD dwIndex,
  533. IN DWORD dwHash,
  534. OUT DWORD *lpdwQueryIndex,
  535. OUT BYTE **lplpbPrevIndex
  536. )
  537. /*++
  538. Routine Description:
  539. Finds the next occurance of lpszHeaderName in the header array, uses
  540. a cached table of well known headers to accerlate the search if the
  541. string is a known header.
  542. N.B. This function MUST be called with the headers already locked
  543. Arguments:
  544. Return Value:
  545. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  546. --*/
  547. {
  548. //
  549. // Now see if this is a known header passed in as a string,
  550. // If it is, we save ourselves the loop, and just map it right in to a known header
  551. //
  552. DWORD dwKnownQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  553. *lpdwQueryIndex = INVALID_HEADER_SLOT;
  554. if ( dwKnownQueryIndex != 0 )
  555. {
  556. dwKnownQueryIndex--;
  557. if ( ((int)dwHeaderNameLength >= GlobalKnownHeaders[dwKnownQueryIndex].Length) &&
  558. strnicmp(lpszHeaderName,
  559. GlobalKnownHeaders[dwKnownQueryIndex].Text,
  560. GlobalKnownHeaders[dwKnownQueryIndex].Length) == 0)
  561. {
  562. *lpdwQueryIndex = dwKnownQueryIndex;
  563. INET_ASSERT((int)(dwHeaderNameLength) == GlobalKnownHeaders[dwKnownQueryIndex].Length);
  564. if ( lplpbPrevIndex )
  565. {
  566. return FastNukeFind(
  567. dwKnownQueryIndex,
  568. dwIndex,
  569. lplpbPrevIndex
  570. );
  571. }
  572. else
  573. {
  574. return FastFind(
  575. dwKnownQueryIndex,
  576. dwIndex
  577. );
  578. }
  579. }
  580. }
  581. //
  582. // Otherwise we painfully enumerate the whole array of headers
  583. //
  584. for (DWORD i = 0; i < _TotalSlots; ++i)
  585. {
  586. HEADER_STRING * pString;
  587. pString = &_lpHeaders[i];
  588. if (!pString->HaveString()) {
  589. continue;
  590. }
  591. if (pString->HashStrnicmp(lpBase,
  592. lpszHeaderName,
  593. dwHeaderNameLength,
  594. dwHash) == 0)
  595. {
  596. //
  597. // if we haven't reached the required index yet, continue
  598. //
  599. if (dwIndex != 0) {
  600. --dwIndex;
  601. continue;
  602. }
  603. return i; // found index/slot
  604. }
  605. }
  606. return INVALID_HEADER_SLOT; // not found
  607. }
  608. DWORD
  609. inline
  610. HTTP_HEADERS::FastFind(
  611. IN DWORD dwQueryIndex,
  612. IN DWORD dwIndex
  613. )
  614. /*++
  615. Routine Description:
  616. Finds the next occurance of a known header string in the lpHeaders array.
  617. Since this is a known string, an index is used to refer to it.
  618. A cached table of well known headers is used to accerlate the search.
  619. N.B. This function MUST be called with the headers already locked
  620. Arguments:
  621. Return Value:
  622. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  623. --*/
  624. {
  625. DWORD dwSlot;
  626. dwSlot = _bKnownHeaders[dwQueryIndex];
  627. while ( (dwIndex > 0) && (dwSlot < INVALID_HEADER_INDEX) )
  628. {
  629. HEADER_STRING * pString;
  630. pString = &_lpHeaders[dwSlot];
  631. dwSlot = pString->GetNextKnownIndex();
  632. dwIndex--;
  633. }
  634. if ( dwSlot >= INVALID_HEADER_INDEX)
  635. {
  636. return INVALID_HEADER_SLOT;
  637. }
  638. return dwSlot; // found it.
  639. }
  640. DWORD
  641. inline
  642. HTTP_HEADERS::FastNukeFind(
  643. IN DWORD dwQueryIndex,
  644. IN DWORD dwIndex,
  645. OUT BYTE **lplpbPrevIndex
  646. )
  647. /*++
  648. Routine Description:
  649. Finds the next occurance of a known header string in the lpHeaders array.
  650. Since this is a known string, an index is used to refer to it.
  651. A cached table of well known headers is used to accerlate the search.
  652. Also provides a ptr to ptr to the slot which directs us to the one found.
  653. This is needed for deletion purposes.
  654. N.B. This function MUST be called with the headers already locked
  655. Arguments:
  656. Return Value:
  657. DWORD - index to Slot in array, or INVALID_HEADER_SLOT if not found
  658. --*/
  659. {
  660. BYTE *lpbSlot;
  661. *lplpbPrevIndex = lpbSlot = &_bKnownHeaders[dwQueryIndex];
  662. dwIndex++;
  663. while ( (dwIndex > 0) && (*lpbSlot < INVALID_HEADER_INDEX) )
  664. {
  665. HEADER_STRING * pString;
  666. pString = &_lpHeaders[*lpbSlot];
  667. *lplpbPrevIndex = lpbSlot;
  668. lpbSlot = pString->GetNextKnownIndexPtr();
  669. dwIndex--;
  670. }
  671. if ( **lplpbPrevIndex >= INVALID_HEADER_INDEX ||
  672. dwIndex > 0 )
  673. {
  674. return INVALID_HEADER_SLOT;
  675. }
  676. return ((DWORD) **lplpbPrevIndex); // found it.
  677. }
  678. VOID
  679. HTTP_HEADERS::RemoveAllByIndex(
  680. IN DWORD dwQueryIndex
  681. )
  682. /*++
  683. Routine Description:
  684. Removes all Known Headers found in the header array.
  685. N.B. This function MUST be called with the headers already locked
  686. Arguments:
  687. dwQueryIndex - index to known header string to remove from array.
  688. Return Value:
  689. None.
  690. --*/
  691. {
  692. BYTE bSlot;
  693. BYTE bPrevSlot;
  694. bSlot = bPrevSlot = _bKnownHeaders[dwQueryIndex];
  695. while (bSlot < INVALID_HEADER_INDEX)
  696. {
  697. HEADER_STRING * pString;
  698. bPrevSlot = bSlot;
  699. pString = &_lpHeaders[bSlot];
  700. bSlot = (BYTE) pString->GetNextKnownIndex();
  701. RemoveHeader(bPrevSlot, dwQueryIndex, &_bKnownHeaders[dwQueryIndex]);
  702. }
  703. _bKnownHeaders[dwQueryIndex] = INVALID_HEADER_INDEX;
  704. return;
  705. }
  706. BOOL
  707. inline
  708. HTTP_HEADERS::HeaderMatch(
  709. IN DWORD dwHash,
  710. IN LPSTR lpszHeaderName,
  711. IN DWORD dwHeaderNameLength,
  712. OUT DWORD *lpdwQueryIndex
  713. )
  714. /*++
  715. Routine Description:
  716. Looks up a Known HTTP header string using its Hash value and
  717. string contained the name of the header.
  718. Arguments:
  719. dwHash - Hash value of header name string
  720. lpszHeaderName - name of header we are matching
  721. dwHeaderNameLength - length of header name string
  722. lpdwQueryIndex - If found, this is the HTTP_QUERY_* based index to the header.
  723. Return Value:
  724. BOOL
  725. Success - The string and hash matched againsted a known header
  726. Failure - There is no known header for that hash & string pair.
  727. --*/
  728. {
  729. *lpdwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  730. if ( *lpdwQueryIndex != 0 )
  731. {
  732. (*lpdwQueryIndex)--;
  733. if ( ((int)dwHeaderNameLength == GlobalKnownHeaders[*lpdwQueryIndex].Length) &&
  734. strnicmp(lpszHeaderName,
  735. GlobalKnownHeaders[*lpdwQueryIndex].Text,
  736. GlobalKnownHeaders[*lpdwQueryIndex].Length) == 0)
  737. {
  738. return TRUE;
  739. }
  740. }
  741. return FALSE;
  742. }
  743. BYTE
  744. inline
  745. HTTP_HEADERS::FastAdd(
  746. IN DWORD dwQueryIndex,
  747. IN DWORD dwSlot
  748. )
  749. /*++
  750. Routine Description:
  751. Rapidly adds a known string to the header array, this function
  752. is used to matain coherency of the _bKnownHeaders which
  753. contained indexed offsets into the header array for known headers.
  754. Note that this function is used instead of latter listed below
  755. in order to maintain proper order in headers received.
  756. N.B. This function MUST be called with the headers already locked
  757. Arguments:
  758. dwQueryIndex - index to known header string to remove from array.
  759. dwSlot - Slot in which this header is being added.
  760. Return Value:
  761. None.
  762. --*/
  763. {
  764. BYTE *lpbSlot;
  765. lpbSlot = &_bKnownHeaders[dwQueryIndex];
  766. while ( (*lpbSlot < INVALID_HEADER_INDEX) )
  767. {
  768. HEADER_STRING * pString;
  769. pString = &_lpHeaders[*lpbSlot];
  770. lpbSlot = pString->GetNextKnownIndexPtr();
  771. }
  772. INET_ASSERT(*lpbSlot == INVALID_HEADER_INDEX);
  773. *lpbSlot = (BYTE) dwSlot;
  774. return INVALID_HEADER_INDEX;
  775. }
  776. //BYTE
  777. //inline
  778. //HTTP_HEADERS::FastAdd(
  779. // IN DWORD dwQueryIndex,
  780. // IN DWORD dwSlot
  781. // )
  782. //{
  783. // BYTE bOldSlot;
  784. //
  785. // bOldSlot = _bKnownHeaders[dwQueryIndex];
  786. // _bKnownHeaders[dwQueryIndex] = (BYTE) dwSlot;
  787. //
  788. // return bOldSlot;
  789. //}
  790. DWORD
  791. HTTP_HEADERS::AddHeader(
  792. IN LPSTR lpszHeaderName,
  793. IN DWORD dwHeaderNameLength,
  794. IN LPSTR lpszHeaderValue,
  795. IN DWORD dwHeaderValueLength,
  796. IN DWORD dwIndex,
  797. IN DWORD dwFlags
  798. )
  799. /*++
  800. Routine Description:
  801. Adds a single header to the array of headers, given the header name and
  802. value. Called via HttpOpenRequest()
  803. Arguments:
  804. lpszHeaderName - pointer to name of header to add, e.g. "Accept:"
  805. dwHeaderNameLength - length of the header name
  806. lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
  807. dwHeaderValueLength - length of the header value
  808. dwIndex - if coalescing headers, index of header to update
  809. dwFlags - flags controlling function:
  810. COALESCE_HEADER_WITH_COMMA
  811. COALESCE_HEADER_WITH_SEMICOLON
  812. - headers of the same name can be combined
  813. CLEAN_HEADER
  814. - header is supplied by user, so we must ensure
  815. it has correct format
  816. Return Value:
  817. DWORD
  818. Success - ERROR_SUCCESS
  819. Failure - ERROR_NOT_ENOUGH_MEMORY
  820. Ran out of memory allocating string
  821. ERROR_INVALID_PARAMETER
  822. The header value was bad
  823. --*/
  824. {
  825. DEBUG_ENTER((DBG_HTTP,
  826. Dword,
  827. "AddHeader",
  828. "%.*q, %d, %.*q, %d, %d, %#x",
  829. min(dwHeaderNameLength + 1, 80),
  830. lpszHeaderName,
  831. dwHeaderNameLength,
  832. min(dwHeaderValueLength + 1, 80),
  833. lpszHeaderValue,
  834. dwHeaderValueLength,
  835. dwIndex,
  836. dwFlags
  837. ));
  838. PERF_ENTER(AddHeader);
  839. LockHeaders();
  840. INET_ASSERT(lpszHeaderName != NULL);
  841. INET_ASSERT(*lpszHeaderName != '\0');
  842. INET_ASSERT(dwHeaderNameLength != 0);
  843. INET_ASSERT(lpszHeaderValue != NULL);
  844. INET_ASSERT(*lpszHeaderValue != '\0');
  845. INET_ASSERT(dwHeaderValueLength != 0);
  846. INET_ASSERT(_FreeSlots <= _TotalSlots);
  847. //
  848. // we may have been handed a header with a trailing colon. We don't care
  849. // for such nasty imagery
  850. //
  851. if (lpszHeaderName[dwHeaderNameLength - 1] == ':') {
  852. --dwHeaderNameLength;
  853. }
  854. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  855. DWORD dwQueryIndex;
  856. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  857. DWORD i = 0;
  858. //
  859. // if we are coalescing headers then find a header with the same name
  860. //
  861. if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
  862. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
  863. {
  864. DWORD dwSlot;
  865. dwSlot = SlowFind(
  866. NULL,
  867. lpszHeaderName,
  868. dwHeaderNameLength,
  869. dwIndex,
  870. dwHash,
  871. &dwQueryIndex,
  872. NULL
  873. );
  874. if (dwSlot != ((DWORD) -1))
  875. {
  876. HEADER_STRING * pString;
  877. pString = &_lpHeaders[dwSlot];
  878. //
  879. // found what we are looking for. Coalesce it
  880. //
  881. pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
  882. pString->Strncat(
  883. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
  884. "; " :
  885. ", ",
  886. 2);
  887. pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
  888. _HeadersLength += 2 + dwHeaderValueLength;
  889. error = ERROR_SUCCESS;
  890. }
  891. }
  892. else
  893. {
  894. //
  895. // Check to verify that the header we're adding is a known header,
  896. // If its a known header we use dwQueryIndex to update the known header array
  897. // otherwise, IF ITS NOT, we make sure to set dwQueryIndex to INVALID_...
  898. //
  899. if (! HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwQueryIndex) )
  900. {
  901. dwQueryIndex = INVALID_HEADER_SLOT;
  902. }
  903. /*
  904. // Perhaps this more efficent ???
  905. dwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
  906. if ( dwQueryIndex != 0 )
  907. {
  908. dwQueryIndex--;
  909. if ( ((int)dwHeaderNameLength < GlobalKnownHeaders[dwQueryIndex].Length) ||
  910. strnicmp(lpszHeaderName,
  911. GlobalKnownHeaders[dwQueryIndex].Text,
  912. GlobalKnownHeaders[dwQueryIndex].Length) != 0)
  913. {
  914. dwQueryIndex = INVALID_HEADER_SLOT;
  915. }
  916. }
  917. else
  918. {
  919. dwQueryIndex = INVALID_HEADER_SLOT;
  920. }
  921. */
  922. }
  923. //
  924. // if we didn't find the header value or we are not coalescing then add the
  925. // header
  926. //
  927. if (error == ERROR_HTTP_HEADER_NOT_FOUND)
  928. {
  929. //
  930. // find the next slot for this header
  931. //
  932. HEADER_STRING * freeHeader;
  933. DWORD iSlot;
  934. freeHeader = FindFreeSlot(&iSlot);
  935. if (freeHeader == NULL) {
  936. error = GetError();
  937. INET_ASSERT(error != ERROR_SUCCESS);
  938. goto quit;
  939. }
  940. freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
  941. dwHeaderNameLength,
  942. dwHeaderNameLength
  943. + sizeof(": ") - 1
  944. + dwHeaderValueLength
  945. + 1 // for extra NULL terminator
  946. );
  947. if (freeHeader->IsError()) {
  948. error = ::GetLastError();
  949. INET_ASSERT(error != ERROR_SUCCESS);
  950. goto quit;
  951. }
  952. freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
  953. freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
  954. _HeadersLength += dwHeaderNameLength
  955. + (sizeof(": ") - 1)
  956. + dwHeaderValueLength
  957. + (sizeof("\r\n") - 1)
  958. ;
  959. freeHeader->SetHash(dwHash);
  960. if ( dwQueryIndex != INVALID_HEADER_SLOT )
  961. {
  962. freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
  963. }
  964. error = ERROR_SUCCESS;
  965. }
  966. quit:
  967. UnlockHeaders();
  968. PERF_LEAVE(AddHeader);
  969. DEBUG_LEAVE(error);
  970. return error;
  971. }
  972. DWORD
  973. HTTP_HEADERS::AddHeader(
  974. IN DWORD dwQueryIndex,
  975. IN LPSTR lpszHeaderValue,
  976. IN DWORD dwHeaderValueLength,
  977. IN DWORD dwIndex,
  978. IN DWORD dwFlags
  979. )
  980. /*++
  981. Routine Description:
  982. Adds a single header to the array of headers, given the header name and
  983. value. Called via HttpOpenRequest()
  984. Arguments:
  985. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  986. lpszHeaderValue - pointer to value of header to add, e.g. "text/html"
  987. dwHeaderValueLength - length of the header value
  988. dwIndex - if coalescing headers, index of header to update
  989. dwFlags - flags controlling function:
  990. COALESCE_HEADER_WITH_COMMA
  991. COALESCE_HEADER_WITH_SEMICOLON
  992. - headers of the same name can be combined
  993. CLEAN_HEADER
  994. - header is supplied by user, so we must ensure
  995. it has correct format
  996. Return Value:
  997. DWORD
  998. Success - ERROR_SUCCESS
  999. Failure - ERROR_NOT_ENOUGH_MEMORY
  1000. Ran out of memory allocating string
  1001. ERROR_INVALID_PARAMETER
  1002. The header value was bad
  1003. --*/
  1004. {
  1005. DEBUG_ENTER((DBG_HTTP,
  1006. Dword,
  1007. "AddHeader",
  1008. "%q, %u, %.*q, %d, %d, %#x",
  1009. GlobalKnownHeaders[dwQueryIndex].Text,
  1010. dwQueryIndex,
  1011. min(dwHeaderValueLength + 1, 80),
  1012. lpszHeaderValue,
  1013. dwHeaderValueLength,
  1014. dwIndex,
  1015. dwFlags
  1016. ));
  1017. PERF_ENTER(AddHeader);
  1018. INET_ASSERT(dwQueryIndex <= HTTP_QUERY_MAX);
  1019. INET_ASSERT(lpszHeaderValue != NULL);
  1020. INET_ASSERT(*lpszHeaderValue != '\0');
  1021. INET_ASSERT(dwHeaderValueLength != 0);
  1022. INET_ASSERT(_FreeSlots <= _TotalSlots);
  1023. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1024. DWORD i = 0;
  1025. LPSTR lpszHeaderName;
  1026. DWORD dwHeaderNameLength;
  1027. DWORD dwHash;
  1028. dwHash = GlobalKnownHeaders[dwQueryIndex].HashVal;
  1029. lpszHeaderName = GlobalKnownHeaders[dwQueryIndex].Text;
  1030. dwHeaderNameLength = GlobalKnownHeaders[dwQueryIndex].Length;
  1031. //
  1032. // if we are coalescing headers then find a header with the same name
  1033. //
  1034. if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
  1035. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
  1036. {
  1037. DWORD dwSlot;
  1038. dwSlot = FastFind(
  1039. dwQueryIndex,
  1040. dwIndex
  1041. );
  1042. if (dwSlot != INVALID_HEADER_SLOT)
  1043. {
  1044. HEADER_STRING * pString;
  1045. pString = &_lpHeaders[dwSlot];
  1046. //
  1047. // found what we are looking for. Coalesce it
  1048. //
  1049. pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
  1050. pString->Strncat(
  1051. (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
  1052. "; " :
  1053. ", ",
  1054. 2);
  1055. pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
  1056. _HeadersLength += 2 + dwHeaderValueLength;
  1057. error = ERROR_SUCCESS;
  1058. }
  1059. }
  1060. //
  1061. // if we didn't find the header value or we are not coalescing then add the
  1062. // header
  1063. //
  1064. if (error == ERROR_HTTP_HEADER_NOT_FOUND)
  1065. {
  1066. //
  1067. // find the next slot for this header
  1068. //
  1069. HEADER_STRING * freeHeader;
  1070. DWORD iSlot;
  1071. freeHeader = FindFreeSlot(&iSlot);
  1072. if (freeHeader == NULL) {
  1073. error = GetError();
  1074. INET_ASSERT(error != ERROR_SUCCESS);
  1075. goto quit;
  1076. }
  1077. freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
  1078. dwHeaderNameLength,
  1079. dwHeaderNameLength
  1080. + sizeof(": ") - 1
  1081. + dwHeaderValueLength
  1082. + 1 // for extra NULL terminator
  1083. );
  1084. if (freeHeader->IsError()) {
  1085. error = ::GetLastError();
  1086. INET_ASSERT(error != ERROR_SUCCESS);
  1087. goto quit;
  1088. }
  1089. freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
  1090. freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
  1091. _HeadersLength += dwHeaderNameLength
  1092. + (sizeof(": ") - 1)
  1093. + dwHeaderValueLength
  1094. + (sizeof("\r\n") - 1)
  1095. ;
  1096. freeHeader->SetHash(dwHash);
  1097. freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
  1098. error = ERROR_SUCCESS;
  1099. }
  1100. quit:
  1101. PERF_LEAVE(AddHeader);
  1102. DEBUG_LEAVE(error);
  1103. return error;
  1104. }
  1105. DWORD
  1106. HTTP_HEADERS::ReplaceHeader(
  1107. IN LPSTR lpszHeaderName,
  1108. IN DWORD dwHeaderNameLength,
  1109. IN LPSTR lpszHeaderValue,
  1110. IN DWORD dwHeaderValueLength,
  1111. IN DWORD dwIndex,
  1112. IN DWORD dwFlags
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. Replaces a HTTP (request) header. The header can be replaced with a NULL
  1117. value, meaning that the header is removed
  1118. Arguments:
  1119. lpszHeaderName - pointer to the header name
  1120. dwHeaderNameLength - length of the header name
  1121. lpszHeaderValue - pointer to the header value
  1122. dwHeaderValueLength - length of the header value
  1123. dwIndex - index of header to replace
  1124. dwFlags - flags controlling function. Allowed flags are:
  1125. COALESCE_HEADER_WITH_COMMA
  1126. COALESCE_HEADER_WITH_SEMICOLON
  1127. - headers of the same name can be combined
  1128. ADD_HEADER
  1129. - if the header-name is not found and there is
  1130. a valid header-value, then the header is added
  1131. ADD_HEADER_IF_NEW
  1132. - if the header-name exists then we return an
  1133. error, else we add the header-value
  1134. Return Value:
  1135. DWORD
  1136. Success - ERROR_SUCCESS
  1137. Failure - ERROR_HTTP_HEADER_NOT_FOUND
  1138. The requested header wasn't found
  1139. ERROR_HTTP_HEADER_ALREADY_EXISTS
  1140. The header already exists, and was not added or replaced
  1141. --*/
  1142. {
  1143. DEBUG_ENTER((DBG_HTTP,
  1144. Dword,
  1145. "ReplaceHeader",
  1146. "%.*q, %d, %.*q, %d, %d, %#x",
  1147. min(dwHeaderNameLength + 1, 80),
  1148. lpszHeaderName,
  1149. dwHeaderNameLength,
  1150. min(dwHeaderValueLength + 1, 80),
  1151. lpszHeaderValue,
  1152. dwHeaderValueLength,
  1153. dwIndex,
  1154. dwFlags
  1155. ));
  1156. PERF_ENTER(ReplaceHeader);
  1157. INET_ASSERT(lpszHeaderName != NULL);
  1158. INET_ASSERT(dwHeaderNameLength != 0);
  1159. INET_ASSERT(lpszHeaderName[dwHeaderNameLength - 1] != ':');
  1160. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1161. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  1162. DWORD dwSlot;
  1163. DWORD dwQueryIndex;
  1164. BYTE *pbPrevByte;
  1165. LockHeaders();
  1166. dwSlot = SlowFind(
  1167. NULL,
  1168. lpszHeaderName,
  1169. dwHeaderNameLength,
  1170. dwIndex,
  1171. dwHash,
  1172. &dwQueryIndex,
  1173. &pbPrevByte
  1174. );
  1175. if ( dwSlot != ((DWORD) -1))
  1176. {
  1177. //
  1178. // if ADD_HEADER_IF_NEW is set, then we already have the header
  1179. //
  1180. if (dwFlags & ADD_HEADER_IF_NEW) {
  1181. error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
  1182. goto quit;
  1183. }
  1184. //
  1185. // for both replace and remove operations, we are going to remove
  1186. // the current header
  1187. //
  1188. RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
  1189. //
  1190. // if replacing then add the new header value
  1191. //
  1192. if (dwHeaderValueLength != 0)
  1193. {
  1194. if ( dwQueryIndex != ((DWORD) -1) )
  1195. {
  1196. error = AddHeader(dwQueryIndex,
  1197. lpszHeaderValue,
  1198. dwHeaderValueLength,
  1199. 0,
  1200. dwFlags
  1201. );
  1202. }
  1203. else
  1204. {
  1205. error = AddHeader(lpszHeaderName,
  1206. dwHeaderNameLength,
  1207. lpszHeaderValue,
  1208. dwHeaderValueLength,
  1209. 0,
  1210. dwFlags
  1211. );
  1212. }
  1213. } else {
  1214. error = ERROR_SUCCESS;
  1215. }
  1216. }
  1217. //
  1218. // if we didn't find the header but ADD_HEADER is set then we simply add it
  1219. // but only if the value length is not zero
  1220. //
  1221. if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
  1222. && (dwHeaderValueLength != 0)
  1223. && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
  1224. {
  1225. if ( dwQueryIndex != ((DWORD) -1) )
  1226. {
  1227. error = AddHeader(dwQueryIndex,
  1228. lpszHeaderValue,
  1229. dwHeaderValueLength,
  1230. 0,
  1231. dwFlags
  1232. );
  1233. }
  1234. else
  1235. {
  1236. error = AddHeader(lpszHeaderName,
  1237. dwHeaderNameLength,
  1238. lpszHeaderValue,
  1239. dwHeaderValueLength,
  1240. 0,
  1241. dwFlags
  1242. );
  1243. }
  1244. }
  1245. quit:
  1246. UnlockHeaders();
  1247. PERF_LEAVE(ReplaceHeader);
  1248. DEBUG_LEAVE(error);
  1249. return error;
  1250. }
  1251. DWORD
  1252. HTTP_HEADERS::ReplaceHeader(
  1253. IN DWORD dwQueryIndex,
  1254. IN LPSTR lpszHeaderValue,
  1255. IN DWORD dwHeaderValueLength,
  1256. IN DWORD dwIndex,
  1257. IN DWORD dwFlags
  1258. )
  1259. /*++
  1260. Routine Description:
  1261. Replaces a HTTP (request) header. The header can be replaced with a NULL
  1262. value, meaning that the header is removed
  1263. Arguments:
  1264. lpszHeaderValue - pointer to the header value
  1265. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1266. dwHeaderValueLength - length of the header value
  1267. dwIndex - index of header to replace
  1268. dwFlags - flags controlling function. Allowed flags are:
  1269. COALESCE_HEADER_WITH_COMMA
  1270. COALESCE_HEADER_WITH_SEMICOLON
  1271. - headers of the same name can be combined
  1272. ADD_HEADER
  1273. - if the header-name is not found and there is
  1274. a valid header-value, then the header is added
  1275. ADD_HEADER_IF_NEW
  1276. - if the header-name exists then we return an
  1277. error, else we add the header-value
  1278. Return Value:
  1279. DWORD
  1280. Success - ERROR_SUCCESS
  1281. Failure - ERROR_HTTP_HEADER_NOT_FOUND
  1282. The requested header wasn't found
  1283. ERROR_HTTP_HEADER_ALREADY_EXISTS
  1284. The header already exists, and was not added or replaced
  1285. --*/
  1286. {
  1287. DEBUG_ENTER((DBG_HTTP,
  1288. Dword,
  1289. "ReplaceHeader",
  1290. "%q, %u, %.*q, %d, %d, %#x",
  1291. GlobalKnownHeaders[dwQueryIndex].Text,
  1292. dwQueryIndex,
  1293. min(dwHeaderValueLength + 1, 80),
  1294. lpszHeaderValue,
  1295. dwHeaderValueLength,
  1296. dwIndex,
  1297. dwFlags
  1298. ));
  1299. PERF_ENTER(ReplaceHeader);
  1300. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1301. DWORD dwSlot;
  1302. BYTE *pbPrevByte;
  1303. LockHeaders();
  1304. dwSlot = FastNukeFind(
  1305. dwQueryIndex,
  1306. dwIndex,
  1307. &pbPrevByte
  1308. );
  1309. if ( dwSlot != INVALID_HEADER_SLOT)
  1310. {
  1311. //
  1312. // if ADD_HEADER_IF_NEW is set, then we already have the header
  1313. //
  1314. if (dwFlags & ADD_HEADER_IF_NEW) {
  1315. error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
  1316. goto quit;
  1317. }
  1318. //
  1319. // for both replace and remove operations, we are going to remove
  1320. // the current header
  1321. //
  1322. RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
  1323. //
  1324. // if replacing then add the new header value
  1325. //
  1326. if (dwHeaderValueLength != 0)
  1327. {
  1328. error = AddHeader(dwQueryIndex,
  1329. lpszHeaderValue,
  1330. dwHeaderValueLength,
  1331. 0,
  1332. dwFlags
  1333. );
  1334. } else {
  1335. error = ERROR_SUCCESS;
  1336. }
  1337. }
  1338. //
  1339. // if we didn't find the header but ADD_HEADER is set then we simply add it
  1340. // but only if the value length is not zero
  1341. //
  1342. if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
  1343. && (dwHeaderValueLength != 0)
  1344. && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
  1345. {
  1346. error = AddHeader(dwQueryIndex,
  1347. lpszHeaderValue,
  1348. dwHeaderValueLength,
  1349. 0,
  1350. dwFlags
  1351. );
  1352. }
  1353. quit:
  1354. UnlockHeaders();
  1355. PERF_LEAVE(ReplaceHeader);
  1356. DEBUG_LEAVE(error);
  1357. return error;
  1358. }
  1359. DWORD
  1360. HTTP_HEADERS::FindHeader(
  1361. IN LPSTR lpBase,
  1362. IN LPSTR lpszHeaderName,
  1363. IN DWORD dwHeaderNameLength,
  1364. IN DWORD dwModifiers,
  1365. OUT LPVOID lpBuffer,
  1366. IN OUT LPDWORD lpdwBufferLength,
  1367. IN OUT LPDWORD lpdwIndex
  1368. )
  1369. /*++
  1370. Routine Description:
  1371. Finds a request or response header
  1372. Arguments:
  1373. lpBase - base for offset HEADER_STRINGs
  1374. lpszHeaderName - pointer to header name
  1375. dwHeaderNameLength - length of header name
  1376. dwModifiers - flags which modify returned value
  1377. lpBuffer - pointer to buffer for results
  1378. lpdwBufferLength - IN: length of lpBuffer
  1379. OUT: length of results, or required length of lpBuffer
  1380. lpdwIndex - IN: 0-based index of header to find
  1381. OUT: next header index if success returned
  1382. Return Value:
  1383. DWORD
  1384. Success - ERROR_SUCCESS
  1385. Failure - ERROR_INSUFFICIENT_BUFFER
  1386. *lpdwBufferLength contains the amount required
  1387. ERROR_HTTP_HEADER_NOT_FOUND
  1388. The specified header (or index of header) was not found
  1389. --*/
  1390. {
  1391. DEBUG_ENTER((DBG_HTTP,
  1392. Dword,
  1393. "HTTP_HEADERS::FindHeader",
  1394. "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
  1395. lpszHeaderName,
  1396. min(dwHeaderNameLength + 1, 80),
  1397. lpszHeaderName,
  1398. dwHeaderNameLength,
  1399. lpBuffer,
  1400. lpdwBufferLength,
  1401. *lpdwBufferLength,
  1402. dwModifiers,
  1403. lpdwIndex,
  1404. *lpdwIndex
  1405. ));
  1406. PERF_ENTER(FindHeader);
  1407. INET_ASSERT(lpdwIndex != NULL);
  1408. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1409. DWORD dwSlot;
  1410. HEADER_STRING * pString;
  1411. DWORD dwQueryIndex;
  1412. DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
  1413. LockHeaders();
  1414. dwSlot = SlowFind(
  1415. lpBase,
  1416. lpszHeaderName,
  1417. dwHeaderNameLength,
  1418. *lpdwIndex,
  1419. dwHash,
  1420. &dwQueryIndex,
  1421. NULL
  1422. );
  1423. if ( dwSlot != ((DWORD) -1) )
  1424. {
  1425. pString = &_lpHeaders[dwSlot];
  1426. //
  1427. // found the header - get to the value
  1428. //
  1429. DWORD stringLen;
  1430. LPSTR value;
  1431. stringLen = pString->StringLength();
  1432. INET_ASSERT(stringLen > dwHeaderNameLength);
  1433. //
  1434. // get a pointer to the value string
  1435. //
  1436. value = pString->StringAddress(lpBase) + dwHeaderNameLength;
  1437. stringLen -= dwHeaderNameLength;
  1438. //
  1439. // the input string could be a substring of a different header
  1440. //
  1441. //INET_ASSERT(*value != ':');
  1442. //
  1443. // find the first non-space character in the value.
  1444. //
  1445. // N.B.: Servers can return empty headers, so we may end up with a
  1446. // zero length string
  1447. //
  1448. do {
  1449. ++value;
  1450. --stringLen;
  1451. } while ((stringLen > 0) && (*value == ' '));
  1452. //
  1453. // get the data in the format requested by the app
  1454. //
  1455. LPVOID lpData;
  1456. DWORD dwDataSize;
  1457. DWORD dwRequiredSize;
  1458. SYSTEMTIME systemTime;
  1459. DWORD number;
  1460. //
  1461. // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
  1462. // really be success either...
  1463. //
  1464. error = ERROR_SUCCESS;
  1465. if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME) {
  1466. char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
  1467. if (stringLen < sizeof(buf)) {
  1468. //
  1469. // value probably does not point at a zero-terminated string
  1470. // which HttpDateToSystemTime() expects, so we make a copy
  1471. // and terminate it
  1472. //
  1473. memcpy((LPVOID)buf, (LPVOID)value, stringLen);
  1474. buf[stringLen] = '\0';
  1475. if (HttpDateToSystemTime(buf, &systemTime)) {
  1476. lpData = (LPVOID)&systemTime;
  1477. dwRequiredSize = dwDataSize = sizeof(systemTime);
  1478. } else {
  1479. //
  1480. // couldn't convert date/time. Presume header must be bogus
  1481. //
  1482. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1483. DEBUG_PRINT(HTTP,
  1484. ERROR,
  1485. ("cannot convert %.40q to SYSTEMTIME\n",
  1486. value
  1487. ));
  1488. }
  1489. } else {
  1490. //
  1491. // we would break the date/time buffer!
  1492. //
  1493. error = ERROR_INTERNET_INTERNAL_ERROR;
  1494. }
  1495. } else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
  1496. if (isdigit(*value)) {
  1497. number = 0;
  1498. for (int i = 0;
  1499. (stringLen > 0) && isdigit(value[i]);
  1500. ++i, --stringLen) {
  1501. number = number * 10 + (DWORD)(value[i] - '0');
  1502. }
  1503. lpData = (LPVOID)&number;
  1504. dwRequiredSize = dwDataSize = sizeof(number);
  1505. } else {
  1506. //
  1507. // not a numeric field. Request must be bogus for this header
  1508. //
  1509. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1510. DEBUG_PRINT(HTTP,
  1511. ERROR,
  1512. ("cannot convert %.20q to NUMBER\n",
  1513. value
  1514. ));
  1515. }
  1516. } else {
  1517. lpData = (LPVOID)value;
  1518. dwDataSize = stringLen;
  1519. dwRequiredSize = dwDataSize + 1;
  1520. }
  1521. //
  1522. // if error == ERROR_SUCCESS then we can attempt to copy the data
  1523. //
  1524. if (error == ERROR_SUCCESS) {
  1525. if (*lpdwBufferLength < dwRequiredSize) {
  1526. *lpdwBufferLength = dwRequiredSize;
  1527. error = ERROR_INSUFFICIENT_BUFFER;
  1528. } else {
  1529. memcpy(lpBuffer, lpData, dwDataSize);
  1530. *lpdwBufferLength = dwDataSize;
  1531. //
  1532. // if dwRequiredSize > dwDataSize, then this is a variable-
  1533. // length item (i.e. a STRING!) so we add a terminating '\0'
  1534. //
  1535. if (dwRequiredSize > dwDataSize) {
  1536. INET_ASSERT(dwRequiredSize - dwDataSize == 1);
  1537. ((LPSTR)lpBuffer)[dwDataSize] = '\0';
  1538. }
  1539. //
  1540. // successfully retrieved the requested header - bump the
  1541. // index
  1542. //
  1543. ++*lpdwIndex;
  1544. }
  1545. }
  1546. }
  1547. UnlockHeaders();
  1548. PERF_LEAVE(FindHeader);
  1549. DEBUG_LEAVE(error);
  1550. return error;
  1551. }
  1552. DWORD
  1553. HTTP_HEADERS::FindHeader(
  1554. IN LPSTR lpBase,
  1555. IN DWORD dwQueryIndex,
  1556. IN DWORD dwModifiers,
  1557. OUT LPVOID lpBuffer,
  1558. IN OUT LPDWORD lpdwBufferLength,
  1559. IN OUT LPDWORD lpdwIndex
  1560. )
  1561. /*++
  1562. Routine Description:
  1563. Finds a request or response header, based on index to the header name we are searching for.
  1564. Arguments:
  1565. lpBase - base for offset HEADER_STRINGs
  1566. dwQueryIndex - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1567. dwModifiers - flags which modify returned value
  1568. lpBuffer - pointer to buffer for results
  1569. lpdwBufferLength - IN: length of lpBuffer
  1570. OUT: length of results, or required length of lpBuffer
  1571. lpdwIndex - IN: 0-based index of header to find
  1572. OUT: next header index if success returned
  1573. Return Value:
  1574. DWORD
  1575. Success - ERROR_SUCCESS
  1576. Failure - ERROR_INSUFFICIENT_BUFFER
  1577. *lpdwBufferLength contains the amount required
  1578. ERROR_HTTP_HEADER_NOT_FOUND
  1579. The specified header (or index of header) was not found
  1580. --*/
  1581. {
  1582. DWORD error;
  1583. LPSTR lpData;
  1584. DWORD dwDataSize = 0;
  1585. DWORD dwRequiredSize;
  1586. SYSTEMTIME systemTime;
  1587. DWORD number;
  1588. error = FastFindHeader(
  1589. lpBase,
  1590. dwQueryIndex,
  1591. (LPVOID *)&lpData,
  1592. &dwDataSize,
  1593. *lpdwIndex
  1594. );
  1595. if ( error != ERROR_SUCCESS )
  1596. {
  1597. goto quit;
  1598. }
  1599. //
  1600. // get the data in the format requested by the app
  1601. //
  1602. if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME)
  1603. {
  1604. char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
  1605. if (dwDataSize < sizeof(buf))
  1606. {
  1607. //
  1608. // value probably does not point at a zero-terminated string
  1609. // which HttpDateToSystemTime() expects, so we make a copy
  1610. // and terminate it
  1611. //
  1612. memcpy((LPVOID)buf, (LPVOID)lpData, dwDataSize);
  1613. buf[dwDataSize] = '\0';
  1614. if (HttpDateToSystemTime(buf, &systemTime)) {
  1615. lpData = (LPSTR)&systemTime;
  1616. dwRequiredSize = dwDataSize = sizeof(systemTime);
  1617. } else {
  1618. //
  1619. // couldn't convert date/time. Presume header must be bogus
  1620. //
  1621. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1622. DEBUG_PRINT(HTTP,
  1623. ERROR,
  1624. ("cannot convert %.40q to SYSTEMTIME\n",
  1625. lpData
  1626. ));
  1627. }
  1628. }
  1629. else
  1630. {
  1631. //
  1632. // we would break the date/time buffer!
  1633. //
  1634. error = ERROR_INTERNET_INTERNAL_ERROR;
  1635. }
  1636. }
  1637. else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER)
  1638. {
  1639. if (isdigit(*lpData)) {
  1640. number = 0;
  1641. for (int i = 0;
  1642. (dwDataSize > 0) && isdigit(lpData[i]);
  1643. ++i, --dwDataSize) {
  1644. number = number * 10 + (DWORD)(lpData[i] - '0');
  1645. }
  1646. lpData = (LPSTR)&number;
  1647. dwRequiredSize = dwDataSize = sizeof(number);
  1648. } else {
  1649. //
  1650. // not a numeric field. Request must be bogus for this header
  1651. //
  1652. error = ERROR_HTTP_INVALID_QUERY_REQUEST;
  1653. DEBUG_PRINT(HTTP,
  1654. ERROR,
  1655. ("cannot convert %.20q to NUMBER\n",
  1656. lpData
  1657. ));
  1658. }
  1659. }
  1660. else
  1661. {
  1662. dwRequiredSize = dwDataSize + 1;
  1663. }
  1664. //
  1665. // if error == ERROR_SUCCESS then we can attempt to copy the data
  1666. //
  1667. if (error == ERROR_SUCCESS)
  1668. {
  1669. if (*lpdwBufferLength < dwRequiredSize)
  1670. {
  1671. *lpdwBufferLength = dwRequiredSize;
  1672. error = ERROR_INSUFFICIENT_BUFFER;
  1673. }
  1674. else
  1675. {
  1676. memcpy(lpBuffer, lpData, dwDataSize);
  1677. *lpdwBufferLength = dwDataSize;
  1678. //
  1679. // if dwRequiredSize > dwDataSize, then this is a variable-
  1680. // length item (i.e. a STRING!) so we add a terminating '\0'
  1681. //
  1682. if (dwRequiredSize > dwDataSize)
  1683. {
  1684. INET_ASSERT(dwRequiredSize - dwDataSize == 1);
  1685. ((LPSTR)lpBuffer)[dwDataSize] = '\0';
  1686. }
  1687. //
  1688. // successfully retrieved the requested header - bump the
  1689. // index
  1690. //
  1691. ++*lpdwIndex;
  1692. }
  1693. }
  1694. quit:
  1695. return error;
  1696. }
  1697. DWORD
  1698. HTTP_HEADERS::FastFindHeader(
  1699. IN LPSTR lpBase,
  1700. IN DWORD dwQueryIndex,
  1701. OUT LPVOID *lplpBuffer,
  1702. IN OUT LPDWORD lpdwBufferLength,
  1703. IN DWORD dwIndex
  1704. )
  1705. /*++
  1706. Routine Description:
  1707. Finds a request or response header slightly quicker than its higher level
  1708. cousin, FindHeader. Unlike FindHeader this function simply returns
  1709. a pointer and length, and does not copy header data.
  1710. lpBase - base address of strings
  1711. dwQueryIndex - a index into a array known HTTP headers, see wininet.h HTTP_QUERY_* codes
  1712. lplpBuffer - pointer to pointer of the actual header to be returned in.
  1713. lpdwBufferLength - OUT: if successful, length of output buffer, minus 1
  1714. for any trailing EOS, or if the buffer is not
  1715. large enough, the size required
  1716. dwIndex - a index of which header we're asking for, as there can be multiple headers
  1717. under the same name.
  1718. Arguments:
  1719. lpBase - base for offset HEADER_STRINGs
  1720. lpszHeaderName - pointer to header name
  1721. dwHeaderNameLength - length of header name
  1722. dwModifiers - flags which modify returned value
  1723. lpBuffer - pointer to buffer for results
  1724. lpdwBufferLength - IN: length of lpBuffer
  1725. OUT: length of results, or required length of lpBuffer
  1726. lpdwIndex - IN: 0-based index of header to find
  1727. OUT: next header index if success returned
  1728. Return Value:
  1729. DWORD
  1730. Success - ERROR_SUCCESS
  1731. Failure - ERROR_INSUFFICIENT_BUFFER
  1732. *lpdwBufferLength contains the amount required
  1733. ERROR_HTTP_HEADER_NOT_FOUND
  1734. The specified header (or index of header) was not found
  1735. --*/
  1736. {
  1737. DEBUG_ENTER((DBG_HTTP,
  1738. Dword,
  1739. "HTTP_HEADERS::FastFindHeader",
  1740. "%q, %#x, %#x [%#x], %u",
  1741. GlobalKnownHeaders[dwQueryIndex].Text,
  1742. lplpBuffer,
  1743. lpdwBufferLength,
  1744. *lpdwBufferLength,
  1745. dwIndex
  1746. ));
  1747. PERF_ENTER(FindHeader);
  1748. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  1749. HEADER_STRING * curHeader;
  1750. DWORD dwSlot;
  1751. dwSlot = FastFind(dwQueryIndex, dwIndex);
  1752. if ( dwSlot != INVALID_HEADER_SLOT)
  1753. {
  1754. //
  1755. // found the header - get to the value
  1756. //
  1757. DWORD stringLen;
  1758. LPSTR value;
  1759. curHeader = GetSlot(dwSlot);
  1760. //
  1761. // get a pointer to the value string
  1762. //
  1763. value = curHeader->StringAddress(lpBase) + (GlobalKnownHeaders[dwQueryIndex].Length+1);
  1764. stringLen = curHeader->StringLength() - (GlobalKnownHeaders[dwQueryIndex].Length+1);
  1765. //
  1766. // find the first non-space character in the value.
  1767. //
  1768. // N.B.: Servers can return empty headers, so we may end up with a
  1769. // zero length string
  1770. //
  1771. while ((stringLen > 0) && (*value == ' '))
  1772. {
  1773. ++value;
  1774. --stringLen;
  1775. }
  1776. //
  1777. // get the data in the format requested by the app
  1778. //
  1779. //
  1780. // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
  1781. // really be success either...
  1782. //
  1783. error = ERROR_SUCCESS;
  1784. *lplpBuffer = (LPVOID)value;
  1785. *lpdwBufferLength = stringLen;
  1786. }
  1787. PERF_LEAVE(FindHeader);
  1788. DEBUG_LEAVE(error);
  1789. return error;
  1790. }
  1791. DWORD
  1792. HTTP_HEADERS::QueryRawHeaders(
  1793. IN LPSTR lpBase,
  1794. IN BOOL bCrLfTerminated,
  1795. IN LPVOID lpBuffer,
  1796. IN OUT LPDWORD lpdwBufferLength
  1797. )
  1798. /*++
  1799. Routine Description:
  1800. Returns all the request or response headers in a single buffer. The headers
  1801. can be returned as ASCIIZ strings, or CR-LF terminated strings
  1802. Arguments:
  1803. lpBase - base address of strings
  1804. bCrLfTerminated - TRUE if each string is terminated with CR-LF
  1805. lpBuffer - pointer to buffer to write headers
  1806. lpdwBufferLength - IN: length of lpBuffer
  1807. OUT: if successful, length of output buffer, minus 1
  1808. for any trailing EOS, or if the buffer is not
  1809. large enough, the size required
  1810. Return Value:
  1811. DWORD
  1812. Success - ERROR_SUCCESS
  1813. Failure - ERROR_INSUFFICIENT_BUFFER
  1814. --*/
  1815. {
  1816. PERF_ENTER(QueryRawHeaders);
  1817. DWORD requiredLength = 0;
  1818. LPSTR lpszBuffer = (LPSTR)lpBuffer;
  1819. LockHeaders();
  1820. for (DWORD i = 0; i < _TotalSlots; ++i) {
  1821. if (_lpHeaders[i].HaveString()) {
  1822. DWORD length;
  1823. length = _lpHeaders[i].StringLength();
  1824. requiredLength += length + (bCrLfTerminated ? 2 : 1);
  1825. if (*lpdwBufferLength > requiredLength) {
  1826. _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
  1827. lpszBuffer += length;
  1828. if (bCrLfTerminated) {
  1829. *lpszBuffer++ = '\r';
  1830. *lpszBuffer++ = '\n';
  1831. } else {
  1832. *lpszBuffer++ = '\0';
  1833. }
  1834. }
  1835. }
  1836. }
  1837. if (bCrLfTerminated)
  1838. {
  1839. requiredLength += 2;
  1840. if (*lpdwBufferLength > requiredLength)
  1841. {
  1842. *lpszBuffer++ = '\r';
  1843. *lpszBuffer++ = '\n';
  1844. }
  1845. }
  1846. UnlockHeaders();
  1847. ++requiredLength;
  1848. DWORD error;
  1849. if (*lpdwBufferLength < requiredLength) {
  1850. error = ERROR_INSUFFICIENT_BUFFER;
  1851. } else {
  1852. *lpszBuffer = '\0';
  1853. --requiredLength; // remove 1 for trailing '\0'
  1854. error = ERROR_SUCCESS;
  1855. }
  1856. *lpdwBufferLength = requiredLength;
  1857. PERF_LEAVE(QueryRawHeaders);
  1858. return error;
  1859. }
  1860. DWORD
  1861. HTTP_HEADERS::QueryFilteredRawHeaders(
  1862. IN LPSTR lpBase,
  1863. IN LPSTR *lplpFilterList,
  1864. IN DWORD cListElements,
  1865. IN BOOL fExclude,
  1866. IN BOOL fSkipVerb,
  1867. IN BOOL bCrLfTerminated,
  1868. IN LPVOID lpBuffer,
  1869. IN OUT LPDWORD lpdwBufferLength
  1870. )
  1871. /*++
  1872. Routine Description:
  1873. Returns all the request or response headers in a single buffer. The headers
  1874. can be returned as ASCIIZ strings, or CR-LF terminated strings
  1875. Arguments:
  1876. lpBase - base address of strings
  1877. bCrLfTerminated - TRUE if each string is terminated with CR-LF
  1878. lpBuffer - pointer to buffer to write headers
  1879. lpdwBufferLength - IN: length of lpBuffer
  1880. OUT: if successful, length of output buffer, minus 1
  1881. for any trailing EOS, or if the buffer is not
  1882. large enough, the size required
  1883. Return Value:
  1884. DWORD
  1885. Success - ERROR_SUCCESS
  1886. Failure - ERROR_INSUFFICIENT_BUFFER
  1887. --*/
  1888. {
  1889. DWORD error = ERROR_NOT_SUPPORTED;
  1890. DWORD requiredLength = 0;
  1891. LPSTR lpszBuffer = (LPSTR)lpBuffer;
  1892. BOOL fCopy;
  1893. DWORD i = fSkipVerb ? 1 : 0;
  1894. for (; i < _TotalSlots; ++i) {
  1895. if (_lpHeaders[i].HaveString()) {
  1896. fCopy = TRUE;
  1897. if (lplpFilterList
  1898. && FMatchList(lplpFilterList, cListElements, _lpHeaders+i, lpBase)) {
  1899. fCopy = fExclude?FALSE:TRUE;
  1900. }
  1901. if (fCopy) {
  1902. DWORD length;
  1903. length = _lpHeaders[i].StringLength();
  1904. requiredLength += length + (bCrLfTerminated ? 2 : 1);
  1905. if (*lpdwBufferLength > requiredLength) {
  1906. _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
  1907. lpszBuffer += length;
  1908. if (bCrLfTerminated) {
  1909. *lpszBuffer++ = '\r';
  1910. *lpszBuffer++ = '\n';
  1911. } else {
  1912. *lpszBuffer++ = '\0';
  1913. }
  1914. }
  1915. }
  1916. }
  1917. }
  1918. if (bCrLfTerminated)
  1919. {
  1920. requiredLength += 2;
  1921. if (*lpdwBufferLength > requiredLength)
  1922. {
  1923. *lpszBuffer++ = '\r';
  1924. *lpszBuffer++ = '\n';
  1925. }
  1926. }
  1927. ++requiredLength;
  1928. if (*lpdwBufferLength < requiredLength) {
  1929. error = ERROR_INSUFFICIENT_BUFFER;
  1930. } else {
  1931. *lpszBuffer = '\0';
  1932. --requiredLength; // remove 1 for trailing '\0'
  1933. error = ERROR_SUCCESS;
  1934. }
  1935. *lpdwBufferLength = requiredLength;
  1936. return error;
  1937. }
  1938. DWORD
  1939. HTTP_HEADERS::AddRequest(
  1940. IN LPSTR lpszVerb,
  1941. IN LPSTR lpszObject,
  1942. IN LPSTR lpszVersion
  1943. )
  1944. /*++
  1945. Routine Description:
  1946. Builds the request line from its constituent parts. The request line is the
  1947. first (0th) header in the request headers
  1948. Assumes: 1. This is the one-and-only call to this method
  1949. 2. lpszObject must already be escaped if necessary
  1950. Arguments:
  1951. lpszVerb - pointer to HTTP verb, e.g. "GET"
  1952. lpszObject - pointer to HTTP object name, e.g. "/users/albert/~emc2.htm".
  1953. lpszVersion - pointer to HTTP version string, e.g. "HTTP/1.0"
  1954. Return Value:
  1955. DWORD
  1956. Success - ERROR_SUCCESS
  1957. Failure - ERROR_NOT_ENOUGH_MEMORY
  1958. --*/
  1959. {
  1960. PERF_ENTER(AddRequest);
  1961. //
  1962. // there must not be a header when this method is called
  1963. //
  1964. INET_ASSERT(_HeadersLength == 0);
  1965. DWORD error = ERROR_SUCCESS;
  1966. int verbLen = lstrlen(lpszVerb);
  1967. int objectLen = lstrlen(lpszObject);
  1968. int versionLen = lstrlen(lpszVersion);
  1969. int len = verbLen // "GET"
  1970. + 1 // ' '
  1971. + objectLen // "/users/albert/~emc2.htm"
  1972. + 1 // ' '
  1973. + versionLen // "HTTP/1.0"
  1974. + 1 // '\0'
  1975. ;
  1976. //
  1977. // we are about to start updating the headers for the current
  1978. // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
  1979. //
  1980. HEADER_STRING * pRequest = GetFirstHeader();
  1981. HEADER_STRING & request = *pRequest;
  1982. if (pRequest == NULL) {
  1983. error = ERROR_NOT_ENOUGH_MEMORY;
  1984. goto quit;
  1985. }
  1986. INET_ASSERT(!request.HaveString());
  1987. _lpszVerb = NULL;
  1988. _dwVerbLength = 0;
  1989. _lpszObjectName = NULL;
  1990. _dwObjectNameLength = 0;
  1991. _lpszVersion = NULL;
  1992. _dwVersionLength = 0;
  1993. request.CreateStringBuffer((LPVOID)lpszVerb, verbLen, len);
  1994. if (request.IsError()) {
  1995. error = GetLastError();
  1996. INET_ASSERT(error != ERROR_SUCCESS);
  1997. } else {
  1998. request += ' ';
  1999. request.Strncat((LPVOID)lpszObject, objectLen);
  2000. request += ' ';
  2001. request.Strncat((LPVOID)lpszVersion, versionLen);
  2002. _HeadersLength = len - 1 + (sizeof("\r\n") - 1);
  2003. //
  2004. // we have used the first free slot in the headers array
  2005. //
  2006. --_FreeSlots;
  2007. //
  2008. // update the component variables in case of a ModifyRequest()
  2009. //
  2010. _lpszVerb = request.StringAddress();
  2011. _dwVerbLength = verbLen;
  2012. _lpszObjectName = _lpszVerb + verbLen + 1;
  2013. _dwObjectNameLength = objectLen;
  2014. _lpszVersion = _lpszObjectName + objectLen + 1;
  2015. _dwVersionLength = versionLen;
  2016. SetRequestVersion();
  2017. error = request.IsError() ? ::GetLastError() : ERROR_SUCCESS;
  2018. }
  2019. quit:
  2020. PERF_LEAVE(AddRequest);
  2021. return error;
  2022. }
  2023. DWORD
  2024. HTTP_HEADERS::ModifyRequest(
  2025. IN HTTP_METHOD_TYPE tMethod,
  2026. IN LPSTR lpszObjectName,
  2027. IN DWORD dwObjectNameLength,
  2028. IN LPSTR lpszVersion OPTIONAL,
  2029. IN DWORD dwVersionLength
  2030. )
  2031. /*++
  2032. Routine Description:
  2033. Updates the request line. Used in redirection
  2034. Arguments:
  2035. tMethod - type of new method
  2036. lpszObjectName - pointer to new object name
  2037. dwObjectNameLength - length of new object name
  2038. lpszVersion - optional pointer to version string
  2039. dwVersionLength - length of lpszVersion string if present
  2040. Return Value:
  2041. DWORD
  2042. Success - ERROR_SUCCESS
  2043. Failure - ERROR_NOT_ENOUGH_MEMORY
  2044. --*/
  2045. {
  2046. DEBUG_ENTER((DBG_HTTP,
  2047. Dword,
  2048. "ModifyRequest",
  2049. "%s, %q, %d, %q, %d",
  2050. MapHttpMethodType(tMethod),
  2051. lpszObjectName,
  2052. dwObjectNameLength,
  2053. lpszVersion,
  2054. dwVersionLength
  2055. ));
  2056. PERF_ENTER(ModifyRequest);
  2057. INET_ASSERT(lpszObjectName != NULL);
  2058. INET_ASSERT(dwObjectNameLength != 0);
  2059. //
  2060. // there must already be a header when this method is called
  2061. //
  2062. INET_ASSERT(_HeadersLength != 0);
  2063. //
  2064. // we are about to start updating the headers for the current
  2065. // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
  2066. //
  2067. //
  2068. // BUGBUG [arthurbi] using two HEADER_STRINGs here causes an extra
  2069. // ReAlloc when use the Copy operator between the two.
  2070. //
  2071. HEADER_STRING * pRequest = GetFirstHeader();
  2072. HEADER_STRING & request = *pRequest;
  2073. HEADER_STRING newRequest;
  2074. LPCSTR lpcszVerb;
  2075. DWORD verbLength;
  2076. DWORD error = ERROR_SUCCESS;
  2077. DWORD length;
  2078. //
  2079. // there must already be a request line
  2080. //
  2081. if (pRequest == NULL) {
  2082. error = ERROR_NOT_ENOUGH_MEMORY;
  2083. goto quit;
  2084. }
  2085. INET_ASSERT(request.HaveString());
  2086. //
  2087. // get the verb/method to use.
  2088. //
  2089. if (tMethod == HTTP_METHOD_TYPE_UNKNOWN) {
  2090. //
  2091. // the method is unknown, read the old one out of the string
  2092. // and save off, basically we're reusing the previous one.
  2093. //
  2094. lpcszVerb = request.StringAddress();
  2095. for (DWORD i = 0; i < request.StringLength(); i++) {
  2096. if (lpcszVerb[i] == ' ') {
  2097. break;
  2098. }
  2099. }
  2100. INET_ASSERT((i > 0) && (i < request.StringLength()));
  2101. verbLength = i;
  2102. } else {
  2103. //
  2104. // its one of the normal kind, just map it.
  2105. //
  2106. verbLength = MapHttpMethodType(tMethod, &lpcszVerb);
  2107. }
  2108. if (lpszVersion == NULL) {
  2109. lpszVersion = _lpszVersion;
  2110. dwVersionLength = _dwVersionLength;
  2111. }
  2112. _lpszVerb = NULL;
  2113. _dwVerbLength = 0;
  2114. _lpszObjectName = NULL;
  2115. _dwObjectNameLength = 0;
  2116. _lpszVersion = NULL;
  2117. _dwVersionLength = 0;
  2118. //
  2119. // calculate the new length from the component lengths we originally set
  2120. // in AddRequest(), and the new object name
  2121. //
  2122. length = verbLength + 1 + dwObjectNameLength + 1 + dwVersionLength + 1;
  2123. //
  2124. // create a new request line
  2125. //
  2126. newRequest.CreateStringBuffer((LPVOID)lpcszVerb, verbLength, length);
  2127. if (newRequest.IsError()) {
  2128. error = GetLastError();
  2129. } else {
  2130. newRequest += ' ';
  2131. newRequest.Strncat((LPVOID)lpszObjectName, dwObjectNameLength);
  2132. newRequest += ' ';
  2133. newRequest.Strncat((LPVOID)lpszVersion, dwVersionLength);
  2134. //
  2135. // remove the current request line length from the header buffer
  2136. // aggregate
  2137. //
  2138. _HeadersLength -= request.StringLength();
  2139. //
  2140. // make the current request line the new one
  2141. //
  2142. request = newRequest.StringAddress();
  2143. //
  2144. // and update the address and length variables (version length is the
  2145. // only thing that stays the same)
  2146. //
  2147. if (!request.IsError()) {
  2148. _lpszVerb = request.StringAddress();
  2149. _dwVerbLength = verbLength;
  2150. _lpszObjectName = _lpszVerb + verbLength + 1;
  2151. _dwObjectNameLength = dwObjectNameLength;
  2152. _lpszVersion = _lpszObjectName + dwObjectNameLength + 1;
  2153. _dwVersionLength = dwVersionLength;
  2154. SetRequestVersion();
  2155. //
  2156. // and the new request line length to the aggregate header length
  2157. //
  2158. _HeadersLength += request.StringLength();
  2159. } else {
  2160. error = GetLastError();
  2161. }
  2162. }
  2163. quit:
  2164. PERF_LEAVE(ModifyRequest);
  2165. DEBUG_LEAVE(error);
  2166. return error;
  2167. }
  2168. VOID
  2169. HTTP_HEADERS::SetRequestVersion(
  2170. VOID
  2171. )
  2172. /*++
  2173. Routine Description:
  2174. Set _RequestVersionMajor and _RequestVersionMinor based on the HTTP
  2175. version string
  2176. Arguments:
  2177. None.
  2178. Return Value:
  2179. None.
  2180. --*/
  2181. {
  2182. DEBUG_ENTER((DBG_HTTP,
  2183. None,
  2184. "HTTP_HEADERS::SetRequestVersion",
  2185. NULL
  2186. ));
  2187. INET_ASSERT(_lpszVersion != NULL);
  2188. _RequestVersionMajor = 0;
  2189. _RequestVersionMinor = 0;
  2190. if (strncmp(_lpszVersion, "HTTP/", sizeof("HTTP/") - 1) == 0) {
  2191. LPSTR pNum = _lpszVersion + sizeof("HTTP/") - 1;
  2192. ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMajor);
  2193. while (!isdigit(*pNum) && (*pNum != '\0')) {
  2194. ++pNum;
  2195. }
  2196. ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMinor);
  2197. DEBUG_PRINT(HTTP,
  2198. INFO,
  2199. ("request version = %d.%d\n",
  2200. _RequestVersionMajor,
  2201. _RequestVersionMinor
  2202. ));
  2203. } else {
  2204. DEBUG_PRINT(HTTP,
  2205. WARNING,
  2206. ("\"HTTP/\" not found in %q\n",
  2207. _lpszVersion
  2208. ));
  2209. }
  2210. DEBUG_LEAVE(0);
  2211. }
  2212. DWORD
  2213. HTTP_HEADERS::QueryRequestVersion(
  2214. IN LPVOID lpBuffer,
  2215. IN OUT LPDWORD lpdwBufferLength
  2216. )
  2217. /*++
  2218. Routine Description:
  2219. Get HtttpVersion used in request.
  2220. Arguments:
  2221. lpBuffer - pointer to buffer to copy version string into
  2222. lpdwBufferLength - IN: size of lpBuffer
  2223. OUT: size of version string excluding terminating '\0'
  2224. if successful, else required buffer length
  2225. Return Value:
  2226. DWORD
  2227. Success - ERROR_SUCCESS
  2228. Failure - ERROR_INSUFFICIENT_BUFFER
  2229. --*/
  2230. {
  2231. DEBUG_ENTER((DBG_HTTP,
  2232. None,
  2233. "HTTP_HEADERS::QueryRequestVersion",
  2234. NULL
  2235. ));
  2236. DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
  2237. LockHeaders();
  2238. if (_lpszVersion != NULL)
  2239. {
  2240. DWORD dwLen = lstrlen(_lpszVersion);
  2241. if (*lpdwBufferLength > dwLen) {
  2242. memcpy(lpBuffer, _lpszVersion, dwLen);
  2243. ((LPSTR)lpBuffer)[dwLen] = '\0';
  2244. *lpdwBufferLength = dwLen;
  2245. error = ERROR_SUCCESS;
  2246. } else {
  2247. *lpdwBufferLength = dwLen + 1;
  2248. error = ERROR_INSUFFICIENT_BUFFER;
  2249. }
  2250. }
  2251. UnlockHeaders();
  2252. DEBUG_LEAVE(0);
  2253. return error;
  2254. }
  2255. LPSTR
  2256. HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer(
  2257. OUT LPDWORD lpdwRequestLength,
  2258. IN LPVOID lpOptional,
  2259. IN DWORD dwOptionalLength,
  2260. IN BOOL bExtraCrLf,
  2261. IN DWORD dwMaxPacketLength,
  2262. OUT LPBOOL lpbCombinedData
  2263. )
  2264. /*++
  2265. Routine Description:
  2266. Creates a request buffer from the HTTP request and headers
  2267. Arguments:
  2268. lpdwRequestLength - pointer to returned buffer length
  2269. lpOptional - pointer to optional data
  2270. dwOptionalLength - length of optional data
  2271. bExtraCrLf - TRUE if we need to add additional CR-LF to buffer
  2272. dwMaxPacketLength - maximum length of buffer
  2273. lpbCombinedData - output TRUE if data successfully combined into one
  2274. Return Value:
  2275. LPSTR
  2276. Success - pointer to allocated buffer
  2277. Failure - NULL
  2278. --*/
  2279. {
  2280. DEBUG_ENTER((DBG_HTTP,
  2281. Pointer,
  2282. "HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer",
  2283. "%#x, %#x, %d, %B, %d, %#x",
  2284. lpdwRequestLength,
  2285. lpOptional,
  2286. dwOptionalLength,
  2287. bExtraCrLf,
  2288. dwMaxPacketLength,
  2289. lpbCombinedData
  2290. ));
  2291. PERF_ENTER(CreateRequestBuffer);
  2292. *lpbCombinedData = FALSE;
  2293. _RequestHeaders.LockHeaders();
  2294. DWORD headersLength;
  2295. DWORD requestLength;
  2296. DWORD optionalLength;
  2297. HEADER_STRING * pRequest = _RequestHeaders.GetFirstHeader();
  2298. HEADER_STRING & request = *pRequest;
  2299. LPSTR requestBuffer = NULL;
  2300. /*
  2301. WCHAR wszUrl[1024];
  2302. LPWSTR pwszUrl = NULL;
  2303. BYTE utf8Url[2048];
  2304. LPBYTE pbUrl = NULL;
  2305. */
  2306. LPSTR pszObject = _RequestHeaders.ObjectName();
  2307. DWORD dwObjectLength = _RequestHeaders.ObjectNameLength();
  2308. if (pRequest == NULL) {
  2309. goto quit;
  2310. }
  2311. INET_ASSERT(request.HaveString());
  2312. headersLength = _RequestHeaders.HeadersLength();
  2313. requestLength = headersLength + (sizeof("\r\n") - 1);
  2314. /*------------------------------------------------------------------
  2315. GlobalEnableUtf8Encoding = FALSE;
  2316. if (GlobalEnableUtf8Encoding
  2317. && StringContainsHighAnsi(pszObject, dwObjectLength)) {
  2318. pwszUrl = wszUrl;
  2319. DWORD arrayElements = ARRAY_ELEMENTS(wszUrl);
  2320. if (dwObjectLength > ARRAY_ELEMENTS(wszUrl)) {
  2321. arrayElements = dwObjectLength;
  2322. pwszUrl = (LPWSTR)ALLOCATE_FIXED_MEMORY(arrayElements * sizeof(*pwszUrl));
  2323. if (pwszUrl == NULL) {
  2324. goto utf8_cleanup;
  2325. }
  2326. }
  2327. PFNINETMULTIBYTETOUNICODE pfnMBToUnicode;
  2328. pfnMBToUnicode = GetInetMultiByteToUnicode( );
  2329. if (pfnMBToUnicode == NULL) {
  2330. goto utf8_cleanup;
  2331. }
  2332. HRESULT hr;
  2333. DWORD dwMode;
  2334. INT nMBChars;
  2335. INT nWChars;
  2336. nMBChars = dwObjectLength;
  2337. nWChars = arrayElements;
  2338. dwMode = 0;
  2339. hr = pfnMBToUnicode(&dwMode,
  2340. GetCodePage(),
  2341. pszObject,
  2342. &nMBChars,
  2343. pwszUrl,
  2344. &nWChars
  2345. );
  2346. if (hr != S_OK || nWChars == 0) {
  2347. goto utf8_cleanup;
  2348. }
  2349. DWORD nBytes;
  2350. nBytes = CountUnicodeToUtf8(pwszUrl, (DWORD)nWChars, TRUE);
  2351. pbUrl = utf8Url;
  2352. if (nBytes > ARRAY_ELEMENTS(utf8Url)) {
  2353. pbUrl = (LPBYTE)ALLOCATE_FIXED_MEMORY(nBytes);
  2354. if (pbUrl == NULL) {
  2355. goto utf8_cleanup;
  2356. }
  2357. }
  2358. DWORD error;
  2359. error = ConvertUnicodeToUtf8(pwszUrl,
  2360. (DWORD)nWChars,
  2361. pbUrl,
  2362. nBytes,
  2363. TRUE
  2364. );
  2365. INET_ASSERT(error == ERROR_SUCCESS);
  2366. if (error != ERROR_SUCCESS) {
  2367. goto utf8_cleanup;
  2368. }
  2369. requestLength = requestLength - dwObjectLength + nBytes;
  2370. headersLength = headersLength - dwObjectLength + nBytes;
  2371. pszObject = (LPSTR)pbUrl;
  2372. dwObjectLength = nBytes;
  2373. goto after_utf8;
  2374. utf8_cleanup:
  2375. if ((pwszUrl != wszUrl) && (pwszUrl != NULL)) {
  2376. FREE_MEMORY(pwszUrl);
  2377. }
  2378. pwszUrl = NULL;
  2379. if ((pbUrl != utf8Url) && (pbUrl != NULL)) {
  2380. FREE_MEMORY(pbUrl);
  2381. }
  2382. pbUrl = NULL;
  2383. pszObject = NULL;
  2384. dwObjectLength = 0;
  2385. }
  2386. after_utf8:
  2387. ------------------------------------------------------------------*/
  2388. optionalLength = (DWORD)(dwOptionalLength + (bExtraCrLf ? (sizeof("\r\n") - 1) : 0));
  2389. if (requestLength + optionalLength <= dwMaxPacketLength) {
  2390. requestLength += optionalLength;
  2391. } else {
  2392. optionalLength = 0;
  2393. bExtraCrLf = FALSE;
  2394. }
  2395. requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
  2396. if (requestBuffer != NULL) {
  2397. if (optionalLength != 0) {
  2398. *lpbCombinedData = TRUE;
  2399. }
  2400. } else if (optionalLength != 0) {
  2401. requestLength = headersLength + (sizeof("\r\n") - 1);
  2402. optionalLength = 0;
  2403. bExtraCrLf = FALSE;
  2404. requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
  2405. }
  2406. if (requestBuffer != NULL) {
  2407. LPSTR buffer = requestBuffer;
  2408. //
  2409. // copy the headers. Remember: header 0 is the request
  2410. //
  2411. //#ifdef COMPRESSED_HEADERS
  2412. // if (vfCompressedHeaders) {
  2413. // DEBUG_PRINT(HTTP,
  2414. // INFO,
  2415. // ("Compressing Headers")
  2416. // );
  2417. // _RequestHeaders.CopyCompressedHeaders(&buffer);
  2418. //
  2419. // }
  2420. // else
  2421. //#endif //COMPRESSED_HEADERS
  2422. {
  2423. _RequestHeaders.CopyHeaders(&buffer, pszObject, dwObjectLength);
  2424. }
  2425. //
  2426. // terminate the request
  2427. //
  2428. *buffer++ = '\r';
  2429. *buffer++ = '\n';
  2430. //#ifdef COMPRESSED_HEADERS
  2431. // if (vfCompressedHeaders) {
  2432. //
  2433. // *lpdwRequestLength = ((DWORD)buffer - (DWORD)requestBuffer);
  2434. //
  2435. // DEBUG_PRINT(HTTP,
  2436. // INFO,
  2437. // ("Compressed Headers: Old Length=%d, New Length = %d, Saved=%d\n",
  2438. // requestLength,
  2439. // *lpdwRequestLength,
  2440. // requestLength - *lpdwRequestLength
  2441. // )
  2442. // );
  2443. // } else {
  2444. //#endif //COMPRESSED_HEADERS
  2445. if (optionalLength != 0) {
  2446. if (dwOptionalLength != 0) {
  2447. memcpy(buffer, lpOptional, dwOptionalLength);
  2448. buffer += dwOptionalLength;
  2449. }
  2450. if (bExtraCrLf) {
  2451. *buffer++ = '\r';
  2452. *buffer++ = '\n';
  2453. }
  2454. }
  2455. INET_ASSERT((SIZE_T)(buffer-requestBuffer) == requestLength);
  2456. *lpdwRequestLength = requestLength;
  2457. //#ifdef COMPRESSED_HEADERS
  2458. // }
  2459. //#endif
  2460. }
  2461. quit:
  2462. _RequestHeaders.UnlockHeaders();
  2463. DEBUG_PRINT(HTTP,
  2464. INFO,
  2465. ("request length = %d, combined = %B\n",
  2466. *lpdwRequestLength,
  2467. *lpbCombinedData
  2468. ));
  2469. /*
  2470. if ((pbUrl != NULL) && (pbUrl != utf8Url)) {
  2471. FREE_MEMORY(pbUrl);
  2472. }
  2473. if ((pwszUrl != NULL) && (pwszUrl != wszUrl)) {
  2474. FREE_MEMORY(pwszUrl);
  2475. }
  2476. */
  2477. PERF_LEAVE(CreateRequestBuffer);
  2478. DEBUG_LEAVE(requestBuffer);
  2479. return requestBuffer;
  2480. }
  2481. DWORD
  2482. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
  2483. IN LPSTR lpszHeaderName,
  2484. IN DWORD dwHeaderNameLength,
  2485. IN LPVOID lpBuffer,
  2486. IN OUT LPDWORD lpdwBufferLength,
  2487. IN DWORD dwModifiers,
  2488. IN OUT LPDWORD lpdwIndex
  2489. )
  2490. /*++
  2491. Routine Description:
  2492. Searches for an arbitrary request header and if found, returns its value
  2493. Arguments:
  2494. lpszHeaderName - pointer to the name of the header to find
  2495. dwHeaderNameLength - length of the header
  2496. lpBuffer - pointer to buffer for results
  2497. lpdwBufferLength - IN: length of lpBuffer
  2498. OUT: length of the returned header value, or required
  2499. length of lpBuffer
  2500. dwModifiers - how to return the data: as number, as SYSTEMTIME
  2501. structure, etc.
  2502. lpdwIndex - IN: 0-based index of header to find
  2503. OUT: next header index if success returned
  2504. Return Value:
  2505. DWORD
  2506. Success - ERROR_SUCCESS
  2507. Failure - ERROR_INSUFFICIENT_BUFFER
  2508. lpBuffer not large enough for results
  2509. ERROR_INTERNET_INCORRECT_FORMAT
  2510. Can't convert the data to the requested format
  2511. ERROR_HTTP_HEADER_NOT_FOUND
  2512. Couldn't find the requested header
  2513. --*/
  2514. {
  2515. DEBUG_ENTER((DBG_HTTP,
  2516. Dword,
  2517. "QueryRequestHeader",
  2518. "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
  2519. lpszHeaderName,
  2520. min(dwHeaderNameLength + 1, 80),
  2521. lpszHeaderName,
  2522. dwHeaderNameLength,
  2523. lpBuffer,
  2524. lpdwBufferLength,
  2525. *lpdwBufferLength,
  2526. dwModifiers,
  2527. lpdwIndex,
  2528. *lpdwIndex
  2529. ));
  2530. PERF_ENTER(QueryRequestHeader);
  2531. DWORD error;
  2532. error = _RequestHeaders.FindHeader(NULL,
  2533. lpszHeaderName,
  2534. dwHeaderNameLength,
  2535. dwModifiers,
  2536. lpBuffer,
  2537. lpdwBufferLength,
  2538. lpdwIndex
  2539. );
  2540. PERF_LEAVE(QueryRequestHeader);
  2541. DEBUG_LEAVE(error);
  2542. return error;
  2543. }
  2544. DWORD
  2545. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
  2546. IN DWORD dwQueryIndex,
  2547. IN LPVOID lpBuffer,
  2548. IN OUT LPDWORD lpdwBufferLength,
  2549. IN DWORD dwModifiers,
  2550. IN OUT LPDWORD lpdwIndex
  2551. )
  2552. /*++
  2553. Routine Description:
  2554. Searches for an arbitrary request header and if found, returns its value
  2555. Arguments:
  2556. lpszHeaderName - pointer to the name of the header to find
  2557. dwHeaderNameLength - length of the header
  2558. lpBuffer - pointer to buffer for results
  2559. lpdwBufferLength - IN: length of lpBuffer
  2560. OUT: length of the returned header value, or required
  2561. length of lpBuffer
  2562. dwModifiers - how to return the data: as number, as SYSTEMTIME
  2563. structure, etc.
  2564. lpdwIndex - IN: 0-based index of header to find
  2565. OUT: next header index if success returned
  2566. Return Value:
  2567. DWORD
  2568. Success - ERROR_SUCCESS
  2569. Failure - ERROR_INSUFFICIENT_BUFFER
  2570. lpBuffer not large enough for results
  2571. ERROR_INTERNET_INCORRECT_FORMAT
  2572. Can't convert the data to the requested format
  2573. ERROR_HTTP_HEADER_NOT_FOUND
  2574. Couldn't find the requested header
  2575. --*/
  2576. {
  2577. DEBUG_ENTER((DBG_HTTP,
  2578. Dword,
  2579. "QueryRequestHeader",
  2580. "%u, %#x [%#x], %#x, %#x [%d]",
  2581. dwQueryIndex,
  2582. lpBuffer,
  2583. lpdwBufferLength,
  2584. *lpdwBufferLength,
  2585. dwModifiers,
  2586. lpdwIndex,
  2587. *lpdwIndex
  2588. ));
  2589. PERF_ENTER(QueryRequestHeader);
  2590. DWORD error;
  2591. error = _RequestHeaders.FindHeader(NULL,
  2592. dwQueryIndex,
  2593. dwModifiers,
  2594. lpBuffer,
  2595. lpdwBufferLength,
  2596. lpdwIndex
  2597. );
  2598. PERF_LEAVE(QueryRequestHeader);
  2599. DEBUG_LEAVE(error);
  2600. return error;
  2601. }
  2602. DWORD
  2603. HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader(
  2604. IN DWORD dwHeaderIndex,
  2605. IN LPSTR lpszHeader,
  2606. IN DWORD dwHeaderLength
  2607. )
  2608. /*++
  2609. Routine Description:
  2610. Adds a created response header to the response header array. Unlike normal
  2611. response headers, this will be a pointer to an actual string, not an offset
  2612. into the response buffer.
  2613. Even if the address of the response buffer changes, created response headers
  2614. will remain fixed
  2615. N.B. The header MUST NOT have a CR-LF terminator
  2616. N.B.-2 This function must be called under the header lock.
  2617. Arguments:
  2618. dwHeaderIndex - index into header value we are actually creating
  2619. lpszHeader - pointer to created (internal) header to add
  2620. dwHeaderLength - length of response header, or -1 if ASCIIZ
  2621. Return Value:
  2622. DWORD
  2623. Success - ERROR_SUCCESS
  2624. Failure - ERROR_NOT_ENOUGH_MEMORY
  2625. --*/
  2626. {
  2627. DEBUG_ENTER((DBG_HTTP,
  2628. Dword,
  2629. "AddInternalResponseHeader",
  2630. "%u [%q], %q, %d",
  2631. dwHeaderIndex,
  2632. GlobalKnownHeaders[dwHeaderIndex].Text,
  2633. lpszHeader,
  2634. dwHeaderLength
  2635. ));
  2636. DWORD error;
  2637. if (dwHeaderLength == (DWORD)-1) {
  2638. dwHeaderLength = lstrlen(lpszHeader);
  2639. }
  2640. INET_ASSERT((lpszHeader[dwHeaderLength - 1] != '\r')
  2641. && (lpszHeader[dwHeaderLength - 1] != '\n'));
  2642. //
  2643. // find the next slot for this header
  2644. //
  2645. HEADER_STRING * freeHeader;
  2646. //
  2647. // if we already have all the headers (the 'empty' header is the last one
  2648. // in the array) then change the last header to be the one we are adding
  2649. // and add a new empty header, else just add this one
  2650. //
  2651. DWORD iSlot;
  2652. freeHeader = _ResponseHeaders.FindFreeSlot(&iSlot);
  2653. if (freeHeader == NULL) {
  2654. error = _ResponseHeaders.GetError();
  2655. INET_ASSERT(error != ERROR_SUCCESS);
  2656. } else {
  2657. HEADER_STRING * lastHeader;
  2658. lastHeader = _ResponseHeaders.GetEmptyHeader();
  2659. if (lastHeader != NULL) {
  2660. //
  2661. // make copy of last header - its an offset string
  2662. //
  2663. *freeHeader = *lastHeader;
  2664. //
  2665. // use what was last header as free header
  2666. //
  2667. freeHeader = lastHeader;
  2668. }
  2669. freeHeader->MakeCopy(lpszHeader, dwHeaderLength);
  2670. freeHeader->SetNextKnownIndex(_ResponseHeaders.FastAdd(dwHeaderIndex, iSlot));
  2671. error = ERROR_SUCCESS;
  2672. }
  2673. DEBUG_LEAVE(error);
  2674. return error;
  2675. }
  2676. DWORD
  2677. HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders(
  2678. IN OUT LPBOOL lpbEof
  2679. )
  2680. /*++
  2681. Routine Description:
  2682. Given the next chunk of the response, updates the response headers. The
  2683. buffer pointer, buffer length and number of bytes received values are all
  2684. maintained in this object (_ResponseBuffer, _ResponseBufferLength and
  2685. _BytesReceived, resp.)
  2686. Arguments:
  2687. lpbEof - IN: TRUE if we have reached the end of the response
  2688. OUT: TRUE if we have reached the end of the response or the end
  2689. of the headers
  2690. Return Value:
  2691. DWORD
  2692. Success - ERROR_SUCCESS
  2693. Failure - ERROR_NOT_ENOUGH_MEMORY
  2694. --*/
  2695. {
  2696. DEBUG_ENTER((DBG_HTTP,
  2697. Dword,
  2698. "HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders",
  2699. "%#x [%.*q], %d, %d, %#x [%B]",
  2700. _ResponseBuffer + _ResponseScanned,
  2701. min(_ResponseBufferLength + 1, 80),
  2702. _ResponseBuffer + _ResponseScanned,
  2703. _ResponseBufferLength,
  2704. _BytesReceived,
  2705. lpbEof,
  2706. *lpbEof
  2707. ));
  2708. PERF_ENTER(UpdateResponseHeaders);
  2709. LPSTR lpszBuffer = (LPSTR)_ResponseBuffer + _ResponseScanned;
  2710. DWORD dwBytesReceived = _BytesReceived - _ResponseScanned;
  2711. DWORD error = ERROR_SUCCESS;
  2712. BOOL success = TRUE;
  2713. HEADER_STRING * statusLine;
  2714. //
  2715. // if input EOF is set then the caller is telling us that the end of the
  2716. // response has been reached at transport level (the server closed the
  2717. // connectiion)
  2718. //
  2719. if (*lpbEof) {
  2720. SetEof(TRUE);
  2721. }
  2722. //
  2723. // lock down the response headers for the duration of this request. The only
  2724. // way another thread is going to wait on this lock is if the reference on
  2725. // the HTTP request object goes to zero, which *shouldn't* happen
  2726. //
  2727. _ResponseHeaders.LockHeaders();
  2728. //
  2729. // if input EOF is set then the caller is telling us that the end of the
  2730. // response has been reached at transport level (the server closed the
  2731. // connectiion)
  2732. //
  2733. if (*lpbEof) {
  2734. SetEof(TRUE);
  2735. }
  2736. //
  2737. // if we don't yet know whether we have a HTTP/1.0 (or greater) or HTTP/0.9
  2738. // response yet, then try to find out.
  2739. //
  2740. // Only responses greater than HTTP/0.9 start with the "HTTP/#.#" string
  2741. //
  2742. if (!IsDownLevel() && !IsUpLevel()) {
  2743. #define MAKE_VERSION_ENTRY(string) string, sizeof(string) - 1
  2744. static struct {
  2745. LPSTR Version;
  2746. DWORD Length;
  2747. } KnownVersionsStrings[] = {
  2748. MAKE_VERSION_ENTRY("HTTP/"),
  2749. MAKE_VERSION_ENTRY("S-HTTP/"),
  2750. MAKE_VERSION_ENTRY("SHTTP/"),
  2751. MAKE_VERSION_ENTRY("Secure-HTTP/"),
  2752. //
  2753. // allow for servers generating slightly off-the-wall responses
  2754. //
  2755. MAKE_VERSION_ENTRY("HTTP /")
  2756. };
  2757. #define NUM_HTTP_VERSIONS ARRAY_ELEMENTS(KnownVersionsStrings)
  2758. //
  2759. // We know this is the start of a HTTP response, but there may be some
  2760. // noise at the start from bad HTML authoring, or bad content-length on
  2761. // the previous response on a keep-alive connection. We will try to sync
  2762. // up to the HTTP header (we will only look for this - I have never seen
  2763. // any of the others, and I doubt its worth the increased complexity and
  2764. // processing time)
  2765. //
  2766. //
  2767. // Due to possible DoS attacks outlined in RAID item 510295 we are to
  2768. //be more stringent as to what sort of 'noise' is allowed before the start
  2769. //of an HTTP response. We now allow the noise to consist of at most
  2770. //8 characters of whitespace or '\0'. If the content-length is slightly
  2771. //off because of a sloppy server, this should skip whatever terminators
  2772. //the server expected us to use.
  2773. //
  2774. const DWORD c_dwSmallestAcceptableStatusLength = ARRAY_ELEMENTS("HTTP/1.1 100\r\n") - 1;
  2775. const DWORD c_dwMaxNoiseAllowed = 8;
  2776. const DWORD c_dwMaxPreHTTPLength = ARRAY_ELEMENTS("Secure-HTTP/")-1;
  2777. LPSTR lpszBuf;
  2778. DWORD bytesLeft;
  2779. lpszBuf = lpszBuffer;
  2780. bytesLeft = dwBytesReceived;
  2781. //
  2782. // Check that we've read enough bytes for a status line to be ready.
  2783. //
  2784. if ((dwBytesReceived < c_dwSmallestAcceptableStatusLength) && !IsEof())
  2785. {
  2786. goto done;
  2787. }
  2788. //
  2789. // Allow up to c_dwMaxNoiseAllowed bytes worth of noise
  2790. //
  2791. int noiseBytesLeft = min(bytesLeft, c_dwMaxNoiseAllowed);
  2792. int noiseBytesScanned = 0;
  2793. while ((noiseBytesLeft > 0)
  2794. && (isspace((unsigned char)*lpszBuf)
  2795. || *lpszBuffer == '\0'))
  2796. {
  2797. ++lpszBuf;
  2798. --bytesLeft;
  2799. --noiseBytesLeft;
  2800. ++noiseBytesScanned;
  2801. }
  2802. //
  2803. // scan for the known version strings
  2804. //
  2805. for (int i = 0; i < NUM_HTTP_VERSIONS; ++i) {
  2806. LPSTR version = KnownVersionsStrings[i].Version;
  2807. DWORD length = KnownVersionsStrings[i].Length;
  2808. if ((bytesLeft >= length)
  2809. //
  2810. // try the most common case as a direct comparison. memcmp()
  2811. // should expand to cmpsd && cmpsb on x86 (most common platform
  2812. // and one on which we are most interested in improving perf)
  2813. //
  2814. && (((i == 0)
  2815. && (memcmp(lpszBuf, "HTTP/", sizeof("HTTP/") - 1) == 0))
  2816. //&& (lpszBuf[0] == 'H')
  2817. //&& (lpszBuf[1] == 'T')
  2818. //&& (lpszBuf[2] == 'T')
  2819. //&& (lpszBuf[3] == 'P')
  2820. //&& (lpszBuf[4] == '/'))
  2821. //
  2822. // "Clients should be tolerant in parsing the Status-Line"
  2823. // quote from HTTP/1.1 spec, therefore we perform a
  2824. // case-insensitive string comparison here
  2825. //
  2826. || (_strnicmp(lpszBuf, version, length) == 0))) {
  2827. //
  2828. // it starts with one of the recognized protocol version strings.
  2829. // We assume its not a down-level server, although it could be,
  2830. // sending back a plain text document that has e.g. "HTTP/1.0..."
  2831. // at its start
  2832. //
  2833. // According to the HTTP "spec", though, it is mentioned that 0.9
  2834. // servers typically only return HTML, hence we shouldn't see
  2835. // even a 0.9 response start with non-HTML data
  2836. //
  2837. SetUpLevel(TRUE);
  2838. _ResponseScanned += noiseBytesScanned;
  2839. //
  2840. // we have start of this response
  2841. //
  2842. lpszBuffer = lpszBuf;
  2843. break;
  2844. }
  2845. }
  2846. if (!IsUpLevel())
  2847. {
  2848. //
  2849. // if we didn't find the start of a valid HTTP response and we have
  2850. // not filled the response buffer sufficiently then allow
  2851. // re-entry to retry.
  2852. //
  2853. // if we didn't find the start of a valid HTTP response and we
  2854. //have filled the buffer sufficiently to expect the response,
  2855. //report the response as invalid.
  2856. //
  2857. if ((bytesLeft < c_dwMaxPreHTTPLength ) && !IsEof())
  2858. {
  2859. goto done;
  2860. }
  2861. else
  2862. {
  2863. //
  2864. // this may be a real down-level server, or it may be the response
  2865. // from an FTP or gopher server via a proxy, in which case there
  2866. // will be no headers. We will add some default headers to make
  2867. // life easier for higher level software
  2868. //
  2869. AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
  2870. "HTTP/1.0 200 OK",
  2871. sizeof("HTTP/1.0 200 OK") - 1
  2872. );
  2873. _StatusCode = HTTP_STATUS_OK;
  2874. //SetDownLevel(TRUE);
  2875. //
  2876. // we're now ready for the app to start reading data out
  2877. //
  2878. SetData(TRUE);
  2879. //
  2880. // down-level server: we're done
  2881. //
  2882. DEBUG_PRINT(HTTP,
  2883. INFO,
  2884. ("Server is down-level\n"
  2885. ));
  2886. goto done;
  2887. }
  2888. }
  2889. }
  2890. //
  2891. // WinHTTP only accepts IsUpLevel() type responses.
  2892. //
  2893. INET_ASSERT(IsUpLevel());
  2894. //
  2895. // Note: at this point we can't store pointers into the response buffer
  2896. // because it might move during a subsequent reallocation. We have to
  2897. // maintain offsets into the buffer and convert to pointers when we come to
  2898. // read the data out of the buffer (when the response is complete, or at
  2899. // least we've finished receiving headers)
  2900. //
  2901. //
  2902. // if we haven't checked the response yet, then the first thing to
  2903. // get is the status line
  2904. //
  2905. statusLine = GetStatusLine();
  2906. if (statusLine == NULL) {
  2907. error = ERROR_NOT_ENOUGH_MEMORY;
  2908. goto quit;
  2909. }
  2910. if (!statusLine->HaveString())
  2911. {
  2912. BOOL fNeedMoreBuffer;
  2913. int majorVersion = 0;
  2914. int minorVersion = 0;
  2915. BOOL fSupportsHttp1_1;
  2916. _StatusCode = 0;
  2917. //
  2918. // Parse the status line. It has already been checked up to the first '/'
  2919. //
  2920. error = _ResponseHeaders.ParseStatusLine(
  2921. (LPSTR)_ResponseBuffer,
  2922. _BytesReceived,
  2923. IsEof(),
  2924. &_ResponseScanned,
  2925. &fNeedMoreBuffer,
  2926. &_StatusCode,
  2927. (LPDWORD)&majorVersion,
  2928. (LPDWORD)&minorVersion
  2929. );
  2930. if (error != ERROR_SUCCESS)
  2931. {
  2932. goto quit;
  2933. }
  2934. if (fNeedMoreBuffer)
  2935. {
  2936. error = ERROR_SUCCESS;
  2937. goto quit;
  2938. }
  2939. DEBUG_PRINT(HTTP,
  2940. INFO,
  2941. ("Version = %d.%d\n",
  2942. majorVersion,
  2943. minorVersion
  2944. ));
  2945. DEBUG_PRINT(HTTP,
  2946. INFO,
  2947. ("_StatusCode = %d\n",
  2948. _StatusCode
  2949. ));
  2950. fSupportsHttp1_1 = FALSE;
  2951. if ( majorVersion > 1 )
  2952. {
  2953. //
  2954. // for higher version servers, the 1.1 spec dictates
  2955. // that we return the highest version the client
  2956. // supports, and in our case that is 1.1.
  2957. //
  2958. fSupportsHttp1_1 = TRUE;
  2959. }
  2960. else if (majorVersion == 1
  2961. && minorVersion >= 1)
  2962. {
  2963. fSupportsHttp1_1 = TRUE;
  2964. }
  2965. SetResponseHttp1_1(fSupportsHttp1_1);
  2966. //
  2967. // record the server HTTP version in the server info object
  2968. //
  2969. CServerInfo * pServerInfo = GetServerInfo();
  2970. if (pServerInfo != NULL)
  2971. {
  2972. if (fSupportsHttp1_1)
  2973. {
  2974. pServerInfo->SetHttp1_1();
  2975. //
  2976. // Set the max connections per HTTP 1.1 server.
  2977. //
  2978. pServerInfo->SetNewLimit(GlobalMaxConnectionsPerServer);
  2979. } else {
  2980. pServerInfo->SetHttp1_0();
  2981. //
  2982. // up the connection limit from HTTP 1.1 (default 2) to
  2983. // HTTP 1.0 (default 4)
  2984. //
  2985. pServerInfo->SetNewLimit(GlobalMaxConnectionsPer1_0Server);
  2986. }
  2987. }
  2988. }
  2989. //
  2990. // continue scanning headers here until we have tested all the current
  2991. // buffer, or we have found the start of the data
  2992. //
  2993. BOOL fFoundEndOfHeaders;
  2994. error = _ResponseHeaders.ParseHeaders(
  2995. (LPSTR)_ResponseBuffer,
  2996. _BytesReceived,
  2997. IsEof(),
  2998. &_ResponseScanned,
  2999. &success,
  3000. &fFoundEndOfHeaders
  3001. );
  3002. if ( error != ERROR_SUCCESS )
  3003. {
  3004. goto quit;
  3005. }
  3006. if ( fFoundEndOfHeaders )
  3007. {
  3008. //
  3009. // we found the end of the headers
  3010. //
  3011. SetEof(TRUE);
  3012. //
  3013. // and the start of the data
  3014. //
  3015. SetData(TRUE);
  3016. _DataOffset = _ResponseScanned;
  3017. DEBUG_PRINT(HTTP,
  3018. INFO,
  3019. ("found end of headers. _DataOffset = %d\n",
  3020. _DataOffset
  3021. ));
  3022. }
  3023. done:
  3024. //
  3025. // if we have reached the end of the headers then we communicate this fact
  3026. // to the caller
  3027. //
  3028. if (IsData() || IsEof()) {
  3029. CheckWellKnownHeaders();
  3030. if (ERROR_SUCCESS != error)
  3031. {
  3032. goto quit;
  3033. }
  3034. *lpbEof = TRUE;
  3035. /*
  3036. Set connection persistency based on these rules:
  3037. persistent = (1.0Request && Con: K-A && 1.0Response && Con: K-A)
  3038. || (1.1Request && Con: K-A && 1.0Response && Con: K-A)
  3039. || (1.0Request && Con: K-A && 1.1Response && Con: K-A)
  3040. || (1.1Request && !Con: Close && 1.1Response && !Con: Close)
  3041. therefore,
  3042. persistent = 1.1Request && 1.1Response
  3043. ? (!Con: Close in request || response)
  3044. : Con: K-A in request && response
  3045. */
  3046. if (IsRequestHttp1_1() && IsResponseHttp1_1()) {
  3047. BOOL bHaveConnCloseRequest;
  3048. bHaveConnCloseRequest = FindConnCloseRequestHeader(
  3049. IsRequestUsingProxy()
  3050. ? HTTP_QUERY_PROXY_CONNECTION
  3051. : HTTP_QUERY_CONNECTION
  3052. );
  3053. if (!(IsConnCloseResponse() || bHaveConnCloseRequest)) {
  3054. DEBUG_PRINT(HTTP,
  3055. INFO,
  3056. ("HTTP/1.1 persistent connection\n"
  3057. ));
  3058. SetKeepAlive(TRUE);
  3059. SetPersistentConnection(IsRequestUsingProxy()
  3060. && !IsTalkingToSecureServerViaProxy()
  3061. );
  3062. } else {
  3063. DEBUG_PRINT(HTTP,
  3064. INFO,
  3065. ("HTTP/1.1 non-persistent connection: close on: request: %B; response: %B\n",
  3066. bHaveConnCloseRequest,
  3067. IsConnCloseResponse()
  3068. ));
  3069. SetKeepAlive(FALSE);
  3070. SetNoLongerKeepAlive();
  3071. ClearPersistentConnection();
  3072. }
  3073. }
  3074. }
  3075. error = ERROR_SUCCESS;
  3076. quit:
  3077. //
  3078. // we are finished updating the response headers (no other thread should be
  3079. // waiting for this if the reference count and object state is correct)
  3080. //
  3081. _ResponseHeaders.UnlockHeaders();
  3082. PERF_LEAVE(UpdateResponseHeaders);
  3083. DEBUG_LEAVE(error);
  3084. return error;
  3085. }
  3086. DWORD
  3087. HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders(
  3088. IN OUT LPSTR* ppszBuffer,
  3089. IN DWORD dwBufferLength
  3090. )
  3091. /*++
  3092. Routine Description:
  3093. Create the response headers given a buffer containing concatenated headers.
  3094. Called when we are creating this object from the cache
  3095. Arguments:
  3096. lpszBuffer - pointer to buffer containing headers
  3097. dwBufferLength - length of lpszBuffer
  3098. Return Value:
  3099. DWORD
  3100. Success - ERROR_SUCCESS
  3101. Failure - ERROR_NOT_ENOUGH_MEMORY
  3102. Couldn't create headers
  3103. --*/
  3104. {
  3105. DEBUG_ENTER((DBG_HTTP,
  3106. Dword,
  3107. "HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders",
  3108. "%.32q, %d",
  3109. ppszBuffer,
  3110. dwBufferLength
  3111. ));
  3112. //
  3113. // there SHOULD NOT already be a response buffer if we're adding an
  3114. // external buffer
  3115. //
  3116. INET_ASSERT(_ResponseBuffer == NULL);
  3117. DWORD error;
  3118. BOOL eof = FALSE;
  3119. _ResponseBuffer = (LPBYTE) *ppszBuffer;
  3120. _ResponseBufferLength = dwBufferLength;
  3121. _BytesReceived = dwBufferLength;
  3122. error = UpdateResponseHeaders(&eof);
  3123. if (error != ERROR_SUCCESS) {
  3124. //
  3125. // if we failed, we will clean up our variables including clearing
  3126. // out the response buffer address and length, but leave freeing
  3127. // the buffer to the caller
  3128. //
  3129. _ResponseBuffer = NULL;
  3130. _ResponseBufferLength = 0;
  3131. ResetResponseVariables();
  3132. } else {
  3133. //
  3134. // Success - the object owns the buffer so the caller should not free.
  3135. //
  3136. *ppszBuffer = NULL;
  3137. }
  3138. DEBUG_LEAVE(error);
  3139. return error;
  3140. }
  3141. DWORD
  3142. HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion(
  3143. IN LPVOID lpBuffer,
  3144. IN OUT LPDWORD lpdwBufferLength
  3145. )
  3146. /*++
  3147. Routine Description:
  3148. Returns the HTTP version string from the status line
  3149. Arguments:
  3150. lpBuffer - pointer to buffer to copy version string into
  3151. lpdwBufferLength - IN: size of lpBuffer
  3152. OUT: size of version string excluding terminating '\0'
  3153. if successful, else required buffer length
  3154. Return Value:
  3155. DWORD
  3156. Success - ERROR_SUCCESS
  3157. Failure - ERROR_INSUFFICIENT_BUFFER
  3158. --*/
  3159. {
  3160. PERF_ENTER(QueryResponseVersion);
  3161. DWORD error;
  3162. HEADER_STRING * statusLine = GetStatusLine();
  3163. if ((statusLine == NULL) || statusLine->IsError()) {
  3164. error = ERROR_INTERNET_INTERNAL_ERROR;
  3165. goto quit;
  3166. }
  3167. LPSTR string;
  3168. DWORD length;
  3169. //
  3170. // get a pointer into the response buffer where the status line starts
  3171. // and its length
  3172. //
  3173. string = statusLine->StringAddress((LPSTR)_ResponseBuffer);
  3174. length = (DWORD)statusLine->StringLength();
  3175. //
  3176. // the version string is the first token on the line, delimited by spaces
  3177. //
  3178. DWORD index;
  3179. for (index = 0; index < length; ++index) {
  3180. //
  3181. // we'll also check for CR and LF, although just space should be
  3182. // sufficient
  3183. //
  3184. if ((string[index] == ' ')
  3185. || (string[index] == '\r')
  3186. || (string[index] == '\n')) {
  3187. break;
  3188. }
  3189. }
  3190. if (*lpdwBufferLength > index) {
  3191. memcpy(lpBuffer, (LPVOID)string, index);
  3192. ((LPSTR)lpBuffer)[index] = '\0';
  3193. *lpdwBufferLength = index;
  3194. error = ERROR_SUCCESS;
  3195. } else {
  3196. *lpdwBufferLength = index + 1;
  3197. error = ERROR_INSUFFICIENT_BUFFER;
  3198. }
  3199. quit:
  3200. PERF_LEAVE(QueryResponseVersion);
  3201. return error;
  3202. }
  3203. DWORD
  3204. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode(
  3205. IN LPVOID lpBuffer,
  3206. IN OUT LPDWORD lpdwBufferLength,
  3207. IN DWORD dwModifiers
  3208. )
  3209. /*++
  3210. Routine Description:
  3211. Returns the status code as a string or a number
  3212. Arguments:
  3213. lpBuffer - pointer to buffer where results written
  3214. lpdwBufferLength - IN: length of buffer
  3215. OUT: size of returned information, or required size'
  3216. of buffer
  3217. dwModifiers - flags which modify returned value
  3218. Return Value:
  3219. DWORD
  3220. Success - ERROR_SUCCESS
  3221. Failure - ERROR_INSUFFICIENT_BUFFER
  3222. --*/
  3223. {
  3224. PERF_ENTER(QueryStatusCode);
  3225. DWORD error;
  3226. DWORD requiredSize;
  3227. if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
  3228. requiredSize = sizeof(_StatusCode);
  3229. if (*lpdwBufferLength >= requiredSize) {
  3230. *(LPDWORD)lpBuffer = _StatusCode;
  3231. error = ERROR_SUCCESS;
  3232. } else {
  3233. error = ERROR_INSUFFICIENT_BUFFER;
  3234. }
  3235. } else {
  3236. //
  3237. // the number should always be only 3 characters long, but we'll be
  3238. // flexible (just in case)
  3239. //
  3240. char numBuf[sizeof("4294967296")];
  3241. requiredSize = wsprintf(numBuf, "%u", _StatusCode) + 1;
  3242. #ifdef DEBUG
  3243. // Debug check to make sure everything is good because the above
  3244. // used to be ultoa.
  3245. char debugBuf[sizeof("4294967296")];
  3246. ultoa(_StatusCode, debugBuf, 10);
  3247. if (strcmp(debugBuf,numBuf))
  3248. {
  3249. INET_ASSERT(FALSE);
  3250. }
  3251. INET_ASSERT(requiredSize == lstrlen(numBuf) + 1);
  3252. #endif
  3253. if (*lpdwBufferLength >= requiredSize) {
  3254. memcpy(lpBuffer, (LPVOID)numBuf, requiredSize);
  3255. *lpdwBufferLength = requiredSize - 1;
  3256. error = ERROR_SUCCESS;
  3257. } else {
  3258. *lpdwBufferLength = requiredSize;
  3259. error = ERROR_INSUFFICIENT_BUFFER;
  3260. }
  3261. }
  3262. PERF_LEAVE(QueryStatusCode);
  3263. return error;
  3264. }
  3265. DWORD
  3266. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText(
  3267. IN LPVOID lpBuffer,
  3268. IN OUT LPDWORD lpdwBufferLength
  3269. )
  3270. /*++
  3271. Routine Description:
  3272. Returns the status text - if any - returned by the server in the status line
  3273. Arguments:
  3274. lpBuffer - pointer to buffer where status text is written
  3275. lpdwBufferLength - IN: size of lpBuffer
  3276. OUT: length of the status text string minus 1 for the
  3277. '\0', or the required buffer length if we return
  3278. ERROR_INSUFFICIENT_BUFFER
  3279. Return Value:
  3280. DWORD
  3281. Success - ERROR_SUCCESS
  3282. Failure - ERROR_INSUFFICIENT_BUFFER
  3283. --*/
  3284. {
  3285. PERF_ENTER(QueryStatusText);
  3286. DWORD error;
  3287. HEADER_STRING * statusLine = GetStatusLine();
  3288. if ((statusLine == NULL) || statusLine->IsError()) {
  3289. error = ERROR_INTERNET_INTERNAL_ERROR;
  3290. goto quit;
  3291. }
  3292. LPSTR str;
  3293. DWORD len;
  3294. //
  3295. // find the third token on the status line. The status line has the form
  3296. //
  3297. // "HTTP/1.0 302 Try again\r\n"
  3298. //
  3299. // ^ ^ ^
  3300. // | | |
  3301. // | | +- status text
  3302. // | +- status code
  3303. // +- version
  3304. //
  3305. str = statusLine->StringAddress((LPSTR)_ResponseBuffer);
  3306. len = statusLine->StringLength();
  3307. DWORD i;
  3308. i = 0;
  3309. int j;
  3310. for (j = 0; j < 2; ++j) {
  3311. while ((i < len) && (str[i] != ' ')) {
  3312. ++i;
  3313. }
  3314. while ((i < len) && (str[i] == ' ')) {
  3315. ++i;
  3316. }
  3317. }
  3318. len -= i;
  3319. if (*lpdwBufferLength > len) {
  3320. memcpy(lpBuffer, (LPVOID)&str[i], len);
  3321. ((LPSTR)lpBuffer)[len] = '\0';
  3322. *lpdwBufferLength = len;
  3323. error = ERROR_SUCCESS;
  3324. } else {
  3325. *lpdwBufferLength = len + 1;
  3326. error = ERROR_INSUFFICIENT_BUFFER;
  3327. }
  3328. quit:
  3329. PERF_LEAVE(QueryStatusText);
  3330. return error;
  3331. }
  3332. DWORD
  3333. HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders(
  3334. IN BOOL bCrLfTerminated,
  3335. OUT LPVOID lpBuffer,
  3336. IN OUT LPDWORD lpdwBufferLength
  3337. )
  3338. /*++
  3339. Routine Description:
  3340. Gets the raw response headers
  3341. Arguments:
  3342. bCrLfTerminated - TRUE if we want RAW_HEADERS_CRLF else RAW_HEADERS
  3343. lpBuffer - pointer to buffer where headers returned
  3344. lpdwBufferLength - IN: length of lpBuffer
  3345. OUT: returned length of lpBuffer
  3346. Return Value:
  3347. DWORD
  3348. Success - ERROR_SUCCESS
  3349. Failure -
  3350. --*/
  3351. {
  3352. DEBUG_ENTER((DBG_HTTP,
  3353. Dword,
  3354. "QueryRawHeaders",
  3355. "%B, %#x, %#x [%d]",
  3356. bCrLfTerminated,
  3357. lpBuffer,
  3358. lpdwBufferLength,
  3359. *lpdwBufferLength
  3360. ));
  3361. PERF_ENTER(QueryRawHeaders);
  3362. DWORD error = _ResponseHeaders.QueryRawHeaders(
  3363. (LPSTR)_ResponseBuffer,
  3364. bCrLfTerminated,
  3365. lpBuffer,
  3366. lpdwBufferLength
  3367. );
  3368. IF_DEBUG_CODE() {
  3369. if (error == ERROR_INSUFFICIENT_BUFFER) {
  3370. DEBUG_PRINT(HTTP,
  3371. INFO,
  3372. ("*lpdwBufferLength = %d\n",
  3373. *lpdwBufferLength
  3374. ));
  3375. }
  3376. }
  3377. PERF_LEAVE(QueryRawHeaders);
  3378. DEBUG_LEAVE(error);
  3379. return error;
  3380. }
  3381. VOID
  3382. HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName(
  3383. IN DWORD dwQueryIndex
  3384. )
  3385. /*++
  3386. Routine Description:
  3387. Removes all headers of a particular type from the request object
  3388. Arguments:
  3389. lpszHeaderName - name of header to remove
  3390. Return Value:
  3391. None.
  3392. --*/
  3393. {
  3394. DEBUG_ENTER((DBG_HTTP,
  3395. None,
  3396. "RemoveAllRequestHeadersByName",
  3397. "%q, %u",
  3398. GlobalKnownHeaders[dwQueryIndex].Text,
  3399. dwQueryIndex
  3400. ));
  3401. PERF_ENTER(RemoveAllRequestHeadersByName);
  3402. _RequestHeaders.RemoveAllByIndex(dwQueryIndex);
  3403. PERF_LEAVE(RemoveAllRequestHeadersByName);
  3404. DEBUG_LEAVE(0);
  3405. }
  3406. //
  3407. // private methods
  3408. //
  3409. PRIVATE
  3410. VOID
  3411. HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders(
  3412. VOID
  3413. )
  3414. /*++
  3415. Routine Description:
  3416. Tests for a couple of well-known headers that are important to us as well as
  3417. the app:
  3418. "Connection: Keep-Alive"
  3419. "Proxy-Connection: Keep-Alive"
  3420. "Connection: Close"
  3421. "Proxy-Connection: Close"
  3422. "Transfer-Encoding: chunked"
  3423. "Content-Length: ####"
  3424. "Content-Range: bytes ####-####/####"
  3425. The header DOES NOT contain CR-LF. That is, dwHeaderLength will not include
  3426. any counts for line termination
  3427. We need to know if the server honoured a request for a keep-alive connection
  3428. so that we don't try to receive until we hit the end of the connection. The
  3429. server will keep it open.
  3430. We need to know the content length if we are talking over a persistent (keep
  3431. alive) connection.
  3432. If either header is found, we set the corresponding flag in the HTTP_HEADERS
  3433. object, and in the case of "Content-Length:" we parse out the length.
  3434. Arguments:
  3435. None.
  3436. Return Value:
  3437. None.
  3438. --*/
  3439. {
  3440. DEBUG_ENTER((DBG_HTTP,
  3441. None,
  3442. "HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders",
  3443. NULL
  3444. ));
  3445. //
  3446. // check for "Content-Length:" and "Content-Range"
  3447. //
  3448. if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_RANGE) )
  3449. {
  3450. _iSlotContentRange = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONTENT_RANGE];
  3451. }
  3452. if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_LENGTH) )
  3453. {
  3454. HEADER_STRING * curHeader;
  3455. DWORD dwHeaderLength;
  3456. LPSTR lpszHeader;
  3457. _iSlotContentLength = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONTENT_LENGTH];
  3458. curHeader = _ResponseHeaders.GetSlot(_iSlotContentLength);
  3459. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  3460. dwHeaderLength = curHeader->StringLength();
  3461. dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
  3462. lpszHeader += GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
  3463. while (dwHeaderLength && (*lpszHeader == ' ')) {
  3464. --dwHeaderLength;
  3465. ++lpszHeader;
  3466. }
  3467. while (dwHeaderLength && isdigit(*lpszHeader)) {
  3468. _ContentLength = _ContentLength * 10 + (*lpszHeader - '0');
  3469. --dwHeaderLength;
  3470. ++lpszHeader;
  3471. }
  3472. //
  3473. // once we have _ContentLength, we don't modify it (unless
  3474. // we fix it up when using a 206 partial response to resume
  3475. // a partial download.) The header value should be returned
  3476. // by HttpQueryInfo(). Instead, we keep account of the
  3477. // amount of keep-alive data left to copy in _BytesRemaining
  3478. //
  3479. _BytesRemaining = _ContentLength;
  3480. //
  3481. // although we said we may be one past the end of the header, in
  3482. // reality, if we received a buffer with "Content-Length:" then we
  3483. // expect it to be terminated by CR-LF (or CR-CR-LF or just LF,
  3484. // depending on the wackiness quotient of the server)
  3485. //
  3486. // darrenmi - commenting out because we're hitting it in stress.
  3487. // headers coming from the cache have an extra space in them in
  3488. // some circumstances. Investigating seperately.
  3489. // 2/25/00
  3490. // INET_ASSERT((*lpszHeader == '\r') || (*lpszHeader == '\n'));
  3491. SetHaveContentLength(TRUE);
  3492. DEBUG_PRINT(HTTP,
  3493. INFO,
  3494. ("_ContentLength = %d\n",
  3495. _ContentLength
  3496. ));
  3497. _BytesInSocket = (_ContentLength != 0)
  3498. ? (_ContentLength - (_BytesReceived - _DataOffset))
  3499. : 0;
  3500. //
  3501. // we could have multiple responses in the same buffer. If
  3502. // the amount received is greater than the content length
  3503. // then we have all the data; there are no bytes left in
  3504. // the socket for the current response
  3505. //
  3506. if ((int)_BytesInSocket < 0) {
  3507. _BytesInSocket = 0;
  3508. }
  3509. DEBUG_PRINT(HTTP,
  3510. INFO,
  3511. ("bytes left in socket = %d\n",
  3512. _BytesInSocket
  3513. ));
  3514. }
  3515. if ( IsResponseHeaderPresent(HTTP_QUERY_CONNECTION) ||
  3516. IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION) )
  3517. {
  3518. //
  3519. // check for "Connection: Keep-Alive" or "Proxy-Connection: Keep-Alive".
  3520. // This test protects us against the unlikely
  3521. // event of a server returning to us a keep-alive response header (because
  3522. // that would cause problems for the proxy)
  3523. //
  3524. if (IsWantKeepAlive() && (!IsKeepAlive() || IsResponseHttp1_1()))
  3525. {
  3526. HEADER_STRING * curHeader;
  3527. DWORD dwHeaderLength, headerNameLength;
  3528. LPSTR lpszHeader;
  3529. DWORD iSlot;
  3530. char ch;
  3531. if (IsRequestUsingProxy() &&
  3532. IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION))
  3533. {
  3534. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
  3535. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
  3536. }
  3537. else if (IsResponseHeaderPresent(HTTP_QUERY_CONNECTION))
  3538. {
  3539. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONNECTION];
  3540. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_CONNECTION].Length+1;
  3541. }
  3542. else
  3543. {
  3544. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
  3545. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
  3546. INET_ASSERT(FALSE);
  3547. }
  3548. curHeader = _ResponseHeaders.GetSlot(iSlot);
  3549. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  3550. dwHeaderLength = curHeader->StringLength();
  3551. dwHeaderLength -= headerNameLength;
  3552. lpszHeader += headerNameLength;
  3553. while (dwHeaderLength && (*lpszHeader == ' ')) {
  3554. ++lpszHeader;
  3555. --dwHeaderLength;
  3556. }
  3557. //
  3558. // both headers use "Keep-Alive" as header-value ONLY for HTTP 1.0 servers
  3559. //
  3560. if (((int)dwHeaderLength >= KEEP_ALIVE_LEN)
  3561. && !strnicmp(lpszHeader, KEEP_ALIVE_SZ, KEEP_ALIVE_LEN)) {
  3562. DEBUG_PRINT(HTTP,
  3563. INFO,
  3564. ("Connection: Keep-Alive\n"
  3565. ));
  3566. //
  3567. // BUGBUG - we are setting k-a when coming from cache!
  3568. //
  3569. SetKeepAlive(TRUE);
  3570. SetPersistentConnection(headerNameLength == HTTP_PROXY_CONNECTION_LEN);
  3571. }
  3572. //
  3573. // also check for "Close" as header-value ONLY for HTTP 1.1 servers
  3574. //
  3575. else if ((*lpszHeader == 'C' || *lpszHeader == 'c')
  3576. && ((int)dwHeaderLength >= CLOSE_LEN)
  3577. && IsResponseHttp1_1()
  3578. && !strnicmp(lpszHeader, CLOSE_SZ, CLOSE_LEN)) {
  3579. DEBUG_PRINT(HTTP,
  3580. INFO,
  3581. ("Connection: Close (HTTP/1.1)\n"
  3582. ));
  3583. SetConnCloseResponse(TRUE);
  3584. }
  3585. }
  3586. }
  3587. //
  3588. // check for "Refresh"
  3589. //
  3590. if ( IsResponseHeaderPresent(HTTP_QUERY_REFRESH)
  3591. && !IsRefresh() )
  3592. {
  3593. DEBUG_PRINT(HTTP,
  3594. INFO,
  3595. ("have \"Refresh:\" header\n"
  3596. ));
  3597. SetRefresh(TRUE);
  3598. }
  3599. //
  3600. // check for "Transfer-Encoding:"
  3601. //
  3602. if (IsResponseHeaderPresent(HTTP_QUERY_TRANSFER_ENCODING) &&
  3603. IsResponseHttp1_1())
  3604. {
  3605. //
  3606. // If Http 1.1, check for Chunked Transfer
  3607. //
  3608. HEADER_STRING * curHeader;
  3609. DWORD dwHeaderLength;
  3610. LPSTR lpszHeader;
  3611. DWORD iSlot;
  3612. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING];
  3613. curHeader = _ResponseHeaders.GetSlot(iSlot);
  3614. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  3615. dwHeaderLength = curHeader->StringLength();
  3616. dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
  3617. lpszHeader += GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
  3618. while (dwHeaderLength && (*lpszHeader == ' ')) {
  3619. ++lpszHeader;
  3620. --dwHeaderLength;
  3621. }
  3622. //
  3623. // look for "chunked" entry that confirms that we're doing chunked transfer encoding
  3624. //
  3625. if (((int)dwHeaderLength >= CHUNKED_LEN)
  3626. && !strnicmp(lpszHeader, CHUNKED_SZ, CHUNKED_LEN))
  3627. {
  3628. SetHaveChunkEncoding(TRUE);
  3629. DEBUG_PRINT(HTTP,
  3630. INFO,
  3631. ("server is sending Chunked Transfer Encoding\n"
  3632. ));
  3633. //
  3634. // if both "transfer-encoding: chunked" and "content-length:"
  3635. // were received then the chunking takes precedence
  3636. //
  3637. INET_ASSERT(!(IsChunkEncoding() && IsContentLength()));
  3638. if (IsContentLength()) {
  3639. SetHaveContentLength(FALSE);
  3640. }
  3641. }
  3642. }
  3643. SetBadNSServer(FALSE);
  3644. if (IsResponseHttp1_1())
  3645. {
  3646. //
  3647. // For IIS 4.0 Servers, and all other normal servers, if we make
  3648. // a HEAD request, we should ignore the Content-Length.
  3649. //
  3650. // IIS 3.0 servers send an illegal body, and this is a bug in the server.
  3651. // since they're not HTTP 1.1 we should be ok here.
  3652. //
  3653. if ( (GetMethodType() == HTTP_METHOD_TYPE_HEAD) &&
  3654. (_ContentLength > 0) &&
  3655. IsWantKeepAlive()
  3656. )
  3657. {
  3658. //
  3659. // set length to 0
  3660. //
  3661. _ContentLength = 0;
  3662. }
  3663. if ( IsRequestHttp1_1() )
  3664. {
  3665. //
  3666. // check for NS servers that don't return correct HTTP/1.1 responses
  3667. //
  3668. LPSTR buffer;
  3669. DWORD buflen;
  3670. DWORD status = FastQueryResponseHeader(HTTP_QUERY_SERVER,
  3671. (LPVOID*)&buffer,
  3672. &buflen,
  3673. 0
  3674. );
  3675. #define NSEP "Netscape-Enterprise/3"
  3676. #define NSEPLEN (sizeof(NSEP) - 1)
  3677. #define NSFT "Netscape-FastTrack/3"
  3678. #define NSFTLEN (sizeof(NSFT) - 1)
  3679. #define NSCS "Netscape-Commerce/3"
  3680. #define NSCSLEN (sizeof(NSCS) - 1)
  3681. if (status == ERROR_SUCCESS) {
  3682. BOOL fIsBadServer = ((buflen > NSEPLEN) && !strnicmp(buffer, NSEP, NSEPLEN))
  3683. || ((buflen > NSFTLEN) && !strnicmp(buffer, NSFT, NSFTLEN))
  3684. || ((buflen > NSCSLEN) && !strnicmp(buffer, NSCS, NSCSLEN));
  3685. if ( fIsBadServer )
  3686. {
  3687. CServerInfo * pServerInfo = GetServerInfo();
  3688. SetBadNSServer(fIsBadServer);
  3689. if (pServerInfo != NULL)
  3690. {
  3691. //
  3692. // Note this Bad Server info in the server info obj,
  3693. // as we they fail to do keep-alive with SSL properly
  3694. //
  3695. pServerInfo->SetBadNSServer();
  3696. }
  3697. DEBUG_PRINT(HTTP,
  3698. INFO,
  3699. ("IsBadNSServer() == %B\n",
  3700. IsBadNSServer()
  3701. ));
  3702. }
  3703. }
  3704. }
  3705. //
  3706. // BUGBUG - content-type: multipart/byteranges means we
  3707. // also have data
  3708. //
  3709. DWORD statusCode = GetStatusCode();
  3710. if (!IsBadNSServer()
  3711. && !IsContentLength()
  3712. && !IsChunkEncoding()
  3713. && (((statusCode >= HTTP_STATUS_CONTINUE) // 100
  3714. && (statusCode < HTTP_STATUS_OK)) // 200
  3715. || (statusCode == HTTP_STATUS_NO_CONTENT) // 204
  3716. || (statusCode == HTTP_STATUS_MOVED) // 301
  3717. || (statusCode == HTTP_STATUS_REDIRECT) // 302
  3718. || (statusCode == HTTP_STATUS_REDIRECT_METHOD) // 303
  3719. || (statusCode == HTTP_STATUS_NOT_MODIFIED) // 304
  3720. || (statusCode == HTTP_STATUS_REDIRECT_KEEP_VERB)) // 307
  3721. || (GetMethodType() == HTTP_METHOD_TYPE_HEAD)) {
  3722. DEBUG_PRINT(HTTP,
  3723. INFO,
  3724. ("header-only HTTP/1.1 response\n"
  3725. ));
  3726. SetData(FALSE);
  3727. }
  3728. }
  3729. DEBUG_LEAVE(0);
  3730. }
  3731. //
  3732. // this array has the same order as the HTTP_METHOD_TYPE enum
  3733. //
  3734. #define MAKE_REQUEST_METHOD_TYPE(Type) \
  3735. sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## Type
  3736. //
  3737. // darrenmi - need a new macro because *_M-POST isn't a valid enum member.
  3738. // we need a seperate enum type and string value.
  3739. //
  3740. // map HTTP_METHOD_TYPE_MPOST <=> "M-POST"
  3741. //
  3742. #define MAKE_REQUEST_METHOD_TYPE2(EnumType,Type) \
  3743. sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## EnumType
  3744. static const struct _REQUEST_METHOD {
  3745. int Length;
  3746. LPSTR Name;
  3747. HTTP_METHOD_TYPE MethodType;
  3748. } MethodNames[] = {
  3749. MAKE_REQUEST_METHOD_TYPE(GET),
  3750. MAKE_REQUEST_METHOD_TYPE(HEAD),
  3751. MAKE_REQUEST_METHOD_TYPE(POST),
  3752. MAKE_REQUEST_METHOD_TYPE(PUT),
  3753. MAKE_REQUEST_METHOD_TYPE(PROPFIND),
  3754. MAKE_REQUEST_METHOD_TYPE(PROPPATCH),
  3755. MAKE_REQUEST_METHOD_TYPE(LOCK),
  3756. MAKE_REQUEST_METHOD_TYPE(UNLOCK),
  3757. MAKE_REQUEST_METHOD_TYPE(COPY),
  3758. MAKE_REQUEST_METHOD_TYPE(MOVE),
  3759. MAKE_REQUEST_METHOD_TYPE(MKCOL),
  3760. MAKE_REQUEST_METHOD_TYPE(CONNECT),
  3761. MAKE_REQUEST_METHOD_TYPE(DELETE),
  3762. MAKE_REQUEST_METHOD_TYPE(LINK),
  3763. MAKE_REQUEST_METHOD_TYPE(UNLINK),
  3764. MAKE_REQUEST_METHOD_TYPE(BMOVE),
  3765. MAKE_REQUEST_METHOD_TYPE(BCOPY),
  3766. MAKE_REQUEST_METHOD_TYPE(BPROPFIND),
  3767. MAKE_REQUEST_METHOD_TYPE(BPROPPATCH),
  3768. MAKE_REQUEST_METHOD_TYPE(BDELETE),
  3769. MAKE_REQUEST_METHOD_TYPE(SUBSCRIBE),
  3770. MAKE_REQUEST_METHOD_TYPE(UNSUBSCRIBE),
  3771. MAKE_REQUEST_METHOD_TYPE(NOTIFY),
  3772. MAKE_REQUEST_METHOD_TYPE(POLL),
  3773. MAKE_REQUEST_METHOD_TYPE(CHECKIN),
  3774. MAKE_REQUEST_METHOD_TYPE(CHECKOUT),
  3775. MAKE_REQUEST_METHOD_TYPE(INVOKE),
  3776. MAKE_REQUEST_METHOD_TYPE(SEARCH),
  3777. MAKE_REQUEST_METHOD_TYPE(PIN),
  3778. MAKE_REQUEST_METHOD_TYPE2(MPOST,M-POST)
  3779. };
  3780. HTTP_METHOD_TYPE
  3781. MapHttpRequestMethod(
  3782. IN LPCSTR lpszVerb
  3783. )
  3784. /*++
  3785. Routine Description:
  3786. Maps request method string to type. Method names *are* case-sensitive
  3787. Arguments:
  3788. lpszVerb - method (verb) string
  3789. Return Value:
  3790. HTTP_METHOD_TYPE
  3791. --*/
  3792. {
  3793. int verbLen = strlen(lpszVerb);
  3794. for (int i = 0; i < ARRAY_ELEMENTS(MethodNames); ++i) {
  3795. if ((MethodNames[i].Length == verbLen)
  3796. && (memcmp(lpszVerb, MethodNames[i].Name, verbLen) == 0)) {
  3797. return MethodNames[i].MethodType;
  3798. }
  3799. }
  3800. //
  3801. // we now hande HTTP_METHOD_TYPE_UNKNOWN
  3802. //
  3803. return HTTP_METHOD_TYPE_UNKNOWN;
  3804. }
  3805. DWORD
  3806. MapHttpMethodType(
  3807. IN HTTP_METHOD_TYPE tMethod,
  3808. OUT LPCSTR * lplpcszName
  3809. )
  3810. /*++
  3811. Routine Description:
  3812. Map a method type to the corresponding name and length
  3813. Arguments:
  3814. tMethod - to map
  3815. lplpcszName - pointer to pointer to returned name
  3816. Return Value:
  3817. DWORD
  3818. Success - length of method name
  3819. Failure - (DWORD)-1
  3820. --*/
  3821. {
  3822. DWORD length;
  3823. if ((tMethod >= HTTP_METHOD_TYPE_FIRST) && (tMethod <= HTTP_METHOD_TYPE_LAST)) {
  3824. *lplpcszName = MethodNames[tMethod].Name;
  3825. length = MethodNames[tMethod].Length;
  3826. } else {
  3827. length = (DWORD)-1;
  3828. }
  3829. return length;
  3830. }
  3831. #if INET_DEBUG
  3832. LPSTR
  3833. MapHttpMethodType(
  3834. IN HTTP_METHOD_TYPE tMethod
  3835. )
  3836. {
  3837. return (tMethod == HTTP_METHOD_TYPE_UNKNOWN)
  3838. ? "Unknown"
  3839. : MethodNames[tMethod].Name;
  3840. }
  3841. #endif
  3842. //
  3843. //DWORD
  3844. //CreateEscapedUrlPath(
  3845. // IN LPSTR lpszUrlPath,
  3846. // OUT LPSTR * lplpszEncodedUrlPath
  3847. // )
  3848. //
  3849. ///*++
  3850. //
  3851. //Routine Description:
  3852. //
  3853. // Given an URL-path, encodes it into a new buffer
  3854. //
  3855. //Arguments:
  3856. //
  3857. // lpszUrlPath - URL-path to encode
  3858. //
  3859. // lplpszEncodedUrlPath - pointer to returned allocated buffer containing
  3860. // escaped URL-path
  3861. //
  3862. //Return Value:
  3863. //
  3864. // DWORD
  3865. // Success - ERROR_SUCCESS
  3866. //
  3867. // Failure -
  3868. //
  3869. //--*/
  3870. //
  3871. //{
  3872. // LPSTR lpszEncodedUrlPath = NULL;
  3873. // DWORD urlPathLength;
  3874. // DWORD encodedUrlPathLength;
  3875. // DWORD error;
  3876. //
  3877. // //
  3878. // // we need to encode the URL-path into a separate buffer (it may grow)
  3879. // //
  3880. //
  3881. // urlPathLength = strlen(lpszUrlPath);
  3882. // encodedUrlPathLength = INTERNET_MAX_PATH_LENGTH;
  3883. //
  3884. // do {
  3885. //
  3886. // //
  3887. // // we allow ourselves to fail due to insufficient buffer (at least once)
  3888. // //
  3889. //
  3890. // lpszEncodedUrlPath = (LPSTR)ResizeBuffer(lpszEncodedUrlPath,
  3891. // encodedUrlPathLength,
  3892. // FALSE
  3893. // );
  3894. // if (lpszEncodedUrlPath != NULL) {
  3895. //
  3896. // DWORD previousLength = encodedUrlPathLength;
  3897. //
  3898. // error = EncodeUrlPath(NO_ENCODE_PATH_SEP,
  3899. //
  3900. // //
  3901. // // BUGBUG - assuming HTTP
  3902. // //
  3903. //
  3904. // SCHEME_HTTP,
  3905. // lpszUrlPath,
  3906. // urlPathLength,
  3907. // lpszEncodedUrlPath,
  3908. // &encodedUrlPathLength
  3909. // );
  3910. //
  3911. // if ((error == ERROR_INSUFFICIENT_BUFFER)
  3912. // && (previousLength >= encodedUrlPathLength)) {
  3913. //
  3914. // //
  3915. // // this should never happen, but we will avoid a loop if it does
  3916. // //
  3917. //
  3918. // INET_ASSERT(FALSE);
  3919. //
  3920. // error = ERROR_INTERNET_INTERNAL_ERROR;
  3921. // }
  3922. // } else {
  3923. //
  3924. // //
  3925. // // failed to (re)alloc
  3926. // //
  3927. //
  3928. // error = ERROR_NOT_ENOUGH_MEMORY;
  3929. // }
  3930. // } while (error == ERROR_INSUFFICIENT_BUFFER);
  3931. //
  3932. // *lplpszEncodedUrlPath = lpszEncodedUrlPath;
  3933. //
  3934. // return error;
  3935. //}
  3936. PRIVATE
  3937. BOOL
  3938. FMatchList(
  3939. LPSTR *lplpList,
  3940. DWORD cListLen,
  3941. HEADER_STRING *lpHeader,
  3942. LPSTR lpBase
  3943. )
  3944. {
  3945. DWORD i;
  3946. for (i=0; i < cListLen; ++i) {
  3947. if (!lpHeader->Strnicmp(lpBase, lplpList[i], strlen(lplpList[i]))) {
  3948. return (TRUE);
  3949. }
  3950. }
  3951. return(FALSE);
  3952. }
  3953. #ifdef COMPRESSED_HEADERS
  3954. DWORD
  3955. LookupHeadermap(
  3956. LPSTR lpszHeader
  3957. )
  3958. {
  3959. DWORD top, mid, bottom, ret = 0;
  3960. int cmp;
  3961. LPSTR lpszColon;
  3962. lpszColon = strchr(lpszHeader, ':');
  3963. if (!lpszColon || (lpszColon == lpszHeader)) {
  3964. return(ret);
  3965. }
  3966. // yuk
  3967. *lpszColon = 0;
  3968. top = 1;
  3969. bottom = sizeof(rgsHeaderMap)/sizeof(HEADER_MAP);
  3970. INET_ASSERT(bottom >= top);
  3971. do {
  3972. mid = (bottom+top)/2;
  3973. if (!(cmp = stricmp( rgsHeaderMap[mid].lpszLongHeader,
  3974. lpszHeader
  3975. ))) {
  3976. // we found a matching header,
  3977. ret = mid;
  3978. break;
  3979. }
  3980. if (cmp > 0) {
  3981. // the mid header is larger than the passed in header
  3982. // so we must check at the upper end of the sorted array of headers
  3983. bottom = mid-1;
  3984. }
  3985. else {
  3986. // the mid header is smaller than the passed in header
  3987. // so we must check at the lower end of the sorted array of headers
  3988. top = mid+1;
  3989. }
  3990. } while (bottom >= top);
  3991. *lpszColon = ':';
  3992. return (ret);
  3993. }
  3994. #endif //COMPRESSED_HEADERS
  3995. //
  3996. // HTTP_HEADER_PARSER implementation
  3997. //
  3998. HTTP_HEADER_PARSER::HTTP_HEADER_PARSER(
  3999. IN LPSTR szHeaders,
  4000. IN DWORD cbHeaders
  4001. ) : HTTP_HEADERS()
  4002. /*++
  4003. Routine Description:
  4004. Constructor for the HTTP_HEADER_PARSER object. Calls ParseHeaders to
  4005. build a parsed version of the header string passed in.
  4006. Arguments:
  4007. szHeaders - pointer to the headers to parse
  4008. cbHeaders - length of the headers
  4009. Return Value:
  4010. None.
  4011. --*/
  4012. {
  4013. DWORD dwBytesScaned = 0;
  4014. BOOL fFoundCompleteLine;
  4015. BOOL fFoundEndOfHeaders;
  4016. DWORD error;
  4017. error = ParseHeaders(
  4018. szHeaders,
  4019. cbHeaders,
  4020. TRUE, // Eof
  4021. &dwBytesScaned,
  4022. &fFoundCompleteLine,
  4023. &fFoundEndOfHeaders
  4024. );
  4025. INET_ASSERT(error == ERROR_SUCCESS);
  4026. INET_ASSERT(fFoundCompleteLine);
  4027. INET_ASSERT(fFoundEndOfHeaders);
  4028. }
  4029. /* // some test cases which can be used to test ParseStatusLine()
  4030. char bad1[] = "HTTP1.1 200 Description yeah yeah\r\n";
  4031. char bad2[] = "HTTP/1234.1 200 Description yeah yeah\r\n";
  4032. char bad3[] = "HTTP/1.1234 200 Description yeah yeah\r\n";
  4033. char bad4[] = "HTTP/1.1 1234 Description yeah yeah\r\n";
  4034. char bad5[] = "HTTP/ 1.1 200 Description yeah yeah\r\n";
  4035. char bad6[] = "HTTP/1.1 200 Description yeah yeah\r\n";
  4036. char bad7[] = "HTTP/1.1 200Description yeah yeah\r\n";
  4037. char bad8[3000] = "HTTP/1.1 200 Description yeah yeah";
  4038. char bad9[] = "HTTP/1 1.1 200 Description yeah yeah\r\n";
  4039. char good1[] = "HTTP/ 123.123 200 Description yeah yeah\r\n";
  4040. */
  4041. DWORD
  4042. HTTP_HEADER_PARSER::ParseStatusLine(
  4043. IN LPSTR lpHeaderBase,
  4044. IN DWORD dwBufferLength,
  4045. IN BOOL fEof,
  4046. IN OUT DWORD *lpdwBufferLengthScanned,
  4047. OUT BOOL *lpfNeedMoreBuffer,
  4048. OUT DWORD *lpdwStatusCode,
  4049. OUT DWORD *lpdwMajorVersion,
  4050. OUT DWORD *lpdwMinorVersion
  4051. )
  4052. /*++
  4053. Routine Description:
  4054. Parses the Status line of an HTTP server response. Takes care of adding the status
  4055. line to HTTP header array.
  4056. From HTTP v1.1. spec:
  4057. {
  4058. Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
  4059. HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
  4060. Status-Code = 1*DIGIT
  4061. Reason-Phrase = *<TEXT, excluding CR LF>
  4062. (1*DIGIT means at least one digit, maybe more)
  4063. }
  4064. WinHTTP strictly enforces the status line spec. The only exception is that up to 3
  4065. spaces are allowed before the Status-Code and major version number.
  4066. To prevent malicious servers from hogging the channel, the integers are limited to
  4067. 3 digits and the Reason-Phrase is limited to GlobalMaxSizeStatusLineResultText characters.
  4068. "HTTP" could be other things like "S-HTTP", this is checked by UpdateFromHeaders()
  4069. before ParseStatusLine() is called. The existence of the first '/' is verified before
  4070. ParseStatusLine is called.
  4071. Arguments:
  4072. lpszHeader - pointer to the header to check
  4073. dwHeaderLength - length of the header
  4074. Return Value:
  4075. BOOL - TRUE if line was successively parsed and processed, FALSE otherwise
  4076. --*/
  4077. {
  4078. #define BEFORE_VERSION_NUMBERS 0
  4079. #define MAJOR_VERSION_NUMBER 1
  4080. #define MINOR_VERSION_NUMBER 2
  4081. #define STATUS_CODE_NUMBER 3
  4082. #define AFTER_STATUS_CODE 4
  4083. #define MAX_STATUS_INTS 4
  4084. LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
  4085. LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
  4086. DWORD dwBytesScanned = 0;
  4087. DWORD dwStatusLineLength = 0;
  4088. LPSTR lpszStatusLine = NULL;
  4089. int ver_state = BEFORE_VERSION_NUMBERS;
  4090. BOOL afStatusIntsFound[MAX_STATUS_INTS] = {0};
  4091. DWORD adwStatusInts[MAX_STATUS_INTS] = {0};
  4092. DWORD dwStatusPieceLength = 0;
  4093. BOOL error = ERROR_INTERNET_INTERNAL_ERROR;
  4094. lpszStatusLine = response;
  4095. //
  4096. // While walking the Status Line looking for terminating \r\n,
  4097. // we extract the Major.Minor Versions and Status Code in that order.
  4098. // text and spaces will lie between/before/after the three numbers
  4099. // but the idea is to remeber which number we're calculating based on a numeric state
  4100. // If all goes well the loop will churn out an array with the 3 numbers plugged in as DWORDs
  4101. //
  4102. while ((response < lpszEnd) && (*response != '\r') && (*response != '\n'))
  4103. {
  4104. switch (ver_state)
  4105. {
  4106. case BEFORE_VERSION_NUMBERS:
  4107. //
  4108. // We've already matched the status line with something
  4109. //of the form "****/" in UpdateFromHeaders(), we can ignore everything
  4110. //through the first '/'.
  4111. //
  4112. if (*response == '/')
  4113. {
  4114. INET_ASSERT(ver_state == BEFORE_VERSION_NUMBERS);
  4115. ver_state++; // = MAJOR_VERSION_NUMBER
  4116. dwStatusPieceLength = 0; // next piece is either spaces or an int
  4117. }
  4118. break;
  4119. case MAJOR_VERSION_NUMBER:
  4120. if (*response == '.' && ver_state == MAJOR_VERSION_NUMBER)
  4121. {
  4122. ver_state++; // = MINOR_VERSION_NUMBER
  4123. dwStatusPieceLength = 0; // next piece is an int
  4124. break;
  4125. }
  4126. // fall through
  4127. case MINOR_VERSION_NUMBER:
  4128. if (*response == ' ' && ver_state == MINOR_VERSION_NUMBER)
  4129. {
  4130. ver_state++; // = STATUS_CODE_NUMBER
  4131. dwStatusPieceLength = 0; // next piece is either spaces or an int.
  4132. break;
  4133. }
  4134. // fall through
  4135. case STATUS_CODE_NUMBER:
  4136. if (isdigit(*response))
  4137. {
  4138. if (!afStatusIntsFound[ver_state])
  4139. {
  4140. // transitioning from counting spaces
  4141. //to counting integers
  4142. dwStatusPieceLength = 0;
  4143. }
  4144. // Allow up to 3 digits per integer.
  4145. if (++dwStatusPieceLength > 3)
  4146. goto doneInvalidStatusLine;
  4147. int val = *response - '0';
  4148. afStatusIntsFound[ver_state] = TRUE;
  4149. adwStatusInts[ver_state] = adwStatusInts[ver_state] * 10 + val;
  4150. }
  4151. else if ( adwStatusInts[STATUS_CODE_NUMBER] > 0 )
  4152. {
  4153. INET_ASSERT(ver_state == STATUS_CODE_NUMBER);
  4154. if (*response != ' ')
  4155. goto doneInvalidStatusLine;
  4156. ver_state++; // = AFTER_STATUS_CODE
  4157. dwStatusPieceLength = 0; // next piece is the status line
  4158. break;
  4159. }
  4160. else if (*response == ' ' && !afStatusIntsFound[ver_state])
  4161. {
  4162. //
  4163. // Before processing MAJOR_VERSION_NUMBER or STATUS_CODE_NUMBER,
  4164. //allow up to 3 spaces.
  4165. //
  4166. // Multiple spaces are being allowed here because it is
  4167. //legacy behavior and may therefore be necessary, and being non-strict
  4168. //about it doesn't put anything at risk.
  4169. //
  4170. if (++dwStatusPieceLength > 3)
  4171. goto doneInvalidStatusLine;
  4172. }
  4173. else
  4174. {
  4175. // We fail if anything outside the spec is found, except
  4176. //for allowing multiple spaces before the status code.
  4177. goto doneInvalidStatusLine;
  4178. }
  4179. break;
  4180. case AFTER_STATUS_CODE:
  4181. //
  4182. // This will advance to the next CR or LF..
  4183. //
  4184. // We limit Reason-Phrase length to protect against malicious socket hogging
  4185. //
  4186. if (++dwStatusPieceLength > GlobalMaxSizeStatusLineResultText)
  4187. {
  4188. goto doneInvalidStatusLine;
  4189. }
  4190. break;
  4191. }
  4192. ++response;
  4193. ++dwBytesScanned;
  4194. }
  4195. //
  4196. // Verify there is enough reponse left to check for a CRLF..
  4197. //
  4198. if (response == lpszEnd
  4199. || response + 1 == lpszEnd)
  4200. {
  4201. //
  4202. //
  4203. // If we're at the end of the connection then the server sent us an
  4204. //incorrectly formatted response, Invalid status line.
  4205. //
  4206. // If more data may come, indicate to retry it.
  4207. //
  4208. DEBUG_PRINT(HTTP,
  4209. INFO,
  4210. ("found end of short response in status line\n"
  4211. ));
  4212. if (fEof)
  4213. goto doneInvalidStatusLine;
  4214. else
  4215. goto doneNeedMoreData;
  4216. }
  4217. dwStatusLineLength = dwBytesScanned;
  4218. //
  4219. // And to finish, verify the CRLF...
  4220. //
  4221. //CR
  4222. // some servers may not send the CR so give them a pass for compatibility
  4223. if ( *response == '\r')
  4224. {
  4225. ++response; // we know its safe to step since we checked if (respone == lpszEnd) above.
  4226. ++dwBytesScanned;
  4227. }
  4228. //LF
  4229. if ( *response != '\n')
  4230. goto doneInvalidStatusLine;
  4231. ++response; // we know its safe to step again since we checked if (respone+1 == lpszEnd) above.
  4232. ++dwBytesScanned;
  4233. //
  4234. // Some validation checking
  4235. //
  4236. // All three status ints must have been found.
  4237. // I found some code that assumes that if the Status Code == 0, then
  4238. //the status line hasn't been parsed yet. To be sure this assumption
  4239. //remains true, explicitly reject status lines with a 0 status code.
  4240. //
  4241. if (afStatusIntsFound[MAJOR_VERSION_NUMBER] != TRUE
  4242. || afStatusIntsFound[MINOR_VERSION_NUMBER] != TRUE
  4243. || afStatusIntsFound[STATUS_CODE_NUMBER] != TRUE
  4244. || adwStatusInts[STATUS_CODE_NUMBER] == 0)
  4245. {
  4246. goto doneInvalidStatusLine;
  4247. }
  4248. //
  4249. // Now we have our parsed header to add to the array
  4250. //
  4251. HEADER_STRING * freeHeader;
  4252. DWORD iSlot;
  4253. freeHeader = FindFreeSlot(&iSlot);
  4254. if (freeHeader == NULL) {
  4255. goto doneFailError;
  4256. } else {
  4257. INET_ASSERT(iSlot == 0); // status line should always be first
  4258. freeHeader->CreateOffsetString((DWORD)(lpszStatusLine - lpHeaderBase), dwStatusLineLength);
  4259. freeHeader->SetHash(0); // status line has no hash value.
  4260. }
  4261. //
  4262. // Success.. fill in the output params appropriately.
  4263. //
  4264. *lpfNeedMoreBuffer = FALSE;
  4265. *lpdwStatusCode = adwStatusInts[STATUS_CODE_NUMBER];
  4266. *lpdwMajorVersion = adwStatusInts[MAJOR_VERSION_NUMBER];
  4267. *lpdwMinorVersion = adwStatusInts[MINOR_VERSION_NUMBER];
  4268. *lpdwBufferLengthScanned += dwBytesScanned;
  4269. error = ERROR_SUCCESS;
  4270. goto exitFinalReturn;
  4271. doneNeedMoreData:
  4272. error = ERROR_SUCCESS;
  4273. *lpfNeedMoreBuffer = TRUE;
  4274. goto exitFinalReturn;
  4275. doneInvalidStatusLine:
  4276. error = ERROR_HTTP_INVALID_SERVER_RESPONSE;
  4277. *lpfNeedMoreBuffer = FALSE;
  4278. goto exitFinalReturn;
  4279. doneFailError:
  4280. error = ERROR_INTERNET_INTERNAL_ERROR;
  4281. *lpfNeedMoreBuffer = FALSE;
  4282. goto exitFinalReturn;
  4283. exitFinalReturn:
  4284. return error;
  4285. }
  4286. DWORD
  4287. HTTP_HEADER_PARSER::ParseHeaders(
  4288. IN LPSTR lpHeaderBase,
  4289. IN DWORD dwBufferLength,
  4290. IN BOOL fEof,
  4291. IN OUT DWORD *lpdwBufferLengthScanned,
  4292. OUT LPBOOL pfFoundCompleteLine,
  4293. OUT LPBOOL pfFoundEndOfHeaders
  4294. )
  4295. /*++
  4296. Routine Description:
  4297. Loads headers into HTTP_HEADERS member for subsequent parsing.
  4298. Parses string based headers and adds their parts to an internally stored
  4299. array of HTTP_HEADERS.
  4300. Input is assumed to be well formed Header Name/Value pairs, each deliminated
  4301. by ':' and '\r\n'.
  4302. Arguments:
  4303. lpszHeader - pointer to the header to check
  4304. dwHeaderLength - length of the header
  4305. Return Value:
  4306. None.
  4307. --*/
  4308. {
  4309. LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
  4310. LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
  4311. DWORD dwBytesScanned = 0;
  4312. BOOL success = FALSE;
  4313. DWORD error = ERROR_SUCCESS;
  4314. *pfFoundEndOfHeaders = FALSE;
  4315. //
  4316. // Each iteration of the following loop
  4317. // walks an HTTP header line of the form:
  4318. // HeaderName: HeaderValue\r\n
  4319. //
  4320. do
  4321. {
  4322. DWORD dwHash = HEADER_HASH_SEED;
  4323. LPSTR lpszHeaderName;
  4324. DWORD dwHeaderNameLength = 0;
  4325. DWORD dwHeaderLineLength = 0;
  4326. DWORD dwPreviousAmountOfBytesScanned = dwBytesScanned;
  4327. //
  4328. // Remove leading whitespace from header
  4329. //
  4330. while ( (response < lpszEnd) && ((*response == ' ') || (*response == '\t')) )
  4331. {
  4332. ++response;
  4333. ++dwBytesScanned;
  4334. }
  4335. //
  4336. // Scan for HeaderName:
  4337. //
  4338. lpszHeaderName = response;
  4339. dwPreviousAmountOfBytesScanned = dwBytesScanned;
  4340. while ((response < lpszEnd) && (*response != ':') && (*response != '\r') && (*response != '\n'))
  4341. {
  4342. //
  4343. // This code incapsulates CalculateHashNoCase as an optimization,
  4344. // we attempt to calculate the Hash value as we parse the header.
  4345. //
  4346. CHAR ch = *response;
  4347. if ((ch >= 'A') && (ch <= 'Z')) {
  4348. ch = MAKE_LOWER(ch);
  4349. }
  4350. dwHash += (DWORD)(dwHash << 5) + ch;
  4351. ++response;
  4352. ++dwBytesScanned;
  4353. }
  4354. dwHeaderNameLength = (DWORD) (response - lpszHeaderName);
  4355. //
  4356. // catch bogus responses: if we find what looks like one of a (very)
  4357. // small set of HTML tags, then assume the previous header was the
  4358. // last
  4359. //
  4360. if ((dwHeaderNameLength >= sizeof("<HTML>") - 1)
  4361. && (*lpszHeaderName == '<')
  4362. && (!strnicmp(lpszHeaderName, "<HTML>", sizeof("<HTML>") - 1)
  4363. || !strnicmp(lpszHeaderName, "<HEAD>", sizeof("<HEAD>") - 1))) {
  4364. *pfFoundEndOfHeaders = TRUE;
  4365. break;
  4366. }
  4367. //
  4368. // Keep scanning till end of the line.
  4369. //
  4370. while ((response < lpszEnd) && (*response != '\r') && (*response != '\n'))
  4371. {
  4372. ++response;
  4373. ++dwBytesScanned;
  4374. }
  4375. dwHeaderLineLength = (DWORD) (response - lpszHeaderName); // note: this headerLINElength
  4376. if (response == lpszEnd) {
  4377. //
  4378. // response now points one past the end of the buffer. We may be looking
  4379. // over the edge...
  4380. //
  4381. // if we're at the end of the connection then the server sent us an
  4382. // incorrectly formatted response. Probably an error.
  4383. //
  4384. // Otherwise its a partial response. We need more
  4385. //
  4386. DEBUG_PRINT(HTTP,
  4387. INFO,
  4388. ("found end of short response\n"
  4389. ));
  4390. success = fEof ? TRUE : FALSE;
  4391. //
  4392. // if we really hit the end of the response then update the amount of
  4393. // headers scanned
  4394. //
  4395. if (!success) {
  4396. dwBytesScanned = dwPreviousAmountOfBytesScanned;
  4397. }
  4398. break;
  4399. }
  4400. else
  4401. {
  4402. //
  4403. // we reached a CR or LF. This is the end of this current header. Find
  4404. // the start of the next one
  4405. //
  4406. //
  4407. // first, strip off any trailing spaces from the current header. We do
  4408. // this by simply reducing the string length. We only look for space
  4409. // and tab characters. Only do this if we have a non-zero length header
  4410. //
  4411. if (dwHeaderLineLength != 0) {
  4412. for (int i = -1; response[i] == ' ' || response[i] == '\t'; --i) {
  4413. --dwHeaderLineLength;
  4414. }
  4415. }
  4416. INET_ASSERT((int)dwHeaderLineLength >= 0);
  4417. //
  4418. // some servers respond with "\r\r\n". Lame
  4419. // A new twist: "\r \r\n". Lamer
  4420. //
  4421. while ((response < lpszEnd)
  4422. && ((*response == '\r') || (*response == ' '))) {
  4423. ++response;
  4424. ++dwBytesScanned;
  4425. }
  4426. if (response == lpszEnd) {
  4427. //
  4428. // hit end of buffer without finding LF
  4429. //
  4430. success = FALSE;
  4431. DEBUG_PRINT(HTTP,
  4432. WARNING,
  4433. ("hit end of buffer without finding LF\n"
  4434. ));
  4435. //
  4436. // get more data, reparse this line
  4437. //
  4438. dwBytesScanned = dwPreviousAmountOfBytesScanned;
  4439. break;
  4440. } else if (*response == '\n') {
  4441. ++response;
  4442. ++dwBytesScanned;
  4443. //
  4444. // if we found the empty line then we are done
  4445. //
  4446. if (dwHeaderLineLength == 0) {
  4447. *pfFoundEndOfHeaders = TRUE;
  4448. break;
  4449. }
  4450. success = TRUE;
  4451. }
  4452. }
  4453. // If a header name has no trailing colon ignore it
  4454. if (lpszHeaderName[dwHeaderNameLength] != ':')
  4455. {
  4456. continue;
  4457. }
  4458. //
  4459. // Now we have our parsed header to add to the array
  4460. //
  4461. HEADER_STRING * freeHeader;
  4462. DWORD iSlot;
  4463. freeHeader = FindFreeSlot(&iSlot);
  4464. if (freeHeader == NULL) {
  4465. error = GetError();
  4466. INET_ASSERT(error != ERROR_SUCCESS);
  4467. goto quit;
  4468. } else {
  4469. freeHeader->CreateOffsetString((DWORD) (lpszHeaderName - lpHeaderBase), dwHeaderLineLength);
  4470. freeHeader->SetHash(dwHash);
  4471. }
  4472. //CHAR szTemp[256];
  4473. //
  4474. //memcpy(szTemp, lpszHeaderName, dwHeaderLineLength);
  4475. //lpszHeaderName[dwHeaderLineLength] = '\0';
  4476. //DEBUG_PRINT(HTTP,
  4477. // INFO,
  4478. // ("ParseHeaders: adding=%q\n", lpszHeaderName
  4479. // ));
  4480. //
  4481. // Now see if this is a known header we are adding, if so then we note that fact
  4482. //
  4483. DWORD dwKnownQueryIndex;
  4484. if (HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwKnownQueryIndex) )
  4485. {
  4486. freeHeader->SetNextKnownIndex(FastAdd(dwKnownQueryIndex, iSlot));
  4487. }
  4488. } while (TRUE);
  4489. quit:
  4490. *lpdwBufferLengthScanned += dwBytesScanned;
  4491. *pfFoundCompleteLine = success;
  4492. return error;
  4493. }
  4494. #if 0
  4495. //
  4496. // Slower version of the function above used for performance work!!! Keep around
  4497. // until we're sure it will never be used.
  4498. //
  4499. DWORD
  4500. HTTP_HEADER_PARSER::ParseHeaders(
  4501. IN LPSTR lpHeaderBase,
  4502. IN DWORD dwBufferLength,
  4503. IN BOOL fEof,
  4504. IN OUT DWORD *lpdwBufferLengthScanned,
  4505. OUT LPBOOL pfFoundCompleteLine,
  4506. OUT LPBOOL pfFoundEndOfHeaders
  4507. )
  4508. /*++
  4509. Routine Description:
  4510. Parses string based headers and adds their parts to an internally stored
  4511. array of HTTP_HEADERS.
  4512. Input is assumed to be well formed Header Name/Value pairs, each deliminated
  4513. by ':' and '\r\n'.
  4514. Arguments:
  4515. lpszHeader - pointer to the header to check
  4516. dwHeaderLength - length of the header
  4517. Return Value:
  4518. None.
  4519. --*/
  4520. {
  4521. #define HTTP_HEADER_PARSE_LEADING_SPACE 0
  4522. #define HTTP_HEADER_PARSE_NAME 1
  4523. #define HTTP_HEADER_PARSE_VALUE 2
  4524. #define HTTP_HEADER_PARSE_CR 3
  4525. #define HTTP_HEADER_PARSE_LF 4
  4526. LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
  4527. LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
  4528. DWORD dwBytesScanned = 0;
  4529. BOOL success = FALSE;
  4530. DWORD error = ERROR_SUCCESS;
  4531. DWORD state = HTTP_HEADER_PARSE_LEADING_SPACE;
  4532. *pfFoundEndOfHeaders = FALSE;
  4533. //
  4534. // Each iteration of the following loop
  4535. // walks an HTTP header line of the form:
  4536. // HeaderName: HeaderValue\r\n
  4537. //
  4538. DWORD dwHash = HEADER_HASH_SEED;
  4539. LPSTR lpszHeaderName;
  4540. DWORD dwHeaderNameLength = 0;
  4541. DWORD dwHeaderLineLength = 0;
  4542. DWORD dwPreviousAmountOfBytesScanned = dwBytesScanned;
  4543. DWORD dwWhiteSpace = 0;
  4544. while ( (response < lpszEnd) )
  4545. {
  4546. switch (state)
  4547. {
  4548. case HTTP_HEADER_PARSE_LEADING_SPACE:
  4549. //
  4550. // Remove leading whitespace from header
  4551. //
  4552. if ( *response == ' ' ||
  4553. *response == '\t' )
  4554. {
  4555. break;
  4556. }
  4557. //
  4558. // Scan for HeaderName:
  4559. //
  4560. state = HTTP_HEADER_PARSE_NAME;
  4561. lpszHeaderName = response;
  4562. dwPreviousAmountOfBytesScanned = dwBytesScanned;
  4563. // fall through
  4564. case HTTP_HEADER_PARSE_NAME:
  4565. switch (*response)
  4566. {
  4567. case ':':
  4568. //
  4569. // Now parse the Header Value
  4570. //
  4571. state = HTTP_HEADER_PARSE_VALUE;
  4572. dwHeaderNameLength = (DWORD) (response - lpszHeaderName);
  4573. break;
  4574. case '\r':
  4575. state = HTTP_HEADER_PARSE_CR;
  4576. // note: this is headerLINElength
  4577. dwHeaderLineLength = (DWORD) (response - lpszHeaderName);
  4578. break;
  4579. case '\n':
  4580. state = HTTP_HEADER_PARSE_LF;
  4581. // note: this is headerLINElength
  4582. dwHeaderLineLength = (DWORD) (response - lpszHeaderName);
  4583. goto Got_LF;
  4584. //break;
  4585. default:
  4586. {
  4587. CHAR ch = *response;
  4588. if ((ch >= 'A') && (ch <= 'Z')) {
  4589. ch = MAKE_LOWER(ch);
  4590. }
  4591. dwHash += (DWORD)(dwHash << 5) + ch;
  4592. break;
  4593. }
  4594. }
  4595. break;
  4596. case HTTP_HEADER_PARSE_VALUE:
  4597. switch ( *response )
  4598. {
  4599. case '\r':
  4600. state = HTTP_HEADER_PARSE_CR;
  4601. // note: this is headerLINElength
  4602. dwHeaderLineLength = (DWORD) (response - lpszHeaderName) - dwWhiteSpace;
  4603. break;
  4604. case '\n':
  4605. state = HTTP_HEADER_PARSE_LF;
  4606. // note: this is headerLINElength
  4607. dwHeaderLineLength = (DWORD) (response - lpszHeaderName) - dwWhiteSpace;
  4608. goto Got_LF;
  4609. case ' ':
  4610. case '\t':
  4611. // count whitespace at end of the line
  4612. dwWhiteSpace++;
  4613. break;
  4614. default:
  4615. dwWhiteSpace = 0;
  4616. break;
  4617. }
  4618. break;
  4619. case HTTP_HEADER_PARSE_CR:
  4620. if (*response == ' ' ||
  4621. *response == '\r' )
  4622. {
  4623. break;
  4624. }
  4625. state = HTTP_HEADER_PARSE_LF;
  4626. // fall through
  4627. case HTTP_HEADER_PARSE_LF:
  4628. Got_LF:
  4629. //
  4630. // if we found the empty line then we are done
  4631. //
  4632. success = TRUE;
  4633. if (dwHeaderLineLength == 0) {
  4634. ++dwBytesScanned;
  4635. ++response;
  4636. *pfFoundEndOfHeaders = TRUE;
  4637. goto quit;
  4638. }
  4639. {
  4640. //
  4641. // Now we have our parsed header to add to the array
  4642. //
  4643. HEADER_STRING * freeHeader;
  4644. DWORD iSlot;
  4645. freeHeader = FindFreeSlot(&iSlot);
  4646. if (freeHeader == NULL) {
  4647. error = GetError();
  4648. INET_ASSERT(error != ERROR_SUCCESS);
  4649. goto quit;
  4650. } else {
  4651. freeHeader->CreateOffsetString((lpszHeaderName - lpHeaderBase), dwHeaderLineLength);
  4652. freeHeader->SetHash(dwHash);
  4653. }
  4654. CHAR szTemp[256];
  4655. memcpy(szTemp, lpszHeaderName, dwHeaderLineLength);
  4656. szTemp[dwHeaderLineLength] = '\0';
  4657. DEBUG_PRINT(HTTP,
  4658. INFO,
  4659. ("ParseHeaders: adding=%q\n", lpszHeaderName
  4660. ));
  4661. //
  4662. // Now see if this is a known header we are adding, if so then we note that fact
  4663. //
  4664. DWORD dwKnownQueryIndex;
  4665. if (HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwKnownQueryIndex) )
  4666. {
  4667. freeHeader->SetNextKnownIndex(FastAdd(dwKnownQueryIndex, iSlot));
  4668. }
  4669. }
  4670. //
  4671. // Move on to our next header.
  4672. //
  4673. dwHash = HEADER_HASH_SEED;
  4674. dwHeaderNameLength = 0;
  4675. dwHeaderLineLength = 0;
  4676. dwPreviousAmountOfBytesScanned = dwBytesScanned;
  4677. dwWhiteSpace = 0;
  4678. state = HTTP_HEADER_PARSE_LEADING_SPACE;
  4679. break;
  4680. } // switch (state)
  4681. ++response;
  4682. ++dwBytesScanned;
  4683. } // while (response < lpszEnd)
  4684. //
  4685. // response now points one past the end of the buffer. We may be looking
  4686. // over the edge...
  4687. //
  4688. // if we're at the end of the connection then the server sent us an
  4689. // incorrectly formatted response. Probably an error.
  4690. //
  4691. // Otherwise its a partial response. We need more
  4692. //
  4693. DEBUG_PRINT(HTTP,
  4694. INFO,
  4695. ("found end of short response\n"
  4696. ));
  4697. success = fEof ? TRUE : FALSE;
  4698. //
  4699. // if we really hit the end of the response then update the amount of
  4700. // headers scanned
  4701. //
  4702. if (!success) {
  4703. dwBytesScanned = dwPreviousAmountOfBytesScanned;
  4704. }
  4705. quit:
  4706. *lpdwBufferLengthScanned += dwBytesScanned;
  4707. *pfFoundCompleteLine = success;
  4708. return error;
  4709. }
  4710. #endif