2
0

OutputCacheModule.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. //
  2. // System.Web.Caching.OutputCacheModule
  3. //
  4. // Authors:
  5. // Jackson Harper ([email protected])
  6. //
  7. // (C) 2003 Novell, Inc (http://www.novell.com)
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.IO;
  30. using System.Web;
  31. using System.Web.UI;
  32. using System.Web.Util;
  33. using System.Collections;
  34. using System.Web.Compilation;
  35. #if NET_2_0
  36. using System.Collections.Generic;
  37. #endif
  38. namespace System.Web.Caching {
  39. internal sealed class OutputCacheModule : IHttpModule
  40. {
  41. CacheItemRemovedCallback response_removed;
  42. #if NET_2_0
  43. static object keysCacheLock = new object ();
  44. Dictionary <string, string> keysCache;
  45. Dictionary <string, string> entriesToInvalidate;
  46. #endif
  47. public OutputCacheModule ()
  48. {
  49. }
  50. public void Dispose ()
  51. {
  52. }
  53. public void Init (HttpApplication context)
  54. {
  55. context.ResolveRequestCache += new EventHandler(OnResolveRequestCache);
  56. context.UpdateRequestCache += new EventHandler(OnUpdateRequestCache);
  57. #if NET_2_0
  58. keysCache = new Dictionary <string, string> ();
  59. entriesToInvalidate = new Dictionary <string, string> ();
  60. BuildManager.RemoveEntry += new BuildManagerRemoveEntryEventHandler (OnBuildManagerRemoveEntry);
  61. #endif
  62. response_removed = new CacheItemRemovedCallback (OnRawResponseRemoved);
  63. }
  64. #if NET_2_0
  65. void OnBuildManagerRemoveEntry (BuildManagerRemoveEntryEventArgs args)
  66. {
  67. string entry = args.EntryName;
  68. HttpContext context = args.Context;
  69. string cacheValue;
  70. lock (keysCacheLock) {
  71. if (!keysCache.TryGetValue (entry, out cacheValue))
  72. return;
  73. keysCache.Remove (entry);
  74. if (context == null && !entriesToInvalidate.ContainsKey (entry)) {
  75. entriesToInvalidate.Add (entry, cacheValue);
  76. return;
  77. }
  78. }
  79. context.Cache.Remove (entry);
  80. if (!String.IsNullOrEmpty (cacheValue))
  81. context.InternalCache.Remove (cacheValue);
  82. }
  83. #endif
  84. void OnResolveRequestCache (object o, EventArgs args)
  85. {
  86. HttpApplication app = (HttpApplication) o;
  87. HttpContext context = app.Context;
  88. string vary_key = context.Request.FilePath;
  89. CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
  90. string key;
  91. CachedRawResponse c;
  92. if (varyby == null)
  93. return;
  94. key = varyby.CreateKey (vary_key, context);
  95. c = context.InternalCache [key] as CachedRawResponse;
  96. if (c == null)
  97. return;
  98. #if NET_2_0
  99. lock (keysCacheLock) {
  100. string invValue;
  101. if (entriesToInvalidate.TryGetValue (vary_key, out invValue) && String.Compare (invValue, key, StringComparison.Ordinal) == 0) {
  102. context.Cache.Remove (vary_key);
  103. context.InternalCache.Remove (key);
  104. entriesToInvalidate.Remove (vary_key);
  105. return;
  106. }
  107. }
  108. #endif
  109. ArrayList callbacks = c.Policy.ValidationCallbacks;
  110. if (callbacks != null && callbacks.Count > 0) {
  111. bool isValid = true;
  112. bool isIgnored = false;
  113. foreach (Pair p in callbacks) {
  114. HttpCacheValidateHandler validate = (HttpCacheValidateHandler)p.First;
  115. object data = p.Second;
  116. HttpValidationStatus status = HttpValidationStatus.Valid;
  117. try {
  118. validate (context, data, ref status);
  119. } catch {
  120. // MS.NET hides the exception
  121. isValid = false;
  122. break;
  123. }
  124. if (status == HttpValidationStatus.Invalid) {
  125. isValid = false;
  126. break;
  127. } else if (status == HttpValidationStatus.IgnoreThisRequest) {
  128. isIgnored = true;
  129. }
  130. }
  131. if (!isValid) {
  132. OnRawResponseRemoved (key, c, CacheItemRemovedReason.Removed);
  133. return;
  134. } else if (isIgnored) {
  135. return;
  136. }
  137. }
  138. context.Response.ClearContent ();
  139. context.Response.BinaryWrite (c.GetData (), 0, c.ContentLength);
  140. context.Response.ClearHeaders ();
  141. context.Response.SetCachedHeaders (c.Headers);
  142. context.Response.StatusCode = c.StatusCode;
  143. context.Response.StatusDescription = c.StatusDescription;
  144. app.CompleteRequest ();
  145. }
  146. void OnUpdateRequestCache (object o, EventArgs args)
  147. {
  148. HttpApplication app = (HttpApplication) o;
  149. HttpContext context = app.Context;
  150. if (context.Response.IsCached && context.Response.StatusCode == 200 &&
  151. !context.Trace.IsEnabled)
  152. DoCacheInsert (context);
  153. }
  154. void DoCacheInsert (HttpContext context)
  155. {
  156. string vary_key = context.Request.FilePath;
  157. string key;
  158. CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
  159. CachedRawResponse prev = null;
  160. bool lookup = true;
  161. #if NET_2_0
  162. string cacheKey = null, cacheValue = null;
  163. #endif
  164. if (varyby == null) {
  165. string path = context.Request.MapPath (vary_key);
  166. string [] files = new string [] { path };
  167. string [] keys = new string [0];
  168. varyby = new CachedVaryBy (context.Response.Cache, vary_key);
  169. context.Cache.Insert (vary_key, varyby,
  170. new CacheDependency (files, keys),
  171. Cache.NoAbsoluteExpiration,
  172. Cache.NoSlidingExpiration,
  173. CacheItemPriority.Normal, null);
  174. lookup = false;
  175. #if NET_2_0
  176. cacheKey = vary_key;
  177. #endif
  178. }
  179. key = varyby.CreateKey (vary_key, context);
  180. if (lookup)
  181. prev = context.InternalCache [key] as CachedRawResponse;
  182. if (prev == null) {
  183. CachedRawResponse c = context.Response.GetCachedResponse ();
  184. if (c != null) {
  185. string [] files = new string [] { };
  186. string [] keys = new string [] { vary_key };
  187. bool sliding = context.Response.Cache.Sliding;
  188. context.InternalCache.Insert (key, c, new CacheDependency (files, keys),
  189. (sliding ? Cache.NoAbsoluteExpiration :
  190. context.Response.Cache.Expires),
  191. (sliding ? TimeSpan.FromSeconds (
  192. context.Response.Cache.Duration) :
  193. Cache.NoSlidingExpiration),
  194. CacheItemPriority.Normal, response_removed);
  195. c.VaryBy = varyby;
  196. varyby.ItemList.Add (key);
  197. #if NET_2_0
  198. cacheValue = key;
  199. #endif
  200. }
  201. }
  202. #if NET_2_0
  203. if (cacheKey != null) {
  204. lock (keysCacheLock) {
  205. if (!keysCache.ContainsKey (cacheKey))
  206. keysCache.Add (cacheKey, cacheValue);
  207. }
  208. }
  209. #endif
  210. }
  211. static void OnRawResponseRemoved (string key, object value, CacheItemRemovedReason reason)
  212. {
  213. CachedRawResponse c = (CachedRawResponse) value;
  214. c.VaryBy.ItemList.Remove (key);
  215. if (c.VaryBy.ItemList.Count != 0)
  216. return;
  217. HttpRuntime.Cache.Remove (c.VaryBy.Key);
  218. }
  219. }
  220. }