2 // System.Web.HttpCachePolicy
5 // Tim Coleman (tim@timcoleman.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.ComponentModel;
33 using System.Globalization;
35 using System.Security.Permissions;
38 using System.Web.Util;
42 // CAS - no InheritanceDemand here as the class is sealed
43 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44 public sealed class HttpCachePolicy
46 internal HttpCachePolicy ()
50 HttpCacheVaryByContentEncodings vary_by_content_encodings = new HttpCacheVaryByContentEncodings ();
51 HttpCacheVaryByHeaders vary_by_headers = new HttpCacheVaryByHeaders ();
52 HttpCacheVaryByParams vary_by_params = new HttpCacheVaryByParams ();
53 ArrayList validation_callbacks;
54 StringBuilder cache_extension;
55 internal HttpCacheability Cacheability;
57 bool etag_from_file_dependencies;
58 bool last_modified_from_file_dependencies;
63 internal bool have_expire_date;
64 internal DateTime expire_date;
65 internal bool have_last_modified;
66 internal DateTime last_modified;
68 //bool LastModifiedFromFileDependencies;
69 HttpCacheRevalidation revalidation;
70 string vary_by_custom;
76 bool sliding_expiration;
78 bool allow_response_in_browser_history;
79 bool allow_server_caching = true;
81 bool set_no_transform;
82 bool valid_until_expires;
85 public HttpCacheVaryByContentEncodings VaryByContentEncodings {
86 get { return vary_by_content_encodings; }
89 public HttpCacheVaryByHeaders VaryByHeaders {
90 get { return vary_by_headers; }
93 public HttpCacheVaryByParams VaryByParams {
94 get { return vary_by_params; }
97 internal bool AllowServerCaching {
98 get { return allow_server_caching; }
101 internal int Duration {
102 get { return duration; }
103 set { duration = value; }
106 internal bool Sliding {
107 get { return sliding_expiration; }
110 internal DateTime Expires {
111 get { return expire_date; }
114 internal ArrayList ValidationCallbacks {
115 get { return validation_callbacks; }
118 internal bool OmitVaryStar {
119 get { return omit_vary_star; }
122 internal bool ValidUntilExpires {
123 get { return valid_until_expires; }
126 internal int ExpireMinutes ()
128 if (!have_expire_date)
131 return (expire_date - DateTime.Now).Minutes;
134 public void AddValidationCallback (HttpCacheValidateHandler handler, object data)
137 throw new ArgumentNullException ("handler");
139 if (validation_callbacks == null)
140 validation_callbacks = new ArrayList ();
142 validation_callbacks.Add (new Pair (handler, data));
145 public void AppendCacheExtension (string extension)
147 if (extension == null)
148 throw new ArgumentNullException ("extension");
150 if (cache_extension == null)
151 cache_extension = new StringBuilder (extension);
153 cache_extension.Append (", " + extension);
157 // This one now allows the full range of Cacheabilities.
159 public void SetCacheability (HttpCacheability cacheability)
161 if (cacheability < HttpCacheability.NoCache || cacheability > HttpCacheability.ServerAndPrivate)
162 throw new ArgumentOutOfRangeException ("cacheability");
164 if (Cacheability > 0 && cacheability > Cacheability)
167 Cacheability = cacheability;
170 public void SetCacheability (HttpCacheability cacheability, string field)
173 throw new ArgumentNullException ("field");
175 if (cacheability != HttpCacheability.NoCache && cacheability != HttpCacheability.Private)
176 throw new ArgumentException ("Must be NoCache or Private", "cacheability");
179 fields = new ArrayList ();
181 fields.Add (new Pair (cacheability, field));
184 public void SetETag (string etag)
187 throw new ArgumentNullException ("etag");
189 if (this.etag != null)
190 throw new InvalidOperationException ("The ETag header has already been set");
192 if (etag_from_file_dependencies)
193 throw new InvalidOperationException ("SetEtagFromFileDependencies has already been called");
198 public void SetETagFromFileDependencies ()
200 if (this.etag != null)
201 throw new InvalidOperationException ("The ETag header has already been set");
203 etag_from_file_dependencies = true;
206 public void SetExpires (DateTime date)
208 if (have_expire_date && date > expire_date)
211 have_expire_date = true;
215 public void SetLastModified (DateTime date)
217 if (date > DateTime.Now)
218 throw new ArgumentOutOfRangeException ("date");
220 if (have_last_modified && date < last_modified)
223 have_last_modified = true;
224 last_modified = date;
227 public void SetLastModifiedFromFileDependencies ()
229 last_modified_from_file_dependencies = true;
232 public void SetMaxAge (TimeSpan date)
234 if (date < TimeSpan.Zero)
235 throw new ArgumentOutOfRangeException ("date");
237 if (HaveMaxAge && MaxAge < date)
244 public void SetNoServerCaching ()
246 allow_server_caching = false;
249 public void SetNoStore ()
254 public void SetNoTransforms ()
256 set_no_transform = true;
259 public void SetProxyMaxAge (TimeSpan delta)
261 if (delta < TimeSpan.Zero)
262 throw new ArgumentOutOfRangeException ("delta");
264 if (HaveProxyMaxAge && ProxyMaxAge < delta)
268 HaveProxyMaxAge = true;
271 public void SetRevalidation (HttpCacheRevalidation revalidation)
273 if (revalidation < HttpCacheRevalidation.AllCaches ||
274 revalidation > HttpCacheRevalidation.None)
275 throw new ArgumentOutOfRangeException ("revalidation");
277 if (this.revalidation > revalidation)
278 this.revalidation = revalidation;
281 public void SetSlidingExpiration (bool slide)
283 sliding_expiration = slide;
286 public void SetValidUntilExpires (bool validUntilExpires)
288 valid_until_expires = validUntilExpires;
291 public void SetVaryByCustom (string custom)
294 throw new ArgumentNullException ("custom");
296 if (vary_by_custom != null)
297 throw new InvalidOperationException ("VaryByCustom has already been set.");
299 vary_by_custom = custom;
302 internal string GetVaryByCustom ()
304 return vary_by_custom;
307 public void SetAllowResponseInBrowserHistory (bool allow)
309 if (Cacheability == HttpCacheability.NoCache || Cacheability == HttpCacheability.ServerAndNoCache)
310 allow_response_in_browser_history = allow;
313 internal void SetHeaders (HttpResponse response, NameValueCollection headers)
315 bool noCache = false;
318 switch (Cacheability) {
319 case HttpCacheability.Public:
323 case HttpCacheability.NoCache:
324 case HttpCacheability.ServerAndNoCache:
329 case HttpCacheability.Private:
330 case HttpCacheability.ServerAndPrivate:
337 response.CacheControl = cc;
338 if (!allow_response_in_browser_history) {
339 headers.Add ("Expires", "-1");
340 headers.Add ("Pragma", "no-cache");
344 cc = String.Concat (cc, ", max-age=", ((long) MaxAge.TotalSeconds).ToString ());
346 if (have_expire_date) {
347 string expires = TimeUtil.ToUtcTimeString (expire_date);
348 headers.Add ("Expires", expires);
353 cc = String.Concat (cc, ", no-store");
354 if (set_no_transform)
355 cc = String.Concat (cc, ", no-transform");
357 headers.Add ("Cache-Control", cc);
359 if (last_modified_from_file_dependencies || etag_from_file_dependencies)
360 HeadersFromFileDependencies (response);
363 headers.Add ("ETag", etag);
365 if (have_last_modified)
366 headers.Add ("Last-Modified", TimeUtil.ToUtcTimeString (last_modified));
368 if (!vary_by_params.IgnoreParams) {
369 string vb = vary_by_params.GetResponseHeaderValue ();
371 headers.Add ("Vary", vb);
375 void HeadersFromFileDependencies (HttpResponse response)
377 string [] fileDeps = response.FileDependencies;
379 if (fileDeps == null || fileDeps.Length == 0)
382 bool doEtag = etag != null && etag_from_file_dependencies;
383 if (!doEtag && !last_modified_from_file_dependencies)
386 DateTime latest_mod = DateTime.MinValue, mod;
387 StringBuilder etagsb = new StringBuilder ();
389 foreach (string f in fileDeps) {
390 if (!File.Exists (f))
393 mod = File.GetLastWriteTime (f);
399 if (last_modified_from_file_dependencies && mod > latest_mod)
402 etagsb.AppendFormat ("{0}", mod.Ticks.ToString ("x"));
405 if (last_modified_from_file_dependencies && latest_mod > DateTime.MinValue) {
406 last_modified = latest_mod;
407 have_last_modified = true;
410 if (doEtag && etagsb.Length > 0)
411 etag = etagsb.ToString ();
414 public void SetOmitVaryStar (bool omit)
416 omit_vary_star = omit;