New test.
[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                 
89 #endregion
90
91                 internal event CacheabilityUpdatedCallback CacheabilityUpdated;
92                 
93 #region Properties
94                 
95                 public HttpCacheVaryByHeaders VaryByHeaders {
96                         get { return vary_by_headers; }
97                 }
98
99                 public HttpCacheVaryByParams VaryByParams {
100                         get { return vary_by_params; }
101                 }
102
103                 internal int Duration {
104                         get { return duration; }
105                         set { duration = value; }
106                 }
107
108                 internal bool Sliding {
109                         get { return sliding_expiration; }
110                 }
111                 
112                 internal DateTime Expires {
113                         get { return expire_date; }
114                 }
115
116                 internal ArrayList ValidationCallbacks {
117                         get { return validation_callbacks; }
118                 }
119
120 #endregion // Properties
121
122 #region Methods
123
124                 internal int ExpireMinutes ()
125                 {
126                         if (!have_expire_date)
127                                 return 0;
128                         
129                         return (expire_date - DateTime.Now).Minutes;
130                 }
131                 
132                 public void AddValidationCallback (HttpCacheValidateHandler handler, object data)
133                 {
134                         if (handler == null)
135                                 throw new ArgumentNullException ("handler");
136
137                         if (validation_callbacks == null)
138                                 validation_callbacks = new ArrayList ();
139
140                         validation_callbacks.Add (new Pair (handler, data));
141                 }
142
143                 public void AppendCacheExtension (string extension)
144                 {
145                         if (extension == null)
146                                 throw new ArgumentNullException ("extension");
147
148                         if (cache_extension == null)
149                                 cache_extension = new StringBuilder (extension);
150                         else
151                                 cache_extension.Append (", " + extension);
152                 }
153
154                 //
155                 // This one now allows the full range of Cacheabilities.
156                 //
157                 public void SetCacheability (HttpCacheability cacheability)
158                 {
159                         if (cacheability < HttpCacheability.NoCache || cacheability > HttpCacheability.ServerAndPrivate)
160                                 throw new ArgumentOutOfRangeException ("cacheability");
161
162                         if (Cacheability > 0 && cacheability > Cacheability)
163                                 return;
164                         
165                         Cacheability = cacheability;
166
167                         if (CacheabilityUpdated != null)
168                                 CacheabilityUpdated (this, new CacheabilityUpdatedEventArgs (cacheability));
169                 }
170
171                 public void SetCacheability (HttpCacheability cacheability, string field)
172                 {
173                         if (field == null)
174                                 throw new ArgumentNullException ("field");
175
176                         if (cacheability != HttpCacheability.NoCache && cacheability != HttpCacheability.Private)
177                                 throw new ArgumentException ("Must be NoCache or Private", "cacheability");
178
179                         if (fields == null)
180                                 fields = new ArrayList ();
181
182                         fields.Add (new Pair (cacheability, field));
183                 }
184
185                 public void SetETag (string etag)
186                 {
187                         if (etag == null)
188                                 throw new ArgumentNullException ("etag");
189
190                         if (this.etag != null)
191                                 throw new InvalidOperationException ("The ETag header has already been set");
192
193                         if (etag_from_file_dependencies)
194                                 throw new InvalidOperationException ("SetEtagFromFileDependencies has already been called");
195
196                         this.etag = etag;
197                 }
198
199                 public void SetETagFromFileDependencies ()
200                 {
201                         if (this.etag != null)
202                                 throw new InvalidOperationException ("The ETag header has already been set");
203
204                         etag_from_file_dependencies = true;
205                 }
206
207                 public void SetExpires (DateTime date)
208                 {
209                         if (have_expire_date && date > expire_date)
210                                 return;
211
212                         have_expire_date = true;
213                         expire_date = date;
214                 }
215
216                 public void SetLastModified (DateTime date)
217                 {
218                         if (date > DateTime.Now)
219                                 throw new ArgumentOutOfRangeException ("date");
220
221                         if (have_last_modified && date < last_modified)
222                                 return;
223
224                         have_last_modified = true;
225                         last_modified = date;
226                 }
227
228                 [MonoTODO]
229                 public void SetLastModifiedFromFileDependencies ()
230                 {
231                         throw new NotImplementedException (); 
232                 }
233
234                 public void SetMaxAge (TimeSpan date)
235                 {
236                         if (date < TimeSpan.Zero)
237                                 throw new ArgumentOutOfRangeException ("date");
238                         
239                         if (HaveMaxAge && MaxAge < date)
240                                 return;
241
242                         MaxAge = date;
243                         HaveMaxAge = true;
244                 }
245
246                 [MonoTODO]
247                 public void SetNoServerCaching ()
248                 {
249                         throw new NotImplementedException (); 
250                 }
251
252                 [MonoTODO]
253                 public void SetNoStore ()
254                 {
255                         throw new NotImplementedException (); 
256                 }
257
258                 [MonoTODO]
259                 public void SetNoTransforms ()
260                 {
261                         throw new NotImplementedException (); 
262                 }
263
264                 public void SetProxyMaxAge (TimeSpan delta)
265                 {
266                         if (delta < TimeSpan.Zero)
267                                 throw new ArgumentOutOfRangeException ("delta");
268
269                         if (HaveProxyMaxAge && ProxyMaxAge < delta)
270                                 return;
271
272                         ProxyMaxAge = delta;
273                 }
274
275                 public void SetRevalidation (HttpCacheRevalidation revalidation)
276                 {
277                         if (revalidation < HttpCacheRevalidation.AllCaches ||
278                             revalidation > HttpCacheRevalidation.None)
279                                 throw new ArgumentOutOfRangeException ("revalidation");
280
281                         if (this.revalidation > revalidation)
282                                 this.revalidation = revalidation;
283                 }
284
285                 public void SetSlidingExpiration (bool slide)
286                 {
287                         sliding_expiration = slide;
288                 }
289
290                 [MonoTODO]
291                 public void SetValidUntilExpires (bool validUntilExpires)
292                 {
293                         throw new NotImplementedException (); 
294                 }
295
296                 public void SetVaryByCustom (string custom)
297                 {
298                         if (custom == null)
299                                 throw new ArgumentNullException ("custom");
300
301                         if (vary_by_custom != null)
302                                 throw new InvalidOperationException ("VaryByCustom has already been set.");
303
304                         vary_by_custom = custom;
305                 }
306
307                 internal string GetVaryByCustom ()
308                 {
309                         return vary_by_custom;
310                 }
311
312                 public void SetAllowResponseInBrowserHistory (bool allow)
313                 {
314                         if (Cacheability == HttpCacheability.NoCache || Cacheability == HttpCacheability.ServerAndNoCache) 
315                                 allow_response_in_browser_history = allow;
316                 }
317
318                 internal void SetHeaders (HttpResponse response, ArrayList headers)
319                 {
320                         bool noCache = false;
321                         string cc = null;
322
323                         switch (Cacheability) {
324                         case HttpCacheability.Public:
325                                 cc = "public";
326                                 break;
327
328                         case HttpCacheability.Private:
329                         case HttpCacheability.ServerAndPrivate:
330                                 cc = "private";
331                                 break;
332
333                         case HttpCacheability.NoCache:
334                         case HttpCacheability.ServerAndNoCache:
335                         default:
336                                 noCache = true;
337                                 cc = "no-cache";
338                                 break;
339                         }
340
341                         if (noCache) {
342                                 response.CacheControl = cc;
343                                 if (!allow_response_in_browser_history) {
344                                         headers.Add (new UnknownResponseHeader ("Expires", "-1"));
345                                         headers.Add (new UnknownResponseHeader ("Pragma", "no-cache"));
346                                 }
347                         } else {
348                                 if (MaxAge.TotalSeconds != 0)
349                                         cc = String.Format ("{0}, max-age={1}", cc, (long) MaxAge.TotalSeconds);
350
351                                 string expires = TimeUtil.ToUtcTimeString (expire_date);
352                                 headers.Add (new UnknownResponseHeader ("Expires", expires));
353                         }
354
355                         headers.Add (new UnknownResponseHeader ("Cache-Control", cc));
356                                                 
357                         if (etag != null)
358                                 headers.Add (new UnknownResponseHeader ("ETag", etag));
359
360                         if (have_last_modified)
361                                 headers.Add (new UnknownResponseHeader ("Last-Modified",
362                                                              TimeUtil.ToUtcTimeString (last_modified)));
363
364                         if (!vary_by_params.IgnoreParams) {
365                                 BaseResponseHeader vb = vary_by_params.GetResponseHeader ();
366                                 if (vb != null)
367                                         headers.Add (vb);
368                         }
369
370                 }
371 #if NET_2_0
372                 [MonoTODO]
373                 public void SetOmitVaryStar (bool omit)
374                 {
375                         throw new NotImplementedException (); 
376                 }
377 #endif
378                 
379 #endregion // Methods
380         }
381 }
382