2 // System.Web.Caching.OutputCacheModule
5 // Jackson Harper (jackson@ximian.com)
7 // (C) 2003 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Web.Util;
35 using System.Collections;
36 using System.Web.Compilation;
39 using System.Collections.Generic;
42 namespace System.Web.Caching {
44 internal sealed class OutputCacheModule : IHttpModule {
46 CacheItemRemovedCallback response_removed;
49 static object keysCacheLock = new object ();
50 Dictionary <string, string> keysCache;
51 Dictionary <string, string> entriesToInvalidate;
54 public OutputCacheModule ()
58 public void Dispose ()
62 public void Init (HttpApplication app)
64 app.ResolveRequestCache += new EventHandler(OnResolveRequestCache);
65 app.UpdateRequestCache += new EventHandler(OnUpdateRequestCache);
67 keysCache = new Dictionary <string, string> ();
68 entriesToInvalidate = new Dictionary <string, string> ();
69 BuildManager.RemoveEntry += new BuildManagerRemoveEntryEventHandler (OnBuildManagerRemoveEntry);
71 response_removed = new CacheItemRemovedCallback (OnRawResponseRemoved);
75 void OnBuildManagerRemoveEntry (BuildManagerRemoveEntryEventArgs args)
77 string entry = args.EntryName;
78 HttpContext context = args.Context;
81 lock (keysCacheLock) {
82 if (!keysCache.TryGetValue (entry, out cacheValue))
85 keysCache.Remove (entry);
86 if (context == null && !entriesToInvalidate.ContainsKey (entry)) {
87 entriesToInvalidate.Add (entry, cacheValue);
92 context.Cache.Remove (entry);
93 if (!String.IsNullOrEmpty (cacheValue))
94 context.InternalCache.Remove (cacheValue);
98 void OnResolveRequestCache (object o, EventArgs args)
100 HttpApplication app = (HttpApplication) o;
101 HttpContext context = app.Context;
103 string vary_key = context.Request.FilePath;
104 CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
111 key = varyby.CreateKey (vary_key, context);
112 c = context.InternalCache [key] as CachedRawResponse;
117 lock (keysCacheLock) {
119 if (entriesToInvalidate.TryGetValue (vary_key, out invValue) && String.Compare (invValue, key, StringComparison.Ordinal) == 0) {
120 context.Cache.Remove (vary_key);
121 context.InternalCache.Remove (key);
122 entriesToInvalidate.Remove (vary_key);
128 ArrayList callbacks = c.Policy.ValidationCallbacks;
129 if (callbacks != null && callbacks.Count > 0) {
131 bool isIgnored = false;
133 foreach (Pair p in callbacks) {
134 HttpCacheValidateHandler validate = (HttpCacheValidateHandler)p.First;
135 object data = p.Second;
136 HttpValidationStatus status = HttpValidationStatus.Valid;
139 validate (context, data, ref status);
141 // MS.NET hides the exception
146 if (status == HttpValidationStatus.Invalid) {
149 } else if (status == HttpValidationStatus.IgnoreThisRequest) {
155 OnRawResponseRemoved (key, c, CacheItemRemovedReason.Removed);
157 } else if (isIgnored) {
162 context.Response.ClearContent ();
163 context.Response.BinaryWrite (c.GetData (), 0, c.ContentLength);
165 context.Response.ClearHeaders ();
166 context.Response.SetCachedHeaders (c.Headers);
168 context.Response.StatusCode = c.StatusCode;
169 context.Response.StatusDescription = c.StatusDescription;
171 app.CompleteRequest ();
174 void OnUpdateRequestCache (object o, EventArgs args)
176 HttpApplication app = (HttpApplication) o;
177 HttpContext context = app.Context;
179 if (context.Response.IsCached && context.Response.StatusCode == 200 &&
180 !context.Trace.IsEnabled)
181 DoCacheInsert (context);
185 void DoCacheInsert (HttpContext context)
187 string vary_key = context.Request.FilePath;
189 CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
190 CachedRawResponse prev = null;
193 string cacheKey = null, cacheValue = null;
196 if (varyby == null) {
197 string path = context.Request.MapPath (vary_key);
198 string [] files = new string [] { path };
199 string [] keys = new string [0];
200 varyby = new CachedVaryBy (context.Response.Cache, vary_key);
201 context.Cache.Insert (vary_key, varyby,
202 new CacheDependency (files, keys),
203 Cache.NoAbsoluteExpiration,
204 Cache.NoSlidingExpiration,
205 CacheItemPriority.Normal, null);
212 key = varyby.CreateKey (vary_key, context);
215 prev = context.InternalCache [key] as CachedRawResponse;
218 CachedRawResponse c = context.Response.GetCachedResponse ();
219 string [] files = new string [] { };
220 string [] keys = new string [] { vary_key };
221 bool sliding = context.Response.Cache.Sliding;
223 context.InternalCache.Insert (key, c, new CacheDependency (files, keys),
224 (sliding ? Cache.NoAbsoluteExpiration :
225 context.Response.Cache.Expires),
226 (sliding ? TimeSpan.FromSeconds (
227 context.Response.Cache.Duration) :
228 Cache.NoSlidingExpiration),
229 CacheItemPriority.Normal, response_removed);
231 varyby.ItemList.Add (key);
238 if (cacheKey != null) {
239 lock (keysCacheLock) {
240 if (!keysCache.ContainsKey (cacheKey))
241 keysCache.Add (cacheKey, cacheValue);
247 void OnRawResponseRemoved (string key, object value, CacheItemRemovedReason reason)
249 CachedRawResponse c = (CachedRawResponse) value;
251 c.VaryBy.ItemList.Remove (key);
252 if (c.VaryBy.ItemList.Count != 0)
255 HttpRuntime.Cache.Remove (c.VaryBy.Key);