2007-05-07 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpCachePolicy.cs
1 // 
2 // System.Web.HttpCachePolicy
3 //
4 // Authors:
5 //      Tim Coleman (tim@timcoleman.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
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
30 using System.Collections;
31 using System.Globalization;
32 using System.Security.Permissions;
33 using System.Text;
34 using System.Web.UI;
35 using System.Web.Util;
36
37 namespace System.Web {
38
39         class CacheabilityUpdatedEventArgs : EventArgs {
40
41                 public readonly HttpCacheability Cacheability;
42
43                 public CacheabilityUpdatedEventArgs (HttpCacheability cacheability)
44                 {
45                         Cacheability = cacheability;
46                 }
47         }
48         
49         internal delegate void CacheabilityUpdatedCallback (object sender, CacheabilityUpdatedEventArgs args);
50         
51         // CAS - no InheritanceDemand here as the class is sealed
52         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
53         public sealed class HttpCachePolicy {
54
55                 internal HttpCachePolicy ()
56                 {
57                 }
58
59 #region Fields
60
61                 HttpCacheVaryByHeaders vary_by_headers = new HttpCacheVaryByHeaders ();
62                 HttpCacheVaryByParams vary_by_params = new HttpCacheVaryByParams ();
63                 ArrayList validation_callbacks;
64                 StringBuilder cache_extension;
65                 internal HttpCacheability Cacheability;
66                 string etag;
67                 bool etag_from_file_dependencies;
68
69                 //
70                 // Used externally
71                 //
72                 internal bool have_expire_date;
73                 internal DateTime expire_date;
74                 internal bool have_last_modified;
75                 internal DateTime last_modified;
76                 
77                 //bool LastModifiedFromFileDependencies;
78                 HttpCacheRevalidation revalidation;
79                 string vary_by_custom;
80                 bool HaveMaxAge;
81                 TimeSpan MaxAge;
82                 bool HaveProxyMaxAge;
83                 TimeSpan ProxyMaxAge;
84                 ArrayList fields;
85                 bool sliding_expiration;
86                 int duration;
87                 bool allow_response_in_browser_history;
88                 bool allow_server_caching = true;
89                 bool set_no_store = false;
90                 bool set_no_transform = false;
91                 bool valid_until_expires = false;
92                 
93                 // always false in 1.x
94                 bool omit_vary_star = false;
95                 
96 #endregion
97
98                 internal event CacheabilityUpdatedCallback CacheabilityUpdated;
99                 
100 #region Properties
101                 
102                 public HttpCacheVaryByHeaders VaryByHeaders {
103                         get { return vary_by_headers; }
104                 }
105
106                 public HttpCacheVaryByParams VaryByParams {
107                         get { return vary_by_params; }
108                 }
109
110                 internal bool AllowServerCaching {
111                         get { return allow_server_caching; }
112                 }
113                 
114                 internal int Duration {
115                         get { return duration; }
116                         set { duration = value; }
117                 }
118
119                 internal bool Sliding {
120                         get { return sliding_expiration; }
121                 }
122                 
123                 internal DateTime Expires {
124                         get { return expire_date; }
125                 }
126
127                 internal ArrayList ValidationCallbacks {
128                         get { return validation_callbacks; }
129                 }
130
131                 // always false in 1.x
132                 internal bool OmitVaryStar {
133                         get { return omit_vary_star; }
134                 }
135
136                 internal bool ValidUntilExpires {
137                         get { return valid_until_expires; }
138                 }
139                 
140 #endregion // Properties
141
142 #region Methods
143
144                 internal int ExpireMinutes ()
145                 {
146                         if (!have_expire_date)
147                                 return 0;
148                         
149                         return (expire_date - DateTime.Now).Minutes;
150                 }
151                 
152                 public void AddValidationCallback (HttpCacheValidateHandler handler, object data)
153                 {
154                         if (handler == null)
155                                 throw new ArgumentNullException ("handler");
156
157                         if (validation_callbacks == null)
158                                 validation_callbacks = new ArrayList ();
159
160                         validation_callbacks.Add (new Pair (handler, data));
161                 }
162
163                 public void AppendCacheExtension (string extension)
164                 {
165                         if (extension == null)
166                                 throw new ArgumentNullException ("extension");
167
168                         if (cache_extension == null)
169                                 cache_extension = new StringBuilder (extension);
170                         else
171                                 cache_extension.Append (", " + extension);
172                 }
173
174                 //
175                 // This one now allows the full range of Cacheabilities.
176                 //
177                 public void SetCacheability (HttpCacheability cacheability)
178                 {
179                         if (cacheability < HttpCacheability.NoCache || cacheability > HttpCacheability.ServerAndPrivate)
180                                 throw new ArgumentOutOfRangeException ("cacheability");
181
182                         if (Cacheability > 0 && cacheability > Cacheability)
183                                 return;
184                         
185                         Cacheability = cacheability;
186
187                         if (CacheabilityUpdated != null)
188                                 CacheabilityUpdated (this, new CacheabilityUpdatedEventArgs (cacheability));
189                 }
190
191                 public void SetCacheability (HttpCacheability cacheability, string field)
192                 {
193                         if (field == null)
194                                 throw new ArgumentNullException ("field");
195
196                         if (cacheability != HttpCacheability.NoCache && cacheability != HttpCacheability.Private)
197                                 throw new ArgumentException ("Must be NoCache or Private", "cacheability");
198
199                         if (fields == null)
200                                 fields = new ArrayList ();
201
202                         fields.Add (new Pair (cacheability, field));
203                 }
204
205                 public void SetETag (string etag)
206                 {
207                         if (etag == null)
208                                 throw new ArgumentNullException ("etag");
209
210                         if (this.etag != null)
211                                 throw new InvalidOperationException ("The ETag header has already been set");
212
213                         if (etag_from_file_dependencies)
214                                 throw new InvalidOperationException ("SetEtagFromFileDependencies has already been called");
215
216                         this.etag = etag;
217                 }
218
219                 public void SetETagFromFileDependencies ()
220                 {
221                         if (this.etag != null)
222                                 throw new InvalidOperationException ("The ETag header has already been set");
223
224                         etag_from_file_dependencies = true;
225                 }
226
227                 public void SetExpires (DateTime date)
228                 {
229                         if (have_expire_date && date > expire_date)
230                                 return;
231
232                         have_expire_date = true;
233                         expire_date = date;
234                 }
235
236                 public void SetLastModified (DateTime date)
237                 {
238                         if (date > DateTime.Now)
239                                 throw new ArgumentOutOfRangeException ("date");
240
241                         if (have_last_modified && date < last_modified)
242                                 return;
243
244                         have_last_modified = true;
245                         last_modified = date;
246                 }
247
248                 [MonoTODO ("Not implemented")]
249                 public void SetLastModifiedFromFileDependencies ()
250                 {
251                         throw new NotImplementedException (); 
252                 }
253
254                 public void SetMaxAge (TimeSpan date)
255                 {
256                         if (date < TimeSpan.Zero)
257                                 throw new ArgumentOutOfRangeException ("date");
258                         
259                         if (HaveMaxAge && MaxAge < date)
260                                 return;
261
262                         MaxAge = date;
263                         HaveMaxAge = true;
264                 }
265
266                 public void SetNoServerCaching ()
267                 {
268                         allow_server_caching = false;
269                 }
270
271                 public void SetNoStore ()
272                 {
273                         set_no_store = true;
274                 }
275
276                 public void SetNoTransforms ()
277                 {
278                         set_no_transform = true;
279                 }
280
281                 public void SetProxyMaxAge (TimeSpan delta)
282                 {
283                         if (delta < TimeSpan.Zero)
284                                 throw new ArgumentOutOfRangeException ("delta");
285
286                         if (HaveProxyMaxAge && ProxyMaxAge < delta)
287                                 return;
288
289                         ProxyMaxAge = delta;
290                 }
291
292                 public void SetRevalidation (HttpCacheRevalidation revalidation)
293                 {
294                         if (revalidation < HttpCacheRevalidation.AllCaches ||
295                             revalidation > HttpCacheRevalidation.None)
296                                 throw new ArgumentOutOfRangeException ("revalidation");
297
298                         if (this.revalidation > revalidation)
299                                 this.revalidation = revalidation;
300                 }
301
302                 public void SetSlidingExpiration (bool slide)
303                 {
304                         sliding_expiration = slide;
305                 }
306
307                 public void SetValidUntilExpires (bool validUntilExpires)
308                 {
309                         valid_until_expires = validUntilExpires;
310                 }
311
312                 public void SetVaryByCustom (string custom)
313                 {
314                         if (custom == null)
315                                 throw new ArgumentNullException ("custom");
316
317                         if (vary_by_custom != null)
318                                 throw new InvalidOperationException ("VaryByCustom has already been set.");
319
320                         vary_by_custom = custom;
321                 }
322
323                 internal string GetVaryByCustom ()
324                 {
325                         return vary_by_custom;
326                 }
327
328                 public void SetAllowResponseInBrowserHistory (bool allow)
329                 {
330                         if (Cacheability == HttpCacheability.NoCache || Cacheability == HttpCacheability.ServerAndNoCache) 
331                                 allow_response_in_browser_history = allow;
332                 }
333
334                 internal void SetHeaders (HttpResponse response, ArrayList headers)
335                 {
336                         bool noCache = false;
337                         string cc = null;
338
339                         switch (Cacheability) {
340                         case HttpCacheability.Public:
341                                 cc = "public";
342                                 break;
343
344                         case HttpCacheability.Private:
345                         case HttpCacheability.ServerAndPrivate:
346                                 cc = "private";
347                                 break;
348
349                         case HttpCacheability.NoCache:
350                         case HttpCacheability.ServerAndNoCache:
351                         default:
352                                 noCache = true;
353                                 cc = "no-cache";
354                                 break;
355                         }
356
357                         if (noCache) {
358                                 response.CacheControl = cc;
359                                 if (!allow_response_in_browser_history) {
360                                         headers.Add (new UnknownResponseHeader ("Expires", "-1"));
361                                         headers.Add (new UnknownResponseHeader ("Pragma", "no-cache"));
362                                 }
363                         } else {
364                                 if (MaxAge.TotalSeconds != 0)
365                                         cc = String.Format ("{0}, max-age={1}", cc, (long) MaxAge.TotalSeconds);
366
367                                 string expires = TimeUtil.ToUtcTimeString (expire_date);
368                                 headers.Add (new UnknownResponseHeader ("Expires", expires));
369                         }
370
371                         if (set_no_store)
372                                 cc = String.Format ("{0}, no-store", cc);
373                         if (set_no_transform)
374                                 cc = String.Format ("{0}, no-transform", cc);
375                         
376                         headers.Add (new UnknownResponseHeader ("Cache-Control", cc));
377                                                 
378                         if (etag != null)
379                                 headers.Add (new UnknownResponseHeader ("ETag", etag));
380
381                         if (have_last_modified)
382                                 headers.Add (new UnknownResponseHeader ("Last-Modified",
383                                                              TimeUtil.ToUtcTimeString (last_modified)));
384
385                         if (!vary_by_params.IgnoreParams) {
386                                 BaseResponseHeader vb = vary_by_params.GetResponseHeader ();
387                                 if (vb != null)
388                                         headers.Add (vb);
389                         }
390
391                 }
392 #if NET_2_0
393                 public void SetOmitVaryStar (bool omit)
394                 {
395                         omit_vary_star = omit;
396                 }
397 #endif
398                 
399 #endregion // Methods
400         }
401 }
402