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.

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