Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web / Cache / OutputCache.cs
blob726f9801ac0801c59c5494ed8e4f901d7b94ddae
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Configuration.Provider;
5 using System.Runtime.Serialization;
6 using System.Security.Permissions;
7 using System.Threading;
8 using System.Web;
9 using System.Web.Compilation;
10 using System.Web.Configuration;
11 using System.Web.Hosting;
12 using System.Web.Management;
13 using System.Web.UI;
14 using System.Web.Util;
15 using System.IO;
16 using System.Runtime.Serialization.Formatters.Binary;
18 namespace System.Web.Caching {
21 We currently have the following buffer types:
23 HttpResponseBufferElement - managed memory
24 HttpResponseUnmanagedBufferElement - native memory
25 HttpResourceResponseElement - pointer to resource
26 HttpFileResponseElement - contains a file name, file can have 64-bit length
27 HttpSubstBlockResponseElement - subst callback
29 Custom output cache providers do not support all features. If we cannot use a custom output cache,
30 provider, we will use the internal cache. We will use a custom output cache provider only if:
32 a) no instance validation callbacks (static callbacks are okay)
33 b) no dependencies other than files
34 c) no sliding expiration
35 d) no instance substitution callbacks (HttpSubstBlockResponseElement) (static callbacks okay)
38 INSERT/GET:
39 ----------
40 The custom cache provider receives an OutputCacheEntry, which contains the status, headers, response elements,
41 cache policy settings, and kernel cache key. The cache provider can coallesce response elements. For example,
42 a disk based cache may return a single response element which is the name of a file containing the entire response.
44 ISSUES:
46 If the custom cache provider is distributed, the cache keys must be unique for two applications with the same name
47 but different domains. For example, a request to http://company1.com/myapp/page.aspx and a request to
48 http://company2.com/myapp/page.aspx need to use output cache entry keys that include the domain name. Today, the
49 key is just /myapp/page.aspx. It would need to include the Host header, but if that is "localhost", "::1", etc,
50 we'd need to use %USERDOMAIN%\%COMPUTERNAME% instead. But it is possible for a single site to have multiple host name
51 bindings, so this would result in multiple cache entries for potentially the same cached result. We could allow this to be
52 adjusted by adding a setting to the cache policy.
56 internal class DependencyCacheEntry {
57 private string _providerName;
58 private string _outputCacheEntryKey;
59 private string _kernelCacheEntryKey;
61 internal string ProviderName { get { return _providerName; } }
62 internal string OutputCacheEntryKey { get { return _outputCacheEntryKey; } }
63 internal string KernelCacheEntryKey { get { return _kernelCacheEntryKey; } }
65 internal DependencyCacheEntry(string oceKey, string kernelCacheEntryKey, string providerName) {
66 _outputCacheEntryKey = oceKey;
67 _kernelCacheEntryKey = kernelCacheEntryKey;
68 _providerName = providerName;
72 public static class OutputCache {
73 private const string OUTPUTCACHE_KEYPREFIX_DEPENDENCIES = CacheInternal.PrefixOutputCache + "D";
74 internal const string ASPNET_INTERNAL_PROVIDER_NAME = "AspNetInternalProvider";
75 private static bool s_inited;
76 private static object s_initLock = new object();
77 private static CacheItemRemovedCallback s_entryRemovedCallback;
78 private static CacheItemRemovedCallback s_dependencyRemovedCallback;
79 private static CacheItemRemovedCallback s_dependencyRemovedCallbackForFragment;
80 private static OutputCacheProvider s_defaultProvider;
81 private static OutputCacheProviderCollection s_providers;
82 // when there are no providers being used, we'll use this count to optimize performance when the value is zero.
83 private static int s_cEntries;
86 // private helper methods
89 private static void AddCacheKeyToDependencies(ref CacheDependency dependencies, string cacheKey) {
90 CacheDependency keyDep = new CacheDependency(0, null, new string[1] {cacheKey});
91 if (dependencies == null) {
92 dependencies = keyDep;
94 else {
95 // if it's not an aggregate, we have to create one because you can't add
96 // it to anything but an aggregate
97 AggregateCacheDependency agg = dependencies as AggregateCacheDependency;
98 if (agg != null) {
99 agg.Add(keyDep);
101 else {
102 agg = new AggregateCacheDependency();
103 agg.Add(keyDep, dependencies);
104 dependencies = agg;
109 private static void EnsureInitialized() {
110 if (s_inited)
111 return;
113 lock (s_initLock) {
114 if (!s_inited) {
115 OutputCacheSection settings = RuntimeConfig.GetAppConfig().OutputCache;
116 s_providers = settings.CreateProviderCollection();
117 s_defaultProvider = settings.GetDefaultProvider(s_providers);
118 s_entryRemovedCallback = new CacheItemRemovedCallback(OutputCache.EntryRemovedCallback);
119 s_dependencyRemovedCallback = new CacheItemRemovedCallback(OutputCache.DependencyRemovedCallback);
120 s_dependencyRemovedCallbackForFragment = new CacheItemRemovedCallback(OutputCache.DependencyRemovedCallbackForFragment);
121 s_inited = true;
126 private static void DecrementCount() {
127 if (Providers == null)
128 Interlocked.Decrement(ref s_cEntries);
131 private static void IncrementCount() {
132 if (Providers == null)
133 Interlocked.Increment(ref s_cEntries);
136 private static OutputCacheProvider GetFragmentProvider(String providerName) {
137 // if providerName is null, use default provider. If default provider is null, we'll use internal cache.
138 // if providerName is not null, get it from the provider collection.
139 OutputCacheProvider provider = null;
140 if (providerName == null) {
141 provider = s_defaultProvider;
143 else {
144 provider = s_providers[providerName];
145 if (provider == null) {
146 Debug.Assert(false, "Unexpected, " + providerName + " should be a member of the collection.");
147 throw new ProviderException(SR.GetString(SR.Provider_Not_Found, providerName));
150 #if DBG
151 string msg = (provider != null) ? provider.GetType().Name : "null";
152 Debug.Trace("OutputCache", "GetFragmentProvider(" + providerName + ") --> " + msg);
153 #endif
154 return provider;
157 private static OutputCacheProvider GetProvider(HttpContext context) {
158 Debug.Assert(context != null, "context != null");
159 if (context == null) {
160 return null;
162 // Call GetOutputCacheProviderName
163 // so it can determine which provider to use.
164 HttpApplication app = context.ApplicationInstance;
165 string name = app.GetOutputCacheProviderName(context);
166 if (name == null) {
167 throw new ProviderException(SR.GetString(SR.GetOutputCacheProviderName_Invalid, name));
169 // AspNetInternalProvider means use the internal cache
170 if (name == OutputCache.ASPNET_INTERNAL_PROVIDER_NAME) {
171 return null;
173 OutputCacheProvider provider = (s_providers == null) ? null : s_providers[name];
174 if (provider == null) {
175 throw new ProviderException(SR.GetString(SR.GetOutputCacheProviderName_Invalid, name));
177 return provider;
180 private static OutputCacheEntry Convert(CachedRawResponse cachedRawResponse, string depKey, string[] fileDependencies) {
181 List<HeaderElement> headerElements = null;
182 ArrayList headers = cachedRawResponse._rawResponse.Headers;
183 int count = (headers != null) ? headers.Count : 0;
184 for (int i = 0; i < count; i++) {
185 if (headerElements == null) {
186 headerElements = new List<HeaderElement>(count);
188 HttpResponseHeader h = (HttpResponseHeader)(headers[i]);
189 headerElements.Add(new HeaderElement(h.Name, h.Value));
192 List<ResponseElement> responseElements = null;
193 ArrayList buffers = cachedRawResponse._rawResponse.Buffers;
194 count = (buffers != null) ? buffers.Count : 0;
195 for (int i = 0; i < count; i++) {
196 if (responseElements == null) {
197 responseElements = new List<ResponseElement>(count);
199 IHttpResponseElement elem = buffers[i] as IHttpResponseElement;
200 if (elem is HttpFileResponseElement) {
201 HttpFileResponseElement fileElement = elem as HttpFileResponseElement;
202 responseElements.Add(new FileResponseElement(fileElement.FileName, fileElement.Offset, elem.GetSize()));
204 else if (elem is HttpSubstBlockResponseElement) {
205 HttpSubstBlockResponseElement substElement = elem as HttpSubstBlockResponseElement;
206 responseElements.Add(new SubstitutionResponseElement(substElement.Callback));
208 else {
209 byte[] b = elem.GetBytes();
210 long length = (b != null) ? b.Length : 0;
211 responseElements.Add(new MemoryResponseElement(b, length));
215 OutputCacheEntry oce = new OutputCacheEntry(
216 cachedRawResponse._cachedVaryId,
217 cachedRawResponse._settings,
218 cachedRawResponse._kernelCacheUrl,
219 depKey,
220 fileDependencies,
221 cachedRawResponse._rawResponse.StatusCode,
222 cachedRawResponse._rawResponse.StatusDescription,
223 headerElements,
224 responseElements
227 return oce;
230 private static CachedRawResponse Convert(OutputCacheEntry oce) {
231 ArrayList headers = null;
232 if (oce.HeaderElements != null && oce.HeaderElements.Count > 0) {
233 headers = new ArrayList(oce.HeaderElements.Count);
234 for (int i = 0; i < oce.HeaderElements.Count; i++) {
235 HttpResponseHeader h = new HttpResponseHeader(oce.HeaderElements[i].Name, oce.HeaderElements[i].Value);
236 headers.Add(h);
240 ArrayList buffers = null;
241 if (oce.ResponseElements != null && oce.ResponseElements.Count > 0) {
242 buffers = new ArrayList(oce.ResponseElements.Count);
243 for (int i = 0; i < oce.ResponseElements.Count; i++) {
244 ResponseElement re = oce.ResponseElements[i];
245 IHttpResponseElement elem = null;
246 if (re is FileResponseElement) {
247 HttpContext context = HttpContext.Current;
248 HttpWorkerRequest wr = (context != null) ? context.WorkerRequest : null;
249 bool supportsLongTransmitFile = (wr != null && wr.SupportsLongTransmitFile);
250 bool isImpersonating = ((context != null && context.IsClientImpersonationConfigured) || HttpRuntime.IsOnUNCShareInternal);
251 FileResponseElement fre = (FileResponseElement)re;
253 // DevDiv #21203: Need to verify permission to access the requested file since handled by native code.
254 HttpRuntime.CheckFilePermission(fre.Path);
256 elem = new HttpFileResponseElement(fre.Path, fre.Offset, fre.Length, isImpersonating, supportsLongTransmitFile);
258 else if (re is MemoryResponseElement) {
259 MemoryResponseElement mre = (MemoryResponseElement)re;
260 int size = System.Convert.ToInt32(mre.Length);
261 elem = new HttpResponseBufferElement(mre.Buffer, size);
263 else if (re is SubstitutionResponseElement) {
264 SubstitutionResponseElement sre = (SubstitutionResponseElement)re;
265 elem = new HttpSubstBlockResponseElement(sre.Callback);
267 else {
268 throw new NotSupportedException();
270 buffers.Add(elem);
273 else {
274 buffers = new ArrayList();
277 HttpRawResponse rawResponse = new HttpRawResponse(oce.StatusCode, oce.StatusDescription, headers, buffers, false /*hasSubstBlocks*/);
278 CachedRawResponse cachedRawResponse = new CachedRawResponse(rawResponse, oce.Settings, oce.KernelCacheUrl, oce.CachedVaryId);
280 return cachedRawResponse;
285 // helpers for accessing InternalCache
288 // add CachedVary
289 private static CachedVary UtcAdd(String key, CachedVary cachedVary) {
290 return (CachedVary) HttpRuntime.Cache.InternalCache.Add(key, cachedVary, null);
293 // add ControlCachedVary
294 private static ControlCachedVary UtcAdd(String key, ControlCachedVary cachedVary) {
295 return (ControlCachedVary) HttpRuntime.Cache.InternalCache.Add(key, cachedVary, null);
298 private static bool IsSubstBlockSerializable(HttpRawResponse rawResponse) {
299 if (!rawResponse.HasSubstBlocks)
300 return true;
301 for (int i = 0; i < rawResponse.Buffers.Count; i++) {
302 HttpSubstBlockResponseElement substBlock = rawResponse.Buffers[i] as HttpSubstBlockResponseElement;
303 if (substBlock == null)
304 continue;
305 if (!substBlock.Callback.Method.IsStatic)
306 return false;
308 return true;
313 // callbacks
316 private static void HandleErrorWithoutContext(Exception e) {
317 HttpApplicationFactory.RaiseError(e);
318 try {
319 WebBaseEvent.RaiseRuntimeError(e, typeof(OutputCache));
321 catch {
325 // only used by providers
326 private static void DependencyRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
327 Debug.Trace("OutputCache", "DependencyRemovedCallback: reason=" + reason + ", key=" + key);
329 DependencyCacheEntry dce = value as DependencyCacheEntry;
330 if (dce.KernelCacheEntryKey != null) {
331 // invalidate kernel cache entry
332 if (HttpRuntime.UseIntegratedPipeline) {
333 UnsafeIISMethods.MgdFlushKernelCache(dce.KernelCacheEntryKey);
335 else {
336 UnsafeNativeMethods.InvalidateKernelCache(dce.KernelCacheEntryKey);
339 if (reason == CacheItemRemovedReason.DependencyChanged) {
340 if (dce.OutputCacheEntryKey != null) {
341 try {
342 OutputCache.RemoveFromProvider(dce.OutputCacheEntryKey, dce.ProviderName);
344 catch (Exception e) {
345 HandleErrorWithoutContext(e);
351 // only used by providers
352 private static void DependencyRemovedCallbackForFragment(string key, object value, CacheItemRemovedReason reason) {
353 Debug.Trace("OutputCache", "DependencyRemovedCallbackForFragment: reason=" + reason + ", key=" + key);
355 if (reason == CacheItemRemovedReason.DependencyChanged) {
356 DependencyCacheEntry dce = value as DependencyCacheEntry;
357 if (dce.OutputCacheEntryKey != null) {
358 try {
359 OutputCache.RemoveFragment(dce.OutputCacheEntryKey, dce.ProviderName);
361 catch (Exception e) {
362 HandleErrorWithoutContext(e);
368 // only used by internal cache
369 private static void EntryRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
370 Debug.Trace("OutputCache", "EntryRemovedCallback: reason=" + reason + ", key=" + key);
372 DecrementCount();
374 PerfCounters.DecrementCounter(AppPerfCounter.OUTPUT_CACHE_ENTRIES);
375 PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_TURNOVER_RATE);
377 CachedRawResponse cachedRawResponse = value as CachedRawResponse;
378 if (cachedRawResponse != null) {
379 String kernelCacheUrl = cachedRawResponse._kernelCacheUrl;
380 // if it is kernel cached, the url will be non-null.
381 // if the entry was re-inserted, don't remove kernel entry since it will be updated
382 if (kernelCacheUrl != null && HttpRuntime.Cache.InternalCache.Get(key) == null) {
383 // invalidate kernel cache entry
384 if (HttpRuntime.UseIntegratedPipeline) {
385 UnsafeIISMethods.MgdFlushKernelCache(kernelCacheUrl);
387 else {
388 UnsafeNativeMethods.InvalidateKernelCache(kernelCacheUrl);
395 // public properties
398 public static string DefaultProviderName {
399 get {
400 EnsureInitialized();
401 return (s_defaultProvider != null) ? s_defaultProvider.Name : OutputCache.ASPNET_INTERNAL_PROVIDER_NAME;
405 public static OutputCacheProviderCollection Providers {
406 get {
407 EnsureInitialized();
408 return s_providers;
414 // internal properties and methods
419 // If we're not using a provider, we can optimize this so the OutputCacheModule
420 // only need run when there are entries--return true iff count is non-zero.
421 // If we're using a provider, it's not easy to keep track of the number of entries,
422 // so always return true (so OutputCacheModule always runs).
424 internal static bool InUse {
425 get {
426 return (Providers == null) ? (s_cEntries != 0) : true;
430 internal static void ThrowIfProviderNotFound(String providerName) {
431 // null means use default provider or internal cache
432 if (providerName == null) {
433 return;
435 OutputCacheProviderCollection providers = Providers;
437 if (providers == null || providers[providerName] == null) {
438 throw new ProviderException(SR.GetString(SR.Provider_Not_Found, providerName));
442 internal static bool HasDependencyChanged(bool isFragment, string depKey, string[] fileDeps, string kernelKey, string oceKey, string providerName) {
444 if (depKey == null)
446 #if DBG
447 Debug.Trace("OutputCache", "HasDependencyChanged(" + depKey + ", ..., " + oceKey + ", ...) --> false");
448 #endif
449 return false;
452 // is the file dependency already in the in-memory cache?
453 if (HttpRuntime.Cache.InternalCache.Get(depKey) != null) {
454 #if DBG
455 Debug.Trace("OutputCache", "HasDependencyChanged(" + depKey + ", ..., " + oceKey + ", ...) --> false");
456 #endif
457 return false;
460 // deserialize the file dependencies
461 CacheDependency dep = new CacheDependency(0, fileDeps);
463 int idStartIndex = OUTPUTCACHE_KEYPREFIX_DEPENDENCIES.Length;
464 int idLength = depKey.Length - idStartIndex;
466 CacheItemRemovedCallback callback = (isFragment) ? s_dependencyRemovedCallbackForFragment : s_dependencyRemovedCallback;
468 // have the file dependencies changed?
469 if (String.Compare(dep.GetUniqueID(), 0, depKey, idStartIndex, idLength, StringComparison.Ordinal) == 0) {
470 // file dependencies have not changed--cache them with callback to remove OutputCacheEntry if they change
471 HttpRuntime.Cache.InternalCache.Insert(depKey, new DependencyCacheEntry(oceKey, kernelKey, providerName), new CacheInsertOptions() {
472 Dependencies = dep,
473 OnRemovedCallback = callback
475 #if DBG
476 Debug.Trace("OutputCache", "HasDependencyChanged(" + depKey + ", ..., " + oceKey + ", ...) --> false, DEPENDENCY RE-INSERTED");
477 #endif
478 return false;
480 else {
481 // file dependencies have changed
482 dep.Dispose();
483 #if DBG
484 Debug.Trace("OutputCache", "HasDependencyChanged(" + depKey + ", ..., " + oceKey + ", ...) --> true, " + dep.GetUniqueID());
485 #endif
486 return true;
490 [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
491 public static void Serialize(Stream stream, Object data) {
492 BinaryFormatter formatter = new BinaryFormatter();
493 if (data is OutputCacheEntry || data is PartialCachingCacheEntry || data is CachedVary || data is ControlCachedVary ||
494 data is FileResponseElement || data is MemoryResponseElement || data is SubstitutionResponseElement) {
495 formatter.Serialize(stream, data);
497 else {
498 throw new ArgumentException(SR.GetString(SR.OutputCacheExtensibility_CantSerializeDeserializeType));
502 [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
503 public static object Deserialize(Stream stream) {
504 BinaryFormatter formatter = new BinaryFormatter();
505 Object data = formatter.Deserialize(stream);
506 if (!(data is OutputCacheEntry || data is PartialCachingCacheEntry || data is CachedVary || data is ControlCachedVary ||
507 data is FileResponseElement || data is MemoryResponseElement || data is SubstitutionResponseElement)) {
508 throw new ArgumentException(SR.GetString(SR.OutputCacheExtensibility_CantSerializeDeserializeType));
510 return data;
513 // lookup cached vary
514 // lookup entry
515 // lookup entry for content-encoding
516 internal static Object Get(String key) {
517 // if it's not in the provider or the default provider is undefined,
518 // check the internal cache (we don't know where it is).
519 Object result = null;
520 OutputCacheProvider provider = GetProvider(HttpContext.Current);
521 if (provider != null) {
522 result = provider.Get(key);
523 OutputCacheEntry oce = result as OutputCacheEntry;
524 if (oce != null) {
525 if (HasDependencyChanged(false /*isFragment*/, oce.DependenciesKey, oce.Dependencies, oce.KernelCacheUrl, key, provider.Name)) {
526 OutputCache.RemoveFromProvider(key, provider.Name);
527 #if DBG
528 Debug.Trace("OutputCache", "Get(" + key + ") --> null, " + provider.Name);
529 #endif
530 return null;
533 result = Convert(oce);
536 if (result == null) {
537 result = HttpRuntime.Cache.InternalCache.Get(key);
538 #if DBG
539 string typeName = (result != null) ? result.GetType().Name : "null";
540 Debug.Trace("OutputCache", "Get(" + key + ") --> " + typeName + ", CacheInternal");
542 else {
543 Debug.Trace("OutputCache", "Get(" + key + ") --> " + result.GetType().Name + ", " + provider.Name);
544 #endif
546 return result;
549 // lookup fragment
550 internal static Object GetFragment(String key, String providerName) {
551 // if providerName is null, use default provider.
552 // if providerName is not null, get it from the provider collection.
553 // if it's not in the provider or the default provider is undefined,
554 // check the internal cache (we don't know where it is).
555 Object result = null;
556 OutputCacheProvider provider = GetFragmentProvider(providerName);
557 if (provider != null) {
558 result = provider.Get(key);
559 PartialCachingCacheEntry fragment = result as PartialCachingCacheEntry;
560 if (fragment != null) {
561 if (HasDependencyChanged(true /*isFragment*/, fragment._dependenciesKey, fragment._dependencies, null /*kernelKey*/, key, provider.Name)) {
562 OutputCache.RemoveFragment(key, provider.Name);
563 #if DBG
564 Debug.Trace("OutputCache", "GetFragment(" + key + "," + providerName + ") --> null, " + providerName);
565 #endif
566 return null;
571 if (result == null) {
572 result = HttpRuntime.Cache.InternalCache.Get(key);
573 #if DBG
574 string typeName = (result != null) ? result.GetType().Name : "null";
575 Debug.Trace("OutputCache", "GetFragment(" + key + "," + providerName + ") --> " + typeName + ", CacheInternal");
577 else {
578 Debug.Trace("OutputCache", "GetFragment(" + key + "," + providerName + ") --> " + result.GetType().Name + ", " + providerName);
579 #endif
581 return result;
584 // remove cache vary
585 // remove entry
586 internal static void Remove(String key, HttpContext context) {
587 // we don't know if it's in the internal cache or
588 // one of the providers. If a context is given,
589 // then we can narrow down to at most one provider.
590 // If the context is null, then we don't know which
591 // provider and we have to check all.
593 HttpRuntime.Cache.InternalCache.Remove(key);
595 if (context == null) {
596 // remove from all providers since we don't know which one it's in.
597 OutputCacheProviderCollection providers = Providers;
598 if (providers != null) {
599 foreach (OutputCacheProvider provider in providers) {
600 provider.Remove(key);
604 else {
605 OutputCacheProvider provider = GetProvider(context);
606 if (provider != null) {
607 provider.Remove(key);
610 #if DBG
611 Debug.Trace("OutputCache", "Remove(" + key + ", context)");
612 #endif
615 // remove cache vary
616 // remove entry
617 internal static void RemoveFromProvider(String key, String providerName) {
618 // we know where it is. If providerName is given,
619 // then it is in that provider. If it's not given,
620 // it's in the internal cache.
621 if (providerName == null) {
622 throw new ArgumentNullException("providerName");
625 OutputCacheProviderCollection providers = Providers;
626 OutputCacheProvider provider = (providers == null) ? null : providers[providerName];
627 if (provider == null) {
628 throw new ProviderException(SR.GetString(SR.Provider_Not_Found, providerName));
631 provider.Remove(key);
632 #if DBG
633 Debug.Trace("OutputCache", "Remove(" + key + ", " + providerName + ")");
634 #endif
637 // remove fragment
638 internal static void RemoveFragment(String key, String providerName) {
639 // if providerName is null, use default provider.
640 // if providerName is not null, get it from the provider collection.
641 // remove it from the provider and the internal cache (we don't know where it is).
642 OutputCacheProvider provider = GetFragmentProvider(providerName);
643 if (provider != null) {
644 provider.Remove(key);
646 HttpRuntime.Cache.InternalCache.Remove(key);
647 #if DBG
648 Debug.Trace("OutputCache", "RemoveFragment(" + key + "," + providerName + ")");
649 #endif
652 // insert fragment
653 internal static void InsertFragment(String cachedVaryKey, ControlCachedVary cachedVary,
654 String fragmentKey, PartialCachingCacheEntry fragment,
655 CacheDependency dependencies,
656 DateTime absExp, TimeSpan slidingExp,
657 String providerName) {
659 // if providerName is not null, find the provider in the collection.
660 // if providerName is null, use default provider.
661 // if the default provider is undefined or the fragment can't be inserted in the
662 // provider, insert it in the internal cache.
663 OutputCacheProvider provider = GetFragmentProvider(providerName);
666 // ControlCachedVary and PartialCachingCacheEntry can be serialized
669 bool useProvider = (provider != null);
670 if (useProvider) {
671 bool canUseProvider = (slidingExp == Cache.NoSlidingExpiration
672 && (dependencies == null || dependencies.IsFileDependency()));
674 if (useProvider && !canUseProvider) {
675 throw new ProviderException(SR.GetString(SR.Provider_does_not_support_policy_for_fragments, providerName));
679 #if DBG
680 bool cachedVaryPutInCache = (cachedVary != null);
681 #endif
682 if (cachedVary != null) {
683 // Add the ControlCachedVary item so that a request will know
684 // which varies are needed to issue another request.
686 // Use the Add method so that we guarantee we only use
687 // a single ControlCachedVary and don't overwrite existing ones.
688 ControlCachedVary cachedVaryInCache;
689 if (!useProvider) {
690 cachedVaryInCache = OutputCache.UtcAdd(cachedVaryKey, cachedVary);
692 else {
693 cachedVaryInCache = (ControlCachedVary) provider.Add(cachedVaryKey, cachedVary, Cache.NoAbsoluteExpiration);
696 if (cachedVaryInCache != null) {
697 if (!cachedVary.Equals(cachedVaryInCache)) {
698 // overwrite existing cached vary
699 if (!useProvider) {
700 HttpRuntime.Cache.InternalCache.Insert(cachedVaryKey, cachedVary, null);
702 else {
703 provider.Set(cachedVaryKey, cachedVary, Cache.NoAbsoluteExpiration);
706 else {
707 cachedVary = cachedVaryInCache;
708 #if DBG
709 cachedVaryPutInCache = false;
710 #endif
714 if (!useProvider) {
715 AddCacheKeyToDependencies(ref dependencies, cachedVaryKey);
718 // not all caches support cache key dependencies, but we can use a "change number" to associate
719 // the ControlCachedVary and the PartialCachingCacheEntry
720 fragment._cachedVaryId = cachedVary.CachedVaryId;
723 // Now insert into the cache (use cache provider if possible, otherwise use internal cache)
724 if (!useProvider) {
725 HttpRuntime.Cache.InternalCache.Insert(fragmentKey, fragment, new CacheInsertOptions() {
726 Dependencies = dependencies,
727 AbsoluteExpiration = absExp,
728 SlidingExpiration = slidingExp
731 else {
732 string depKey = null;
733 if (dependencies != null) {
734 depKey = OUTPUTCACHE_KEYPREFIX_DEPENDENCIES + dependencies.GetUniqueID();
735 fragment._dependenciesKey = depKey;
736 fragment._dependencies = dependencies.GetFileDependencies();
738 provider.Set(fragmentKey, fragment, absExp);
739 if (dependencies != null) {
740 // use Add and dispose dependencies if there's already one in the cache
741 Object d = HttpRuntime.Cache.InternalCache.Add(depKey, new DependencyCacheEntry(fragmentKey, null, provider.Name),
742 new CacheInsertOptions() {
743 Dependencies = dependencies,
744 AbsoluteExpiration = absExp,
745 OnRemovedCallback = s_dependencyRemovedCallbackForFragment
747 if (d != null) {
748 dependencies.Dispose();
753 #if DBG
754 string cachedVaryType = (cachedVaryPutInCache) ? "ControlCachedVary" : "";
755 string providerUsed = (useProvider) ? provider.Name : "CacheInternal";
756 Debug.Trace("OutputCache", "InsertFragment("
757 + cachedVaryKey + ", "
758 + cachedVaryType + ", "
759 + fragmentKey + ", PartialCachingCacheEntry, ...) -->"
760 + providerUsed);
761 #endif
764 // insert cached vary or output cache entry
765 internal static void InsertResponse(String cachedVaryKey, CachedVary cachedVary,
766 String rawResponseKey, CachedRawResponse rawResponse,
767 CacheDependency dependencies,
768 DateTime absExp, TimeSpan slidingExp) {
770 // if the provider is undefined or the fragment can't be inserted in the
771 // provider, insert it in the internal cache.
772 OutputCacheProvider provider = GetProvider(HttpContext.Current);
775 // CachedVary can be serialized.
776 // CachedRawResponse is not always serializable.
779 bool useProvider = (provider != null);
780 if (useProvider) {
781 bool canUseProvider = (IsSubstBlockSerializable(rawResponse._rawResponse)
782 && rawResponse._settings.IsValidationCallbackSerializable()
783 && slidingExp == Cache.NoSlidingExpiration
784 && (dependencies == null || dependencies.IsFileDependency()));
786 if (useProvider && !canUseProvider) {
787 throw new ProviderException(SR.GetString(SR.Provider_does_not_support_policy_for_responses, provider.Name));
791 #if DBG
792 bool cachedVaryPutInCache = (cachedVary != null);
793 #endif
794 if (cachedVary != null) {
796 * Add the CachedVary item so that a request will know
797 * which headers are needed to issue another request.
799 * Use the Add method so that we guarantee we only use
800 * a single CachedVary and don't overwrite existing ones.
803 CachedVary cachedVaryInCache;
804 if (!useProvider) {
805 cachedVaryInCache = OutputCache.UtcAdd(cachedVaryKey, cachedVary);
807 else {
808 cachedVaryInCache = (CachedVary) provider.Add(cachedVaryKey, cachedVary, Cache.NoAbsoluteExpiration);
811 if (cachedVaryInCache != null) {
812 if (!cachedVary.Equals(cachedVaryInCache)) {
813 if (!useProvider) {
814 HttpRuntime.Cache.InternalCache.Insert(cachedVaryKey, cachedVary, null);
816 else {
817 provider.Set(cachedVaryKey, cachedVary, Cache.NoAbsoluteExpiration);
820 else {
821 cachedVary = cachedVaryInCache;
822 #if DBG
823 cachedVaryPutInCache = false;
824 #endif
828 if (!useProvider) {
829 AddCacheKeyToDependencies(ref dependencies, cachedVaryKey);
832 // not all caches support cache key dependencies, but we can use a "change number" to associate
833 // the ControlCachedVary and the PartialCachingCacheEntry
834 rawResponse._cachedVaryId = cachedVary.CachedVaryId;
837 // Now insert into the cache (use cache provider if possible, otherwise use internal cache)
838 if (!useProvider) {
839 HttpRuntime.Cache.InternalCache.Insert(rawResponseKey, rawResponse, new CacheInsertOptions() {
840 Dependencies = dependencies,
841 AbsoluteExpiration = absExp,
842 SlidingExpiration = slidingExp,
843 OnRemovedCallback = s_entryRemovedCallback
846 IncrementCount();
848 PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_ENTRIES);
849 PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_TURNOVER_RATE);
851 else {
852 string depKey = null;
853 string[] fileDeps = null;
854 if (dependencies != null) {
855 depKey = OUTPUTCACHE_KEYPREFIX_DEPENDENCIES + dependencies.GetUniqueID();
856 fileDeps = dependencies.GetFileDependencies();
858 OutputCacheEntry oce = Convert(rawResponse, depKey, fileDeps);
859 provider.Set(rawResponseKey, oce, absExp);
860 if (dependencies != null) {
861 // use Add and dispose dependencies if there's already one in the cache
862 Object d = HttpRuntime.Cache.InternalCache.Add(depKey, new DependencyCacheEntry(rawResponseKey, oce.KernelCacheUrl, provider.Name),
863 new CacheInsertOptions() {
864 Dependencies = dependencies,
865 AbsoluteExpiration = absExp,
866 OnRemovedCallback = s_dependencyRemovedCallbackForFragment
868 if (d != null) {
869 dependencies.Dispose();
873 #if DBG
874 string cachedVaryType = (cachedVaryPutInCache) ? "CachedVary" : "";
875 string providerUsed = (useProvider) ? provider.Name : "CacheInternal";
876 Debug.Trace("OutputCache", "InsertResposne("
877 + cachedVaryKey + ", "
878 + cachedVaryType + ", "
879 + rawResponseKey + ", CachedRawResponse, ...) -->"
880 + providerUsed);
881 #endif