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 ()
51 HttpCacheVaryByContentEncodings vary_by_content_encodings = new HttpCacheVaryByContentEncodings ();
53 HttpCacheVaryByHeaders vary_by_headers = new HttpCacheVaryByHeaders ();
54 HttpCacheVaryByParams vary_by_params = new HttpCacheVaryByParams ();
55 ArrayList validation_callbacks;
56 StringBuilder cache_extension;
57 internal HttpCacheability Cacheability;
59 bool etag_from_file_dependencies;
60 bool last_modified_from_file_dependencies;
65 internal bool have_expire_date;
66 internal DateTime expire_date;
67 internal bool have_last_modified;
68 internal DateTime last_modified;
70 //bool LastModifiedFromFileDependencies;
71 HttpCacheRevalidation revalidation;
72 string vary_by_custom;
78 bool sliding_expiration;
80 bool allow_response_in_browser_history;
81 bool allow_server_caching = true;
83 bool set_no_transform;
84 bool valid_until_expires;
88 public HttpCacheVaryByContentEncodings VaryByContentEncodings {
89 get { return vary_by_content_encodings; }
93 public HttpCacheVaryByHeaders VaryByHeaders {
94 get { return vary_by_headers; }
97 public HttpCacheVaryByParams VaryByParams {
98 get { return vary_by_params; }
101 internal bool AllowServerCaching {
102 get { return allow_server_caching; }
105 internal int Duration {
106 get { return duration; }
107 set { duration = value; }
110 internal bool Sliding {
111 get { return sliding_expiration; }
114 internal DateTime Expires {
115 get { return expire_date; }
118 internal ArrayList ValidationCallbacks {
119 get { return validation_callbacks; }
122 // always false in 1.x
123 internal bool OmitVaryStar {
126 return omit_vary_star;
133 internal bool ValidUntilExpires {
134 get { return valid_until_expires; }
137 internal int ExpireMinutes ()
139 if (!have_expire_date)
142 return (expire_date - DateTime.Now).Minutes;
145 public void AddValidationCallback (HttpCacheValidateHandler handler, object data)
148 throw new ArgumentNullException ("handler");
150 if (validation_callbacks == null)
151 validation_callbacks = new ArrayList ();
153 validation_callbacks.Add (new Pair (handler, data));
156 public void AppendCacheExtension (string extension)
158 if (extension == null)
159 throw new ArgumentNullException ("extension");
161 if (cache_extension == null)
162 cache_extension = new StringBuilder (extension);
164 cache_extension.Append (", " + extension);
168 // This one now allows the full range of Cacheabilities.
170 public void SetCacheability (HttpCacheability cacheability)
172 if (cacheability < HttpCacheability.NoCache || cacheability > HttpCacheability.ServerAndPrivate)
173 throw new ArgumentOutOfRangeException ("cacheability");
175 if (Cacheability > 0 && cacheability > Cacheability)
178 Cacheability = cacheability;
181 public void SetCacheability (HttpCacheability cacheability, string field)
184 throw new ArgumentNullException ("field");
186 if (cacheability != HttpCacheability.NoCache && cacheability != HttpCacheability.Private)
187 throw new ArgumentException ("Must be NoCache or Private", "cacheability");
190 fields = new ArrayList ();
192 fields.Add (new Pair (cacheability, field));
195 public void SetETag (string etag)
198 throw new ArgumentNullException ("etag");
200 if (this.etag != null)
201 throw new InvalidOperationException ("The ETag header has already been set");
203 if (etag_from_file_dependencies)
204 throw new InvalidOperationException ("SetEtagFromFileDependencies has already been called");
209 public void SetETagFromFileDependencies ()
211 if (this.etag != null)
212 throw new InvalidOperationException ("The ETag header has already been set");
214 etag_from_file_dependencies = true;
217 public void SetExpires (DateTime date)
219 if (have_expire_date && date > expire_date)
222 have_expire_date = true;
226 public void SetLastModified (DateTime date)
228 if (date > DateTime.Now)
229 throw new ArgumentOutOfRangeException ("date");
231 if (have_last_modified && date < last_modified)
234 have_last_modified = true;
235 last_modified = date;
238 public void SetLastModifiedFromFileDependencies ()
240 last_modified_from_file_dependencies = true;
243 public void SetMaxAge (TimeSpan date)
245 if (date < TimeSpan.Zero)
246 throw new ArgumentOutOfRangeException ("date");
248 if (HaveMaxAge && MaxAge < date)
255 public void SetNoServerCaching ()
257 allow_server_caching = false;
260 public void SetNoStore ()
265 public void SetNoTransforms ()
267 set_no_transform = true;
270 public void SetProxyMaxAge (TimeSpan delta)
272 if (delta < TimeSpan.Zero)
273 throw new ArgumentOutOfRangeException ("delta");
275 if (HaveProxyMaxAge && ProxyMaxAge < delta)
279 HaveProxyMaxAge = true;
282 public void SetRevalidation (HttpCacheRevalidation revalidation)
284 if (revalidation < HttpCacheRevalidation.AllCaches ||
285 revalidation > HttpCacheRevalidation.None)
286 throw new ArgumentOutOfRangeException ("revalidation");
288 if (this.revalidation > revalidation)
289 this.revalidation = revalidation;
292 public void SetSlidingExpiration (bool slide)
294 sliding_expiration = slide;
297 public void SetValidUntilExpires (bool validUntilExpires)
299 valid_until_expires = validUntilExpires;
302 public void SetVaryByCustom (string custom)
305 throw new ArgumentNullException ("custom");
307 if (vary_by_custom != null)
308 throw new InvalidOperationException ("VaryByCustom has already been set.");
310 vary_by_custom = custom;
313 internal string GetVaryByCustom ()
315 return vary_by_custom;
318 public void SetAllowResponseInBrowserHistory (bool allow)
320 if (Cacheability == HttpCacheability.NoCache || Cacheability == HttpCacheability.ServerAndNoCache)
321 allow_response_in_browser_history = allow;
324 internal void SetHeaders (HttpResponse response, NameValueCollection headers)
326 bool noCache = false;
329 switch (Cacheability) {
330 case HttpCacheability.Public:
334 case HttpCacheability.NoCache:
335 case HttpCacheability.ServerAndNoCache:
340 case HttpCacheability.Private:
341 case HttpCacheability.ServerAndPrivate:
348 response.CacheControl = cc;
349 if (!allow_response_in_browser_history) {
350 headers.Add ("Expires", "-1");
351 headers.Add ("Pragma", "no-cache");
355 cc = String.Concat (cc, ", max-age=", ((long) MaxAge.TotalSeconds).ToString ());
357 if (have_expire_date) {
358 string expires = TimeUtil.ToUtcTimeString (expire_date);
359 headers.Add ("Expires", expires);
364 cc = String.Concat (cc, ", no-store");
365 if (set_no_transform)
366 cc = String.Concat (cc, ", no-transform");
368 headers.Add ("Cache-Control", cc);
370 if (last_modified_from_file_dependencies || etag_from_file_dependencies)
371 HeadersFromFileDependencies (response);
374 headers.Add ("ETag", etag);
376 if (have_last_modified)
377 headers.Add ("Last-Modified", TimeUtil.ToUtcTimeString (last_modified));
379 if (!vary_by_params.IgnoreParams) {
380 string vb = vary_by_params.GetResponseHeaderValue ();
382 headers.Add ("Vary", vb);
386 void HeadersFromFileDependencies (HttpResponse response)
388 string [] fileDeps = response.FileDependencies;
390 if (fileDeps == null || fileDeps.Length == 0)
393 bool doEtag = etag != null && etag_from_file_dependencies;
394 if (!doEtag && !last_modified_from_file_dependencies)
397 DateTime latest_mod = DateTime.MinValue, mod;
398 StringBuilder etagsb = new StringBuilder ();
400 foreach (string f in fileDeps) {
401 if (!File.Exists (f))
404 mod = File.GetLastWriteTime (f);
410 if (last_modified_from_file_dependencies && mod > latest_mod)
413 etagsb.AppendFormat ("{0}", mod.Ticks.ToString ("x"));
416 if (last_modified_from_file_dependencies && latest_mod > DateTime.MinValue) {
417 last_modified = latest_mod;
418 have_last_modified = true;
421 if (doEtag && etagsb.Length > 0)
422 etag = etagsb.ToString ();
426 public void SetOmitVaryStar (bool omit)
428 omit_vary_star = omit;