private static bool IsOutDated(string ifRangeHeader, DateTime lastModified) { try { DateTime utcLastModified = lastModified.ToUniversalTime(); DateTime utc = HttpDate.UtcParse(ifRangeHeader); return(utc < utcLastModified); } catch { return(true); } }
/* * Try to find this request in the cache. If so, return it. Otherwise, * store the cache key for use on Leave. */ /// <include file='doc\OutputCacheModule.uex' path='docs/doc[@for="OutputCacheModule.OnEnter"]/*' /> /// <devdoc> /// <para>Raises the <see langword='Enter'/> /// event, which searches the output cache for an item to satisfy the HTTP request. </para> /// </devdoc> internal /*public*/ void OnEnter(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; string key; HttpRequest request; HttpResponse response; Object item; CachedRawResponse cachedRawResponse; HttpCachePolicySettings settings; int i, n; bool sendBody; HttpValidationStatus validationStatus, validationStatusFinal; ValidationCallbackInfo callbackInfo; string ifModifiedSinceHeader; DateTime utcIfModifiedSince; string etag; string[] etags; int send304; string cacheControl; string[] cacheDirectives = null; string pragma; string[] pragmaDirectives = null; string directive; int maxage; int minfresh; int age; int fresh; bool hasValidationPolicy; CachedVary cachedVary; CacheInternal cacheInternal; Debug.Trace("OutputCacheModuleEnter", "Beginning OutputCacheModule::Enter"); app = (HttpApplication)source; context = app.Context; request = context.Request; response = context.Response; _key = null; _recordedCacheMiss = false; _method = CacheRequestMethod.Invalid; /* * Check if the request can be resolved for this method. */ switch (request.HttpMethod) { case "HEAD": _method = CacheRequestMethod.Head; break; case "GET": _method = CacheRequestMethod.Get; break; case "POST": _method = CacheRequestMethod.Post; break; default: Debug.Trace("OutputCacheModuleEnter", "Output cache miss, Http method not GET, POST, or HEAD" + "\nReturning from OutputCacheModule::Enter"); return; } /* * Create a lookup key. Remember the key for use inside Leave() */ _key = key = CreateOutputCachedItemKey(context, null); Debug.Assert(_key != null, "_key != null"); /* * Lookup the cached item. */ cacheInternal = HttpRuntime.CacheInternal; item = cacheInternal.Get(key); if (item == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, item not found.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); return; } cachedVary = item as CachedVary; if (cachedVary != null) { /* * This cached output has a Vary policy. Create a new key based * on the vary headers in cachedRawResponse and try again. */ Debug.Trace("OutputCacheModuleEnter", "Output cache found CachedVary, \n\tkey=" + key); key = CreateOutputCachedItemKey(context, cachedVary); if (key == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, key could not be created for vary-by item." + "\nReturning from OutputCacheModule::Enter"); return; } item = cacheInternal.Get(key); if (item == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, item not found.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); return; } } Debug.Assert(item.GetType() == typeof(CachedRawResponse), "item.GetType() == typeof(CachedRawResponse)"); cachedRawResponse = (CachedRawResponse)item; settings = cachedRawResponse._settings; if (cachedVary == null && !settings.IgnoreParams) { /* * This cached output has no vary policy, so make sure it doesn't have a query string or form post. */ if (_method == CacheRequestMethod.Post) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but method is POST and no VaryByParam specified." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } string queryStringText = request.QueryStringText; if (queryStringText != null && queryStringText.Length > 0) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but contains a querystring and no VaryByParam specified." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } hasValidationPolicy = settings.HasValidationPolicy(); /* * Determine whether the client can accept a cached copy, and * get values of other cache control directives. * * We do this after lookup so we don't have to crack the headers * if the item is not found. Cracking the headers is expensive. */ if (!hasValidationPolicy) { cacheControl = request.Headers["Cache-Control"]; if (cacheControl != null) { cacheDirectives = cacheControl.Split(s_fieldSeparators); for (i = 0; i < cacheDirectives.Length; i++) { directive = cacheDirectives[i]; if (directive == "no-cache") { Debug.Trace("OutputCacheModuleEnter", "Skipping lookup because of Cache-Control: no-cache directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } if (directive.StartsWith("max-age=")) { try { maxage = Convert.ToInt32(directive.Substring(8)); } catch { maxage = -1; } if (maxage >= 0) { age = (int)((context.UtcTimestamp.Ticks - settings.UtcTimestampCreated.Ticks) / TimeSpan.TicksPerSecond); if (age >= maxage) { Debug.Trace("OutputCacheModuleEnter", "Not returning found item due to Cache-Control: max-age directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } else if (directive.StartsWith("min-fresh=")) { try { minfresh = Convert.ToInt32(directive.Substring(10)); } catch { minfresh = -1; } if (minfresh >= 0 && settings.IsExpiresSet && !settings.SlidingExpiration) { fresh = (int)((settings.UtcExpires.Ticks - context.UtcTimestamp.Ticks) / TimeSpan.TicksPerSecond); if (fresh < minfresh) { Debug.Trace("OutputCacheModuleEnter", "Not returning found item due to Cache-Control: min-fresh directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } } } pragma = request.Headers["Pragma"]; if (pragma != null) { pragmaDirectives = pragma.Split(s_fieldSeparators); for (i = 0; i < pragmaDirectives.Length; i++) { if (pragmaDirectives[i] == "no-cache") { Debug.Trace("OutputCacheModuleEnter", "Skipping lookup because of Pragma: no-cache directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } } else if (settings.ValidationCallbackInfo != null) { /* * Check if the item is still valid. */ validationStatus = HttpValidationStatus.Valid; validationStatusFinal = validationStatus; for (i = 0, n = settings.ValidationCallbackInfo.Length; i < n; i++) { callbackInfo = settings.ValidationCallbackInfo[i]; try { callbackInfo.handler(context, callbackInfo.data, ref validationStatus); } catch (Exception e) { validationStatus = HttpValidationStatus.Invalid; HttpApplicationFactory.RaiseError(e); } switch (validationStatus) { case HttpValidationStatus.Invalid: Debug.Trace("OutputCacheModuleEnter", "Output cache item found but callback invalidated it." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); cacheInternal.Remove(key); RecordCacheMiss(); return; case HttpValidationStatus.IgnoreThisRequest: validationStatusFinal = HttpValidationStatus.IgnoreThisRequest; break; case HttpValidationStatus.Valid: break; default: Debug.Trace("OutputCacheModuleEnter", "Invalid validation status, ignoring it, status=" + validationStatus + "\n\tkey=" + key); validationStatus = validationStatusFinal; break; } } if (validationStatusFinal == HttpValidationStatus.IgnoreThisRequest) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but callback status is IgnoreThisRequest." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } Debug.Assert(validationStatusFinal == HttpValidationStatus.Valid, "validationStatusFinal == HttpValidationStatus.Valid"); } /* * Try to satisfy a conditional request. The cached response * must satisfy all conditions that are present. * * We can only satisfy a conditional request if the response * is buffered and has no substitution blocks. * * N.B. RFC 2616 says conditional requests only occur * with the GET method, but we try to satisfy other * verbs (HEAD, POST) as well. */ send304 = -1; if (response.IsBuffered() && !cachedRawResponse._rawResponse.HasSubstBlocks) { /* Check "If-Modified-Since" header */ ifModifiedSinceHeader = request.IfModifiedSince; if (ifModifiedSinceHeader != null) { send304 = 0; try { utcIfModifiedSince = HttpDate.UtcParse(ifModifiedSinceHeader); if (settings.IsLastModifiedSet && settings.UtcLastModified <= utcIfModifiedSince && utcIfModifiedSince <= context.UtcTimestamp) { send304 = 1; } } catch { Debug.Trace("OutputCacheModuleEnter", "Ignore If-Modified-Since header, invalid format: " + ifModifiedSinceHeader); } } /* Check "If-None-Match" header */ if (send304 != 0) { etag = request.IfNoneMatch; if (etag != null) { send304 = 0; etags = etag.Split(s_fieldSeparators); for (i = 0, n = etags.Length; i < n; i++) { if (i == 0 && etags[i].Equals("*")) { send304 = 1; break; } if (etags[i].Equals(settings.ETag)) { send304 = 1; break; } } } } } if (send304 == 1) { /* * Send 304 Not Modified */ Debug.Trace("OutputCacheModuleEnter", "Output cache hit & conditional request satisfied, status=304." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); response.ClearAll(); response.StatusCode = 304; } else { /* * Send the full response. */ #if DBG if (send304 == -1) { Debug.Trace("OutputCacheModuleEnter", "Output cache hit.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); } else { Debug.Trace("OutputCacheModuleEnter", "Output cache hit but conditional request not satisfied.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); } #endif sendBody = (_method != CacheRequestMethod.Head); // UseSnapshot calls ClearAll response.UseSnapshot(cachedRawResponse._rawResponse, sendBody); } response.Cache.ResetFromHttpCachePolicySettings(settings, context.UtcTimestamp); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_RATIO_BASE); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_HITS); _key = null; _recordedCacheMiss = false; _method = CacheRequestMethod.Invalid; app.CompleteRequest(); }
internal void OnEnter(object source, EventArgs eventArgs) { this._key = null; this._recordedCacheMiss = false; if (OutputCache.InUse) { string[] strArray2 = null; string[] strArray3 = null; HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; context.GetFilePathData(); HttpRequest request = context.Request; HttpResponse response = context.Response; switch (request.HttpVerb) { case HttpVerb.GET: case HttpVerb.HEAD: case HttpVerb.POST: { string str; this._key = str = this.CreateOutputCachedItemKey(context, null); object obj2 = OutputCache.Get(str); if (obj2 != null) { int num; int length; CachedVary cachedVary = obj2 as CachedVary; if (cachedVary != null) { str = this.CreateOutputCachedItemKey(context, cachedVary); if (str == null) { return; } if (cachedVary._contentEncodings == null) { obj2 = OutputCache.Get(str); } else { obj2 = null; bool flag3 = true; string knownRequestHeader = context.WorkerRequest.GetKnownRequestHeader(0x16); if (knownRequestHeader != null) { string[] contentEncodings = cachedVary._contentEncodings; int startIndex = 0; bool flag4 = false; while (!flag4) { flag4 = true; int index = GetAcceptableEncoding(contentEncodings, startIndex, knownRequestHeader); if (index > -1) { flag3 = false; obj2 = OutputCache.Get(str + contentEncodings[index]); if (obj2 == null) { startIndex = index + 1; if (startIndex < contentEncodings.Length) { flag4 = false; } } } else if (index == -2) { flag3 = false; } } } if ((obj2 == null) && flag3) { obj2 = OutputCache.Get(str); } } if ((obj2 == null) || (((CachedRawResponse)obj2)._cachedVaryId != cachedVary.CachedVaryId)) { if (obj2 != null) { OutputCache.Remove(str, context); } return; } } CachedRawResponse response2 = (CachedRawResponse)obj2; HttpCachePolicySettings settings = response2._settings; if ((cachedVary == null) && !settings.IgnoreParams) { if (request.HttpVerb == HttpVerb.POST) { this.RecordCacheMiss(); return; } if (request.HasQueryString) { this.RecordCacheMiss(); return; } } if (settings.IgnoreRangeRequests) { string str8 = request.Headers["Range"]; if (StringUtil.StringStartsWithIgnoreCase(str8, "bytes")) { return; } } if (!settings.HasValidationPolicy()) { string str4 = request.Headers["Cache-Control"]; if (str4 != null) { strArray2 = str4.Split(s_fieldSeparators); for (num = 0; num < strArray2.Length; num++) { string str6 = strArray2[num]; switch (str6) { case "no-cache": case "no-store": this.RecordCacheMiss(); return; } if (StringUtil.StringStartsWith(str6, "max-age=")) { int num4; try { num4 = Convert.ToInt32(str6.Substring(8), CultureInfo.InvariantCulture); } catch { num4 = -1; } if (num4 >= 0) { int num6 = (int)((context.UtcTimestamp.Ticks - settings.UtcTimestampCreated.Ticks) / 0x989680L); if (num6 >= num4) { this.RecordCacheMiss(); return; } } } else if (StringUtil.StringStartsWith(str6, "min-fresh=")) { int num5; try { num5 = Convert.ToInt32(str6.Substring(10), CultureInfo.InvariantCulture); } catch { num5 = -1; } if (((num5 >= 0) && settings.IsExpiresSet) && !settings.SlidingExpiration) { int num7 = (int)((settings.UtcExpires.Ticks - context.UtcTimestamp.Ticks) / 0x989680L); if (num7 < num5) { this.RecordCacheMiss(); return; } } } } } string str5 = request.Headers["Pragma"]; if (str5 != null) { strArray3 = str5.Split(s_fieldSeparators); for (num = 0; num < strArray3.Length; num++) { if (strArray3[num] == "no-cache") { this.RecordCacheMiss(); return; } } } } else if (settings.ValidationCallbackInfo != null) { HttpValidationStatus valid = HttpValidationStatus.Valid; HttpValidationStatus ignoreThisRequest = valid; num = 0; length = settings.ValidationCallbackInfo.Length; while (num < length) { ValidationCallbackInfo info = settings.ValidationCallbackInfo[num]; try { info.handler(context, info.data, ref valid); } catch (Exception exception) { valid = HttpValidationStatus.Invalid; HttpApplicationFactory.RaiseError(exception); } switch (valid) { case HttpValidationStatus.Invalid: OutputCache.Remove(str, context); this.RecordCacheMiss(); return; case HttpValidationStatus.IgnoreThisRequest: ignoreThisRequest = HttpValidationStatus.IgnoreThisRequest; break; case HttpValidationStatus.Valid: break; default: valid = ignoreThisRequest; break; } num++; } if (ignoreThisRequest == HttpValidationStatus.IgnoreThisRequest) { this.RecordCacheMiss(); return; } } HttpRawResponse rawResponse = response2._rawResponse; if ((cachedVary == null) || (cachedVary._contentEncodings == null)) { string acceptEncoding = request.Headers["Accept-Encoding"]; string contentEncoding = null; ArrayList headers = rawResponse.Headers; if (headers != null) { foreach (HttpResponseHeader header in headers) { if (header.Name == "Content-Encoding") { contentEncoding = header.Value; break; } } } if (!IsAcceptableEncoding(contentEncoding, acceptEncoding)) { this.RecordCacheMiss(); return; } } int num3 = -1; if (!rawResponse.HasSubstBlocks) { string ifModifiedSince = request.IfModifiedSince; if (ifModifiedSince != null) { num3 = 0; try { DateTime time = HttpDate.UtcParse(ifModifiedSince); if ((settings.IsLastModifiedSet && (settings.UtcLastModified <= time)) && (time <= context.UtcTimestamp)) { num3 = 1; } } catch { } } if (num3 != 0) { string ifNoneMatch = request.IfNoneMatch; if (ifNoneMatch != null) { num3 = 0; string[] strArray = ifNoneMatch.Split(s_fieldSeparators); num = 0; length = strArray.Length; while (num < length) { if ((num == 0) && strArray[num].Equals("*")) { num3 = 1; break; } if (strArray[num].Equals(settings.ETag)) { num3 = 1; break; } num++; } } } } if (num3 == 1) { response.ClearAll(); response.StatusCode = 0x130; } else { bool sendBody = request.HttpVerb != HttpVerb.HEAD; response.UseSnapshot(rawResponse, sendBody); } response.Cache.ResetFromHttpCachePolicySettings(settings, context.UtcTimestamp); string originalCacheUrl = response2._kernelCacheUrl; if (originalCacheUrl != null) { response.SetupKernelCaching(originalCacheUrl); } PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_RATIO_BASE); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_HITS); this._key = null; this._recordedCacheMiss = false; application.CompleteRequest(); return; } return; } } } }