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.

2176 lines
58 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. headers.cxx
  5. Abstract:
  6. Contents:
  7. HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer
  8. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader
  9. HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader
  10. HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders
  11. HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders
  12. HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion
  13. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode
  14. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText
  15. HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders
  16. HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName
  17. HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders
  18. MapHttpMethodType
  19. CreateEscapedUrlPath
  20. (CalculateHashNoCase)
  21. Author:
  22. Richard L Firth (rfirth) 20-Dec-1995
  23. Revision History:
  24. 20-Dec-1995 rfirth
  25. Created
  26. --*/
  27. #include <wininetp.h>
  28. #include <perfdiag.hxx>
  29. #include "httpp.h"
  30. VOID
  31. HTTP_REQUEST_HANDLE_OBJECT::ReplaceStatusHeader(
  32. IN LPCSTR lpszStatus
  33. )
  34. /*
  35. Description:
  36. Replace the status line in a header (eg. "200 OK") with the specified text.
  37. Arguments:
  38. lpszStatus - Status text (eg. "200 OK")
  39. Return Value:
  40. None
  41. */
  42. {
  43. LockHeaders();
  44. //INET_ASSERT (!_CacheWriteInProgress);
  45. LPSTR pszHeader = _ResponseHeaders.GetHeaderPointer(_ResponseBuffer, 0);
  46. INET_ASSERT(pszHeader);
  47. LPSTR pszStatus = StrChr (pszHeader, ' ');
  48. SKIPWS(pszStatus);
  49. INET_ASSERT (!memcmp(pszStatus, "206", 3));
  50. memcpy(pszStatus, lpszStatus, lstrlen(lpszStatus)+1);
  51. _ResponseHeaders.ShrinkHeader(_ResponseBuffer, 0,
  52. HTTP_QUERY_STATUS_TEXT, HTTP_QUERY_STATUS_TEXT,
  53. (DWORD) (pszStatus - pszHeader) + lstrlen(lpszStatus));
  54. UnlockHeaders();
  55. }
  56. LPSTR
  57. HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer(
  58. OUT LPDWORD lpdwRequestLength,
  59. IN LPVOID lpOptional,
  60. IN DWORD dwOptionalLength,
  61. IN BOOL bExtraCrLf,
  62. IN DWORD dwMaxPacketLength,
  63. OUT LPBOOL lpbCombinedData
  64. )
  65. /*++
  66. Routine Description:
  67. Creates a request buffer from the HTTP request and headers
  68. Arguments:
  69. lpdwRequestLength - pointer to returned buffer length
  70. lpOptional - pointer to optional data
  71. dwOptionalLength - length of optional data
  72. bExtraCrLf - TRUE if we need to add additional CR-LF to buffer
  73. dwMaxPacketLength - maximum length of buffer
  74. lpbCombinedData - output TRUE if data successfully combined into one
  75. Return Value:
  76. LPSTR
  77. Success - pointer to allocated buffer
  78. Failure - NULL
  79. --*/
  80. {
  81. DEBUG_ENTER((DBG_HTTP,
  82. Pointer,
  83. "HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer",
  84. "%#x, %#x, %d, %B, %d, %#x",
  85. lpdwRequestLength,
  86. lpOptional,
  87. dwOptionalLength,
  88. bExtraCrLf,
  89. dwMaxPacketLength,
  90. lpbCombinedData
  91. ));
  92. PERF_ENTER(CreateRequestBuffer);
  93. LPSTR requestBuffer = NULL;
  94. *lpbCombinedData = FALSE;
  95. if (!_RequestHeaders.LockHeaders())
  96. {
  97. goto quit;
  98. }
  99. DWORD headersLength;
  100. DWORD requestLength;
  101. DWORD optionalLength;
  102. HEADER_STRING * pRequest = _RequestHeaders.GetFirstHeader();
  103. HEADER_STRING & request = *pRequest;
  104. /*
  105. WCHAR wszUrl[1024];
  106. LPWSTR pwszUrl = NULL;
  107. BYTE utf8Url[2048];
  108. LPBYTE pbUrl = NULL;
  109. */
  110. LPSTR pszObject = _RequestHeaders.ObjectName();
  111. DWORD dwObjectLength = _RequestHeaders.ObjectNameLength();
  112. if (pRequest == NULL) {
  113. goto Cleanup;
  114. }
  115. INET_ASSERT(request.HaveString());
  116. headersLength = _RequestHeaders.HeadersLength();
  117. requestLength = headersLength + (sizeof("\r\n") - 1);
  118. /*------------------------------------------------------------------
  119. GlobalEnableUtf8Encoding = FALSE;
  120. if (GlobalEnableUtf8Encoding
  121. && StringContainsHighAnsi(pszObject, dwObjectLength)) {
  122. pwszUrl = wszUrl;
  123. DWORD arrayElements = ARRAY_ELEMENTS(wszUrl);
  124. if (dwObjectLength > ARRAY_ELEMENTS(wszUrl)) {
  125. arrayElements = dwObjectLength;
  126. pwszUrl = (LPWSTR)ALLOCATE_FIXED_MEMORY(arrayElements * sizeof(*pwszUrl));
  127. if (pwszUrl == NULL) {
  128. goto utf8_cleanup;
  129. }
  130. }
  131. PFNINETMULTIBYTETOUNICODE pfnMBToUnicode;
  132. pfnMBToUnicode = GetInetMultiByteToUnicode( );
  133. if (pfnMBToUnicode == NULL) {
  134. goto utf8_cleanup;
  135. }
  136. HRESULT hr;
  137. DWORD dwMode;
  138. INT nMBChars;
  139. INT nWChars;
  140. nMBChars = dwObjectLength;
  141. nWChars = arrayElements;
  142. dwMode = 0;
  143. hr = pfnMBToUnicode(&dwMode,
  144. GetCodePage(),
  145. pszObject,
  146. &nMBChars,
  147. pwszUrl,
  148. &nWChars
  149. );
  150. if (hr != S_OK || nWChars == 0) {
  151. goto utf8_cleanup;
  152. }
  153. DWORD nBytes;
  154. nBytes = CountUnicodeToUtf8(pwszUrl, (DWORD)nWChars, TRUE);
  155. pbUrl = utf8Url;
  156. if (nBytes > ARRAY_ELEMENTS(utf8Url)) {
  157. pbUrl = (LPBYTE)ALLOCATE_FIXED_MEMORY(nBytes);
  158. if (pbUrl == NULL) {
  159. goto utf8_cleanup;
  160. }
  161. }
  162. DWORD error;
  163. error = ConvertUnicodeToUtf8(pwszUrl,
  164. (DWORD)nWChars,
  165. pbUrl,
  166. nBytes,
  167. TRUE
  168. );
  169. INET_ASSERT(error == ERROR_SUCCESS);
  170. if (error != ERROR_SUCCESS) {
  171. goto utf8_cleanup;
  172. }
  173. requestLength = requestLength - dwObjectLength + nBytes;
  174. headersLength = headersLength - dwObjectLength + nBytes;
  175. pszObject = (LPSTR)pbUrl;
  176. dwObjectLength = nBytes;
  177. goto after_utf8;
  178. utf8_cleanup:
  179. if ((pwszUrl != wszUrl) && (pwszUrl != NULL)) {
  180. FREE_MEMORY(pwszUrl);
  181. }
  182. pwszUrl = NULL;
  183. if ((pbUrl != utf8Url) && (pbUrl != NULL)) {
  184. FREE_MEMORY(pbUrl);
  185. }
  186. pbUrl = NULL;
  187. pszObject = NULL;
  188. dwObjectLength = 0;
  189. }
  190. after_utf8:
  191. ------------------------------------------------------------------*/
  192. optionalLength = (DWORD)(dwOptionalLength + (bExtraCrLf ? (sizeof("\r\n") - 1) : 0));
  193. if (requestLength + optionalLength <= dwMaxPacketLength) {
  194. requestLength += optionalLength;
  195. } else {
  196. optionalLength = 0;
  197. bExtraCrLf = FALSE;
  198. }
  199. requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
  200. if (requestBuffer != NULL) {
  201. if (optionalLength != 0) {
  202. *lpbCombinedData = TRUE;
  203. }
  204. } else if (optionalLength != 0) {
  205. requestLength = headersLength + (sizeof("\r\n") - 1);
  206. optionalLength = 0;
  207. bExtraCrLf = FALSE;
  208. requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
  209. }
  210. if (requestBuffer != NULL) {
  211. LPSTR buffer = requestBuffer;
  212. //
  213. // copy the headers. Remember: header 0 is the request
  214. //
  215. if (ERROR_SUCCESS != _RequestHeaders.CopyHeaders(&buffer, pszObject, dwObjectLength))
  216. {
  217. ResizeBuffer(requestBuffer, 0, FALSE);
  218. requestBuffer = buffer = NULL;
  219. goto Cleanup;
  220. }
  221. //
  222. // terminate the request
  223. //
  224. *buffer++ = '\r';
  225. *buffer++ = '\n';
  226. if (optionalLength != 0) {
  227. if (dwOptionalLength != 0) {
  228. memcpy(buffer, lpOptional, dwOptionalLength);
  229. buffer += dwOptionalLength;
  230. }
  231. if (bExtraCrLf) {
  232. *buffer++ = '\r';
  233. *buffer++ = '\n';
  234. }
  235. }
  236. INET_ASSERT((SIZE_T)(buffer-requestBuffer) == requestLength);
  237. *lpdwRequestLength = requestLength;
  238. }
  239. Cleanup:
  240. _RequestHeaders.UnlockHeaders();
  241. DEBUG_PRINT(HTTP,
  242. INFO,
  243. ("request length = %d, combined = %B\n",
  244. *lpdwRequestLength,
  245. *lpbCombinedData
  246. ));
  247. /*
  248. if ((pbUrl != NULL) && (pbUrl != utf8Url)) {
  249. FREE_MEMORY(pbUrl);
  250. }
  251. if ((pwszUrl != NULL) && (pwszUrl != wszUrl)) {
  252. FREE_MEMORY(pwszUrl);
  253. }
  254. */
  255. quit:
  256. PERF_LEAVE(CreateRequestBuffer);
  257. DEBUG_LEAVE(requestBuffer);
  258. return requestBuffer;
  259. }
  260. DWORD
  261. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
  262. IN LPCSTR lpszHeaderName,
  263. IN DWORD dwHeaderNameLength,
  264. IN LPVOID lpBuffer,
  265. IN OUT LPDWORD lpdwBufferLength,
  266. IN DWORD dwModifiers,
  267. IN OUT LPDWORD lpdwIndex
  268. )
  269. /*++
  270. Routine Description:
  271. Searches for an arbitrary request header and if found, returns its value
  272. Arguments:
  273. lpszHeaderName - pointer to the name of the header to find
  274. dwHeaderNameLength - length of the header
  275. lpBuffer - pointer to buffer for results
  276. lpdwBufferLength - IN: length of lpBuffer
  277. OUT: length of the returned header value, or required
  278. length of lpBuffer
  279. dwModifiers - how to return the data: as number, as SYSTEMTIME
  280. structure, etc.
  281. lpdwIndex - IN: 0-based index of header to find
  282. OUT: next header index if success returned
  283. Return Value:
  284. DWORD
  285. Success - ERROR_SUCCESS
  286. Failure - ERROR_INSUFFICIENT_BUFFER
  287. lpBuffer not large enough for results
  288. ERROR_HTTP_HEADER_NOT_FOUND
  289. Couldn't find the requested header
  290. --*/
  291. {
  292. DEBUG_ENTER((DBG_HTTP,
  293. Dword,
  294. "QueryRequestHeader",
  295. "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
  296. lpszHeaderName,
  297. min(dwHeaderNameLength + 1, 80),
  298. lpszHeaderName,
  299. dwHeaderNameLength,
  300. lpBuffer,
  301. lpdwBufferLength,
  302. *lpdwBufferLength,
  303. dwModifiers,
  304. lpdwIndex,
  305. *lpdwIndex
  306. ));
  307. PERF_ENTER(QueryRequestHeader);
  308. DWORD error;
  309. error = _RequestHeaders.FindHeader(NULL,
  310. lpszHeaderName,
  311. dwHeaderNameLength,
  312. dwModifiers,
  313. lpBuffer,
  314. lpdwBufferLength,
  315. lpdwIndex
  316. );
  317. PERF_LEAVE(QueryRequestHeader);
  318. DEBUG_LEAVE(error);
  319. return error;
  320. }
  321. DWORD
  322. HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
  323. IN DWORD dwQueryIndex,
  324. IN LPVOID lpBuffer,
  325. IN OUT LPDWORD lpdwBufferLength,
  326. IN DWORD dwModifiers,
  327. IN OUT LPDWORD lpdwIndex
  328. )
  329. /*++
  330. Routine Description:
  331. Searches for an arbitrary request header and if found, returns its value
  332. Arguments:
  333. lpszHeaderName - pointer to the name of the header to find
  334. dwHeaderNameLength - length of the header
  335. lpBuffer - pointer to buffer for results
  336. lpdwBufferLength - IN: length of lpBuffer
  337. OUT: length of the returned header value, or required
  338. length of lpBuffer
  339. dwModifiers - how to return the data: as number, as SYSTEMTIME
  340. structure, etc.
  341. lpdwIndex - IN: 0-based index of header to find
  342. OUT: next header index if success returned
  343. Return Value:
  344. DWORD
  345. Success - ERROR_SUCCESS
  346. Failure - ERROR_INSUFFICIENT_BUFFER
  347. lpBuffer not large enough for results
  348. ERROR_HTTP_HEADER_NOT_FOUND
  349. Couldn't find the requested header
  350. --*/
  351. {
  352. DEBUG_ENTER((DBG_HTTP,
  353. Dword,
  354. "QueryRequestHeader",
  355. "%u, %#x [%#x], %#x, %#x [%d]",
  356. dwQueryIndex,
  357. lpBuffer,
  358. lpdwBufferLength,
  359. *lpdwBufferLength,
  360. dwModifiers,
  361. lpdwIndex,
  362. *lpdwIndex
  363. ));
  364. PERF_ENTER(QueryRequestHeader);
  365. DWORD error;
  366. error = _RequestHeaders.FindHeader(NULL,
  367. dwQueryIndex,
  368. dwModifiers,
  369. lpBuffer,
  370. lpdwBufferLength,
  371. lpdwIndex
  372. );
  373. PERF_LEAVE(QueryRequestHeader);
  374. DEBUG_LEAVE(error);
  375. return error;
  376. }
  377. DWORD
  378. HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader(
  379. IN DWORD dwHeaderIndex,
  380. IN LPSTR lpszHeader,
  381. IN DWORD dwHeaderLength
  382. )
  383. /*++
  384. Routine Description:
  385. Adds a created response header to the response header array. Unlike normal
  386. response headers, this will be a pointer to an actual string, not an offset
  387. into the response buffer.
  388. Even if the address of the response buffer changes, created response headers
  389. will remain fixed
  390. N.B. The header MUST NOT have a CR-LF terminator
  391. N.B.-2 This function must be called under the header lock.
  392. Arguments:
  393. dwHeaderIndex - index into header value we are actually creating
  394. lpszHeader - pointer to created (internal) header to add
  395. dwHeaderLength - length of response header, or -1 if ASCIIZ
  396. Return Value:
  397. DWORD
  398. Success - ERROR_SUCCESS
  399. Failure - ERROR_NOT_ENOUGH_MEMORY
  400. --*/
  401. {
  402. DEBUG_ENTER((DBG_HTTP,
  403. Dword,
  404. "AddInternalResponseHeader",
  405. "%u [%q], %q, %d",
  406. dwHeaderIndex,
  407. GlobalKnownHeaders[dwHeaderIndex].Text,
  408. lpszHeader,
  409. dwHeaderLength
  410. ));
  411. DWORD error;
  412. if (dwHeaderLength == (DWORD)-1) {
  413. dwHeaderLength = lstrlen(lpszHeader);
  414. }
  415. INET_ASSERT((lpszHeader[dwHeaderLength - 1] != '\r')
  416. && (lpszHeader[dwHeaderLength - 1] != '\n'));
  417. //
  418. // find the next slot for this header
  419. //
  420. HEADER_STRING * freeHeader;
  421. //
  422. // if we already have all the headers (the 'empty' header is the last one
  423. // in the array) then change the last header to be the one we are adding
  424. // and add a new empty header, else just add this one
  425. //
  426. DWORD iSlot;
  427. freeHeader = _ResponseHeaders.FindFreeSlot(&iSlot);
  428. if (freeHeader == NULL) {
  429. error = _ResponseHeaders.GetError();
  430. INET_ASSERT(error != ERROR_SUCCESS);
  431. } else {
  432. HEADER_STRING * lastHeader;
  433. lastHeader = _ResponseHeaders.GetEmptyHeader();
  434. if (lastHeader != NULL) {
  435. //
  436. // make copy of last header - its an offset string
  437. //
  438. *freeHeader = *lastHeader;
  439. //
  440. // use what was last header as free header
  441. //
  442. freeHeader = lastHeader;
  443. }
  444. freeHeader->MakeCopy(lpszHeader, dwHeaderLength);
  445. freeHeader->SetNextKnownIndex(_ResponseHeaders.FastAdd(dwHeaderIndex, iSlot));
  446. error = ERROR_SUCCESS;
  447. }
  448. DEBUG_LEAVE(error);
  449. return error;
  450. }
  451. DWORD
  452. HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders(
  453. IN OUT LPBOOL lpbEof
  454. )
  455. /*++
  456. Routine Description:
  457. Given the next chunk of the response, updates the response headers. The
  458. buffer pointer, buffer length and number of bytes received values are all
  459. maintained in this object (_ResponseBuffer, _ResponseBufferLength and
  460. _BytesReceived, resp.)
  461. Arguments:
  462. lpbEof - IN: TRUE if we have reached the end of the response
  463. OUT: TRUE if we have reached the end of the response or the end
  464. of the headers
  465. Return Value:
  466. DWORD
  467. Success - ERROR_SUCCESS
  468. Failure - ERROR_NOT_ENOUGH_MEMORY
  469. --*/
  470. {
  471. DEBUG_ENTER((DBG_HTTP,
  472. Dword,
  473. "HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders",
  474. "%#x [%.*q], %d, %d, %#x [%B]",
  475. _ResponseBuffer + _ResponseScanned,
  476. min(_ResponseBufferLength + 1, 80),
  477. _ResponseBuffer + _ResponseScanned,
  478. _ResponseBufferLength,
  479. _BytesReceived,
  480. lpbEof,
  481. *lpbEof
  482. ));
  483. PERF_ENTER(UpdateResponseHeaders);
  484. LPSTR lpszBuffer = (LPSTR)_ResponseBuffer + _ResponseScanned;
  485. DWORD dwBytesReceived = _BytesReceived - _ResponseScanned;
  486. DWORD error = ERROR_SUCCESS;
  487. BOOL success = TRUE;
  488. HEADER_STRING * statusLine;
  489. //
  490. // lock down the response headers for the duration of this request. The only
  491. // way another thread is going to wait on this lock is if the reference on
  492. // the HTTP request object goes to zero, which *shouldn't* happen
  493. //
  494. if (!_ResponseHeaders.LockHeaders())
  495. {
  496. error = ERROR_NOT_ENOUGH_MEMORY;
  497. goto quit;
  498. }
  499. //
  500. // if input EOF is set then the caller is telling us that the end of the
  501. // response has been reached at transport level (the server closed the
  502. // connectiion)
  503. //
  504. if (*lpbEof) {
  505. SetEof(TRUE);
  506. }
  507. //
  508. // if we don't yet know whether we have a HTTP/1.0 (or greater) or HTTP/0.9
  509. // response yet, then try to find out.
  510. //
  511. // Only responses greater than HTTP/0.9 start with the "HTTP/#.#" string
  512. //
  513. if (!IsDownLevel() && !IsUpLevel()) {
  514. if ((dwBytesReceived < sizeof("Secure-HTTP/")) && !*lpbEof) {
  515. goto done;
  516. }
  517. #define MAKE_VERSION_ENTRY(string) string, sizeof(string) - 1
  518. static struct {
  519. LPSTR Version;
  520. DWORD Length;
  521. } KnownVersionsStrings[] = {
  522. MAKE_VERSION_ENTRY("HTTP/"),
  523. MAKE_VERSION_ENTRY("S-HTTP/"),
  524. MAKE_VERSION_ENTRY("SHTTP/"),
  525. MAKE_VERSION_ENTRY("Secure-HTTP/"),
  526. //
  527. // allow for servers generating slightly off-the-wall responses
  528. //
  529. MAKE_VERSION_ENTRY("HTTP ")
  530. };
  531. #define NUM_HTTP_VERSIONS ARRAY_ELEMENTS(KnownVersionsStrings)
  532. //
  533. // We know this is the start of a HTTP response, but there may be some
  534. // noise at the start from bad HTML authoring, or bad content-length on
  535. // the previous response on a keep-alive connection. We will try to sync
  536. // up to the HTTP header (we will only look for this - I have never seen
  537. // any of the others, and I doubt its worth the increased complexity and
  538. // processing time)
  539. //
  540. LPSTR lpszBuf;
  541. DWORD bytesLeft;
  542. BOOL bFoundStart;
  543. lpszBuf = lpszBuffer;
  544. bytesLeft = dwBytesReceived;
  545. bFoundStart = FALSE;
  546. do {
  547. while ((bytesLeft > 0) && (*lpszBuf != 'H') && (*lpszBuf != 'h')) {
  548. ++lpszBuf;
  549. --bytesLeft;
  550. ++_ResponseScanned;
  551. }
  552. if (bytesLeft == 0) {
  553. break;
  554. }
  555. //
  556. // scan for the known version strings
  557. //
  558. for (int i = 0; i < NUM_HTTP_VERSIONS; ++i) {
  559. LPSTR version = KnownVersionsStrings[i].Version;
  560. DWORD length = KnownVersionsStrings[i].Length;
  561. if ((bytesLeft >= length)
  562. //
  563. // try the most common case as a direct comparison. memcmp()
  564. // should expand to cmpsd && cmpsb on x86 (most common platform
  565. // and one on which we are most interested in improving perf)
  566. //
  567. && (((i == 0)
  568. && (memcmp(lpszBuf, "HTTP/", sizeof("HTTP/") - 1) == 0))
  569. //&& (lpszBuf[0] == 'H')
  570. //&& (lpszBuf[1] == 'T')
  571. //&& (lpszBuf[2] == 'T')
  572. //&& (lpszBuf[3] == 'P')
  573. //&& (lpszBuf[4] == '/'))
  574. //
  575. // "Clients should be tolerant in parsing the Status-Line"
  576. // quote from HTTP/1.1 spec, therefore we perform a
  577. // case-insensitive string comparison here
  578. //
  579. || (_strnicmp(lpszBuf, version, length) == 0))) {
  580. //
  581. // it starts with one of the recognized protocol version strings.
  582. // We assume its not a down-level server, although it could be,
  583. // sending back a plain text document that has e.g. "HTTP/1.0..."
  584. // at its start
  585. //
  586. // According to the HTTP "spec", though, it is mentioned that 0.9
  587. // servers typically only return HTML, hence we shouldn't see
  588. // even a 0.9 response start with non-HTML data
  589. //
  590. SetUpLevel(TRUE);
  591. //
  592. // we have start of this response
  593. //
  594. lpszBuffer = lpszBuf;
  595. bFoundStart = TRUE;
  596. break;
  597. }
  598. }
  599. //
  600. // if we didn't find the start of the HTTP response then search again
  601. //
  602. if (!bFoundStart) {
  603. ++lpszBuf;
  604. --bytesLeft;
  605. ++_ResponseScanned;
  606. }
  607. } while (!bFoundStart && (bytesLeft > 0));
  608. //
  609. // if we didn't find a recognizable HTTP 1.x response then we assume its
  610. // a down-level response
  611. //
  612. if (!IsUpLevel()) {
  613. //
  614. // if we didn't find the start of a valid HTTP response and we have
  615. // not filled the response buffer or hit the end of the connection
  616. // then quit so we can get the next packet
  617. //
  618. if ((_BytesReceived < _ResponseBufferLength) && !IsEof()) {
  619. DEBUG_PRINT(HTTP,
  620. WARNING,
  621. ("Didn't find start of response. Try again\n"
  622. ));
  623. //dprintf("*** didn't find start of response. Try again\n");
  624. goto done;
  625. }
  626. //
  627. // this may be a real down-level server, or it may be the response
  628. // from an FTP or gopher server via a proxy, in which case there
  629. // will be no headers. We will add some default headers to make
  630. // life easier for higher level software
  631. //
  632. AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
  633. "HTTP/1.0 200 OK",
  634. sizeof("HTTP/1.0 200 OK") - 1
  635. );
  636. _StatusCode = HTTP_STATUS_OK;
  637. //SetDownLevel(TRUE);
  638. //
  639. // we're now ready for the app to start reading data out
  640. //
  641. SetData(TRUE);
  642. //
  643. // down-level server: we're done
  644. //
  645. DEBUG_PRINT(HTTP,
  646. INFO,
  647. ("Server is down-level\n"
  648. ));
  649. goto done;
  650. }
  651. }
  652. //
  653. // this stuff's only for uplevel responses, sorry
  654. //
  655. INET_ASSERT(IsUpLevel());
  656. //
  657. // Note: at this point we can't store pointers into the response buffer
  658. // because it might move during a subsequent reallocation. We have to
  659. // maintain offsets into the buffer and convert to pointers when we come to
  660. // read the data out of the buffer (when the response is complete, or at
  661. // least we've finished receiving headers)
  662. //
  663. //
  664. // if we haven't checked the response yet, then the first thing to
  665. // get is the status line
  666. //
  667. statusLine = GetStatusLine();
  668. if (statusLine == NULL) {
  669. error = ERROR_NOT_ENOUGH_MEMORY;
  670. goto Cleanup;
  671. }
  672. if (!statusLine->HaveString())
  673. {
  674. int majorVersion = 0;
  675. int minorVersion = 0;
  676. BOOL fSupportsHttp1_1;
  677. _StatusCode = 0;
  678. //
  679. // find the status line
  680. //
  681. success = _ResponseHeaders.ParseStatusLine(
  682. (LPSTR)_ResponseBuffer,
  683. _BytesReceived,
  684. IsEof(),
  685. &_ResponseScanned,
  686. &_StatusCode,
  687. (LPDWORD)&majorVersion,
  688. (LPDWORD)&minorVersion
  689. );
  690. if ( !success )
  691. {
  692. error = ERROR_SUCCESS;
  693. goto Cleanup;
  694. }
  695. DEBUG_PRINT(HTTP,
  696. INFO,
  697. ("Version = %d.%d\n",
  698. majorVersion,
  699. minorVersion
  700. ));
  701. DEBUG_PRINT(HTTP,
  702. INFO,
  703. ("_StatusCode = %d\n",
  704. _StatusCode
  705. ));
  706. fSupportsHttp1_1 = FALSE;
  707. if ( majorVersion > 1 )
  708. {
  709. //
  710. // for higher version servers, the 1.1 spec dictates
  711. // that we return the highest version the client
  712. // supports, and in our case that is 1.1.
  713. //
  714. fSupportsHttp1_1 = TRUE;
  715. }
  716. else if ( majorVersion == 1 )
  717. {
  718. if ( minorVersion >= 1 )
  719. {
  720. fSupportsHttp1_1 = TRUE;
  721. }
  722. } else if ((majorVersion < 0) || (minorVersion < 0)) {
  723. error = ERROR_HTTP_INVALID_SERVER_RESPONSE;
  724. goto Cleanup;
  725. }
  726. SetResponseHttp1_1(fSupportsHttp1_1);
  727. //
  728. // record the server HTTP version in the server info object
  729. //
  730. CServerInfo * pServerInfo = GetServerInfo();
  731. if (pServerInfo != NULL) {
  732. if (fSupportsHttp1_1) {
  733. pServerInfo->SetHttp1_1();
  734. //
  735. // Set the max connections per HTTP 1.1 server.
  736. //
  737. pServerInfo->SetNewLimit(GetMaxConnectionsPerServer(WINHTTP_OPTION_MAX_CONNS_PER_SERVER));
  738. } else {
  739. pServerInfo->SetHttp1_0();
  740. //
  741. // Set the max connections per HTTP 1.0 server.
  742. //
  743. pServerInfo->SetNewLimit(GetMaxConnectionsPerServer(WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER));
  744. }
  745. }
  746. if (_StatusCode == 0) {
  747. //
  748. // BUGBUG [arthurbi] malformed header, should we really just accept it?
  749. // what if we get indeterminate garbage?
  750. //
  751. INET_ASSERT(FALSE);
  752. AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
  753. "HTTP/1.0 200 OK",
  754. sizeof("HTTP/1.0 200 OK") - 1
  755. );
  756. _StatusCode = HTTP_STATUS_OK;
  757. error = ERROR_SUCCESS;
  758. goto Cleanup;
  759. }
  760. }
  761. //
  762. // continue scanning headers here until we have tested all the current
  763. // buffer, or we have found the start of the data
  764. //
  765. BOOL fFoundEndOfHeaders;
  766. error = _ResponseHeaders.ParseHeaders(
  767. (LPSTR)_ResponseBuffer,
  768. _BytesReceived,
  769. IsEof(),
  770. &_ResponseScanned,
  771. &success,
  772. &fFoundEndOfHeaders
  773. );
  774. if ( error != ERROR_SUCCESS )
  775. {
  776. goto Cleanup;
  777. }
  778. if ( fFoundEndOfHeaders )
  779. {
  780. //
  781. // we found the end of the headers
  782. //
  783. SetEof(TRUE);
  784. //
  785. // and the start of the data
  786. //
  787. SetData(TRUE);
  788. _DataOffset = _ResponseScanned;
  789. DEBUG_PRINT(HTTP,
  790. INFO,
  791. ("found end of headers. _DataOffset = %d\n",
  792. _DataOffset
  793. ));
  794. }
  795. done:
  796. //
  797. // if we have reached the end of the headers then we communicate this fact
  798. // to the caller
  799. //
  800. if (IsData() || IsEof()) {
  801. error = CheckWellKnownHeaders();
  802. if (ERROR_SUCCESS != error)
  803. {
  804. goto Cleanup;
  805. }
  806. *lpbEof = TRUE;
  807. /*
  808. Set connection persistency based on these rules:
  809. persistent = (1.0Request && Con: K-A && 1.0Response && Con: K-A)
  810. || (1.1Request && Con: K-A && 1.0Response && Con: K-A)
  811. || (1.0Request && Con: K-A && 1.1Response && Con: K-A)
  812. || (1.1Request && !Con: Close && 1.1Response && !Con: Close)
  813. therefore,
  814. persistent = 1.1Request && 1.1Response
  815. ? (!Con: Close in request || response)
  816. : Con: K-A in request && response
  817. */
  818. if (IsRequestHttp1_1() && IsResponseHttp1_1()) {
  819. BOOL bHaveConnCloseRequest;
  820. bHaveConnCloseRequest = FindConnCloseRequestHeader(
  821. IsRequestUsingProxy()
  822. ? HTTP_QUERY_PROXY_CONNECTION
  823. : HTTP_QUERY_CONNECTION
  824. );
  825. if (!(IsConnCloseResponse() || bHaveConnCloseRequest)) {
  826. DEBUG_PRINT(HTTP,
  827. INFO,
  828. ("HTTP/1.1 persistent connection\n"
  829. ));
  830. SetKeepAlive(TRUE);
  831. SetPersistentConnection(IsRequestUsingProxy()
  832. && !IsTalkingToSecureServerViaProxy()
  833. );
  834. } else {
  835. DEBUG_PRINT(HTTP,
  836. INFO,
  837. ("HTTP/1.1 non-persistent connection: close on: request: %B; response: %B\n",
  838. bHaveConnCloseRequest,
  839. IsConnCloseResponse()
  840. ));
  841. SetKeepAlive(FALSE);
  842. SetNoLongerKeepAlive();
  843. ClearPersistentConnection();
  844. }
  845. }
  846. }
  847. Cleanup:
  848. //
  849. // we are finished updating the response headers (no other thread should be
  850. // waiting for this if the reference count and object state is correct)
  851. //
  852. _ResponseHeaders.UnlockHeaders();
  853. quit:
  854. PERF_LEAVE(UpdateResponseHeaders);
  855. DEBUG_LEAVE(error);
  856. return error;
  857. }
  858. DWORD
  859. HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders(
  860. IN OUT LPSTR* ppszBuffer,
  861. IN DWORD dwBufferLength
  862. )
  863. /*++
  864. Routine Description:
  865. Create the response headers given a buffer containing concatenated headers.
  866. Called when we are creating this object from the cache
  867. Arguments:
  868. lpszBuffer - pointer to buffer containing headers
  869. dwBufferLength - length of lpszBuffer
  870. Return Value:
  871. DWORD
  872. Success - ERROR_SUCCESS
  873. Failure - ERROR_NOT_ENOUGH_MEMORY
  874. Couldn't create headers
  875. --*/
  876. {
  877. DEBUG_ENTER((DBG_HTTP,
  878. Dword,
  879. "HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders",
  880. "%.32q, %d",
  881. ppszBuffer,
  882. dwBufferLength
  883. ));
  884. //
  885. // there SHOULD NOT already be a response buffer if we're adding an
  886. // external buffer
  887. //
  888. INET_ASSERT(_ResponseBuffer == NULL);
  889. DWORD error;
  890. BOOL eof = FALSE;
  891. _ResponseBuffer = (LPBYTE) *ppszBuffer;
  892. _ResponseBufferLength = dwBufferLength;
  893. _BytesReceived = dwBufferLength;
  894. error = UpdateResponseHeaders(&eof);
  895. if (error != ERROR_SUCCESS) {
  896. //
  897. // if we failed, we will clean up our variables including clearing
  898. // out the response buffer address and length, but leave freeing
  899. // the buffer to the caller
  900. //
  901. _ResponseBuffer = NULL;
  902. _ResponseBufferLength = 0;
  903. ResetResponseVariables();
  904. } else {
  905. //
  906. // Success - the object owns the buffer so the caller should not free.
  907. //
  908. *ppszBuffer = NULL;
  909. }
  910. DEBUG_LEAVE(error);
  911. return error;
  912. }
  913. DWORD
  914. HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion(
  915. IN LPVOID lpBuffer,
  916. IN OUT LPDWORD lpdwBufferLength
  917. )
  918. /*++
  919. Routine Description:
  920. Returns the HTTP version string from the status line
  921. Arguments:
  922. lpBuffer - pointer to buffer to copy version string into
  923. lpdwBufferLength - IN: size of lpBuffer
  924. OUT: size of version string excluding terminating '\0'
  925. if successful, else required buffer length
  926. Return Value:
  927. DWORD
  928. Success - ERROR_SUCCESS
  929. Failure - ERROR_INSUFFICIENT_BUFFER
  930. --*/
  931. {
  932. PERF_ENTER(QueryResponseVersion);
  933. DWORD error;
  934. HEADER_STRING * statusLine = GetStatusLine();
  935. if ((statusLine == NULL) || statusLine->IsError()) {
  936. error = ERROR_WINHTTP_INTERNAL_ERROR;
  937. goto quit;
  938. }
  939. LPSTR string;
  940. DWORD length;
  941. //
  942. // get a pointer into the response buffer where the status line starts
  943. // and its length
  944. //
  945. string = statusLine->StringAddress((LPSTR)_ResponseBuffer);
  946. length = (DWORD)statusLine->StringLength();
  947. //
  948. // the version string is the first token on the line, delimited by spaces
  949. //
  950. DWORD index;
  951. for (index = 0; index < length; ++index) {
  952. //
  953. // we'll also check for CR and LF, although just space should be
  954. // sufficient
  955. //
  956. if ((string[index] == ' ')
  957. || (string[index] == '\r')
  958. || (string[index] == '\n')) {
  959. break;
  960. }
  961. }
  962. if (*lpdwBufferLength > index) {
  963. memcpy(lpBuffer, (LPVOID)string, index);
  964. ((LPSTR)lpBuffer)[index] = '\0';
  965. *lpdwBufferLength = index;
  966. error = ERROR_SUCCESS;
  967. } else {
  968. *lpdwBufferLength = index + 1;
  969. error = ERROR_INSUFFICIENT_BUFFER;
  970. }
  971. quit:
  972. PERF_LEAVE(QueryResponseVersion);
  973. return error;
  974. }
  975. DWORD
  976. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode(
  977. IN LPVOID lpBuffer,
  978. IN OUT LPDWORD lpdwBufferLength,
  979. IN DWORD dwModifiers
  980. )
  981. /*++
  982. Routine Description:
  983. Returns the status code as a string or a number
  984. Arguments:
  985. lpBuffer - pointer to buffer where results written
  986. lpdwBufferLength - IN: length of buffer
  987. OUT: size of returned information, or required size'
  988. of buffer
  989. dwModifiers - flags which modify returned value
  990. Return Value:
  991. DWORD
  992. Success - ERROR_SUCCESS
  993. Failure - ERROR_INSUFFICIENT_BUFFER
  994. --*/
  995. {
  996. PERF_ENTER(QueryStatusCode);
  997. DWORD error;
  998. DWORD requiredSize;
  999. if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
  1000. requiredSize = sizeof(_StatusCode);
  1001. if (*lpdwBufferLength >= requiredSize) {
  1002. *(LPDWORD)lpBuffer = _StatusCode;
  1003. error = ERROR_SUCCESS;
  1004. } else {
  1005. error = ERROR_INSUFFICIENT_BUFFER;
  1006. }
  1007. } else {
  1008. //
  1009. // the number should always be only 3 characters long, but we'll be
  1010. // flexible (just in case)
  1011. //
  1012. char numBuf[sizeof("4294967296")];
  1013. requiredSize = wsprintf(numBuf, "%u", _StatusCode) + 1;
  1014. #ifdef DEBUG
  1015. // Debug check to make sure everything is good because the above
  1016. // used to be ultoa.
  1017. char debugBuf[sizeof("4294967296")];
  1018. ultoa(_StatusCode, debugBuf, 10);
  1019. if (strcmp(debugBuf,numBuf))
  1020. {
  1021. INET_ASSERT(FALSE);
  1022. }
  1023. INET_ASSERT(requiredSize == lstrlen(numBuf) + 1);
  1024. #endif
  1025. if (*lpdwBufferLength >= requiredSize) {
  1026. memcpy(lpBuffer, (LPVOID)numBuf, requiredSize);
  1027. *lpdwBufferLength = requiredSize - 1;
  1028. error = ERROR_SUCCESS;
  1029. } else {
  1030. *lpdwBufferLength = requiredSize;
  1031. error = ERROR_INSUFFICIENT_BUFFER;
  1032. }
  1033. }
  1034. PERF_LEAVE(QueryStatusCode);
  1035. return error;
  1036. }
  1037. DWORD
  1038. HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText(
  1039. IN LPVOID lpBuffer,
  1040. IN OUT LPDWORD lpdwBufferLength
  1041. )
  1042. /*++
  1043. Routine Description:
  1044. Returns the status text - if any - returned by the server in the status line
  1045. Arguments:
  1046. lpBuffer - pointer to buffer where status text is written
  1047. lpdwBufferLength - IN: size of lpBuffer
  1048. OUT: length of the status text string minus 1 for the
  1049. '\0', or the required buffer length if we return
  1050. ERROR_INSUFFICIENT_BUFFER
  1051. Return Value:
  1052. DWORD
  1053. Success - ERROR_SUCCESS
  1054. Failure - ERROR_INSUFFICIENT_BUFFER
  1055. --*/
  1056. {
  1057. PERF_ENTER(QueryStatusText);
  1058. DWORD error;
  1059. HEADER_STRING * statusLine = GetStatusLine();
  1060. if ((statusLine == NULL) || statusLine->IsError()) {
  1061. error = ERROR_WINHTTP_INTERNAL_ERROR;
  1062. goto quit;
  1063. }
  1064. LPSTR str;
  1065. DWORD len;
  1066. //
  1067. // find the third token on the status line. The status line has the form
  1068. //
  1069. // "HTTP/1.0 302 Try again\r\n"
  1070. //
  1071. // ^ ^ ^
  1072. // | | |
  1073. // | | +- status text
  1074. // | +- status code
  1075. // +- version
  1076. //
  1077. str = statusLine->StringAddress((LPSTR)_ResponseBuffer);
  1078. len = statusLine->StringLength();
  1079. DWORD i;
  1080. i = 0;
  1081. int j;
  1082. for (j = 0; j < 2; ++j) {
  1083. while ((i < len) && (str[i] != ' ')) {
  1084. ++i;
  1085. }
  1086. while ((i < len) && (str[i] == ' ')) {
  1087. ++i;
  1088. }
  1089. }
  1090. len -= i;
  1091. if (*lpdwBufferLength > len) {
  1092. memcpy(lpBuffer, (LPVOID)&str[i], len);
  1093. ((LPSTR)lpBuffer)[len] = '\0';
  1094. *lpdwBufferLength = len;
  1095. error = ERROR_SUCCESS;
  1096. } else {
  1097. *lpdwBufferLength = len + 1;
  1098. error = ERROR_INSUFFICIENT_BUFFER;
  1099. }
  1100. quit:
  1101. PERF_LEAVE(QueryStatusText);
  1102. return error;
  1103. }
  1104. DWORD
  1105. HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders(
  1106. IN BOOL bCrLfTerminated,
  1107. OUT LPVOID lpBuffer,
  1108. IN OUT LPDWORD lpdwBufferLength
  1109. )
  1110. /*++
  1111. Routine Description:
  1112. Gets the raw response headers
  1113. Arguments:
  1114. bCrLfTerminated - TRUE if we want RAW_HEADERS_CRLF else RAW_HEADERS
  1115. lpBuffer - pointer to buffer where headers returned
  1116. lpdwBufferLength - IN: length of lpBuffer
  1117. OUT: returned length of lpBuffer
  1118. Return Value:
  1119. DWORD
  1120. Success - ERROR_SUCCESS
  1121. Failure -
  1122. --*/
  1123. {
  1124. DEBUG_ENTER((DBG_HTTP,
  1125. Dword,
  1126. "QueryRawHeaders",
  1127. "%B, %#x, %#x [%d]",
  1128. bCrLfTerminated,
  1129. lpBuffer,
  1130. lpdwBufferLength,
  1131. *lpdwBufferLength
  1132. ));
  1133. PERF_ENTER(QueryRawHeaders);
  1134. DWORD error = _ResponseHeaders.QueryRawHeaders(
  1135. (LPSTR)_ResponseBuffer,
  1136. bCrLfTerminated,
  1137. lpBuffer,
  1138. lpdwBufferLength
  1139. );
  1140. IF_DEBUG_CODE() {
  1141. if (error == ERROR_INSUFFICIENT_BUFFER) {
  1142. DEBUG_PRINT(HTTP,
  1143. INFO,
  1144. ("*lpdwBufferLength = %d\n",
  1145. *lpdwBufferLength
  1146. ));
  1147. }
  1148. }
  1149. PERF_LEAVE(QueryRawHeaders);
  1150. DEBUG_LEAVE(error);
  1151. return error;
  1152. }
  1153. VOID
  1154. HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName(
  1155. IN DWORD dwQueryIndex
  1156. )
  1157. /*++
  1158. Routine Description:
  1159. Removes all headers of a particular type from the request object
  1160. Arguments:
  1161. lpszHeaderName - name of header to remove
  1162. Return Value:
  1163. None.
  1164. --*/
  1165. {
  1166. DEBUG_ENTER((DBG_HTTP,
  1167. None,
  1168. "RemoveAllRequestHeadersByName",
  1169. "%q, %u",
  1170. GlobalKnownHeaders[dwQueryIndex].Text,
  1171. dwQueryIndex
  1172. ));
  1173. PERF_ENTER(RemoveAllRequestHeadersByName);
  1174. _RequestHeaders.RemoveAllByIndex(dwQueryIndex);
  1175. PERF_LEAVE(RemoveAllRequestHeadersByName);
  1176. DEBUG_LEAVE(0);
  1177. }
  1178. //
  1179. // private methods
  1180. //
  1181. PRIVATE
  1182. DWORD
  1183. HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders(
  1184. VOID
  1185. )
  1186. /*++
  1187. Routine Description:
  1188. Tests for a couple of well-known headers that are important to us as well as
  1189. the app:
  1190. "Connection: Keep-Alive"
  1191. "Proxy-Connection: Keep-Alive"
  1192. "Connection: Close"
  1193. "Proxy-Connection: Close"
  1194. "Transfer-Encoding: chunked"
  1195. "Content-Length: ####"
  1196. "Content-Range: bytes ####-####/####"
  1197. The header DOES NOT contain CR-LF. That is, dwHeaderLength will not include
  1198. any counts for line termination
  1199. We need to know if the server honoured a request for a keep-alive connection
  1200. so that we don't try to receive until we hit the end of the connection. The
  1201. server will keep it open.
  1202. We need to know the content length if we are talking over a persistent (keep
  1203. alive) connection.
  1204. If either header is found, we set the corresponding flag in the HTTP_HEADERS
  1205. object, and in the case of "Content-Length:" we parse out the length.
  1206. Arguments:
  1207. None.
  1208. Return Value:
  1209. DWORD
  1210. Success - ERROR_SUCCESS
  1211. Failure - ERROR_NOT_ENOUGH_MEMORY
  1212. --*/
  1213. {
  1214. DEBUG_ENTER((DBG_HTTP,
  1215. None,
  1216. "HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders",
  1217. NULL
  1218. ));
  1219. DWORD dwError = ERROR_SUCCESS;
  1220. //
  1221. // check for "Content-Length:"
  1222. //
  1223. if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_LENGTH) )
  1224. {
  1225. HEADER_STRING * curHeader;
  1226. DWORD dwHeaderLength;
  1227. LPSTR lpszHeader;
  1228. DWORD iSlotContentLength = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONTENT_LENGTH];
  1229. curHeader = _ResponseHeaders.GetSlot(iSlotContentLength);
  1230. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  1231. dwHeaderLength = curHeader->StringLength();
  1232. dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
  1233. lpszHeader += GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
  1234. while (dwHeaderLength && (*lpszHeader == ' ')) {
  1235. --dwHeaderLength;
  1236. ++lpszHeader;
  1237. }
  1238. while (dwHeaderLength && isdigit(*lpszHeader)) {
  1239. _ContentLength = _ContentLength * 10 + (*lpszHeader - '0');
  1240. --dwHeaderLength;
  1241. ++lpszHeader;
  1242. }
  1243. //
  1244. // once we have _ContentLength, we don't modify it (unless
  1245. // we fix it up when using a 206 partial response to resume
  1246. // a partial download.) The header value should be returned
  1247. // by HttpQueryInfo(). Instead, we keep account of the
  1248. // amount of keep-alive data left to copy in _BytesRemaining
  1249. //
  1250. _BytesRemaining = _ContentLength;
  1251. //
  1252. // although we said we may be one past the end of the header, in
  1253. // reality, if we received a buffer with "Content-Length:" then we
  1254. // expect it to be terminated by CR-LF (or CR-CR-LF or just LF,
  1255. // depending on the wackiness quotient of the server)
  1256. //
  1257. // MSXML3 bug 56001: commenting-out this assert; it's informational
  1258. // only and ignorable.
  1259. // INET_ASSERT((*lpszHeader == '\r') || (*lpszHeader == '\n'));
  1260. SetHaveContentLength(TRUE);
  1261. DEBUG_PRINT(HTTP,
  1262. INFO,
  1263. ("_ContentLength = %d\n",
  1264. _ContentLength
  1265. ));
  1266. _BytesInSocket = (_ContentLength != 0)
  1267. ? (_ContentLength - (_BytesReceived - _DataOffset))
  1268. : 0;
  1269. //
  1270. // we could have multiple responses in the same buffer. If
  1271. // the amount received is greater than the content length
  1272. // then we have all the data; there are no bytes left in
  1273. // the socket for the current response
  1274. //
  1275. if ((int)_BytesInSocket < 0) {
  1276. _BytesInSocket = 0;
  1277. }
  1278. DEBUG_PRINT(HTTP,
  1279. INFO,
  1280. ("bytes left in socket = %d\n",
  1281. _BytesInSocket
  1282. ));
  1283. }
  1284. if ( IsResponseHeaderPresent(HTTP_QUERY_CONNECTION) ||
  1285. IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION) )
  1286. {
  1287. //
  1288. // check for "Connection: Keep-Alive" or "Proxy-Connection: Keep-Alive".
  1289. // This test protects us against the unlikely
  1290. // event of a server returning to us a keep-alive response header (because
  1291. // that would cause problems for the proxy)
  1292. //
  1293. if (IsWantKeepAlive() && (!IsKeepAlive() || IsResponseHttp1_1()))
  1294. {
  1295. HEADER_STRING * curHeader;
  1296. DWORD dwHeaderLength, headerNameLength;
  1297. LPSTR lpszHeader;
  1298. DWORD iSlot;
  1299. char ch;
  1300. if (IsRequestUsingProxy() &&
  1301. IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION))
  1302. {
  1303. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
  1304. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
  1305. }
  1306. else if (IsResponseHeaderPresent(HTTP_QUERY_CONNECTION))
  1307. {
  1308. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONNECTION];
  1309. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_CONNECTION].Length+1;
  1310. }
  1311. else
  1312. {
  1313. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
  1314. headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
  1315. INET_ASSERT(FALSE);
  1316. }
  1317. curHeader = _ResponseHeaders.GetSlot(iSlot);
  1318. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  1319. dwHeaderLength = curHeader->StringLength();
  1320. dwHeaderLength -= headerNameLength;
  1321. lpszHeader += headerNameLength;
  1322. while (dwHeaderLength && (*lpszHeader == ' ')) {
  1323. ++lpszHeader;
  1324. --dwHeaderLength;
  1325. }
  1326. //
  1327. // both headers use "Keep-Alive" as header-value ONLY for HTTP 1.0 servers
  1328. //
  1329. if (((int)dwHeaderLength >= KEEP_ALIVE_LEN)
  1330. && !strnicmp(lpszHeader, KEEP_ALIVE_SZ, KEEP_ALIVE_LEN)) {
  1331. DEBUG_PRINT(HTTP,
  1332. INFO,
  1333. ("Connection: Keep-Alive\n"
  1334. ));
  1335. //
  1336. // BUGBUG - we are setting k-a when coming from cache!
  1337. //
  1338. SetKeepAlive(TRUE);
  1339. SetPersistentConnection(headerNameLength == HTTP_PROXY_CONNECTION_LEN);
  1340. }
  1341. //
  1342. // also check for "Close" as header-value ONLY for HTTP 1.1 servers
  1343. //
  1344. else if ((*lpszHeader == 'C' || *lpszHeader == 'c')
  1345. && ((int)dwHeaderLength >= CLOSE_LEN)
  1346. && IsResponseHttp1_1()
  1347. && !strnicmp(lpszHeader, CLOSE_SZ, CLOSE_LEN)) {
  1348. DEBUG_PRINT(HTTP,
  1349. INFO,
  1350. ("Connection: Close (HTTP/1.1)\n"
  1351. ));
  1352. SetConnCloseResponse(TRUE);
  1353. }
  1354. }
  1355. }
  1356. //
  1357. // check for "Transfer-Encoding:"
  1358. //
  1359. if (IsResponseHeaderPresent(HTTP_QUERY_TRANSFER_ENCODING) &&
  1360. IsResponseHttp1_1())
  1361. {
  1362. //
  1363. // If Http 1.1, check for Chunked Transfer
  1364. //
  1365. HEADER_STRING * curHeader;
  1366. DWORD dwHeaderLength;
  1367. LPSTR lpszHeader;
  1368. DWORD iSlot;
  1369. iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING];
  1370. curHeader = _ResponseHeaders.GetSlot(iSlot);
  1371. lpszHeader = curHeader->StringAddress((LPSTR)_ResponseBuffer);
  1372. dwHeaderLength = curHeader->StringLength();
  1373. dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
  1374. lpszHeader += GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
  1375. while (dwHeaderLength && (*lpszHeader == ' ')) {
  1376. ++lpszHeader;
  1377. --dwHeaderLength;
  1378. }
  1379. //
  1380. // look for "chunked" entry that confirms that we're doing chunked transfer encoding
  1381. //
  1382. if (((int)dwHeaderLength >= CHUNKED_LEN)
  1383. && !strnicmp(lpszHeader, CHUNKED_SZ, CHUNKED_LEN))
  1384. {
  1385. INTERNET_HANDLE_OBJECT* pRoot = GetRootHandle(this);
  1386. DWORD_PTR dwChunkFilterCtx = 0;
  1387. // Now that we know this is a chunked response, allocate
  1388. // a decoder context for parsing the data later. If anything
  1389. // fails here, the request needs to fail.
  1390. if (S_OK != pRoot->_ChunkFilter.RegisterContext(&dwChunkFilterCtx))
  1391. {
  1392. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1393. goto quit;
  1394. }
  1395. else if (!_ResponseFilterList.Insert(&pRoot->_ChunkFilter, dwChunkFilterCtx))
  1396. {
  1397. pRoot->_ChunkFilter.UnregisterContext(dwChunkFilterCtx);
  1398. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1399. goto quit;
  1400. }
  1401. SetHaveChunkEncoding(TRUE);
  1402. DEBUG_PRINT(HTTP,
  1403. INFO,
  1404. ("server is sending Chunked Transfer Encoding\n"
  1405. ));
  1406. //
  1407. // if both "transfer-encoding: chunked" and "content-length:"
  1408. // were received then the chunking takes precedence
  1409. //
  1410. INET_ASSERT(!(IsChunkEncoding() && IsContentLength()));
  1411. if (IsContentLength()) {
  1412. SetHaveContentLength(FALSE);
  1413. }
  1414. }
  1415. }
  1416. SetBadNSServer(FALSE);
  1417. if (IsResponseHttp1_1())
  1418. {
  1419. //
  1420. // For IIS 4.0 Servers, and all other normal servers, if we make
  1421. // a HEAD request, we should ignore the Content-Length.
  1422. //
  1423. // IIS 3.0 servers send an illegal body, and this is a bug in the server.
  1424. // since they're not HTTP 1.1 we should be ok here.
  1425. //
  1426. if ( (GetMethodType() == HTTP_METHOD_TYPE_HEAD) &&
  1427. (_ContentLength > 0) &&
  1428. IsWantKeepAlive()
  1429. )
  1430. {
  1431. //
  1432. // set length to 0
  1433. //
  1434. _ContentLength = 0;
  1435. }
  1436. if ( IsRequestHttp1_1() )
  1437. {
  1438. //
  1439. // check for NS servers that don't return correct HTTP/1.1 responses
  1440. //
  1441. LPSTR buffer;
  1442. DWORD buflen;
  1443. DWORD status = FastQueryResponseHeader(HTTP_QUERY_SERVER,
  1444. (LPVOID*)&buffer,
  1445. &buflen,
  1446. 0
  1447. );
  1448. #define NSEP "Netscape-Enterprise/3"
  1449. #define NSEPLEN (sizeof(NSEP) - 1)
  1450. #define NSFT "Netscape-FastTrack/3"
  1451. #define NSFTLEN (sizeof(NSFT) - 1)
  1452. #define NSCS "Netscape-Commerce/3"
  1453. #define NSCSLEN (sizeof(NSCS) - 1)
  1454. if (status == ERROR_SUCCESS) {
  1455. BOOL fIsBadServer = ((buflen > NSEPLEN) && !strnicmp(buffer, NSEP, NSEPLEN))
  1456. || ((buflen > NSFTLEN) && !strnicmp(buffer, NSFT, NSFTLEN))
  1457. || ((buflen > NSCSLEN) && !strnicmp(buffer, NSCS, NSCSLEN));
  1458. if ( fIsBadServer )
  1459. {
  1460. CServerInfo * pServerInfo = GetServerInfo();
  1461. SetBadNSServer(fIsBadServer);
  1462. if (pServerInfo != NULL)
  1463. {
  1464. //
  1465. // Note this Bad Server info in the server info obj,
  1466. // as we they fail to do keep-alive with SSL properly
  1467. //
  1468. pServerInfo->SetBadNSServer();
  1469. }
  1470. DEBUG_PRINT(HTTP,
  1471. INFO,
  1472. ("IsBadNSServer() == %B\n",
  1473. IsBadNSServer()
  1474. ));
  1475. }
  1476. }
  1477. }
  1478. //
  1479. // BUGBUG - content-type: multipart/byteranges means we
  1480. // also have data
  1481. //
  1482. DWORD statusCode = GetStatusCode();
  1483. if (!IsBadNSServer()
  1484. && !IsContentLength()
  1485. && !IsChunkEncoding()
  1486. && (((statusCode >= HTTP_STATUS_CONTINUE) // 100
  1487. && (statusCode < HTTP_STATUS_OK)) // 200
  1488. || (statusCode == HTTP_STATUS_NO_CONTENT) // 204
  1489. || (statusCode == HTTP_STATUS_MOVED) // 301
  1490. || (statusCode == HTTP_STATUS_REDIRECT) // 302
  1491. || (statusCode == HTTP_STATUS_REDIRECT_METHOD) // 303
  1492. || (statusCode == HTTP_STATUS_NOT_MODIFIED) // 304
  1493. || (statusCode == HTTP_STATUS_REDIRECT_KEEP_VERB)) // 307
  1494. || (GetMethodType() == HTTP_METHOD_TYPE_HEAD)) {
  1495. DEBUG_PRINT(HTTP,
  1496. INFO,
  1497. ("header-only HTTP/1.1 response\n"
  1498. ));
  1499. SetData(FALSE);
  1500. }
  1501. }
  1502. quit:
  1503. DEBUG_LEAVE(dwError);
  1504. return dwError;
  1505. }
  1506. //
  1507. // this array has the same order as the HTTP_METHOD_TYPE enum
  1508. //
  1509. #define MAKE_REQUEST_METHOD_TYPE(Type) \
  1510. sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## Type
  1511. //
  1512. // darrenmi - need a new macro because *_M-POST isn't a valid enum member.
  1513. // we need a seperate enum type and string value.
  1514. //
  1515. // map HTTP_METHOD_TYPE_MPOST <=> "M-POST"
  1516. //
  1517. #define MAKE_REQUEST_METHOD_TYPE2(EnumType,Type) \
  1518. sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## EnumType
  1519. static const struct _REQUEST_METHOD {
  1520. int Length;
  1521. LPSTR Name;
  1522. HTTP_METHOD_TYPE MethodType;
  1523. } MethodNames[] = {
  1524. MAKE_REQUEST_METHOD_TYPE(GET),
  1525. MAKE_REQUEST_METHOD_TYPE(HEAD),
  1526. MAKE_REQUEST_METHOD_TYPE(POST),
  1527. MAKE_REQUEST_METHOD_TYPE(PUT),
  1528. MAKE_REQUEST_METHOD_TYPE(PROPFIND),
  1529. MAKE_REQUEST_METHOD_TYPE(PROPPATCH),
  1530. MAKE_REQUEST_METHOD_TYPE(LOCK),
  1531. MAKE_REQUEST_METHOD_TYPE(UNLOCK),
  1532. MAKE_REQUEST_METHOD_TYPE(COPY),
  1533. MAKE_REQUEST_METHOD_TYPE(MOVE),
  1534. MAKE_REQUEST_METHOD_TYPE(MKCOL),
  1535. MAKE_REQUEST_METHOD_TYPE(CONNECT),
  1536. MAKE_REQUEST_METHOD_TYPE(DELETE),
  1537. MAKE_REQUEST_METHOD_TYPE(LINK),
  1538. MAKE_REQUEST_METHOD_TYPE(UNLINK),
  1539. MAKE_REQUEST_METHOD_TYPE(BMOVE),
  1540. MAKE_REQUEST_METHOD_TYPE(BCOPY),
  1541. MAKE_REQUEST_METHOD_TYPE(BPROPFIND),
  1542. MAKE_REQUEST_METHOD_TYPE(BPROPPATCH),
  1543. MAKE_REQUEST_METHOD_TYPE(BDELETE),
  1544. MAKE_REQUEST_METHOD_TYPE(SUBSCRIBE),
  1545. MAKE_REQUEST_METHOD_TYPE(UNSUBSCRIBE),
  1546. MAKE_REQUEST_METHOD_TYPE(NOTIFY),
  1547. MAKE_REQUEST_METHOD_TYPE(POLL),
  1548. MAKE_REQUEST_METHOD_TYPE(CHECKIN),
  1549. MAKE_REQUEST_METHOD_TYPE(CHECKOUT),
  1550. MAKE_REQUEST_METHOD_TYPE(INVOKE),
  1551. MAKE_REQUEST_METHOD_TYPE(SEARCH),
  1552. MAKE_REQUEST_METHOD_TYPE(PIN),
  1553. MAKE_REQUEST_METHOD_TYPE2(MPOST,M-POST)
  1554. };
  1555. HTTP_METHOD_TYPE
  1556. MapHttpRequestMethod(
  1557. IN LPCSTR lpszVerb
  1558. )
  1559. /*++
  1560. Routine Description:
  1561. Maps request method string to type. Method names *are* case-sensitive
  1562. Arguments:
  1563. lpszVerb - method (verb) string
  1564. Return Value:
  1565. HTTP_METHOD_TYPE
  1566. --*/
  1567. {
  1568. int verbLen = strlen(lpszVerb);
  1569. for (int i = 0; i < ARRAY_ELEMENTS(MethodNames); ++i) {
  1570. if ((MethodNames[i].Length == verbLen)
  1571. && (memcmp(lpszVerb, MethodNames[i].Name, verbLen) == 0)) {
  1572. return MethodNames[i].MethodType;
  1573. }
  1574. }
  1575. //
  1576. // we now hande HTTP_METHOD_TYPE_UNKNOWN
  1577. //
  1578. return HTTP_METHOD_TYPE_UNKNOWN;
  1579. }
  1580. DWORD
  1581. MapHttpMethodType(
  1582. IN HTTP_METHOD_TYPE tMethod,
  1583. OUT LPCSTR * lplpcszName
  1584. )
  1585. /*++
  1586. Routine Description:
  1587. Map a method type to the corresponding name and length
  1588. Arguments:
  1589. tMethod - to map
  1590. lplpcszName - pointer to pointer to returned name
  1591. Return Value:
  1592. DWORD
  1593. Success - length of method name
  1594. Failure - (DWORD)-1
  1595. --*/
  1596. {
  1597. DWORD length;
  1598. if ((tMethod >= HTTP_METHOD_TYPE_FIRST) && (tMethod <= HTTP_METHOD_TYPE_LAST)) {
  1599. *lplpcszName = MethodNames[tMethod].Name;
  1600. length = MethodNames[tMethod].Length;
  1601. } else {
  1602. length = (DWORD)-1;
  1603. }
  1604. return length;
  1605. }
  1606. #if INET_DEBUG
  1607. LPSTR
  1608. MapHttpMethodType(
  1609. IN HTTP_METHOD_TYPE tMethod
  1610. )
  1611. {
  1612. return (tMethod == HTTP_METHOD_TYPE_UNKNOWN)
  1613. ? "Unknown"
  1614. : MethodNames[tMethod].Name;
  1615. }
  1616. #endif