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.

3502 lines
95 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. Contains the HTTP response cache logic.
  7. Author:
  8. Michael Courage (mcourage) 17-May-1999
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "cachep.h"
  13. BOOLEAN g_InitUriCacheCalled;
  14. //
  15. // Global hash table
  16. //
  17. HASHTABLE g_UriCacheTable;
  18. LIST_ENTRY g_ZombieListHead;
  19. UL_URI_CACHE_CONFIG g_UriCacheConfig;
  20. UL_URI_CACHE_STATS g_UriCacheStats;
  21. UL_SPIN_LOCK g_UriCacheSpinLock;
  22. //
  23. // Turn on/off cache at runtime
  24. //
  25. LONG g_CacheMemEnabled = TRUE;
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text( INIT, UlInitializeUriCache )
  28. #pragma alloc_text( PAGE, UlTerminateUriCache )
  29. #pragma alloc_text( PAGE, UlCheckCachePreconditions )
  30. #pragma alloc_text( PAGE, UlCheckCacheResponseConditions )
  31. #pragma alloc_text( PAGE, UlCheckoutUriCacheEntry )
  32. #pragma alloc_text( PAGE, UlCheckinUriCacheEntry )
  33. #pragma alloc_text( PAGE, UlFlushCache )
  34. #pragma alloc_text( PAGE, UlpFlushFilterAll )
  35. #pragma alloc_text( PAGE, UlFlushCacheByProcess )
  36. #pragma alloc_text( PAGE, UlpFlushFilterProcess )
  37. #pragma alloc_text( PAGE, UlFlushCacheByUri )
  38. #pragma alloc_text( PAGE, UlpFlushUri )
  39. #pragma alloc_text( PAGE, UlAddCacheEntry )
  40. #pragma alloc_text( PAGE, UlpFilteredFlushUriCache )
  41. #pragma alloc_text( PAGE, UlpFilteredFlushUriCacheInline )
  42. #pragma alloc_text( PAGE, UlpFilteredFlushUriCacheWorker )
  43. #pragma alloc_text( PAGE, UlpAddZombie )
  44. #pragma alloc_text( PAGE, UlpClearZombieList )
  45. #pragma alloc_text( PAGE, UlpDestroyUriCacheEntry )
  46. #pragma alloc_text( PAGE, UlPeriodicCacheScavenger )
  47. #pragma alloc_text( PAGE, UlpFlushFilterPeriodicScavenger )
  48. #pragma alloc_text( PAGE, UlTrimCache )
  49. #pragma alloc_text( PAGE, UlpFlushFilterTrimCache )
  50. #pragma alloc_text( PAGE, UlpQueryTranslateHeader )
  51. #pragma alloc_text( PAGE, UlpQueryExpectHeader )
  52. #pragma alloc_text( PAGE, UlAddFragmentToCache )
  53. #pragma alloc_text( PAGE, UlReadFragmentFromCache )
  54. #pragma alloc_text( PAGE, UlpCreateFragmentCacheEntry )
  55. #pragma alloc_text( PAGE, UlAllocateCacheEntry )
  56. #pragma alloc_text( PAGE, UlAddCacheEntry )
  57. #pragma alloc_text( PAGE, UlDisableCache )
  58. #pragma alloc_text( PAGE, UlEnableCache )
  59. #endif // ALLOC_PRAGMA
  60. #if 0
  61. NOT PAGEABLE -- UlpCheckTableSpace
  62. NOT PAGEABLE -- UlpCheckSpaceAndAddEntryStats
  63. NOT PAGEABLE -- UlpRemoveEntryStats
  64. #endif
  65. /***************************************************************************++
  66. Routine Description:
  67. Performs global initialization of the URI cache.
  68. Return Value:
  69. NTSTATUS - Completion status.
  70. --***************************************************************************/
  71. NTSTATUS
  72. UlInitializeUriCache(
  73. PUL_CONFIG pConfig
  74. )
  75. {
  76. NTSTATUS Status = STATUS_SUCCESS;
  77. //
  78. // Sanity check.
  79. //
  80. PAGED_CODE();
  81. ASSERT( !g_InitUriCacheCalled );
  82. UlTrace(URI_CACHE, ("Http!UlInitializeUriCache\n"));
  83. if ( !g_InitUriCacheCalled )
  84. {
  85. PUL_URI_CACHE_CONFIG pUriConfig = &pConfig->UriConfig;
  86. g_UriCacheConfig.EnableCache = pUriConfig->EnableCache;
  87. g_UriCacheConfig.MaxCacheUriCount = pUriConfig->MaxCacheUriCount;
  88. g_UriCacheConfig.MaxCacheMegabyteCount =
  89. pUriConfig->MaxCacheMegabyteCount;
  90. g_UriCacheConfig.MaxCacheByteCount =
  91. (((ULONGLONG) g_UriCacheConfig.MaxCacheMegabyteCount)
  92. << MEGABYTE_SHIFT);
  93. //
  94. // Don't want to scavenge more than once every ten seconds.
  95. // In particular, do not want to scavenge every 0 seconds, as the
  96. // machine will become completely unresponsive.
  97. //
  98. g_UriCacheConfig.ScavengerPeriod =
  99. max(pUriConfig->ScavengerPeriod, 10);
  100. g_UriCacheConfig.MaxUriBytes = pUriConfig->MaxUriBytes;
  101. g_UriCacheConfig.HashTableBits = pUriConfig->HashTableBits;
  102. RtlZeroMemory(&g_UriCacheStats, sizeof(g_UriCacheStats));
  103. InitializeListHead(&g_ZombieListHead);
  104. UlInitializeSpinLock( &g_UriCacheSpinLock, "g_UriCacheSpinLock" );
  105. if (g_UriCacheConfig.EnableCache)
  106. {
  107. Status = UlInitializeResource(
  108. &g_pUlNonpagedData->UriZombieResource,
  109. "UriZombieResource",
  110. 0,
  111. UL_ZOMBIE_RESOURCE_TAG
  112. );
  113. if (NT_SUCCESS(Status))
  114. {
  115. Status = UlInitializeHashTable(
  116. &g_UriCacheTable,
  117. PagedPool,
  118. g_UriCacheConfig.HashTableBits
  119. );
  120. if (NT_SUCCESS(Status))
  121. {
  122. ASSERT(IS_VALID_HASHTABLE(&g_UriCacheTable));
  123. Status = UlInitializeScavengerThread();
  124. g_InitUriCacheCalled = TRUE;
  125. }
  126. }
  127. else
  128. {
  129. UlDeleteResource(&g_pUlNonpagedData->UriZombieResource);
  130. }
  131. }
  132. else
  133. {
  134. UlTrace(URI_CACHE, ("URI Cache disabled.\n"));
  135. g_InitUriCacheCalled = TRUE;
  136. }
  137. }
  138. else
  139. {
  140. UlTrace(URI_CACHE, ("URI CACHE INITIALIZED TWICE!\n"));
  141. }
  142. return Status;
  143. } // UlInitializeUriCache
  144. /***************************************************************************++
  145. Routine Description:
  146. Performs global termination of the URI cache.
  147. --***************************************************************************/
  148. VOID
  149. UlTerminateUriCache(
  150. VOID
  151. )
  152. {
  153. NTSTATUS Status;
  154. //
  155. // Sanity check.
  156. //
  157. PAGED_CODE();
  158. UlTrace(URI_CACHE, ("Http!UlTerminateUriCache\n"));
  159. if (g_InitUriCacheCalled && g_UriCacheConfig.EnableCache)
  160. {
  161. // Must terminate the scavenger before destroying the hash table
  162. UlTerminateScavengerThread();
  163. UlTerminateHashTable(&g_UriCacheTable);
  164. Status = UlDeleteResource(&g_pUlNonpagedData->UriZombieResource);
  165. ASSERT(NT_SUCCESS(Status));
  166. }
  167. g_InitUriCacheCalled = FALSE;
  168. } // UlTerminateUriCache
  169. /***************************************************************************++
  170. Routine Description:
  171. This routine checks a request (and its connection) to see if it's
  172. ok to serve this request from the cache. Basically we only accept
  173. simple GET requests with no conditional headers.
  174. Arguments:
  175. pHttpConn - The connection to be checked
  176. Return Value:
  177. BOOLEAN - True if it's ok to serve from cache
  178. --***************************************************************************/
  179. BOOLEAN
  180. UlCheckCachePreconditions(
  181. PUL_INTERNAL_REQUEST pRequest,
  182. PUL_HTTP_CONNECTION pHttpConn
  183. )
  184. {
  185. URI_PRECONDITION Precondition = URI_PRE_OK;
  186. UNREFERENCED_PARAMETER(pHttpConn);
  187. //
  188. // Sanity check
  189. //
  190. PAGED_CODE();
  191. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConn) );
  192. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  193. if (!g_UriCacheConfig.EnableCache)
  194. {
  195. Precondition = URI_PRE_DISABLED;
  196. }
  197. else if (pRequest->ParseState != ParseDoneState)
  198. {
  199. Precondition = URI_PRE_ENTITY_BODY;
  200. }
  201. else if (pRequest->Verb != HttpVerbGET)
  202. {
  203. Precondition = URI_PRE_VERB;
  204. }
  205. else if (HTTP_NOT_EQUAL_VERSION(pRequest->Version, 1, 1)
  206. && HTTP_NOT_EQUAL_VERSION(pRequest->Version, 1, 0))
  207. {
  208. Precondition = URI_PRE_PROTOCOL;
  209. }
  210. // check for Translate: f (DAV)
  211. else if ( UlpQueryTranslateHeader(pRequest) )
  212. {
  213. Precondition = URI_PRE_TRANSLATE;
  214. }
  215. // check for non-100-continue expectation
  216. else if ( !UlpQueryExpectHeader(pRequest) )
  217. {
  218. Precondition = URI_PRE_EXPECTATION_FAILED;
  219. }
  220. // check for Authorization header
  221. else if (pRequest->HeaderValid[HttpHeaderAuthorization])
  222. {
  223. Precondition = URI_PRE_AUTHORIZATION;
  224. }
  225. //
  226. // check for some of the If-* headers
  227. // NOTE: See UlpCheckCacheControlHeaders for handling of other If-* headers
  228. //
  229. else if (pRequest->HeaderValid[HttpHeaderIfRange])
  230. {
  231. Precondition = URI_PRE_CONDITIONAL;
  232. }
  233. // CODEWORK: check for other evil headers
  234. else if (pRequest->HeaderValid[HttpHeaderRange])
  235. {
  236. Precondition = URI_PRE_OTHER_HEADER;
  237. }
  238. UlTrace(URI_CACHE,
  239. ("Http!UlCheckCachePreconditions(req = %p, '%ls', httpconn = %p)\n"
  240. " OkToServeFromCache = %d, Precondition = %d\n",
  241. pRequest,
  242. pRequest->CookedUrl.pUrl,
  243. pHttpConn,
  244. (URI_PRE_OK == Precondition) ? 1 : 0,
  245. Precondition
  246. ));
  247. return (BOOLEAN) (URI_PRE_OK == Precondition);
  248. } // UlCheckCachePreconditions
  249. /***************************************************************************++
  250. Routine Description:
  251. This routine checks a response to see if it's cacheable. Basically
  252. we'll take it if:
  253. * the cache policy is right
  254. * the size is small enough
  255. * there is room in the cache
  256. * we get the response all at once
  257. Arguments:
  258. pHttpConn - The connection to be checked
  259. Return Value:
  260. BOOLEAN - True if it's ok to serve from cache
  261. --***************************************************************************/
  262. BOOLEAN
  263. UlCheckCacheResponseConditions(
  264. PUL_INTERNAL_REQUEST pRequest,
  265. PUL_INTERNAL_RESPONSE pResponse,
  266. ULONG Flags,
  267. HTTP_CACHE_POLICY CachePolicy
  268. )
  269. {
  270. URI_PRECONDITION Precondition = URI_PRE_OK;
  271. //
  272. // Sanity check
  273. //
  274. PAGED_CODE();
  275. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  276. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE(pResponse) );
  277. if (pRequest->CachePreconditions == FALSE) {
  278. Precondition = URI_PRE_REQUEST;
  279. }
  280. // check policy
  281. else if (CachePolicy.Policy == HttpCachePolicyNocache) {
  282. Precondition = URI_PRE_POLICY;
  283. }
  284. // check if Date: header is valid (can affect If-Modified-Since handling)
  285. else if (!pResponse->GenDateHeader || (0L == pResponse->CreationTime.QuadPart)) {
  286. Precondition = URI_PRE_PROTOCOL;
  287. }
  288. // check size of response
  289. else if ((pResponse->ResponseLength - pResponse->HeaderLength) >
  290. g_UriCacheConfig.MaxUriBytes) {
  291. Precondition = URI_PRE_SIZE;
  292. }
  293. // check if the header length exceeds the limit
  294. else if (pResponse->HeaderLength > g_UlMaxFixedHeaderSize) {
  295. Precondition = URI_PRE_SIZE;
  296. }
  297. // check for full response
  298. else if (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) {
  299. Precondition = URI_PRE_FRAGMENT;
  300. }
  301. // check available cache table space
  302. else if (!UlpCheckTableSpace(pResponse->ResponseLength)) {
  303. Precondition = URI_PRE_NOMEMORY;
  304. }
  305. // Check for bogus responses
  306. else if ((pResponse->ResponseLength < 1) || (pResponse->ChunkCount < 2)) {
  307. Precondition = URI_PRE_BOGUS;
  308. }
  309. // FUTURE: check if multiple Content-Encodings are applied
  310. // else if ( /* multiple encodings */ )
  311. // {
  312. // Precondition = URI_PRE_BOGUS;
  313. // }
  314. UlTrace(URI_CACHE,
  315. ("Http!UlCheckCacheResponseConditions("
  316. "pRequest = %p, '%ls', pResponse = %p)\n"
  317. " OkToCache = %d, Precondition = %d\n",
  318. pRequest,
  319. pRequest->CookedUrl.pUrl,
  320. pResponse,
  321. (URI_PRE_OK == Precondition),
  322. Precondition
  323. ));
  324. return (BOOLEAN) (URI_PRE_OK == Precondition);
  325. } // UlCheckCacheResponseConditions
  326. /***************************************************************************++
  327. Routine Description:
  328. This routine does a cache lookup to see if there is a valid entry
  329. corresponding to the request URI.
  330. Arguments:
  331. pSearchKey - The -extended or normal- Uri Key
  332. Return Value:
  333. PUL_URI_CACHE_ENTRY - Pointer to the entry, if found. NULL otherwise.
  334. --***************************************************************************/
  335. PUL_URI_CACHE_ENTRY
  336. UlCheckoutUriCacheEntry(
  337. PURI_SEARCH_KEY pSearchKey
  338. )
  339. {
  340. PUL_URI_CACHE_ENTRY pUriCacheEntry = NULL;
  341. //
  342. // Sanity check
  343. //
  344. PAGED_CODE();
  345. ASSERT(!g_UriCacheConfig.EnableCache
  346. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  347. ASSERT(IS_VALID_URI_SEARCH_KEY(pSearchKey));
  348. pUriCacheEntry = UlGetFromHashTable(
  349. &g_UriCacheTable,
  350. pSearchKey
  351. );
  352. if (pUriCacheEntry != NULL)
  353. {
  354. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  355. //
  356. // see if entry has expired; if so, check it right back in
  357. // without touching the stats. We expect the scavenger to
  358. // deal with flushing this entry the next time it runs, so
  359. // we can defer the flush.
  360. //
  361. if ( HttpCachePolicyTimeToLive == pUriCacheEntry->CachePolicy.Policy )
  362. {
  363. LARGE_INTEGER Now;
  364. KeQuerySystemTime(&Now);
  365. if ( Now.QuadPart > pUriCacheEntry->ExpirationTime.QuadPart )
  366. {
  367. UlTrace(URI_CACHE,
  368. ("Http!UlCheckoutUriCacheEntry: pUriCacheEntry %p is EXPIRED\n",
  369. pUriCacheEntry
  370. ));
  371. UlCheckinUriCacheEntry(pUriCacheEntry);
  372. pUriCacheEntry = NULL;
  373. goto end;
  374. }
  375. }
  376. pUriCacheEntry->HitCount++;
  377. // reset scavenger counter
  378. pUriCacheEntry->ScavengerTicks = 0;
  379. UlTrace(URI_CACHE,
  380. ("Http!UlCheckoutUriCacheEntry(pUriCacheEntry %p, '%ls') "
  381. "refcount = %d\n",
  382. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  383. pUriCacheEntry->ReferenceCount
  384. ));
  385. }
  386. else
  387. {
  388. UlTrace(URI_CACHE,
  389. ("Http!UlCheckoutUriCacheEntry(failed: Token:'%ls' '%ls' )\n",
  390. pSearchKey->Type == UriKeyTypeExtended ?
  391. pSearchKey->ExKey.pToken :
  392. L"",
  393. pSearchKey->Type == UriKeyTypeExtended ?
  394. pSearchKey->ExKey.pAbsPath :
  395. pSearchKey->Key.pUri
  396. ));
  397. }
  398. end:
  399. return pUriCacheEntry;
  400. } // UlCheckoutUriCacheEntry
  401. /***************************************************************************++
  402. Routine Description:
  403. Decrements the refcount on a cache entry. Cleans up non-cached
  404. entries.
  405. Arguments:
  406. pUriCacheEntry - the entry to deref
  407. --***************************************************************************/
  408. VOID
  409. UlCheckinUriCacheEntry(
  410. PUL_URI_CACHE_ENTRY pUriCacheEntry
  411. )
  412. {
  413. LONG ReferenceCount;
  414. //
  415. // Sanity check
  416. //
  417. PAGED_CODE();
  418. ASSERT(!g_UriCacheConfig.EnableCache
  419. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  420. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  421. UlTrace(URI_CACHE,
  422. ("Http!UlCheckinUriCacheEntry(pUriCacheEntry %p, '%ls')\n",
  423. pUriCacheEntry, pUriCacheEntry->UriKey.pUri
  424. ));
  425. //
  426. // decrement count
  427. //
  428. ReferenceCount = DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CHECKIN);
  429. ASSERT(ReferenceCount >= 0);
  430. } // UlCheckinUriCacheEntry
  431. /***************************************************************************++
  432. Routine Description:
  433. Temporarly disables the indexing mechanism in CB Logging.
  434. Removes all cache entries, unconditionally.
  435. Writes a cache flush notification to the CB log file, and enables the
  436. indexing mechanism back.
  437. --***************************************************************************/
  438. VOID
  439. UlFlushCache(
  440. IN PUL_CONTROL_CHANNEL pControlChannel
  441. )
  442. {
  443. //
  444. // Sanity check
  445. //
  446. PAGED_CODE();
  447. ASSERT(!g_UriCacheConfig.EnableCache
  448. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  449. //
  450. // Caller needs to hold the CG Lock exclusive, the flushes needs to
  451. // be serialized.
  452. //
  453. ASSERT(UlDbgResourceOwnedExclusive(
  454. &g_pUlNonpagedData->ConfigGroupResource
  455. ));
  456. //
  457. // Nothing to do, if cache is disabled.
  458. //
  459. if (g_UriCacheConfig.EnableCache)
  460. {
  461. UlTrace(URI_CACHE,("Http!UlFlushCache()\n"));
  462. // TODO: Need to notify every control channel
  463. //
  464. // This is to prevent any outstanding sends for the zombified
  465. // cache entries to refer to the obsolete indexes, in case the
  466. // sends get completed after we write the cache notification
  467. // entry to the log file. The CB logging calls here must be
  468. // preserved in this order and must be used while holding the
  469. // CG lock. Do not release the lock acquire it again and call
  470. // the other.
  471. //
  472. if (pControlChannel)
  473. {
  474. UlDisableIndexingForCacheHits(pControlChannel);
  475. }
  476. //
  477. // Unconditionally zombifies all of the uri cache entries.
  478. //
  479. UlpFilteredFlushUriCache(UlpFlushFilterAll, NULL, NULL, 0);
  480. //
  481. // HandleFlush will enable the (CB Logging) indexing,
  482. // once it is done writting the notification record.
  483. //
  484. if (pControlChannel)
  485. {
  486. UlHandleCacheFlushedNotification(pControlChannel);
  487. }
  488. }
  489. } // UlFlushCache
  490. /***************************************************************************++
  491. Routine Description:
  492. A filter for UlFlushCache. Called by UlpFilteredFlushUriCache.
  493. Arguments:
  494. pUriCacheEntry - the entry to check
  495. pContext - ignored
  496. --***************************************************************************/
  497. UL_CACHE_PREDICATE
  498. UlpFlushFilterAll(
  499. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  500. IN PVOID pContext
  501. )
  502. {
  503. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  504. //
  505. // Sanity check
  506. //
  507. PAGED_CODE();
  508. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  509. ASSERT( pUriFilterContext != NULL
  510. && URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature
  511. && pUriFilterContext->pCallerContext == NULL );
  512. UlTrace(URI_CACHE, (
  513. "Http!UlpFlushFilterAll(pUriCacheEntry %p '%ls') refcount = %d\n",
  514. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  515. pUriCacheEntry->ReferenceCount));
  516. //
  517. // Second BOOLEAN must * only * be true if the UlpFlushFilterAll is
  518. // called by UlFlushCache (since it writes a binary log file record
  519. // of flush).
  520. //
  521. return UlpZombifyEntry(
  522. TRUE,
  523. TRUE,
  524. pUriCacheEntry,
  525. pUriFilterContext
  526. );
  527. } // UlpFlushFilterAll
  528. /***************************************************************************++
  529. Routine Description:
  530. Removes any cache entries that were created by the given process.
  531. Arguments:
  532. pProcess - a process that is going away
  533. --***************************************************************************/
  534. VOID
  535. UlFlushCacheByProcess(
  536. PUL_APP_POOL_PROCESS pProcess
  537. )
  538. {
  539. //
  540. // sanity check
  541. //
  542. PAGED_CODE();
  543. ASSERT( IS_VALID_AP_PROCESS(pProcess) );
  544. ASSERT(!g_UriCacheConfig.EnableCache
  545. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  546. if (g_UriCacheConfig.EnableCache)
  547. {
  548. UlTrace(URI_CACHE, (
  549. "Http!UlFlushCacheByProcess(proc = %p)\n",
  550. pProcess
  551. ));
  552. UlpFilteredFlushUriCache(UlpFlushFilterProcess, pProcess, NULL, 0);
  553. }
  554. } // UlFlushCacheByProcess
  555. /***************************************************************************++
  556. Routine Description:
  557. If recursive flag has been picked this function removes the cache entries
  558. matching with the given prefix. (pUri)
  559. Otherwise removes the specific URL from the cache.
  560. Arguments:
  561. pUri - the uri prefix to match against
  562. Length - length of the prefix, in bytes
  563. Flags - HTTP_FLUSH_RESPONSE_FLAG_RECURSIVE indicates a tree flush
  564. pProcess - the process that made the call
  565. --***************************************************************************/
  566. VOID
  567. UlFlushCacheByUri(
  568. IN PWSTR pUri,
  569. IN ULONG Length,
  570. IN ULONG Flags,
  571. IN PUL_APP_POOL_PROCESS pProcess
  572. )
  573. {
  574. NTSTATUS Status;
  575. BOOLEAN Recursive;
  576. PWSTR pCopiedUri;
  577. //
  578. // Sanity check.
  579. //
  580. PAGED_CODE();
  581. ASSERT( IS_VALID_AP_PROCESS(pProcess) );
  582. ASSERT( !g_UriCacheConfig.EnableCache
  583. || IS_VALID_HASHTABLE(&g_UriCacheTable) );
  584. if (!g_UriCacheConfig.EnableCache)
  585. {
  586. return;
  587. }
  588. Status = STATUS_SUCCESS;
  589. Recursive = (BOOLEAN) (0 != (Flags & HTTP_FLUSH_RESPONSE_FLAG_RECURSIVE));
  590. pCopiedUri = NULL;
  591. UlTrace(URI_CACHE, (
  592. "Http!UlFlushCacheByUri(\n"
  593. " uri = '%S'\n"
  594. " len = %d\n"
  595. " flags = %08x, recursive = %d\n"
  596. " proc = %p\n",
  597. pUri,
  598. Length,
  599. Flags,
  600. (int) Recursive,
  601. pProcess
  602. ));
  603. //
  604. // Ensure pUri ends with a L'/' if Recursive flag is set.
  605. //
  606. if (Recursive &&
  607. pUri[(Length-sizeof(WCHAR))/sizeof(WCHAR)] != L'/')
  608. {
  609. //
  610. // Make a copy of the origianl URL and append a L'/' to it.
  611. //
  612. pCopiedUri = (PWSTR) UL_ALLOCATE_POOL(
  613. PagedPool,
  614. Length + sizeof(WCHAR) + sizeof(WCHAR),
  615. UL_UNICODE_STRING_POOL_TAG
  616. );
  617. if (!pCopiedUri)
  618. {
  619. Status = STATUS_NO_MEMORY;
  620. }
  621. else
  622. {
  623. RtlCopyMemory(
  624. pCopiedUri,
  625. pUri,
  626. Length
  627. );
  628. pCopiedUri[Length/sizeof(WCHAR)] = L'/';
  629. pCopiedUri[(Length+sizeof(WCHAR))/sizeof(WCHAR)] = UNICODE_NULL;
  630. pUri = pCopiedUri;
  631. Length += sizeof(WCHAR);
  632. }
  633. }
  634. if (NT_SUCCESS(Status))
  635. {
  636. if (Recursive)
  637. {
  638. //
  639. // When the recursive flag is set, we are supposed
  640. // to do prefix match with respect to provided URL.
  641. // Any cache entry matches with the prefix will be
  642. // flushed out from the cache.
  643. //
  644. UlpFilteredFlushUriCache(
  645. UlpFlushFilterUriRecursive,
  646. pProcess,
  647. pUri,
  648. Length
  649. );
  650. }
  651. else
  652. {
  653. UlpFlushUri(
  654. pUri,
  655. Length,
  656. pProcess
  657. );
  658. UlpClearZombieList();
  659. }
  660. }
  661. if (pCopiedUri)
  662. {
  663. UL_FREE_POOL(pCopiedUri, UL_UNICODE_STRING_POOL_TAG);
  664. }
  665. } // UlFlushCacheByUri
  666. /***************************************************************************++
  667. Routine Description:
  668. Removes a single URI from the table if the name and process match an
  669. entry.
  670. Arguments:
  671. --***************************************************************************/
  672. VOID
  673. UlpFlushUri(
  674. IN PWSTR pUri,
  675. IN ULONG Length,
  676. PUL_APP_POOL_PROCESS pProcess
  677. )
  678. {
  679. PUL_URI_CACHE_ENTRY pUriCacheEntry = NULL;
  680. URI_KEY Key;
  681. //
  682. // Sanity check
  683. //
  684. PAGED_CODE();
  685. //
  686. // find bucket
  687. //
  688. Key.Hash = HashRandomizeBits(HashStringNoCaseW(pUri, 0));
  689. Key.Length = Length;
  690. Key.pUri = pUri;
  691. Key.pPath = NULL;
  692. pUriCacheEntry = UlDeleteFromHashTable(&g_UriCacheTable, &Key, pProcess);
  693. if (NULL != pUriCacheEntry)
  694. {
  695. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  696. UlTrace(URI_CACHE, (
  697. "Http!UlpFlushUri(pUriCacheEntry %p '%ls') refcount = %d\n",
  698. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  699. pUriCacheEntry->ReferenceCount));
  700. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, FLUSH);
  701. //
  702. // Perfmon counters
  703. //
  704. UlIncCounter(HttpGlobalCounterTotalFlushedUris);
  705. }
  706. } // UlpFlushUri
  707. /***************************************************************************++
  708. Routine Description:
  709. A filter for UlFlushCacheByProcess. Called by UlpFilteredFlushUriCache.
  710. Arguments:
  711. pUriCacheEntry - the entry to check
  712. pContext - pointer to the UL_APP_POOL_PROCESS that's going away
  713. --***************************************************************************/
  714. UL_CACHE_PREDICATE
  715. UlpFlushFilterProcess(
  716. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  717. IN PVOID pContext
  718. )
  719. {
  720. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  721. PUL_APP_POOL_PROCESS pProcess;
  722. //
  723. // Sanity check
  724. //
  725. PAGED_CODE();
  726. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  727. ASSERT( IS_VALID_FILTER_CONTEXT(pUriFilterContext)
  728. && pUriFilterContext->pCallerContext != NULL );
  729. pProcess = (PUL_APP_POOL_PROCESS) pUriFilterContext->pCallerContext;
  730. ASSERT( IS_VALID_AP_PROCESS(pProcess) );
  731. return UlpZombifyEntry(
  732. (BOOLEAN) (pProcess == pUriCacheEntry->pProcess),
  733. FALSE,
  734. pUriCacheEntry,
  735. pUriFilterContext
  736. );
  737. } // UlpFlushFilterProcess
  738. /***************************************************************************++
  739. Routine Description:
  740. A filter for UlpFilteredFlushUriCache. If the given cache entry has a
  741. URI which is prefix of the URI inside the filter context this function
  742. returns delete. Otherwise do not care.
  743. Arguments:
  744. pUriCacheEntry - the entry to check
  745. pContext - pointer to the filter context which holds the appool and
  746. the URI key for the prefix matching.
  747. --***************************************************************************/
  748. UL_CACHE_PREDICATE
  749. UlpFlushFilterUriRecursive(
  750. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  751. IN PVOID pContext
  752. )
  753. {
  754. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  755. PUL_APP_POOL_PROCESS pProcess;
  756. BOOLEAN bZombify = FALSE;
  757. UL_CACHE_PREDICATE Predicate = ULC_NO_ACTION;
  758. //
  759. // Sanity check
  760. //
  761. PAGED_CODE();
  762. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  763. ASSERT( IS_VALID_FILTER_CONTEXT(pUriFilterContext)
  764. && pUriFilterContext->pCallerContext != NULL );
  765. pProcess = (PUL_APP_POOL_PROCESS) pUriFilterContext->pCallerContext;
  766. ASSERT( IS_VALID_AP_PROCESS(pProcess) );
  767. if ( pUriFilterContext->UriKey.pUri == NULL
  768. || pUriFilterContext->UriKey.Length == 0
  769. || pUriFilterContext->UriKey.Length >
  770. pUriCacheEntry->UriKey.Length
  771. )
  772. {
  773. return ULC_NO_ACTION;
  774. }
  775. bZombify =
  776. (BOOLEAN) (pProcess == pUriCacheEntry->pProcess)
  777. &&
  778. UlPrefixUriKeys(&pUriFilterContext->UriKey,
  779. &pUriCacheEntry->UriKey)
  780. ;
  781. Predicate =
  782. UlpZombifyEntry(
  783. bZombify,
  784. FALSE,
  785. pUriCacheEntry,
  786. pUriFilterContext
  787. );
  788. //
  789. // Make sure that the Zombify function do not returns ULC_DELETE_STOP.
  790. // So that our caller proceeds with the search through the entire
  791. // cache table.
  792. //
  793. ASSERT( Predicate == ULC_DELETE || Predicate == ULC_NO_ACTION );
  794. return Predicate;
  795. } // UlpFlushFilterUriRecursive
  796. /***************************************************************************++
  797. Routine Description:
  798. Checks the hash table to make sure there is room for one more
  799. entry of a given size.
  800. Arguments:
  801. EntrySize - the size in bytes of the entry to be added
  802. --***************************************************************************/
  803. BOOLEAN
  804. UlpCheckTableSpace(
  805. IN ULONGLONG EntrySize
  806. )
  807. {
  808. ULONG UriCount;
  809. ULONGLONG ByteCount;
  810. //
  811. // CODEWORK: MaxCacheMegabyteCount of zero should mean adaptive limit,
  812. // but for now I'll take it to mean "no limit".
  813. //
  814. if (g_UriCacheConfig.MaxCacheMegabyteCount == 0)
  815. ByteCount = 0;
  816. else
  817. ByteCount = g_UriCacheStats.ByteCount + ROUND_TO_PAGES(EntrySize);
  818. //
  819. // MaxCacheUriCount of zero means no limit on number of URIs cached
  820. //
  821. if (g_UriCacheConfig.MaxCacheUriCount == 0)
  822. UriCount = 0;
  823. else
  824. UriCount = g_UriCacheStats.UriCount + 1;
  825. if (
  826. UriCount <= g_UriCacheConfig.MaxCacheUriCount &&
  827. ByteCount <= g_UriCacheConfig.MaxCacheByteCount
  828. )
  829. {
  830. return TRUE;
  831. }
  832. else
  833. {
  834. UlTrace(URI_CACHE, (
  835. "Http!UlpCheckTableSpace(%I64u) FALSE\n"
  836. " UriCount = %lu\n"
  837. " ByteCount = %I64u (%luMB)\n"
  838. " MaxCacheUriCount = %lu\n"
  839. " MaxCacheMegabyteCount = %luMB\n"
  840. " MaxCacheByteCount = %I64u\n",
  841. EntrySize,
  842. g_UriCacheStats.UriCount,
  843. g_UriCacheStats.ByteCount,
  844. (ULONG) (g_UriCacheStats.ByteCount >> MEGABYTE_SHIFT),
  845. g_UriCacheConfig.MaxCacheUriCount,
  846. g_UriCacheConfig.MaxCacheMegabyteCount,
  847. g_UriCacheConfig.MaxCacheByteCount
  848. ));
  849. return FALSE;
  850. }
  851. } // UlpCheckTableSpace
  852. /***************************************************************************++
  853. Routine Description:
  854. Tries to add a cache entry to the hash table.
  855. Arguments:
  856. pUriCacheEntry - the entry to be added
  857. --***************************************************************************/
  858. NTSTATUS
  859. UlAddCacheEntry(
  860. PUL_URI_CACHE_ENTRY pUriCacheEntry
  861. )
  862. {
  863. NTSTATUS Status;
  864. //
  865. // Sanity check
  866. //
  867. PAGED_CODE();
  868. ASSERT(!g_UriCacheConfig.EnableCache
  869. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  870. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  871. ASSERT(! pUriCacheEntry->Zombie);
  872. pUriCacheEntry->BucketEntry.Next = NULL;
  873. pUriCacheEntry->Cached = FALSE;
  874. // First, check if still has space for storing the cache entry
  875. if (UlpCheckSpaceAndAddEntryStats(pUriCacheEntry))
  876. {
  877. pUriCacheEntry->Cached = TRUE;
  878. //
  879. // Insert this record into the hash table
  880. // Check first to see if the key already presents
  881. //
  882. Status = UlAddToHashTable(&g_UriCacheTable, pUriCacheEntry);
  883. if (!NT_SUCCESS(Status))
  884. {
  885. // This can fail if it's a duplicate name
  886. UlpRemoveEntryStats(pUriCacheEntry);
  887. pUriCacheEntry->Cached = FALSE;
  888. }
  889. }
  890. else
  891. {
  892. Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
  893. }
  894. UlTrace(URI_CACHE, (
  895. "Http!UlAddCacheEntry(urientry %p '%ls') %s added to table. "
  896. "RefCount=%d, lkrc=%d.\n",
  897. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  898. pUriCacheEntry->Cached ? "was" : "was not",
  899. pUriCacheEntry->ReferenceCount,
  900. Status
  901. ));
  902. return Status;
  903. } // UlAddCacheEntry
  904. /***************************************************************************++
  905. Routine Description:
  906. Check to see if we have space to add this cache entry and if so update
  907. cache statistics to reflect the addition of an entry. This has to be
  908. done together inside a lock.
  909. Arguments:
  910. pUriCacheEntry - entry being added
  911. --***************************************************************************/
  912. BOOLEAN
  913. UlpCheckSpaceAndAddEntryStats(
  914. PUL_URI_CACHE_ENTRY pUriCacheEntry
  915. )
  916. {
  917. KIRQL OldIrql;
  918. ULONG EntrySize;
  919. //
  920. // Sanity check
  921. //
  922. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  923. EntrySize = pUriCacheEntry->HeaderLength + pUriCacheEntry->ContentLength;
  924. UlAcquireSpinLock( &g_UriCacheSpinLock, &OldIrql );
  925. if (UlpCheckTableSpace(EntrySize))
  926. {
  927. g_UriCacheStats.UriCount++;
  928. g_UriCacheStats.UriAddedTotal++;
  929. g_UriCacheStats.UriCountMax = MAX(
  930. g_UriCacheStats.UriCountMax,
  931. g_UriCacheStats.UriCount
  932. );
  933. g_UriCacheStats.ByteCount += EntrySize;
  934. g_UriCacheStats.ByteCountMax = MAX(
  935. g_UriCacheStats.ByteCountMax,
  936. g_UriCacheStats.ByteCount
  937. );
  938. UlReleaseSpinLock( &g_UriCacheSpinLock, OldIrql );
  939. //
  940. // Update Uri's site binding info stats.
  941. //
  942. switch (pUriCacheEntry->ConfigInfo.SiteUrlType)
  943. {
  944. case HttpUrlSite_None:
  945. InterlockedIncrement((PLONG) &g_UriCacheStats.UriTypeNotSpecifiedCount);
  946. break;
  947. case HttpUrlSite_Name:
  948. InterlockedIncrement((PLONG) &g_UriCacheStats.UriTypeHostBoundCount);
  949. break;
  950. case HttpUrlSite_NamePlusIP:
  951. InterlockedIncrement((PLONG) &g_UriCacheStats.UriTypeHostPlusIpBoundCount);
  952. break;
  953. case HttpUrlSite_IP:
  954. InterlockedIncrement((PLONG) &g_UriCacheStats.UriTypeIpBoundCount);
  955. break;
  956. case HttpUrlSite_WeakWildcard:
  957. InterlockedIncrement((PLONG) &g_UriCacheStats.UriTypeWildCardCount);
  958. break;
  959. default:
  960. ASSERT(!"Invalid url site binding type while adding to cache !");
  961. break;
  962. }
  963. UlTrace(URI_CACHE, (
  964. "Http!UlpCheckSpaceAndAddEntryStats (urientry %p '%ls')\n",
  965. pUriCacheEntry, pUriCacheEntry->UriKey.pUri
  966. ));
  967. //
  968. // Perfmon counters
  969. //
  970. UlIncCounter(HttpGlobalCounterCurrentUrisCached);
  971. UlIncCounter(HttpGlobalCounterTotalUrisCached);
  972. return TRUE;
  973. }
  974. UlReleaseSpinLock( &g_UriCacheSpinLock, OldIrql );
  975. return FALSE;
  976. } // UlpCheckSpaceAndAddEntryStats
  977. /***************************************************************************++
  978. Routine Description:
  979. Updates cache statistics to reflect the removal of an entry
  980. Arguments:
  981. pUriCacheEntry - entry being removed
  982. --***************************************************************************/
  983. VOID
  984. UlpRemoveEntryStats(
  985. PUL_URI_CACHE_ENTRY pUriCacheEntry
  986. )
  987. {
  988. KIRQL OldIrql;
  989. ULONG EntrySize;
  990. //
  991. // Sanity check
  992. //
  993. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  994. ASSERT( pUriCacheEntry->Cached );
  995. ASSERT( 0 == pUriCacheEntry->ReferenceCount );
  996. EntrySize = pUriCacheEntry->HeaderLength + pUriCacheEntry->ContentLength;
  997. UlAcquireSpinLock( &g_UriCacheSpinLock, &OldIrql );
  998. g_UriCacheStats.UriCount--;
  999. g_UriCacheStats.ByteCount -= EntrySize;
  1000. UlReleaseSpinLock( &g_UriCacheSpinLock, OldIrql );
  1001. //
  1002. // Update Uri's site binding info stats.
  1003. //
  1004. switch (pUriCacheEntry->ConfigInfo.SiteUrlType)
  1005. {
  1006. case HttpUrlSite_None:
  1007. InterlockedDecrement((PLONG) &g_UriCacheStats.UriTypeNotSpecifiedCount);
  1008. break;
  1009. case HttpUrlSite_Name:
  1010. InterlockedDecrement((PLONG) &g_UriCacheStats.UriTypeHostBoundCount);
  1011. break;
  1012. case HttpUrlSite_NamePlusIP:
  1013. InterlockedDecrement((PLONG) &g_UriCacheStats.UriTypeHostPlusIpBoundCount);
  1014. break;
  1015. case HttpUrlSite_IP:
  1016. InterlockedDecrement((PLONG) &g_UriCacheStats.UriTypeIpBoundCount);
  1017. break;
  1018. case HttpUrlSite_WeakWildcard:
  1019. InterlockedDecrement((PLONG) &g_UriCacheStats.UriTypeWildCardCount);
  1020. break;
  1021. default:
  1022. ASSERT(!"Invalid url site binding type while adding to cache !");
  1023. break;
  1024. }
  1025. UlTrace(URI_CACHE, (
  1026. "Http!UlpRemoveEntryStats (urientry %p '%ls')\n",
  1027. pUriCacheEntry, pUriCacheEntry->UriKey.pUri
  1028. ));
  1029. //
  1030. // Perfmon counters
  1031. //
  1032. UlDecCounter(HttpGlobalCounterCurrentUrisCached);
  1033. } // UlpRemoveEntryStats
  1034. /***************************************************************************++
  1035. Routine Description:
  1036. Helper function for the filter callbacks indirectly invoked by
  1037. UlpFilteredFlushUriCache. Adds deleteable entries to a temporary
  1038. list.
  1039. Arguments:
  1040. MustZombify - if TRUE, add entry to the private zombie list
  1041. pUriCacheEntry - entry to zombify
  1042. pUriFilterContext - contains private list
  1043. --***************************************************************************/
  1044. UL_CACHE_PREDICATE
  1045. UlpZombifyEntry(
  1046. BOOLEAN MustZombify,
  1047. BOOLEAN MustResetIndex,
  1048. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1049. IN PURI_FILTER_CONTEXT pUriFilterContext
  1050. )
  1051. {
  1052. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1053. ASSERT(URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature);
  1054. ASSERT(! pUriCacheEntry->Zombie);
  1055. ASSERT(NULL == pUriCacheEntry->ZombieListEntry.Flink);
  1056. if (MustZombify)
  1057. {
  1058. //
  1059. // Temporarily bump the refcount up so that it won't go down
  1060. // to zero when it's removed from the hash table, automatically
  1061. // invoking UlpDestroyUriCacheEntry, which we are trying to defer.
  1062. //
  1063. pUriCacheEntry->ZombieAddReffed = TRUE;
  1064. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, ZOMBIFY);
  1065. InsertTailList(
  1066. &pUriFilterContext->ZombieListHead,
  1067. &pUriCacheEntry->ZombieListEntry);
  1068. pUriCacheEntry->Zombie = TRUE;
  1069. //
  1070. // Force Raw Logging code to generate an index record for the
  1071. // cache entry, in case there's a send on the fly waiting for
  1072. // completion. This is because a flush record will be written
  1073. // before the actual record.
  1074. //
  1075. if (MustResetIndex)
  1076. {
  1077. InterlockedExchange(
  1078. (PLONG) &pUriCacheEntry->BinaryIndexWritten,
  1079. 0
  1080. );
  1081. }
  1082. //
  1083. // reset timer so we can track how long an entry is on the list
  1084. //
  1085. pUriCacheEntry->ScavengerTicks = 0;
  1086. ++ pUriFilterContext->ZombieCount;
  1087. // now remove it from the hash table
  1088. return ULC_DELETE;
  1089. }
  1090. // do not remove pUriCacheEntry from table
  1091. return ULC_NO_ACTION;
  1092. } // UlpZombifyEntry
  1093. /***************************************************************************++
  1094. Routine Description:
  1095. Adds a list of entries to the global zombie list, then calls
  1096. UlpClearZombieList. This cleans up the list of deferred deletions
  1097. built up by UlpFilteredFlushUriCache.
  1098. Runs at passive level.
  1099. Arguments:
  1100. pWorkItem - workitem within a URI_FILTER_CONTEXT containing private list
  1101. --***************************************************************************/
  1102. VOID
  1103. UlpZombifyList(
  1104. IN PUL_WORK_ITEM pWorkItem
  1105. )
  1106. {
  1107. PURI_FILTER_CONTEXT pUriFilterContext;
  1108. PLIST_ENTRY pContextHead;
  1109. PLIST_ENTRY pContextTail;
  1110. PLIST_ENTRY pZombieHead;
  1111. PAGED_CODE();
  1112. ASSERT(NULL != pWorkItem);
  1113. pUriFilterContext
  1114. = CONTAINING_RECORD(pWorkItem, URI_FILTER_CONTEXT, WorkItem);
  1115. ASSERT(URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature);
  1116. UlTrace(URI_CACHE, (
  1117. "http!UlpZombifyList, ctxt = %p\n",
  1118. pUriFilterContext
  1119. ));
  1120. UlAcquireResourceExclusive(&g_pUlNonpagedData->UriZombieResource, TRUE);
  1121. //
  1122. // Splice the entire private list into the head of the Zombie list
  1123. //
  1124. ASSERT(! IsListEmpty(&pUriFilterContext->ZombieListHead));
  1125. pContextHead = pUriFilterContext->ZombieListHead.Flink;
  1126. pContextTail = pUriFilterContext->ZombieListHead.Blink;
  1127. pZombieHead = g_ZombieListHead.Flink;
  1128. pContextTail->Flink = pZombieHead;
  1129. pZombieHead->Blink = pContextTail;
  1130. g_ZombieListHead.Flink = pContextHead;
  1131. pContextHead->Blink = &g_ZombieListHead;
  1132. // Update stats
  1133. g_UriCacheStats.ZombieCount += pUriFilterContext->ZombieCount;
  1134. g_UriCacheStats.ZombieCountMax = MAX(g_UriCacheStats.ZombieCount,
  1135. g_UriCacheStats.ZombieCountMax);
  1136. #if DBG
  1137. {
  1138. PLIST_ENTRY pEntry;
  1139. ULONG ZombieCount;
  1140. // Walk forwards through the zombie list and check that it contains
  1141. // exactly as many valid zombied UriCacheEntries as we expect.
  1142. for (pEntry = g_ZombieListHead.Flink, ZombieCount = 0;
  1143. pEntry != &g_ZombieListHead;
  1144. pEntry = pEntry->Flink, ++ZombieCount)
  1145. {
  1146. PUL_URI_CACHE_ENTRY pUriCacheEntry
  1147. = CONTAINING_RECORD(pEntry, UL_URI_CACHE_ENTRY, ZombieListEntry);
  1148. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1149. ASSERT(pUriCacheEntry->Zombie);
  1150. ASSERT(pUriCacheEntry->ZombieAddReffed
  1151. ? pUriCacheEntry->ScavengerTicks == 0
  1152. : pUriCacheEntry->ScavengerTicks > 0);
  1153. ASSERT(ZombieCount < g_UriCacheStats.ZombieCount);
  1154. }
  1155. ASSERT(ZombieCount == g_UriCacheStats.ZombieCount);
  1156. // And backwards too, like Ginger Rogers
  1157. for (pEntry = g_ZombieListHead.Blink, ZombieCount = 0;
  1158. pEntry != &g_ZombieListHead;
  1159. pEntry = pEntry->Blink, ++ZombieCount)
  1160. {
  1161. PUL_URI_CACHE_ENTRY pUriCacheEntry
  1162. = CONTAINING_RECORD(pEntry, UL_URI_CACHE_ENTRY, ZombieListEntry);
  1163. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1164. ASSERT(pUriCacheEntry->Zombie);
  1165. ASSERT(pUriCacheEntry->ZombieAddReffed
  1166. ? pUriCacheEntry->ScavengerTicks == 0
  1167. : pUriCacheEntry->ScavengerTicks > 0);
  1168. ASSERT(ZombieCount < g_UriCacheStats.ZombieCount);
  1169. }
  1170. ASSERT(ZombieCount == g_UriCacheStats.ZombieCount);
  1171. }
  1172. #endif // DBG
  1173. UlReleaseResource(&g_pUlNonpagedData->UriZombieResource);
  1174. UL_FREE_POOL_WITH_SIG(pUriFilterContext, URI_FILTER_CONTEXT_POOL_TAG);
  1175. // Now purge those entries, if there are no outstanding references
  1176. UlpClearZombieList();
  1177. } // UlpZombifyList
  1178. /***************************************************************************++
  1179. Routine Description:
  1180. Removes entries based on a caller specified filter. The caller
  1181. provides a boolean function which takes a cache entry as a
  1182. parameter. The function will be called with each item in the cache.
  1183. The function should conclude with a call to UlpZombifyEntry, passing
  1184. in whether or not the item should be deleted. See sample usage
  1185. elsewhere in this file. The deletion of the entries is deferred
  1186. Arguments:
  1187. pFilterRoutine - A pointer to the filter function
  1188. pCallerContext - a parameter to the filter function
  1189. --***************************************************************************/
  1190. VOID
  1191. UlpFilteredFlushUriCache(
  1192. IN PUL_URI_FILTER pFilterRoutine,
  1193. IN PVOID pCallerContext,
  1194. IN PWSTR pUri,
  1195. IN ULONG Length
  1196. )
  1197. {
  1198. UlpFilteredFlushUriCacheWorker( pFilterRoutine,
  1199. pCallerContext,
  1200. pUri,
  1201. Length,
  1202. FALSE );
  1203. } // UlpFilteredFlushUriCache
  1204. /***************************************************************************++
  1205. Routine Description:
  1206. Removes entries based on a caller specified filter. The caller
  1207. provides a boolean function which takes a cache entry as a
  1208. parameter. The function will be called with each item in the cache.
  1209. The function should conclude with a call to UlpZombifyEntry, passing
  1210. in whether or not the item should be deleted. See sample usage
  1211. elsewhere in this file. The deletion of the entries is completed inline
  1212. Arguments:
  1213. pFilterRoutine - A pointer to the filter function
  1214. pCallerContext - a parameter to the filter function
  1215. --***************************************************************************/
  1216. VOID
  1217. UlpFilteredFlushUriCacheInline(
  1218. IN PUL_URI_FILTER pFilterRoutine,
  1219. IN PVOID pCallerContext,
  1220. IN PWSTR pUri,
  1221. IN ULONG Length
  1222. )
  1223. {
  1224. UlpFilteredFlushUriCacheWorker( pFilterRoutine,
  1225. pCallerContext,
  1226. pUri,
  1227. Length,
  1228. TRUE );
  1229. } // UlpFilteredFlushUriCacheInline
  1230. /***************************************************************************++
  1231. Routine Description:
  1232. Removes entries based on a caller specified filter. The caller
  1233. provides a boolean function which takes a cache entry as a
  1234. parameter. The function will be called with each item in the cache.
  1235. The function should conclude with a call to UlpZombifyEntry, passing
  1236. in whether or not the item should be deleted. See sample usage
  1237. elsewhere in this file.
  1238. Arguments:
  1239. pFilterRoutine - A pointer to the filter function
  1240. pCallerContext - a parameter to the filter function
  1241. InlineFlush - If FALSE, queue a work item to delete entries,
  1242. If TRUE, delete them now
  1243. --***************************************************************************/
  1244. VOID
  1245. UlpFilteredFlushUriCacheWorker(
  1246. IN PUL_URI_FILTER pFilterRoutine,
  1247. IN PVOID pCallerContext,
  1248. IN PWSTR pUri,
  1249. IN ULONG Length,
  1250. IN BOOLEAN InlineFlush
  1251. )
  1252. {
  1253. PURI_FILTER_CONTEXT pUriFilterContext;
  1254. ULONG ZombieCount = 0;
  1255. //
  1256. // sanity check
  1257. //
  1258. PAGED_CODE();
  1259. ASSERT( NULL != pFilterRoutine );
  1260. //
  1261. // Perfmon counters
  1262. //
  1263. UlIncCounter(HttpGlobalCounterUriCacheFlushes);
  1264. //
  1265. // Short-circuit if the hashtable is empty. Traversing the entire
  1266. // hashtable is expensive.
  1267. //
  1268. if (0 == g_UriCacheStats.UriCount)
  1269. {
  1270. UlTrace(URI_CACHE,
  1271. ("Http!UlpFilteredFlushUriCache(filt=%p, caller ctxt=%p): "
  1272. "Not flushing because UriCount==0.\n",
  1273. pFilterRoutine, pCallerContext
  1274. ));
  1275. return;
  1276. }
  1277. pUriFilterContext = UL_ALLOCATE_STRUCT(
  1278. NonPagedPool,
  1279. URI_FILTER_CONTEXT,
  1280. URI_FILTER_CONTEXT_POOL_TAG);
  1281. if (pUriFilterContext == NULL)
  1282. return;
  1283. UlInitializeWorkItem(&pUriFilterContext->WorkItem);
  1284. pUriFilterContext->Signature = URI_FILTER_CONTEXT_POOL_TAG;
  1285. pUriFilterContext->ZombieCount = 0;
  1286. InitializeListHead(&pUriFilterContext->ZombieListHead);
  1287. pUriFilterContext->pCallerContext = pCallerContext;
  1288. KeQuerySystemTime(&pUriFilterContext->Now);
  1289. //
  1290. // Store the Uri Info for the recursive Uri Flushes.
  1291. //
  1292. if (pUri && Length)
  1293. {
  1294. pUriFilterContext->UriKey.Hash = 0;
  1295. pUriFilterContext->UriKey.Length = Length;
  1296. pUriFilterContext->UriKey.pUri = pUri;
  1297. pUriFilterContext->UriKey.pPath = NULL;
  1298. }
  1299. else
  1300. {
  1301. pUriFilterContext->UriKey.Hash = 0;
  1302. pUriFilterContext->UriKey.Length = 0;
  1303. pUriFilterContext->UriKey.pUri = NULL;
  1304. pUriFilterContext->UriKey.pPath = NULL;
  1305. }
  1306. UlTrace(URI_CACHE, (
  1307. "Http!UlpFilteredFlushUriCache("
  1308. "filt=%p, filter ctxt=%p, caller ctxt=%p)\n",
  1309. pFilterRoutine, pUriFilterContext, pCallerContext
  1310. ));
  1311. if (IS_VALID_HASHTABLE(&g_UriCacheTable))
  1312. {
  1313. ZombieCount = UlFilterFlushHashTable(
  1314. &g_UriCacheTable,
  1315. pFilterRoutine,
  1316. pUriFilterContext
  1317. );
  1318. ASSERT(ZombieCount == pUriFilterContext->ZombieCount);
  1319. if (0 != ZombieCount)
  1320. {
  1321. UlAddCounter(HttpGlobalCounterTotalFlushedUris, ZombieCount);
  1322. if( InlineFlush ) {
  1323. UL_CALL_PASSIVE(
  1324. &pUriFilterContext->WorkItem,
  1325. UlpZombifyList
  1326. );
  1327. } else {
  1328. UL_QUEUE_WORK_ITEM(
  1329. &pUriFilterContext->WorkItem,
  1330. UlpZombifyList
  1331. );
  1332. }
  1333. }
  1334. else
  1335. {
  1336. UL_FREE_POOL_WITH_SIG(pUriFilterContext,
  1337. URI_FILTER_CONTEXT_POOL_TAG);
  1338. }
  1339. UlTrace(URI_CACHE, (
  1340. "Http!UlpFilteredFlushUriCache(filt=%p, caller ctxt=%p)"
  1341. " Zombified: %d\n",
  1342. pFilterRoutine,
  1343. pCallerContext,
  1344. ZombieCount
  1345. ));
  1346. }
  1347. } // UlpFilteredFlushUriCacheWorker
  1348. /***************************************************************************++
  1349. Routine Description:
  1350. Scans the zombie list for entries whose refcount has dropped to "zero".
  1351. (The calling routine is generally expected to have added a reference
  1352. (and set the ZombieAddReffed field within the entries), so that
  1353. otherwise unreferenced entries will actually have a refcount of one. It
  1354. works this way because we don't want the scavenger directly triggering
  1355. calls to UlpDestroyUriCacheEntry)
  1356. --***************************************************************************/
  1357. VOID
  1358. UlpClearZombieList(
  1359. VOID
  1360. )
  1361. {
  1362. ULONG ZombiesFreed = 0;
  1363. ULONG ZombiesSpared = 0;
  1364. PLIST_ENTRY pCurrent;
  1365. //
  1366. // sanity check
  1367. //
  1368. PAGED_CODE();
  1369. UlAcquireResourceExclusive(&g_pUlNonpagedData->UriZombieResource, TRUE);
  1370. pCurrent = g_ZombieListHead.Flink;
  1371. while (pCurrent != &g_ZombieListHead)
  1372. {
  1373. PUL_URI_CACHE_ENTRY pUriCacheEntry
  1374. = CONTAINING_RECORD(pCurrent, UL_URI_CACHE_ENTRY, ZombieListEntry);
  1375. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1376. ASSERT(pUriCacheEntry->Zombie);
  1377. //
  1378. // get next entry now, because we might destroy this one
  1379. //
  1380. pCurrent = pCurrent->Flink;
  1381. //
  1382. // ReferenceCount is modified with interlocked ops, but in
  1383. // this case we know the ReferenceCount can't go up on
  1384. // a Zombie, and if an entry hits one just after we look
  1385. // at it, we'll just get it on the next pass
  1386. //
  1387. if (pUriCacheEntry->ZombieAddReffed)
  1388. {
  1389. BOOLEAN LastRef = (BOOLEAN) (pUriCacheEntry->ReferenceCount == 1);
  1390. if (LastRef)
  1391. {
  1392. RemoveEntryList(&pUriCacheEntry->ZombieListEntry);
  1393. pUriCacheEntry->ZombieListEntry.Flink = NULL;
  1394. ++ ZombiesFreed;
  1395. ASSERT(g_UriCacheStats.ZombieCount > 0);
  1396. -- g_UriCacheStats.ZombieCount;
  1397. }
  1398. else
  1399. {
  1400. // track age of zombie
  1401. ++ pUriCacheEntry->ScavengerTicks;
  1402. ++ ZombiesSpared;
  1403. }
  1404. pUriCacheEntry->ZombieAddReffed = FALSE;
  1405. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, UNZOMBIFY);
  1406. // NOTE: ref can go to zero, so ZombiesSpared may be wrong.
  1407. }
  1408. else
  1409. {
  1410. ASSERT(pUriCacheEntry->ScavengerTicks > 0);
  1411. // track age of zombie
  1412. ++ pUriCacheEntry->ScavengerTicks;
  1413. ++ ZombiesSpared;
  1414. if (pUriCacheEntry->ScavengerTicks > ZOMBIE_AGE_THRESHOLD)
  1415. {
  1416. UlTrace(URI_CACHE, (
  1417. "Http!UlpClearZombieList()\n"
  1418. " WARNING: %p '%ls' (refs = %d) "
  1419. "has been a zombie for %d ticks!\n",
  1420. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  1421. pUriCacheEntry->ReferenceCount,
  1422. pUriCacheEntry->ScavengerTicks
  1423. ));
  1424. }
  1425. }
  1426. }
  1427. ASSERT((g_UriCacheStats.ZombieCount == 0)
  1428. == IsListEmpty(&g_ZombieListHead));
  1429. UlReleaseResource(&g_pUlNonpagedData->UriZombieResource);
  1430. UlTrace(URI_CACHE,
  1431. ("Http!UlpClearZombieList(): Freed = %d, Remaining = %d.\n\n",
  1432. ZombiesFreed,
  1433. ZombiesSpared
  1434. ));
  1435. } // UlpClearZombieList
  1436. /***************************************************************************++
  1437. Routine Description:
  1438. Frees a URI entry to the pool. Removes references to other objects.
  1439. Arguments:
  1440. pTracker - Supplies the UL_READ_TRACKER to manipulate.
  1441. --***************************************************************************/
  1442. VOID
  1443. UlpDestroyUriCacheEntry(
  1444. PUL_URI_CACHE_ENTRY pUriCacheEntry
  1445. )
  1446. {
  1447. NTSTATUS Status;
  1448. //
  1449. // Sanity check
  1450. //
  1451. PAGED_CODE();
  1452. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1453. // CODEWORK: real cleanup will need to release
  1454. // config & process references.
  1455. ASSERT(0 == pUriCacheEntry->ReferenceCount);
  1456. UlTrace(URI_CACHE,
  1457. ("Http!UlpDestroyUriCacheEntry: Entry %p, '%ls', Refs=%d\n",
  1458. pUriCacheEntry, pUriCacheEntry->UriKey.pUri,
  1459. pUriCacheEntry->ReferenceCount
  1460. ));
  1461. //
  1462. // Release the UL_URL_CONFIG_GROUP_INFO block
  1463. //
  1464. if (IS_RESPONSE_CACHE_ENTRY(pUriCacheEntry))
  1465. {
  1466. Status = UlConfigGroupInfoRelease(&pUriCacheEntry->ConfigInfo);
  1467. ASSERT(NT_SUCCESS(Status));
  1468. }
  1469. else
  1470. {
  1471. ASSERT(IS_FRAGMENT_CACHE_ENTRY(pUriCacheEntry));
  1472. ASSERT(!IS_VALID_URL_CONFIG_GROUP_INFO(&pUriCacheEntry->ConfigInfo));
  1473. }
  1474. //
  1475. // Remove from g_ZombieListHead if neccessary
  1476. //
  1477. if (pUriCacheEntry->ZombieListEntry.Flink != NULL)
  1478. {
  1479. ASSERT(pUriCacheEntry->Zombie);
  1480. ASSERT(! pUriCacheEntry->ZombieAddReffed);
  1481. UlAcquireResourceExclusive(
  1482. &g_pUlNonpagedData->UriZombieResource,
  1483. TRUE);
  1484. if (pUriCacheEntry->ZombieListEntry.Flink != NULL)
  1485. {
  1486. RemoveEntryList(&pUriCacheEntry->ZombieListEntry);
  1487. ASSERT(g_UriCacheStats.ZombieCount > 0);
  1488. -- g_UriCacheStats.ZombieCount;
  1489. }
  1490. UlReleaseResource(&g_pUlNonpagedData->UriZombieResource);
  1491. }
  1492. UlFreeCacheEntry( pUriCacheEntry );
  1493. } // UlpDestroyUriCacheEntry
  1494. /***************************************************************************++
  1495. Routine Description:
  1496. Looks through the cache for expired entries to put on the zombie list,
  1497. and then empties out the list. Increments ScavengerTicks of each entry
  1498. Arguments:
  1499. Age - #Scavenger calls since last periodic cleanup
  1500. --***************************************************************************/
  1501. VOID
  1502. UlPeriodicCacheScavenger(
  1503. ULONG Age
  1504. )
  1505. {
  1506. PAGED_CODE();
  1507. UlpFilteredFlushUriCacheInline(UlpFlushFilterPeriodicScavenger,
  1508. &Age, NULL, 0);
  1509. } // UlPeriodicScavenger
  1510. /***************************************************************************++
  1511. Routine Description:
  1512. A filter for UlPeriodicCacheScavenger. Called by UlpFilteredFlushUriCache.
  1513. Increments pUriCacheEntry->ScavengerTicks
  1514. Arguments:
  1515. pUriCacheEntry - the entry to check
  1516. pContext - Has pointer to the max age in the pCallerContext field
  1517. --***************************************************************************/
  1518. UL_CACHE_PREDICATE
  1519. UlpFlushFilterPeriodicScavenger(
  1520. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1521. IN PVOID pContext
  1522. )
  1523. {
  1524. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  1525. BOOLEAN ToZombify;
  1526. ULONG Age;
  1527. //
  1528. // Sanity check
  1529. //
  1530. PAGED_CODE();
  1531. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1532. ASSERT( pUriFilterContext != NULL
  1533. && URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature
  1534. && pUriFilterContext->pCallerContext != NULL );
  1535. Age = *((PULONG)pUriFilterContext->pCallerContext);
  1536. ToZombify = (BOOLEAN) (pUriCacheEntry->ScavengerTicks > Age);
  1537. pUriCacheEntry->ScavengerTicks = 1; // reset to 0 on cache hit
  1538. //
  1539. // Check for expiration time as well
  1540. //
  1541. if (!ToZombify &&
  1542. HttpCachePolicyTimeToLive == pUriCacheEntry->CachePolicy.Policy )
  1543. {
  1544. ASSERT( 0 != pUriCacheEntry->ExpirationTime.QuadPart );
  1545. ToZombify =
  1546. (BOOLEAN) (pUriFilterContext->Now.QuadPart >
  1547. pUriCacheEntry->ExpirationTime.QuadPart);
  1548. }
  1549. return UlpZombifyEntry(
  1550. ToZombify,
  1551. FALSE,
  1552. pUriCacheEntry,
  1553. pUriFilterContext
  1554. );
  1555. } // UlpFlushFilterPeriodicScavenger
  1556. /***************************************************************************++
  1557. Routine Description:
  1558. Purges entries from the cache until a specified amount of memory is
  1559. reclaimed
  1560. Arguments:
  1561. Blocks - Number of 8-byte blocks to Reclaim
  1562. Age - # Scavenger calls since past periodic cleanup
  1563. --***************************************************************************/
  1564. VOID
  1565. UlTrimCache(
  1566. IN ULONG_PTR Pages,
  1567. IN ULONG Age
  1568. )
  1569. {
  1570. LONG_PTR PagesTarget;
  1571. UL_CACHE_TRIM_FILTER_CONTEXT FilterContext;
  1572. ASSERT((LONG)Pages > 0);
  1573. ASSERT((LONG)Age > 0);
  1574. PagesTarget = UlGetHashTablePages() - Pages;
  1575. if(PagesTarget < 0) {
  1576. PagesTarget = 0;
  1577. }
  1578. FilterContext.Pages = Pages;
  1579. FilterContext.Age = Age;
  1580. while((FilterContext.Pages > 0) && (FilterContext.Age >= 0)
  1581. && ((ULONG)PagesTarget < UlGetHashTablePages())) {
  1582. UlTraceVerbose(URI_CACHE, ("UlTrimCache: Age %d Target %d\n", FilterContext.Age, FilterContext.Pages));
  1583. UlpFilteredFlushUriCacheInline( UlpFlushFilterTrimCache, &FilterContext, NULL, 0 );
  1584. FilterContext.Age--;
  1585. }
  1586. UlTraceVerbose(URI_CACHE, ("UlTrimCache: Finished: Age %d Pages %d\n", FilterContext.Age, FilterContext.Pages));
  1587. UlpFilteredFlushUriCacheInline( UlpFlushFilterIncScavengerTicks, NULL, NULL, 0 );
  1588. } // UlTrimCache
  1589. /***************************************************************************++
  1590. Routine Description:
  1591. A filter for UlTrimCache. Called by UlpFilteredFlushUriCache.
  1592. Arguments:
  1593. pUriCacheEntry - the entry to check
  1594. pContext - Has a pointer to pCallerContext
  1595. pCallerContext[0] = Blocks to trim
  1596. pCallerContext[1] = Current Age
  1597. --***************************************************************************/
  1598. UL_CACHE_PREDICATE
  1599. UlpFlushFilterTrimCache(
  1600. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1601. IN PVOID pContext
  1602. )
  1603. {
  1604. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  1605. ULONG MinimumAge;
  1606. ULONG_PTR PagesReclaimed;
  1607. PUL_CACHE_TRIM_FILTER_CONTEXT FilterContext;
  1608. UL_CACHE_PREDICATE ToZombify;
  1609. // Sanity check
  1610. PAGED_CODE();
  1611. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1612. ASSERT( pUriFilterContext != NULL
  1613. && URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature
  1614. && pUriFilterContext->pCallerContext != NULL );
  1615. FilterContext = (PUL_CACHE_TRIM_FILTER_CONTEXT) pUriFilterContext->pCallerContext;
  1616. if(FilterContext->Pages <= 0) {
  1617. return ULC_ABORT;
  1618. }
  1619. ASSERT( FilterContext->Pages > 0 );
  1620. ASSERT( (LONG)FilterContext->Age >= 0 );
  1621. MinimumAge = FilterContext->Age;
  1622. ToZombify = UlpZombifyEntry(
  1623. (BOOLEAN) (pUriCacheEntry->ScavengerTicks >= MinimumAge),
  1624. FALSE,
  1625. pUriCacheEntry,
  1626. pUriFilterContext
  1627. );
  1628. if(ToZombify == ULC_DELETE) {
  1629. PagesReclaimed = pUriCacheEntry->NumPages;
  1630. FilterContext->Pages -= PagesReclaimed;
  1631. }
  1632. return ToZombify;
  1633. } // UlpFlushFilterTrimCache
  1634. /***************************************************************************++
  1635. Routine Description:
  1636. Increment Scavenger Ticks
  1637. Arguments:
  1638. pUriCacheEntry - the entry to check
  1639. pContext - ignored
  1640. --***************************************************************************/
  1641. UL_CACHE_PREDICATE
  1642. UlpFlushFilterIncScavengerTicks(
  1643. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1644. IN PVOID pContext
  1645. )
  1646. {
  1647. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  1648. PAGED_CODE();
  1649. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1650. ASSERT( pUriFilterContext != NULL
  1651. && URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature
  1652. && pUriFilterContext->pCallerContext == NULL );
  1653. pUriCacheEntry->ScavengerTicks++;
  1654. return UlpZombifyEntry(
  1655. FALSE,
  1656. FALSE,
  1657. pUriCacheEntry,
  1658. pUriFilterContext
  1659. );
  1660. } // UlpFlushFilterIncScavengerTicks
  1661. /***************************************************************************++
  1662. Routine Description:
  1663. Looks through the cache for centralized logged entries. Mark them all
  1664. NOT logged.
  1665. This funtion is normally called when a binary log file is recycled
  1666. or reconfigured.
  1667. Arguments:
  1668. pContext - When we enable multiple binary logs, we must only mess with
  1669. those cache entries logged to this specific binary log file
  1670. until then discarted.
  1671. --***************************************************************************/
  1672. VOID
  1673. UlClearCentralizedLogged(
  1674. IN PVOID pContext
  1675. )
  1676. {
  1677. PAGED_CODE();
  1678. UNREFERENCED_PARAMETER(pContext);
  1679. ASSERT(!g_UriCacheConfig.EnableCache
  1680. || IS_VALID_HASHTABLE(&g_UriCacheTable));
  1681. if (g_UriCacheConfig.EnableCache)
  1682. {
  1683. UlTrace2Either(BINARY_LOGGING, URI_CACHE,(
  1684. "Http!UlClearCentralizedLogged()\n"));
  1685. UlpFilteredFlushUriCache(
  1686. UlpFlushFilterClearCentralizedLogged,
  1687. NULL,
  1688. NULL,
  1689. 0
  1690. );
  1691. }
  1692. } // UlClearCentralizedLogged
  1693. /***************************************************************************++
  1694. Routine Description:
  1695. Basically a fake filter, which will always returns FALSE. But it updates
  1696. the CentralizedLogged flag on the entry.
  1697. Arguments:
  1698. pUriCacheEntry - the entry to check
  1699. pContext - ignored
  1700. --***************************************************************************/
  1701. UL_CACHE_PREDICATE
  1702. UlpFlushFilterClearCentralizedLogged(
  1703. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1704. IN PVOID pContext
  1705. )
  1706. {
  1707. #if DBG
  1708. PURI_FILTER_CONTEXT pUriFilterContext = (PURI_FILTER_CONTEXT) pContext;
  1709. #else
  1710. UNREFERENCED_PARAMETER(pContext);
  1711. #endif
  1712. //
  1713. // Sanity check
  1714. //
  1715. PAGED_CODE();
  1716. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1717. ASSERT( pUriFilterContext != NULL
  1718. && URI_FILTER_CONTEXT_POOL_TAG == pUriFilterContext->Signature
  1719. && pUriFilterContext->pCallerContext == NULL );
  1720. InterlockedExchange((PLONG) &pUriCacheEntry->BinaryIndexWritten, 0);
  1721. //
  1722. // Updated the flag. Just bail out.
  1723. //
  1724. return ULC_NO_ACTION;
  1725. } // UlpFlushFilterClearCentralizedLogged
  1726. /***************************************************************************++
  1727. Routine Description:
  1728. Determine if the Translate header is present AND has a value of 'F' or 'f'.
  1729. Arguments:
  1730. pRequest - Supplies the request to query.
  1731. Return Value:
  1732. BOOLEAN - TRUE if "Translate: F", FALSE otherwise
  1733. --***************************************************************************/
  1734. BOOLEAN
  1735. UlpQueryTranslateHeader(
  1736. IN PUL_INTERNAL_REQUEST pRequest
  1737. )
  1738. {
  1739. BOOLEAN ret = FALSE;
  1740. if ( pRequest->HeaderValid[HttpHeaderTranslate] )
  1741. {
  1742. PUCHAR pValue = pRequest->Headers[HttpHeaderTranslate].pHeader;
  1743. ASSERT(NULL != pValue);
  1744. if (('f' == pValue[0] || 'F' == pValue[0]) && '\0' == pValue[1])
  1745. {
  1746. ASSERT(pRequest->Headers[HttpHeaderTranslate].HeaderLength == 1);
  1747. ret = TRUE;
  1748. }
  1749. }
  1750. return ret;
  1751. } // UlpQueryTranslateHeader
  1752. /***************************************************************************++
  1753. Routine Description:
  1754. Determine if the Expect header is present AND has a value
  1755. of EXACTLY "100-continue".
  1756. Arguments:
  1757. pRequest - Supplies the request to check.
  1758. Return Value:
  1759. BOOLEAN - TRUE if "Expect: 100-continue" or not present, FALSE otherwise
  1760. --***************************************************************************/
  1761. BOOLEAN
  1762. UlpQueryExpectHeader(
  1763. IN PUL_INTERNAL_REQUEST pRequest
  1764. )
  1765. {
  1766. BOOLEAN ret = TRUE;
  1767. if ( pRequest->HeaderValid[HttpHeaderExpect] )
  1768. {
  1769. PCSTR pValue = (PCSTR) pRequest->Headers[HttpHeaderExpect].pHeader;
  1770. ASSERT(NULL != pValue);
  1771. if ((strlen(pValue) != HTTP_CONTINUE_LENGTH) ||
  1772. (0 != strncmp(pValue, HTTP_100_CONTINUE, HTTP_CONTINUE_LENGTH)))
  1773. {
  1774. ret = FALSE;
  1775. }
  1776. }
  1777. return ret;
  1778. } // UlQueryExpectHeader
  1779. /***************************************************************************++
  1780. Routine Description:
  1781. Add a reference on a cache entry
  1782. Arguments:
  1783. pUriCacheEntry - the entry to addref
  1784. --***************************************************************************/
  1785. LONG
  1786. UlAddRefUriCacheEntry(
  1787. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1788. IN REFTRACE_ACTION Action
  1789. REFERENCE_DEBUG_FORMAL_PARAMS
  1790. )
  1791. {
  1792. LONG RefCount;
  1793. UNREFERENCED_PARAMETER(Action);
  1794. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1795. RefCount = InterlockedIncrement(&pUriCacheEntry->ReferenceCount);
  1796. WRITE_REF_TRACE_LOG(
  1797. g_pUriTraceLog,
  1798. Action,
  1799. RefCount,
  1800. pUriCacheEntry,
  1801. pFileName,
  1802. LineNumber
  1803. );
  1804. UlTrace(URI_CACHE, (
  1805. "Http!UlAddRefUriCacheEntry: (%p, '%ls', refcount=%d)\n",
  1806. pUriCacheEntry, pUriCacheEntry->UriKey.pUri, RefCount
  1807. ));
  1808. ASSERT(RefCount > 0);
  1809. return RefCount;
  1810. } // UlAddRefUriCacheEntry
  1811. /***************************************************************************++
  1812. Routine Description:
  1813. Release a reference on a cache entry
  1814. Arguments:
  1815. pUriCacheEntry - the entry to release
  1816. --***************************************************************************/
  1817. LONG
  1818. UlReleaseUriCacheEntry(
  1819. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1820. IN REFTRACE_ACTION Action
  1821. REFERENCE_DEBUG_FORMAL_PARAMS
  1822. )
  1823. {
  1824. LONG RefCount;
  1825. UNREFERENCED_PARAMETER(Action);
  1826. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1827. RefCount = InterlockedDecrement(&pUriCacheEntry->ReferenceCount);
  1828. WRITE_REF_TRACE_LOG(
  1829. g_pUriTraceLog,
  1830. Action,
  1831. RefCount,
  1832. pUriCacheEntry,
  1833. pFileName,
  1834. LineNumber
  1835. );
  1836. UlTrace(URI_CACHE, (
  1837. "Http!UlReleaseUriCacheEntry: (%p, '%ls', refcount=%d)\n",
  1838. pUriCacheEntry, pUriCacheEntry->UriKey.pUri, RefCount
  1839. ));
  1840. ASSERT(RefCount >= 0);
  1841. if (RefCount == 0)
  1842. {
  1843. if (pUriCacheEntry->Cached)
  1844. UlpRemoveEntryStats(pUriCacheEntry);
  1845. UlpDestroyUriCacheEntry(pUriCacheEntry);
  1846. }
  1847. return RefCount;
  1848. } // UlReleaseUriCacheEntry
  1849. /***************************************************************************++
  1850. Routine Description:
  1851. UL_URI_CACHE_ENTRY pseudo-constructor. Primarily used for
  1852. AddRef and tracelogging.
  1853. Arguments:
  1854. pUriCacheEntry - the entry to initialize
  1855. Hash - Hash code of pUrl
  1856. Length - Length (in bytes) of pUrl
  1857. pUrl - Unicode URL to copy
  1858. pAbsPath - Points to the AbsPath of the Url.
  1859. pRoutingToken - Optional
  1860. RoutingTokenLength - Optional (in bytes)
  1861. --***************************************************************************/
  1862. VOID
  1863. UlInitCacheEntry(
  1864. PUL_URI_CACHE_ENTRY pUriCacheEntry,
  1865. ULONG Hash,
  1866. ULONG Length,
  1867. PCWSTR pUrl,
  1868. PCWSTR pAbsPath,
  1869. PCWSTR pRoutingToken,
  1870. USHORT RoutingTokenLength
  1871. )
  1872. {
  1873. pUriCacheEntry->Signature = UL_URI_CACHE_ENTRY_POOL_TAG;
  1874. pUriCacheEntry->ReferenceCount = 0;
  1875. pUriCacheEntry->HitCount = 1;
  1876. pUriCacheEntry->Zombie = FALSE;
  1877. pUriCacheEntry->ZombieAddReffed = FALSE;
  1878. pUriCacheEntry->ZombieListEntry.Flink = NULL;
  1879. pUriCacheEntry->ZombieListEntry.Blink = NULL;
  1880. pUriCacheEntry->Cached = FALSE;
  1881. pUriCacheEntry->ScavengerTicks = 0;
  1882. pUriCacheEntry->UriKey.Hash = Hash;
  1883. pUriCacheEntry->UriKey.Length = Length;
  1884. pUriCacheEntry->UriKey.pUri = (PWSTR) ((PCHAR)pUriCacheEntry +
  1885. ALIGN_UP(sizeof(UL_URI_CACHE_ENTRY), PVOID));
  1886. if (pRoutingToken)
  1887. {
  1888. PWSTR pUri = pUriCacheEntry->UriKey.pUri;
  1889. ASSERT(wcslen(pRoutingToken) * sizeof(WCHAR) == RoutingTokenLength);
  1890. pUriCacheEntry->UriKey.Length += RoutingTokenLength;
  1891. RtlCopyMemory(
  1892. pUri,
  1893. pRoutingToken,
  1894. RoutingTokenLength
  1895. );
  1896. RtlCopyMemory(
  1897. &pUri[RoutingTokenLength/sizeof(WCHAR)],
  1898. pUrl,
  1899. Length + sizeof(WCHAR)
  1900. );
  1901. ASSERT(wcslen(pUri) * sizeof(WCHAR) == pUriCacheEntry->UriKey.Length );
  1902. pUriCacheEntry->UriKey.pPath =
  1903. pUri + (RoutingTokenLength / sizeof(WCHAR));
  1904. UlTrace(URI_CACHE, (
  1905. "Http!UlInitCacheEntry Extended (%p = '%ls' + '%ls')\n",
  1906. pUriCacheEntry, pRoutingToken, pUrl
  1907. ));
  1908. }
  1909. else
  1910. {
  1911. RtlCopyMemory(
  1912. pUriCacheEntry->UriKey.pUri,
  1913. pUrl,
  1914. pUriCacheEntry->UriKey.Length + sizeof(WCHAR)
  1915. );
  1916. if (pAbsPath)
  1917. {
  1918. ASSERT( pAbsPath >= pUrl );
  1919. ASSERT( DIFF(pAbsPath - pUrl) <= Length );
  1920. pUriCacheEntry->UriKey.pPath =
  1921. pUriCacheEntry->UriKey.pUri + DIFF(pAbsPath - pUrl);
  1922. }
  1923. else
  1924. {
  1925. // Possibly a fragment cache entry.
  1926. pUriCacheEntry->UriKey.pPath = NULL;
  1927. }
  1928. UlTrace(URI_CACHE, (
  1929. "Http!UlInitCacheEntry (%p = '%ls')\n",
  1930. pUriCacheEntry, pUriCacheEntry->UriKey.pUri
  1931. ));
  1932. }
  1933. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CREATE);
  1934. } // UlInitCacheEntry
  1935. /***************************************************************************++
  1936. Routine Description:
  1937. Adds a fragment cache entry to the response cache database.
  1938. Arguments:
  1939. pProcess - process that is adding the fragment cache entry
  1940. pFragmentName - key of the fragment cache entry
  1941. pDataChunk - specifies the data chunk to be put into the cache entry
  1942. pCachePolicy - specifies the policy of the new fragment cache entry
  1943. Return Value:
  1944. NTSTATUS
  1945. --***************************************************************************/
  1946. NTSTATUS
  1947. UlAddFragmentToCache(
  1948. IN PUL_APP_POOL_PROCESS pProcess,
  1949. IN PUNICODE_STRING pFragmentName,
  1950. IN PHTTP_DATA_CHUNK pDataChunk,
  1951. IN PHTTP_CACHE_POLICY pCachePolicy,
  1952. IN KPROCESSOR_MODE RequestorMode
  1953. )
  1954. {
  1955. PUL_APP_POOL_OBJECT pAppPool;
  1956. PWSTR pAppPoolName;
  1957. PWSTR pEndName;
  1958. ULONG AppPoolNameLength;
  1959. PUL_URI_CACHE_ENTRY pCacheEntry;
  1960. ULONGLONG Length;
  1961. PFAST_IO_DISPATCH pFastIoDispatch;
  1962. PFILE_OBJECT pFileObject;
  1963. PDEVICE_OBJECT pDeviceObject;
  1964. FILE_STANDARD_INFORMATION FileInfo;
  1965. IO_STATUS_BLOCK IoStatusBlock;
  1966. PUCHAR pReadBuffer;
  1967. PLARGE_INTEGER pOffset;
  1968. HTTP_DATA_CHUNK LocalDataChunk;
  1969. NTSTATUS Status;
  1970. UNICODE_STRING FragmentName;
  1971. UL_URL_CONFIG_GROUP_INFO UrlInfo;
  1972. PWSTR pSanitizedUrl;
  1973. HTTP_PARSED_URL ParsedUrl;
  1974. HTTP_BYTE_RANGE ByteRange = {0,0};
  1975. //
  1976. // Validate if the data chunk can be put into the cache.
  1977. //
  1978. if (FALSE == g_UriCacheConfig.EnableCache)
  1979. {
  1980. return STATUS_NOT_SUPPORTED;
  1981. }
  1982. //
  1983. // Use a local copy of DataChunk onwards to ensure fields inside won't
  1984. // get changed.
  1985. //
  1986. LocalDataChunk = *pDataChunk;
  1987. if (HttpDataChunkFromMemory != LocalDataChunk.DataChunkType &&
  1988. HttpDataChunkFromFileHandle != LocalDataChunk.DataChunkType)
  1989. {
  1990. return STATUS_NOT_IMPLEMENTED;
  1991. }
  1992. pCacheEntry = NULL;
  1993. pFileObject = NULL;
  1994. pReadBuffer = NULL;
  1995. pSanitizedUrl = NULL;
  1996. UlInitializeUrlInfo(&UrlInfo);
  1997. //
  1998. // Validate the AppPool name of the process matches the first portion
  1999. // of the fragment name before "/".
  2000. //
  2001. __try
  2002. {
  2003. Status =
  2004. UlProbeAndCaptureUnicodeString(
  2005. pFragmentName,
  2006. RequestorMode,
  2007. &FragmentName,
  2008. 0
  2009. );
  2010. if (!NT_SUCCESS(Status))
  2011. {
  2012. goto end;
  2013. }
  2014. //
  2015. // The fragment name convention is different for a transient app
  2016. // (where AppPool name is "") and a normal WAS-type of app. The
  2017. // former starts with a URL that the AppPool listens, the latter
  2018. // starts using the AppPool name itself. The name validation has
  2019. // to be done differently as well.
  2020. //
  2021. //
  2022. pAppPool = pProcess->pAppPool;
  2023. if (pAppPool->NameLength)
  2024. {
  2025. pAppPoolName = FragmentName.Buffer;
  2026. pEndName = wcschr(pAppPoolName, L'/');
  2027. if (!pEndName)
  2028. {
  2029. Status = STATUS_INVALID_PARAMETER;
  2030. goto end;
  2031. }
  2032. AppPoolNameLength = DIFF((PUCHAR)pEndName - (PUCHAR)pAppPoolName);
  2033. if (pAppPool->NameLength != AppPoolNameLength ||
  2034. _wcsnicmp(
  2035. pAppPool->pName,
  2036. pAppPoolName,
  2037. AppPoolNameLength / sizeof(WCHAR)
  2038. ))
  2039. {
  2040. Status = STATUS_INVALID_PARAMETER;
  2041. goto end;
  2042. }
  2043. }
  2044. else
  2045. {
  2046. //
  2047. // Do a reverse-lookup to find out the AppPool that listens on the
  2048. // URL/FragmentName passed in. Need to sanitize the URL because
  2049. // of IP based URLs get internally expanded when stored in CG.
  2050. // For instance, when stored, http://127.0.0.1:80/Test/ becomes
  2051. // http://127.0.0.1:80:127.0.0.1/Test/.
  2052. //
  2053. Status = UlSanitizeUrl(
  2054. FragmentName.Buffer,
  2055. FragmentName.Length / sizeof(WCHAR),
  2056. FALSE,
  2057. &pSanitizedUrl,
  2058. &ParsedUrl
  2059. );
  2060. if (!NT_SUCCESS(Status))
  2061. {
  2062. goto end;
  2063. }
  2064. ASSERT(pSanitizedUrl);
  2065. Status = UlGetConfigGroupInfoForUrl(
  2066. pSanitizedUrl,
  2067. NULL,
  2068. &UrlInfo
  2069. );
  2070. if (!NT_SUCCESS(Status))
  2071. {
  2072. goto end;
  2073. }
  2074. if (UrlInfo.pAppPool != pAppPool)
  2075. {
  2076. Status = STATUS_INVALID_ID_AUTHORITY;
  2077. goto end;
  2078. }
  2079. }
  2080. if (HttpDataChunkFromMemory == LocalDataChunk.DataChunkType)
  2081. {
  2082. //
  2083. // Cache a FromMemory data chunk. ContentLength is BufferLength.
  2084. //
  2085. Length = LocalDataChunk.FromMemory.BufferLength;
  2086. }
  2087. else
  2088. {
  2089. //
  2090. // Cache a FromFileHandle data chunk. ContentLength is the size
  2091. // of the file.
  2092. //
  2093. Status = ObReferenceObjectByHandle(
  2094. LocalDataChunk.FromFileHandle.FileHandle,
  2095. FILE_READ_ACCESS,
  2096. *IoFileObjectType,
  2097. UserMode,
  2098. (PVOID *) &pFileObject,
  2099. NULL
  2100. );
  2101. if (!NT_SUCCESS(Status))
  2102. {
  2103. goto end;
  2104. }
  2105. //
  2106. // Non-cached reads are not supported since they require
  2107. // us to align both file offset and length.
  2108. //
  2109. if (!(pFileObject->Flags & FO_CACHE_SUPPORTED))
  2110. {
  2111. Status = STATUS_NOT_SUPPORTED;
  2112. goto end;
  2113. }
  2114. pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
  2115. pFastIoDispatch = pDeviceObject->DriverObject->FastIoDispatch;
  2116. if (!pFastIoDispatch ||
  2117. pFastIoDispatch->SizeOfFastIoDispatch <=
  2118. FIELD_OFFSET(FAST_IO_DISPATCH, FastIoQueryStandardInfo) ||
  2119. !pFastIoDispatch->FastIoQueryStandardInfo ||
  2120. !pFastIoDispatch->FastIoQueryStandardInfo(
  2121. pFileObject,
  2122. TRUE,
  2123. &FileInfo,
  2124. &IoStatusBlock,
  2125. pDeviceObject
  2126. ))
  2127. {
  2128. Status = ZwQueryInformationFile(
  2129. LocalDataChunk.FromFileHandle.FileHandle,
  2130. &IoStatusBlock,
  2131. &FileInfo,
  2132. sizeof(FILE_STANDARD_INFORMATION),
  2133. FileStandardInformation
  2134. );
  2135. if (!NT_SUCCESS(Status))
  2136. {
  2137. goto end;
  2138. }
  2139. }
  2140. Status = UlSanitizeFileByteRange(
  2141. &LocalDataChunk.FromFileHandle.ByteRange,
  2142. &ByteRange,
  2143. FileInfo.EndOfFile.QuadPart
  2144. );
  2145. if (!NT_SUCCESS(Status))
  2146. {
  2147. goto end;
  2148. }
  2149. Length = ByteRange.Length.QuadPart;
  2150. }
  2151. //
  2152. // It doesn't make sense to add a zero-length fragment.
  2153. //
  2154. if (!Length)
  2155. {
  2156. Status = STATUS_INVALID_PARAMETER;
  2157. goto end;
  2158. }
  2159. //
  2160. // Enforce the MaxUriBytes limit.
  2161. //
  2162. if (Length > g_UriCacheConfig.MaxUriBytes)
  2163. {
  2164. Status = STATUS_NOT_SUPPORTED;
  2165. goto end;
  2166. }
  2167. //
  2168. // Build a fragment cache entry.
  2169. //
  2170. Status = UlpCreateFragmentCacheEntry(
  2171. pProcess,
  2172. FragmentName.Buffer,
  2173. FragmentName.Length,
  2174. (ULONG) Length,
  2175. pCachePolicy,
  2176. &pCacheEntry
  2177. );
  2178. if (!NT_SUCCESS(Status))
  2179. {
  2180. goto end;
  2181. }
  2182. ASSERT(pCacheEntry);
  2183. //
  2184. // Fill up the content of the fragment cache entry.
  2185. //
  2186. if (HttpDataChunkFromMemory == LocalDataChunk.DataChunkType)
  2187. {
  2188. UlProbeForRead(
  2189. LocalDataChunk.FromMemory.pBuffer,
  2190. LocalDataChunk.FromMemory.BufferLength,
  2191. sizeof(PVOID),
  2192. RequestorMode
  2193. );
  2194. if (FALSE == UlCacheEntrySetData(
  2195. pCacheEntry,
  2196. (PUCHAR) LocalDataChunk.FromMemory.pBuffer,
  2197. (ULONG) Length,
  2198. 0
  2199. ))
  2200. {
  2201. Status = STATUS_INSUFFICIENT_RESOURCES;
  2202. goto end;
  2203. }
  2204. }
  2205. else
  2206. {
  2207. pReadBuffer = (PUCHAR) MmMapLockedPagesSpecifyCache(
  2208. pCacheEntry->pMdl,
  2209. KernelMode,
  2210. MmCached,
  2211. NULL,
  2212. FALSE,
  2213. LowPagePriority
  2214. );
  2215. if (pReadBuffer)
  2216. {
  2217. pOffset = (PLARGE_INTEGER)
  2218. &LocalDataChunk.FromFileHandle.ByteRange.StartingOffset;
  2219. //
  2220. // CODEWORK: support async read for file handles opened as
  2221. // non-buffered.
  2222. //
  2223. Status = ZwReadFile(
  2224. LocalDataChunk.FromFileHandle.FileHandle,
  2225. NULL,
  2226. NULL,
  2227. NULL,
  2228. &IoStatusBlock,
  2229. pReadBuffer,
  2230. (ULONG) Length,
  2231. pOffset,
  2232. NULL
  2233. );
  2234. MmUnmapLockedPages(pReadBuffer, pCacheEntry->pMdl);
  2235. }
  2236. else
  2237. {
  2238. Status = STATUS_INSUFFICIENT_RESOURCES;
  2239. }
  2240. if (!NT_SUCCESS(Status))
  2241. {
  2242. goto end;
  2243. }
  2244. }
  2245. }
  2246. __except( UL_EXCEPTION_FILTER() )
  2247. {
  2248. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  2249. goto end;
  2250. }
  2251. //
  2252. // Add the fragment cache entry.
  2253. //
  2254. Status = UlAddCacheEntry(pCacheEntry);
  2255. //
  2256. // Release the reference count of the cache entry because
  2257. // UlAddCacheEntry adds an extra reference in the success case.
  2258. //
  2259. DEREFERENCE_URI_CACHE_ENTRY(pCacheEntry, CREATE);
  2260. //
  2261. // Reset pCacheEntry so we won't double-free if UlAddCacheEntry fails.
  2262. //
  2263. pCacheEntry = NULL;
  2264. end:
  2265. UlFreeCapturedUnicodeString(&FragmentName);
  2266. UlConfigGroupInfoRelease(&UrlInfo);
  2267. if (pFileObject)
  2268. {
  2269. ObDereferenceObject(pFileObject);
  2270. }
  2271. if (!NT_SUCCESS(Status))
  2272. {
  2273. if (pCacheEntry)
  2274. {
  2275. UlFreeCacheEntry(pCacheEntry);
  2276. }
  2277. }
  2278. if (pSanitizedUrl)
  2279. {
  2280. UL_FREE_POOL(pSanitizedUrl, URL_POOL_TAG);
  2281. }
  2282. return Status;
  2283. } // UlAddFragmentToCache
  2284. /***************************************************************************++
  2285. Routine Description:
  2286. Creates a fragment cache entry.
  2287. Arguments:
  2288. pProcess - process that is adding the fragment cache entry
  2289. pFragmentName - key of the fragment cache entry
  2290. FragmentNameLength - length of the fragment name
  2291. pBuffer - data to be associated with the fragment cache entry
  2292. BufferLength - length of the data
  2293. pCachePolicy - specifies the policy of the new fragment cache entry
  2294. Return Value:
  2295. NTSTATUS
  2296. --***************************************************************************/
  2297. NTSTATUS
  2298. UlpCreateFragmentCacheEntry(
  2299. IN PUL_APP_POOL_PROCESS pProcess,
  2300. IN PWSTR pFragmentName,
  2301. IN ULONG FragmentNameLength,
  2302. IN ULONG Length,
  2303. IN PHTTP_CACHE_POLICY pCachePolicy,
  2304. OUT PUL_URI_CACHE_ENTRY *ppCacheEntry
  2305. )
  2306. {
  2307. PUL_URI_CACHE_ENTRY pCacheEntry;
  2308. ULONG Hash;
  2309. NTSTATUS Status = STATUS_SUCCESS;
  2310. //
  2311. // Sanity check.
  2312. //
  2313. PAGED_CODE();
  2314. ASSERT(pCachePolicy);
  2315. ASSERT(ppCacheEntry);
  2316. if ( HttpCachePolicyTimeToLive == pCachePolicy->Policy
  2317. && 0 == pCachePolicy->SecondsToLive )
  2318. {
  2319. // A TTL of 0 seconds doesn't make sense. Bail out.
  2320. *ppCacheEntry = NULL;
  2321. return STATUS_INVALID_PARAMETER;
  2322. }
  2323. pCacheEntry = UlAllocateCacheEntry(
  2324. FragmentNameLength + sizeof(WCHAR),
  2325. Length
  2326. );
  2327. if (pCacheEntry)
  2328. {
  2329. __try
  2330. {
  2331. //
  2332. // Initialize the cache entry.
  2333. //
  2334. Hash = HashRandomizeBits(HashStringNoCaseW(pFragmentName, 0));
  2335. UlInitCacheEntry(
  2336. pCacheEntry,
  2337. Hash,
  2338. FragmentNameLength,
  2339. pFragmentName,
  2340. NULL,
  2341. NULL,
  2342. 0
  2343. );
  2344. pCacheEntry->CachePolicy = *pCachePolicy;
  2345. if (pCachePolicy->Policy == HttpCachePolicyTimeToLive)
  2346. {
  2347. ASSERT( 0 != pCachePolicy->SecondsToLive );
  2348. KeQuerySystemTime(&pCacheEntry->ExpirationTime);
  2349. if ( pCachePolicy->SecondsToLive > C_SECS_PER_YEAR )
  2350. {
  2351. // Maximum TTL is 1 year
  2352. pCacheEntry->CachePolicy.SecondsToLive = C_SECS_PER_YEAR;
  2353. }
  2354. //
  2355. // Convert seconds to 100 nanosecond intervals (x * 10^7).
  2356. //
  2357. pCacheEntry->ExpirationTime.QuadPart +=
  2358. pCacheEntry->CachePolicy.SecondsToLive * C_NS_TICKS_PER_SEC;
  2359. }
  2360. else
  2361. {
  2362. pCacheEntry->ExpirationTime.QuadPart = 0;
  2363. }
  2364. //
  2365. // Remember who created us.
  2366. //
  2367. pCacheEntry->pProcess = pProcess;
  2368. pCacheEntry->pAppPool = pProcess->pAppPool;
  2369. //
  2370. // Generate the content of the cache entry.
  2371. //
  2372. pCacheEntry->ContentLength = Length;
  2373. }
  2374. __except( UL_EXCEPTION_FILTER() )
  2375. {
  2376. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  2377. }
  2378. }
  2379. else
  2380. {
  2381. Status = STATUS_NO_MEMORY;
  2382. }
  2383. if (!NT_SUCCESS(Status))
  2384. {
  2385. if (pCacheEntry)
  2386. {
  2387. UlFreeCacheEntry(pCacheEntry);
  2388. pCacheEntry = NULL;
  2389. }
  2390. }
  2391. *ppCacheEntry = pCacheEntry;
  2392. return Status;
  2393. } // UlpCreateFragmentCacheEntry
  2394. /***************************************************************************++
  2395. Routine Description:
  2396. Reads a fragment back from cache.
  2397. Arguments:
  2398. pProcess - process that is reading the fragment
  2399. pInputBuffer - points to a buffer that describes HTTP_READ_FRAGMENT_INFO
  2400. InputBufferLength - length of the input buffer
  2401. pOutputBuffer - points to a buffer to copy the data from the fragment
  2402. cache entry
  2403. OutputBufferLength - length of the buffer to copy
  2404. pBytesRead - optionally tells how many bytes are copied or needed
  2405. Return Value:
  2406. NTSTATUS
  2407. --***************************************************************************/
  2408. NTSTATUS
  2409. UlReadFragmentFromCache(
  2410. IN PUL_APP_POOL_PROCESS pProcess,
  2411. IN PVOID pInputBuffer,
  2412. IN ULONG InputBufferLength,
  2413. OUT PVOID pOutputBuffer,
  2414. IN ULONG OutputBufferLength,
  2415. IN KPROCESSOR_MODE RequestorMode,
  2416. OUT PULONG pBytesRead
  2417. )
  2418. {
  2419. PUL_URI_CACHE_ENTRY pCacheEntry;
  2420. PMDL pMdl;
  2421. HTTP_READ_FRAGMENT_INFO ReadInfo;
  2422. PUNICODE_STRING pFragmentName;
  2423. PHTTP_BYTE_RANGE pByteRange;
  2424. PVOID pContentBuffer;
  2425. ULONGLONG Offset;
  2426. ULONGLONG Length;
  2427. ULONGLONG ContentLength;
  2428. ULONG ReadLength;
  2429. NTSTATUS Status;
  2430. UNICODE_STRING FragmentName;
  2431. //
  2432. // Validate pInputBuffer and InputBufferLength.
  2433. //
  2434. if (!pInputBuffer || InputBufferLength < sizeof(HTTP_READ_FRAGMENT_INFO))
  2435. {
  2436. return STATUS_INVALID_PARAMETER;
  2437. }
  2438. //
  2439. // Initialization.
  2440. //
  2441. pCacheEntry = NULL;
  2442. pMdl = NULL;
  2443. RtlInitEmptyUnicodeString(&FragmentName, NULL, 0);
  2444. __try
  2445. {
  2446. //
  2447. // Capture HTTP_READ_FRAGMENT_INFO into the local ReadInfo.
  2448. //
  2449. UlProbeForRead(
  2450. pInputBuffer,
  2451. sizeof(HTTP_READ_FRAGMENT_INFO),
  2452. sizeof(PVOID),
  2453. RequestorMode
  2454. );
  2455. ReadInfo = *((PHTTP_READ_FRAGMENT_INFO) pInputBuffer);
  2456. pFragmentName = &ReadInfo.FragmentName;
  2457. pByteRange = &ReadInfo.ByteRange;
  2458. Status = UlProbeAndCaptureUnicodeString(
  2459. pFragmentName,
  2460. RequestorMode,
  2461. &FragmentName,
  2462. 0
  2463. );
  2464. if (!NT_SUCCESS(Status))
  2465. {
  2466. goto end;
  2467. }
  2468. //
  2469. // Check out the fragment cache entry based on key URL passed in.
  2470. //
  2471. Status = UlCheckoutFragmentCacheEntry(
  2472. FragmentName.Buffer,
  2473. FragmentName.Length,
  2474. pProcess,
  2475. &pCacheEntry
  2476. );
  2477. if (!NT_SUCCESS(Status))
  2478. {
  2479. goto end;
  2480. }
  2481. ASSERT(pCacheEntry);
  2482. ContentLength = pCacheEntry->ContentLength;
  2483. Offset = pByteRange->StartingOffset.QuadPart;
  2484. //
  2485. // Validate byte range for offset and length.
  2486. //
  2487. if (Offset >= ContentLength)
  2488. {
  2489. Status = STATUS_INVALID_PARAMETER;
  2490. goto end;
  2491. }
  2492. if (pByteRange->Length.QuadPart == HTTP_BYTE_RANGE_TO_EOF)
  2493. {
  2494. Length = ContentLength - Offset;
  2495. }
  2496. else
  2497. {
  2498. Length = pByteRange->Length.QuadPart;
  2499. }
  2500. if (!Length || Length > ULONG_MAX || Length > (ContentLength - Offset))
  2501. {
  2502. Status = STATUS_INVALID_PARAMETER;
  2503. goto end;
  2504. }
  2505. ASSERT((Length + Offset) <= ContentLength);
  2506. ReadLength = (ULONG) Length;
  2507. //
  2508. // Check if we have enough buffer space, if not, tell the caller the
  2509. // exact bytes needed to complete the read.
  2510. //
  2511. if (OutputBufferLength < ReadLength)
  2512. {
  2513. *pBytesRead = ReadLength;
  2514. Status = STATUS_BUFFER_OVERFLOW;
  2515. goto end;
  2516. }
  2517. //
  2518. // Build a partial MDL to read the data.
  2519. //
  2520. pMdl = UlAllocateMdl(
  2521. (PCHAR) MmGetMdlVirtualAddress(pCacheEntry->pMdl) + Offset,
  2522. ReadLength,
  2523. FALSE,
  2524. FALSE,
  2525. NULL
  2526. );
  2527. if (NULL == pMdl)
  2528. {
  2529. Status = STATUS_INSUFFICIENT_RESOURCES;
  2530. goto end;
  2531. }
  2532. IoBuildPartialMdl(
  2533. pCacheEntry->pMdl,
  2534. pMdl,
  2535. (PCHAR) MmGetMdlVirtualAddress(pCacheEntry->pMdl) + Offset,
  2536. ReadLength
  2537. );
  2538. pContentBuffer = MmGetSystemAddressForMdlSafe(
  2539. pMdl,
  2540. NormalPagePriority
  2541. );
  2542. if (NULL == pContentBuffer)
  2543. {
  2544. Status = STATUS_INSUFFICIENT_RESOURCES;
  2545. goto end;
  2546. }
  2547. //
  2548. // Copy the data from the cache entry back to the output buffer.
  2549. // UlFreeMdl unmaps the data for partial MDLs so no need to unmap
  2550. // if either copy succeeds or an exception is raised.
  2551. //
  2552. UlProbeForWrite(
  2553. pOutputBuffer,
  2554. ReadLength,
  2555. sizeof(PVOID),
  2556. RequestorMode
  2557. );
  2558. RtlCopyMemory(
  2559. pOutputBuffer,
  2560. pContentBuffer,
  2561. ReadLength
  2562. );
  2563. //
  2564. // Set how many bytes we have copied.
  2565. //
  2566. *pBytesRead = ReadLength;
  2567. Status = STATUS_SUCCESS;
  2568. }
  2569. __except( UL_EXCEPTION_FILTER() )
  2570. {
  2571. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  2572. }
  2573. end:
  2574. UlFreeCapturedUnicodeString(&FragmentName);
  2575. if (pMdl)
  2576. {
  2577. UlFreeMdl(pMdl);
  2578. }
  2579. if (pCacheEntry)
  2580. {
  2581. UlCheckinUriCacheEntry(pCacheEntry);
  2582. }
  2583. return Status;
  2584. } // UlReadFragmentFromCache
  2585. // Memory allocator front end
  2586. /***************************************************************************++
  2587. Routine Description:
  2588. Allocate a cache entry from paged pool + space for response
  2589. from physical memory
  2590. Arguments:
  2591. SpaceLength - Length of space for URI + ETag + LoggingData
  2592. ResponseLength - Length of Response
  2593. Return Value:
  2594. Pointer to allocated entry or NULL on failure
  2595. --***************************************************************************/
  2596. PUL_URI_CACHE_ENTRY
  2597. UlAllocateCacheEntry(
  2598. ULONG SpaceLength,
  2599. ULONG ResponseLength
  2600. )
  2601. {
  2602. PUL_URI_CACHE_ENTRY pEntry;
  2603. PAGED_CODE();
  2604. if(!g_CacheMemEnabled)
  2605. return NULL;
  2606. // Allocate from LargeMem
  2607. pEntry = UL_ALLOCATE_STRUCT_WITH_SPACE(
  2608. PagedPool,
  2609. UL_URI_CACHE_ENTRY,
  2610. SpaceLength,
  2611. UL_URI_CACHE_ENTRY_POOL_TAG
  2612. );
  2613. if( NULL == pEntry ) {
  2614. return NULL;
  2615. }
  2616. RtlZeroMemory(pEntry, sizeof(UL_URI_CACHE_ENTRY));
  2617. pEntry->pMdl = UlLargeMemAllocate(ResponseLength);
  2618. if( NULL == pEntry->pMdl ) {
  2619. UL_FREE_POOL_WITH_SIG( pEntry, UL_URI_CACHE_ENTRY_POOL_TAG );
  2620. return NULL;
  2621. }
  2622. pEntry->NumPages = ROUND_TO_PAGES(ResponseLength) >> PAGE_SHIFT;
  2623. return pEntry;
  2624. }
  2625. /***************************************************************************++
  2626. Routine Description:
  2627. Free a cache entry
  2628. Arguments:
  2629. pEntry - Cache Entry to be freed
  2630. Return Value:
  2631. Nothing
  2632. --***************************************************************************/
  2633. VOID
  2634. UlFreeCacheEntry(
  2635. PUL_URI_CACHE_ENTRY pEntry
  2636. )
  2637. {
  2638. PAGED_CODE();
  2639. ASSERT( IS_VALID_URI_CACHE_ENTRY(pEntry) );
  2640. ASSERT( pEntry->pMdl != NULL );
  2641. UlLargeMemFree( pEntry->pMdl );
  2642. UL_FREE_POOL_WITH_SIG( pEntry, UL_URI_CACHE_ENTRY_POOL_TAG );
  2643. }
  2644. /***************************************************************************++
  2645. Routine Description:
  2646. Turn off the UL cache
  2647. --***************************************************************************/
  2648. VOID
  2649. UlDisableCache(
  2650. VOID
  2651. )
  2652. {
  2653. PAGED_CODE();
  2654. InterlockedExchange(&g_CacheMemEnabled, FALSE);
  2655. } // UlDisableCache
  2656. /***************************************************************************++
  2657. Routine Description:
  2658. Turn on the UL cache
  2659. --***************************************************************************/
  2660. VOID
  2661. UlEnableCache(
  2662. VOID
  2663. )
  2664. {
  2665. PAGED_CODE();
  2666. InterlockedExchange(&g_CacheMemEnabled, TRUE);
  2667. } // UlEnableCache