* Cache.cs: Close entries that are removed.
[mono.git] / mcs / class / System.Web / System.Web.Caching / OutputCacheModule.cs
1 //
2 // System.Web.Caching.OutputCacheModule
3 //
4 // Authors:
5 //  Jackson Harper (jackson@ximian.com)
6 //
7 // (C) 2003 Novell, Inc (http://www.novell.com)
8 //
9
10 using System.IO;
11 using System.Web;
12 using System.Web.Util;
13 using System.Collections;
14
15 namespace System.Web.Caching {
16         
17         internal sealed class OutputCacheModule : IHttpModule {
18
19                 private CacheItemRemovedCallback response_removed;
20                 
21                 public OutputCacheModule ()
22                 {
23                 }
24
25                 public void Dispose ()
26                 {
27                 }
28
29                 public void Init (HttpApplication app)
30                 {
31                         app.AddOnResolveRequestCacheAsync (
32                                 new BeginEventHandler (OnBeginRequestCache),
33                                 new EndEventHandler (OnEndRequestCache));
34
35                         app.AddOnUpdateRequestCacheAsync (
36                                 new BeginEventHandler (OnBeginUpdateCache),
37                                 new EndEventHandler (OnEndUpdateCache));
38  
39                         response_removed = new CacheItemRemovedCallback (OnRawResponseRemoved);
40                 }
41
42                 IAsyncResult OnBeginRequestCache (object o, EventArgs args, AsyncCallback cb, object data)
43                 {
44                         HttpApplication app = (HttpApplication) o;
45                         HttpContext context = app.Context;
46                         
47                         string vary_key = context.Request.FilePath;
48                         CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
49                         string key;
50                         CachedRawResponse c;
51
52                         if (varyby == null)
53                                 goto leave;
54
55                         key = varyby.CreateKey (vary_key, context);
56                         c = context.Cache [key] as CachedRawResponse;
57                         
58                         if (c != null) {
59                                 
60                                 context.Response.ClearContent ();
61                                 context.Response.BinaryWrite (c.GetData (), 0, c.ContentLength);
62
63                                 context.Response.ClearHeaders ();
64                                 c.DateHeader.Value = TimeUtil.ToUtcTimeString (DateTime.Now);
65                                 context.Response.SetCachedHeaders (c.Headers);
66
67                                 context.Response.StatusCode = c.StatusCode;
68                                 context.Response.StatusDescription = c.StatusDescription;
69                                 
70                                 app.CompleteRequest ();
71                         } else if (c != null) {
72                                 context.Cache.Remove (key);
73                         }
74
75                 leave:
76                         HttpAsyncResult result = new HttpAsyncResult (cb,this);
77                         result.Complete (true, o, null);
78                         
79                         return result;
80                 }
81
82                 void OnEndRequestCache (IAsyncResult result)
83                 {
84                 }
85
86                 IAsyncResult OnBeginUpdateCache (object o, EventArgs args, AsyncCallback cb, object data)
87                 {
88                         HttpApplication app = (HttpApplication) o;
89                         HttpContext context = app.Context;
90                         HttpAsyncResult result;
91
92                         if (context.Response.IsCached && context.Response.StatusCode == 200 && 
93                             !context.Trace.IsEnabled)
94                                 DoCacheInsert (context);
95
96                         result = new HttpAsyncResult (cb, this);
97                         result.Complete (true, o, null);
98                         return result;
99                 }
100
101                 void OnEndUpdateCache (IAsyncResult result)
102                 {
103                 }
104
105                 private void DoCacheInsert (HttpContext context)
106                 {
107                         string vary_key = context.Request.FilePath;
108                         string key;
109                         CachedVaryBy varyby = context.Cache [vary_key] as CachedVaryBy;
110                         CachedRawResponse prev = null;
111                         bool lookup = true;
112                         
113                         if (varyby == null) {
114                                 string path = context.Request.MapPath (vary_key);
115                                 string [] files = new string [] { path };
116                                 string [] keys = new string [0];
117                                 varyby = new CachedVaryBy (context.Response.Cache, vary_key);
118                                 context.Cache.InsertPrivate (vary_key, varyby,
119                                                 new CacheDependency (files, keys),
120                                                 Cache.NoAbsoluteExpiration,
121                                                 Cache.NoSlidingExpiration,
122                                                 CacheItemPriority.Normal, null);
123                                 lookup = false;
124                         } 
125                         
126                         key = varyby.CreateKey (vary_key, context);
127
128                         if (lookup)
129                                 prev = context.Cache [key] as CachedRawResponse;
130                         
131                         if (prev == null) {
132                                 CachedRawResponse c = context.Response.GetCachedResponse ();
133                                 string [] files = new string [] { };
134                                 string [] keys = new string [] { vary_key };
135                                 bool sliding = context.Response.Cache.Sliding;
136
137                                 context.Cache.InsertPrivate (key, c, new CacheDependency (files, keys),
138                                                 (sliding ? Cache.NoAbsoluteExpiration :
139                                                                 context.Response.Cache.Expires),
140                                                 (sliding ? TimeSpan.FromSeconds (
141                                                         context.Response.Cache.Duration) :
142                                                                 Cache.NoSlidingExpiration),
143                                                 CacheItemPriority.Normal, response_removed);
144                                 c.VaryBy = varyby;
145                                 varyby.ItemList.Add (key);
146                         } 
147                 }
148
149                 private void OnRawResponseRemoved (string key, object value, CacheItemRemovedReason reason)
150                 {
151                         CachedRawResponse c = (CachedRawResponse) value;
152
153                         c.VaryBy.ItemList.Remove (key);                 
154                         if (c.VaryBy.ItemList.Count != 0)
155                                 return;
156                         
157                         Cache cache = HttpRuntime.Cache;
158                         cache.Remove (c.VaryBy.Key);
159                 }
160         }
161 }
162