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.

890 lines
25 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. cachewrite.cxx
  5. Abstract:
  6. This file contains the implementation of the HTTPCACHE request object which involve writing to the cache
  7. Author:
  8. Revision History:
  9. --*/
  10. #include <wininetp.h>
  11. #define __CACHE_INCLUDE__
  12. #include "..\urlcache\cache.hxx"
  13. #include "..\http\headers.h"
  14. #include "cachelogic.hxx"
  15. #include "internalapi.hxx"
  16. #include "..\http\proc.h"
  17. struct AddUrlArg
  18. {
  19. LPCSTR pszUrl;
  20. LPCSTR pszRedirect;
  21. LPCTSTR pszFilePath;
  22. DWORD dwFileSize;
  23. LONGLONG qwExpires;
  24. LONGLONG qwLastMod;
  25. LONGLONG qwPostCheck;
  26. FILETIME ftCreate;
  27. DWORD dwEntryType;
  28. LPCSTR pbHeaders;
  29. DWORD cbHeaders;
  30. LPCSTR pszFileExt;
  31. BOOL fImage;
  32. DWORD dwIdentity;
  33. };
  34. DWORD UrlCacheCommitFile (IN AddUrlArg* pArgs);
  35. DWORD
  36. UrlCacheCreateFile
  37. (
  38. IN LPCSTR szUrl,
  39. IN OUT LPTSTR szFile,
  40. IN LPTSTR szExt,
  41. IN HANDLE* phfHandle,
  42. IN BOOL fCreatePerUser = FALSE,
  43. IN DWORD dwExpectedLength = 0
  44. );
  45. PRIVATE PRIVATE BOOL HTTPCACHE_REQUEST::IsStaticImage()
  46. /*++
  47. Routine Description:
  48. To improve performance, Internet Explorer classify whether a cached
  49. contents is a static image or not. Images usually do not change as
  50. often as text contents, so they do not have to be revalidated
  51. from the server as often, thus improving performance.
  52. Let the cache know if the item is likely to be a static image if it
  53. satisfies all four of the following requirements
  54. 1. No expire time.
  55. 2. Has last-modified time.
  56. 3. The content-type is image/*
  57. 4. No '?' in the URL.
  58. Pre-condition:
  59. CalculateTimeStampsForCache() has been called
  60. Side Effects:
  61. NONE
  62. Return Value:
  63. BOOL to indicate whether the image is a static image or not
  64. --*/
  65. {
  66. TCHAR szHeader[256];
  67. DWORD dwSize = 256;
  68. return (!FT2LL(_ftExpiryTime)
  69. && FT2LL(_ftLastModTime)
  70. && (HttpQueryInfoA(_hRequest, HTTP_QUERY_CONTENT_TYPE,
  71. NULL, (LPVOID *) &szHeader, &dwSize, 0) == TRUE)
  72. && (StrCmpNI (szHeader, "image/", sizeof("image/")-1) == 0)
  73. && (!StrChr (GetUrl(), '?'))
  74. );
  75. }
  76. PRIVATE PRIVATE BOOL HTTPCACHE_REQUEST::ExtractHeadersForCacheCommit(
  77. OUT LPSTR lpszHeaderInfo,
  78. OUT LPDWORD lpdwHeaderLen
  79. )
  80. /*++
  81. Routine Description:
  82. Most HTTP response headers are to be kept inside the cache in index.dat, so
  83. that those pieces of information can be retrieved and examined
  84. in subsequent requests.
  85. This function will extract all the HTTP response headers that will normally be
  86. committed to the cache entry
  87. Similar to FilterHeaders in Wininet
  88. Parameters:
  89. LPSTR lpszHeaderInfo - Caller should allocate sufficient memory to hold the
  90. extracted headers
  91. Side Effects:
  92. NONE
  93. Return Value:
  94. BOOL to indicate whether the call is successful or not
  95. --*/
  96. {
  97. DEBUG_ENTER((DBG_CACHE,
  98. Bool,
  99. "HTTPCACHE_REQUEST::ExtractHeadersForCacheCommit",
  100. NULL
  101. ));
  102. BOOL fResult = FALSE;
  103. if (lpszHeaderInfo == NULL)
  104. {
  105. DEBUG_PRINT(CACHE, ERROR, ("First parameter is NULL. Cannot continue\n"));
  106. goto quit;
  107. }
  108. DWORD i, len, lenT, reduced = 0, dwHeaderTableCount;
  109. LPSTR lpT, lpMark, lpNext, *lprgszHeaderExcludeTable;
  110. LPSTR rgszExcludeHeaders[] = {
  111. HTTP_SET_COOKIE_SZ,
  112. HTTP_LAST_MODIFIED_SZ,
  113. HTTP_SERVER_SZ,
  114. HTTP_DATE_SZ,
  115. HTTP_EXPIRES_SZ,
  116. HTTP_CONNECTION_SZ,
  117. HTTP_PROXY_CONNECTION_SZ,
  118. HTTP_VIA_SZ,
  119. HTTP_VARY_SZ,
  120. HTTP_AGE_SZ,
  121. HTTP_CACHE_CONTROL_SZ,
  122. HTTP_ACCEPT_RANGES_SZ,
  123. HTTP_CONTENT_DISPOSITION_SZ
  124. };
  125. lprgszHeaderExcludeTable = rgszExcludeHeaders;
  126. dwHeaderTableCount = sizeof(rgszExcludeHeaders) / sizeof(LPSTR);
  127. DWORD dwRawHeaderLen = 1024;
  128. TCHAR szRawHeaderInfo[1024];
  129. // We need to get the raw headers (with line breaks), and then
  130. // parse out the junk in the rest of this routine
  131. if (!HttpQueryInfoA(_hRequest,
  132. HTTP_QUERY_RAW_HEADERS_CRLF,
  133. NULL,
  134. (LPVOID *) &szRawHeaderInfo,
  135. &dwRawHeaderLen,
  136. NULL))
  137. {
  138. DEBUG_PRINT(CACHE, ERROR, ("HttpQueryInfoA failed\n"));
  139. goto quit;
  140. }
  141. // skip over the status line
  142. // NB this assumes that the raw buffer is nullterminated (CR/LF)
  143. lpT = strchr(szRawHeaderInfo, '\r');
  144. if (!lpT) {
  145. goto quit;
  146. }
  147. INET_ASSERT(*(lpT + 1) == '\n');
  148. lpT += 2;
  149. do
  150. {
  151. // find the header portion
  152. lpMark = strchr(lpT, ':');
  153. if (!lpMark)
  154. {
  155. break;
  156. }
  157. // get the end of the header line
  158. lpNext = strchr(lpMark, '\r');
  159. if (!lpNext)
  160. {
  161. INET_ASSERT(FALSE);
  162. // A properly formed header _should_ terminate with \r\n, but sometimes
  163. // that just doesn't happen
  164. lpNext = lpMark;
  165. while (*lpNext)
  166. {
  167. lpNext++;
  168. }
  169. }
  170. else
  171. {
  172. INET_ASSERT(*(lpNext + 1) == '\n');
  173. lpNext += 2;
  174. }
  175. len = (DWORD) PtrDifference(lpMark, lpT) + 1;
  176. lenT = dwRawHeaderLen; // doing all this to see it properly in debugger
  177. BOOL bFound = FALSE;
  178. for (i = 0; i < dwHeaderTableCount; ++i)
  179. {
  180. if (!strnicmp(lpT, lprgszHeaderExcludeTable[i], len))
  181. {
  182. bFound = TRUE;
  183. break;
  184. }
  185. }
  186. // If bFound is true, then it means that it's not one of the special headers,
  187. // so nuke the header
  188. if (bFound)
  189. {
  190. len = lenT - (DWORD)PtrDifference(lpNext, szRawHeaderInfo) + 1; // for NULL character
  191. // ACHTUNG memove because of overlapped copies
  192. memmove(lpT, lpNext, len);
  193. // keep count of how much we reduced the header by
  194. reduced += (DWORD) PtrDifference(lpNext, lpT);
  195. // lpT is already properly positioned because of the move
  196. }
  197. else
  198. {
  199. lpT = lpNext;
  200. }
  201. } while (TRUE);
  202. dwRawHeaderLen -= reduced;
  203. fResult = TRUE;
  204. // return results
  205. strncpy(lpszHeaderInfo, szRawHeaderInfo, dwRawHeaderLen);
  206. *lpdwHeaderLen = dwRawHeaderLen;
  207. DEBUG_PRINT(CACHE,
  208. INFO,
  209. ("Extracted header is %s. Length = %d\n",
  210. lpszHeaderInfo,
  211. *lpdwHeaderLen));
  212. quit:
  213. DEBUG_LEAVE(fResult);
  214. return fResult;
  215. }
  216. PRIVATE BOOL HTTPCACHE_REQUEST::CommitCacheFileEntry(
  217. IN BOOL fNormal
  218. )
  219. /*++
  220. Routine Description:
  221. Commit the downloaded resource to the cache index. No redirection
  222. is taken into account here (unlike the way wininet works)
  223. Similar to INTERNET_CONNECT_HANDLE_OBJECT::LocalEndCacheWrite and
  224. EndCacheWrite from wininet
  225. Pre-conditions:
  226. CalculateTimeStampsForCache() has been called
  227. Parameters:
  228. fNormal - TRUE if normal end cache write operation of the complete resource
  229. FALSE if interruptions occur during ReadData such that the entire
  230. resource hasn't been fully downloaded yet, and the resource will
  231. be marked as a partial cache entry.
  232. Side effects:
  233. NONE
  234. Return Value:
  235. BOOL to indicate whether the call is successful or not
  236. --*/
  237. {
  238. DEBUG_ENTER((DBG_CACHE,
  239. Bool,
  240. "HTTPCACHE_REQUEST::CommitCacheFileEntry",
  241. "%B",
  242. fNormal
  243. ));
  244. TCHAR szFileExtension[] = ""; // eventally need to change
  245. TCHAR szFileName[MAX_PATH];
  246. DWORD dwCacheEntryType;
  247. BOOL fImage;
  248. TCHAR szHeaderInfo[2048];
  249. DWORD dwHeaderLen;
  250. BOOL fResult;
  251. DWORD dwError;
  252. FILETIME ftCreate;
  253. BOOL fHttp1_1;
  254. // We need a few more pieces of information before we
  255. // can call UrlCommitFile:
  256. // 1) cache entry type
  257. // 2) whether the item is a static image or not
  258. // 3) header info
  259. // 4) The time at which the cache file is created
  260. // Let's grab them now!
  261. dwCacheEntryType = 0;
  262. if (!fNormal)
  263. {
  264. if (!IsPartialResponseCacheable())
  265. {
  266. DEBUG_PRINT(CACHE, INFO, ("Partial response not cacheable, downloaded will be aborted\n"));
  267. DEBUG_LEAVE(FALSE);
  268. return FALSE;
  269. }
  270. else
  271. {
  272. DEBUG_PRINT(CACHE, INFO, ("Partial response will be cached\n"));
  273. dwCacheEntryType |= SPARSE_CACHE_ENTRY;
  274. fNormal = TRUE;
  275. }
  276. }
  277. if (fNormal)
  278. {
  279. // 1) Grab cache entry type information
  280. if (_dwCacheFlags & CACHE_FLAG_MAKE_PERSISTENT)
  281. dwCacheEntryType |= STICKY_CACHE_ENTRY;
  282. if (InternalIsResponseHttp1_1(_hRequest) == TRUE)
  283. {
  284. dwCacheEntryType |= HTTP_1_1_CACHE_ENTRY;
  285. if (_fMustRevalidate == TRUE)
  286. dwCacheEntryType |= MUST_REVALIDATE_CACHE_ENTRY;
  287. }
  288. // 2) Is it an image?
  289. fImage = IsStaticImage();
  290. // 3) Grab the header info
  291. if (!ExtractHeadersForCacheCommit(szHeaderInfo, &dwHeaderLen))
  292. {
  293. DEBUG_PRINT(CACHE, INFO, ("ExtractHeadersForCacheCommit failed\n"));
  294. DEBUG_LEAVE(FALSE);
  295. return FALSE;
  296. }
  297. }
  298. // 4) Get filetime for the cache file, and close the file handle
  299. // that we first opened to write the content into
  300. INET_ASSERT(_hCacheWriteFile != INVALID_HANDLE_VALUE);
  301. GetFileTime( _hCacheWriteFile, &ftCreate, NULL, NULL );
  302. CalculateTimeStampsForCache();
  303. CloseHandle(_hCacheWriteFile);
  304. _hCacheWriteFile = INVALID_HANDLE_VALUE;
  305. DEBUG_PRINT(CACHE,
  306. INFO,
  307. ("Cache write EntryType = %x\r\n",
  308. dwCacheEntryType
  309. ));
  310. // Now do the real thing
  311. AddUrlArg Args;
  312. memset(&Args, 0, sizeof(Args));
  313. Args.pszUrl = GetUrl();
  314. Args.pszFilePath = _lpszCacheWriteLocalFilename;
  315. Args.dwFileSize = _RealCacheFileSize;
  316. Args.qwExpires = *((LONGLONG *) &_ftExpiryTime);
  317. Args.qwLastMod = *((LONGLONG *) &_ftLastModTime);
  318. Args.qwPostCheck = *((LONGLONG *) &_ftPostCheckTime);
  319. Args.ftCreate = ftCreate;
  320. Args.dwEntryType = dwCacheEntryType;
  321. Args.pbHeaders = szHeaderInfo;
  322. Args.cbHeaders = dwHeaderLen;
  323. Args.pszFileExt = _lpszFileExtension;
  324. Args.pszRedirect = NULL; // BUGBUG should we pass in GetUrl() instead?
  325. Args.fImage = fImage;
  326. Args.dwIdentity = 0;
  327. dwError = UrlCacheCommitFile(&Args);
  328. if (dwError != ERROR_SUCCESS)
  329. {
  330. DEBUG_PRINT(CACHE, ERROR,
  331. ("CommitUrlCacheEntry(%q) failed\n",
  332. _lpszCacheWriteLocalFilename
  333. ));
  334. if (dwError == ERROR_SHARING_VIOLATION)
  335. {
  336. // we got new URL data, but the old one is in use.
  337. // expire it, so any new user's will go to the net
  338. // ExpireUrl();
  339. }
  340. DEBUG_LEAVE(FALSE);
  341. return FALSE;
  342. }
  343. DEBUG_LEAVE(TRUE);
  344. return TRUE;
  345. }
  346. PRIVATE PRIVATE VOID HTTPCACHE_REQUEST::SetFilenameAndExtForCacheWrite()
  347. /*
  348. Routine Description:
  349. This function attempts to find out the filename and extension for the resource to be
  350. cached by querying the appropriate HTTP headers.
  351. It is possible that EITHER filename or extension will by NULL, but they will NEVER
  352. be both NULL.
  353. Adopt from HTTP_REQUEST_HANDLE_OBJECT::FHttpBeginCacheWrite from Wininet
  354. TODO: content-length ??
  355. Side effects:
  356. LPSTR _lpszFileName
  357. LPSTR _lpszFileExtension
  358. These variables will be set. These variables are needed
  359. 1) when first creating a file for cache write
  360. 2) when the file is committed to the cache
  361. --*/
  362. {
  363. DEBUG_ENTER((DBG_CACHE,
  364. None,
  365. "HTTPCACHE_REQUEST::SetFilenameAndExtForCacheWrite",
  366. NULL
  367. ));
  368. BOOL fResult = FALSE;
  369. TCHAR cExt[DEFAULT_MAX_EXTENSION_LENGTH + 1];
  370. TCHAR szFileName[MAX_PATH];
  371. const TCHAR szDefaultExtension[] = "txt";
  372. DWORD dwLen, cbFileName, dwIndex;
  373. BOOL fIsUncertainMime = FALSE;
  374. TCHAR szBuf[256];
  375. LPSTR ptr, pToken;
  376. // If the content-disposition header is available, then parse out any filename and use it
  377. // From RFC2616: The Content-Disposition response-header field has been proposed as a means for
  378. // the origin server to suggest a default filename if the user requests that the content is saved to a file.
  379. if (HttpQueryInfoA(_hRequest, WINHTTP_QUERY_CONTENT_DISPOSITION, NULL,
  380. szBuf, &(dwLen = sizeof(szBuf)), &dwIndex))
  381. {
  382. // Could have multiple tokens in the Content-Disposition header. Scan for the "filename" token.
  383. ptr = pToken = szBuf;
  384. while (ptr = StrTokExA(&pToken, ";"))
  385. {
  386. // Skip any leading ws in token.
  387. SKIPWS(ptr);
  388. // Compare against "filename".
  389. if (!strnicmp(ptr, FILENAME_SZ, FILENAME_LEN))
  390. {
  391. // Found it.
  392. ptr += FILENAME_LEN;
  393. // Skip ws before '='.
  394. SKIPWS(ptr);
  395. // Must have '='
  396. if (*ptr == '=')
  397. {
  398. // Skip any ws after '=' and point
  399. // to beginning of the file name
  400. ptr++;
  401. SKIPWS(ptr);
  402. // Skip past any quotes
  403. if (*ptr == '\"')
  404. ptr++;
  405. SKIPWS(ptr);
  406. cbFileName = strlen(ptr);
  407. if (cbFileName)
  408. {
  409. // Ignore any trailing quote.
  410. if (ptr[cbFileName-1] == '\"')
  411. cbFileName--;
  412. memcpy(szFileName, ptr, cbFileName);
  413. szFileName[cbFileName] = '\0';
  414. _lpszFileName = NewString(szFileName, MAX_PATH);
  415. }
  416. }
  417. break;
  418. }
  419. }
  420. }
  421. // Either no Content-disposition header or filename not parsed, so try to figure out
  422. // the file extension using the Content-Type header
  423. if (!_lpszFileName)
  424. {
  425. DWORD dwMimeLen = sizeof(szBuf);
  426. if ((HttpQueryInfoA(_hRequest, WINHTTP_QUERY_CONTENT_ENCODING, NULL,
  427. szBuf, &dwMimeLen, 0)) && StrCmpNI(szBuf,"binary",6) )
  428. {
  429. // if there is content encoding, we should not use
  430. // content-type for file extension
  431. //Modifying this for bug 98611.
  432. //For 'binary' encoding use the Content-Type to find extension
  433. _lpszFileExtension = NULL;
  434. }
  435. else if (HttpQueryInfoA(_hRequest, HTTP_QUERY_CONTENT_TYPE, NULL,
  436. szBuf, &(dwMimeLen = sizeof(szBuf)), 0))
  437. {
  438. dwLen = sizeof(cExt);
  439. fIsUncertainMime = strnicmp(szBuf, "text/plain", dwMimeLen)==0;
  440. if (!fIsUncertainMime &&
  441. GetFileExtensionFromMimeType(szBuf, dwMimeLen, cExt, &dwLen))
  442. {
  443. // get past the '.' because the cache expects it that way
  444. _lpszFileExtension = NewString(&cExt[1], DEFAULT_MAX_EXTENSION_LENGTH);
  445. }
  446. }
  447. //
  448. // if we couldn't get the MIME type or failed to map it then try to get
  449. // the file extension from the object name requested
  450. //
  451. if (_lpszFileExtension == NULL)
  452. {
  453. dwLen = DEFAULT_MAX_EXTENSION_LENGTH + 1;
  454. LPSTR lpszExt;
  455. lpszExt = GetFileExtensionFromUrl(GetUrl(), &dwLen);
  456. if (_lpszFileExtension != NULL)
  457. {
  458. _lpszFileExtension = NewString(lpszExt, dwLen);
  459. _lpszFileExtension[dwLen] = '\0';
  460. }
  461. }
  462. if ((_lpszFileExtension == NULL) && fIsUncertainMime)
  463. {
  464. INET_ASSERT(sizeof(szDefaultExtension) < DEFAULT_MAX_EXTENSION_LENGTH);
  465. _lpszFileExtension = NewString(szDefaultExtension, sizeof(szDefaultExtension));
  466. }
  467. }
  468. DEBUG_PRINT(CACHE, INFO, ("Filename = %s, File extension = %s\n", _lpszFileName, _lpszFileExtension));
  469. DEBUG_LEAVE(NULL);
  470. }
  471. PRIVATE BOOL
  472. HTTPCACHE_REQUEST::CreateCacheWriteFile()
  473. /*++
  474. Routine Description:
  475. Create a file for subsequent cache writes to write the data to
  476. Similar to INTERNET_CONNECT_HANDLE_OBJECT::BeginCacheWrite
  477. in wininet
  478. Pre-condition:
  479. SetFilenameAndExtForCacheWrite() has been called
  480. Side Effects:
  481. _lpszCacheWriteLocalFilename will contain the full path of the file at
  482. which the cache contents will be written to
  483. _hCacheWriteFile will contain the handle to the cache write file
  484. Return Value:
  485. BOOL indicating whether the call succeeds or not
  486. --*/
  487. {
  488. DEBUG_ENTER((DBG_CACHE,
  489. Bool,
  490. "HTTPCACHE_REQUEST::CreateCacheWriteFile",
  491. NULL
  492. ));
  493. TCHAR szFileName[MAX_PATH];
  494. BOOL fResult = FALSE;
  495. // NOTE: We are not making use of _lpszFileName from the
  496. // previous call to SetFilenameAndExtForCacheWrite()...
  497. // Possibly a bug??
  498. if (!CreateUrlCacheEntryA(GetUrl(), 0, _lpszFileExtension,
  499. szFileName, 0))
  500. {
  501. DEBUG_PRINT(CACHE,
  502. ERROR,
  503. ("Error: CreateUrlCacheEntry failed for %s\n",
  504. GetUrl()));
  505. goto quit;
  506. }
  507. else
  508. {
  509. DEBUG_PRINT(CACHE, INFO, ("Cache filename = %q\n", szFileName));
  510. }
  511. // monkey around with the local filename (szFileName)
  512. _hCacheWriteFile = CreateFile(szFileName,
  513. GENERIC_WRITE,
  514. 0,
  515. NULL,
  516. CREATE_ALWAYS,
  517. FILE_ATTRIBUTE_NORMAL,
  518. NULL);
  519. if (_hCacheWriteFile == INVALID_HANDLE_VALUE)
  520. {
  521. DEBUG_PRINT (CACHE, INFO, ("CreateFile API failed %x\n", GetLastError()));
  522. if( _hCacheWriteFile != INVALID_HANDLE_VALUE )
  523. {
  524. CloseHandle(_hCacheWriteFile);
  525. _hCacheWriteFile = INVALID_HANDLE_VALUE;
  526. }
  527. DeleteFile(szFileName);
  528. goto quit;
  529. }
  530. INET_ASSERT(_lpszCacheWriteLocalFilename == NULL);
  531. _lpszCacheWriteLocalFilename = NewString(szFileName);
  532. if (!_lpszCacheWriteLocalFilename)
  533. {
  534. if (_lpszCacheWriteLocalFilename != NULL)
  535. {
  536. (void)FREE_MEMORY((HLOCAL)_lpszCacheWriteLocalFilename);
  537. _lpszCacheWriteLocalFilename = NULL;
  538. DeleteFile(szFileName);
  539. }
  540. }
  541. INET_ASSERT(_hCacheWriteFile != INVALID_HANDLE_VALUE);
  542. fResult = TRUE;
  543. quit:
  544. DEBUG_LEAVE(fResult);
  545. return fResult;
  546. }
  547. PRIVATE BOOL
  548. HTTPCACHE_REQUEST::WriteToCacheFile(
  549. LPBYTE lpBuffer,
  550. DWORD dwBufferLen,
  551. LPDWORD lpdwBytesWritten
  552. )
  553. {
  554. DEBUG_ENTER((DBG_CACHE,
  555. Bool,
  556. "HTTPCACHE_REQUEST::WriteToCacheFile",
  557. "%d",
  558. dwBufferLen
  559. ));
  560. BOOL fResult;
  561. fResult = WriteFile(_hCacheWriteFile,
  562. lpBuffer,
  563. dwBufferLen,
  564. lpdwBytesWritten,
  565. NULL );
  566. _RealCacheFileSize += *lpdwBytesWritten;
  567. DEBUG_PRINT(CACHE,
  568. INFO,
  569. ("%d bytes written to the cache file %s",
  570. *lpdwBytesWritten,
  571. _lpszCacheWriteLocalFilename));
  572. DEBUG_LEAVE(fResult);
  573. return fResult;
  574. }
  575. PRIVATE BOOL
  576. HTTPCACHE_REQUEST::FCanWriteHTTP1_1ResponseToCache(
  577. BOOL * fNoCache
  578. )
  579. /*
  580. Routine Description:
  581. Examine the cache-control headers and MIME exclusion list
  582. to see if the given resource can be written to cache storage
  583. Similar to HTTP_REQUEST_HANDLE_OBJECT::FCanWiteToCache in Wininet
  584. Precondition:
  585. The resource comes from a HTTP 1.1 server
  586. Side Effects:
  587. NONE
  588. Parameters:
  589. fNoCache - indicate whether the cache entry must not be remained from the cache
  590. because the response contains certain explicitly/implicitly HTTP headers
  591. --*/
  592. {
  593. DEBUG_ENTER((DBG_CACHE,
  594. Bool,
  595. "HTTPCACHE_REQUEST::FCanWriteHTTP1_1ResponseToCache",
  596. NULL
  597. ));
  598. BOOL ok = FALSE;
  599. BOOL fVary = FALSE;
  600. *fNoCache = FALSE; // Set fNoCache if there is pragma: no-cache
  601. DWORD index;
  602. TCHAR szBuf[1024];
  603. DWORD dwBufLen = sizeof(szBuf);
  604. //
  605. // Also set fNoCache if there is Cache-Control: no-cache or no-store header,
  606. // if there is a Cache-Control: private header and we're *not* on NT with user profiles,
  607. // or any Vary: headers. These are only checked for HTTP 1.1 servers.
  608. //
  609. CHAR *ptr, *pToken;
  610. index = 0;
  611. // Scan for Cache-Control header.
  612. dwBufLen = sizeof(szBuf);
  613. while (HttpQueryInfoA(_hRequest,
  614. WINHTTP_QUERY_CACHE_CONTROL,
  615. NULL,
  616. szBuf,
  617. &dwBufLen,
  618. &index))
  619. {
  620. // Check for no-cache or no-store or private.
  621. CHAR chTemp = szBuf[dwBufLen];
  622. szBuf[dwBufLen] = '\0';
  623. pToken = ptr = szBuf;
  624. // Parse a token from the string; test for sub headers.
  625. while (*pToken != '\0')
  626. {
  627. SKIPWS(pToken);
  628. // no-cache, no-store.
  629. if (strnicmp(NO_CACHE_SZ, pToken, NO_CACHE_LEN) == 0)
  630. {
  631. *fNoCache = TRUE;
  632. break;
  633. }
  634. if( strnicmp(NO_STORE_SZ, pToken, NO_STORE_LEN) == 0)
  635. {
  636. *fNoCache = TRUE;
  637. }
  638. // The PRIVATE_SZ tag should be handled one level higher
  639. // private.
  640. // if (strnicmp(PRIVATE_SZ, pToken, PRIVATE_LEN) == 0)
  641. // {
  642. // SetPerUserItem(TRUE);
  643. // }
  644. while (*pToken != '\0')
  645. {
  646. if ( *pToken == ',')
  647. {
  648. pToken++;
  649. break;
  650. }
  651. pToken++;
  652. }
  653. } // while (*pToken != '\0')
  654. // We've finished parsing it, now return our terminator back to its proper place
  655. szBuf[dwBufLen] = chTemp;
  656. // If fNoCache, we're done. Break out of switch.
  657. if (*fNoCache)
  658. break;
  659. index++;
  660. } // while FastQueryResponseHeader == ERROR_SUCCESS
  661. // Finally, check if any Vary: headers exist, EXCEPT "Vary: User-Agent"
  662. dwBufLen = sizeof(szBuf);
  663. if (HttpQueryInfoA(_hRequest,
  664. HTTP_QUERY_VARY,
  665. NULL,
  666. szBuf,
  667. &dwBufLen,
  668. NULL) == TRUE
  669. && !(dwBufLen == USER_AGENT_LEN && !strnicmp (szBuf, USER_AGENT_SZ, dwBufLen)) )
  670. {
  671. fVary = TRUE;
  672. goto quit;
  673. }
  674. DWORD StatusCode;
  675. DWORD dwSize = sizeof(DWORD);
  676. // accept HTTP/1.0 or downlevel server responses
  677. if (HttpQueryInfoA(_hRequest,
  678. WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
  679. NULL,
  680. &StatusCode,
  681. &dwSize,
  682. NULL))
  683. {
  684. if (StatusCode == HTTP_STATUS_OK || StatusCode == 0)
  685. {
  686. dwBufLen = sizeof(szBuf);
  687. if (HttpQueryInfoA(_hRequest,
  688. WINHTTP_QUERY_CONTENT_TYPE,
  689. NULL,
  690. szBuf,
  691. &dwBufLen,
  692. NULL))
  693. {
  694. if (::FExcludedMimeType(szBuf, dwBufLen))
  695. {
  696. ok = FALSE;
  697. DEBUG_PRINT(CACHE,
  698. INFO,
  699. ("%s Mime Excluded from caching\n",
  700. szBuf
  701. ));
  702. goto quit;
  703. }
  704. }
  705. // BUGBUGBUG: What are we going to do with the Vary header???????
  706. ok = TRUE;
  707. goto quit;
  708. }
  709. }
  710. quit:
  711. DEBUG_LEAVE(ok);
  712. return ok;
  713. }