public void OnActionExecuting(ActionExecutingContext filterContext) { Logger.Debug("Incoming request for URL '{0}'.", filterContext.RequestContext.HttpContext.Request.RawUrl); // This filter is not reentrant (multiple executions within the same request are // not supported) so child actions are ignored completely. if (filterContext.IsChildAction) { Logger.Debug("Action '{0}' ignored because it's a child action.", filterContext.ActionDescriptor.ActionName); return; } _now = _clock.UtcNow; _workContext = _workContextAccessor.GetContext(); var configurations = _cacheService.GetRouteConfigs(); if (configurations.Any()) { var route = filterContext.Controller.ControllerContext.RouteData.Route; var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route); _cacheRouteConfig = configurations.FirstOrDefault(c => c.RouteKey == key); } if (!RequestIsCacheable(filterContext)) { return; } // Computing the cache key after we know that the request is cacheable means that we are only performing this calculation on requests that require it _cacheKey = String.Intern(ComputeCacheKey(filterContext, GetCacheKeyParameters(filterContext))); _invariantCacheKey = ComputeCacheKey(filterContext, null); Logger.Debug("Cache key '{0}' was created.", _cacheKey); try { // Is there a cached item, and are we allowed to serve it? var allowServeFromCache = filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache" || CacheSettings.IgnoreNoCache; var cacheItem = GetCacheItem(_cacheKey); if (allowServeFromCache && cacheItem != null) { Logger.Debug("Item '{0}' was found in cache.", _cacheKey); // Is the cached item in its grace period? if (cacheItem.IsInGracePeriod(_now)) { // Render the content unless another request is already doing so. if (Monitor.TryEnter(_cacheKey)) { Logger.Debug("Item '{0}' is in grace period and not currently being rendered; rendering item...", _cacheKey); BeginRenderItem(filterContext); return; } } // Cached item is not yet in its grace period, or is already being // rendered by another request; serve it from cache. Logger.Debug("Serving item '{0}' from cache.", _cacheKey); ServeCachedItem(filterContext, cacheItem); return; } // No cached item found, or client doesn't want it; acquire the cache key // lock to render the item. Logger.Debug("Item '{0}' was not found in cache or client refuses it. Acquiring cache key lock...", _cacheKey); if (Monitor.TryEnter(_cacheKey)) { Logger.Debug("Cache key lock for item '{0}' was acquired.", _cacheKey); // Item might now have been rendered and cached by another request; if so serve it from cache. if (allowServeFromCache) { cacheItem = GetCacheItem(_cacheKey); if (cacheItem != null) { Logger.Debug("Item '{0}' was now found; releasing cache key lock and serving from cache.", _cacheKey); Monitor.Exit(_cacheKey); ServeCachedItem(filterContext, cacheItem); return; } } } // Either we acquired the cache key lock and the item was still not in cache, or // the lock acquisition timed out. In either case render the item. Logger.Debug("Rendering item '{0}'...", _cacheKey); BeginRenderItem(filterContext); } catch { // Remember to release the cache key lock in the event of an exception! Logger.Debug("Exception occurred for item '{0}'; releasing any acquired lock.", _cacheKey); ReleaseCacheKeyLock(); throw; } }
public void OnResultExecuted(ResultExecutedContext filterContext) { var captureHandlerIsAttached = false; try { // This filter is not reentrant (multiple executions within the same request are // not supported) so child actions are ignored completely. if (filterContext.IsChildAction || !_isCachingRequest) { return; } Logger.Debug("Item '{0}' was rendered.", _cacheKey); // Obtain individual route configuration, if any. CacheRouteConfig configuration = null; var configurations = _cacheService.GetRouteConfigs(); if (configurations.Any()) { var route = filterContext.Controller.ControllerContext.RouteData.Route; var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route); configuration = configurations.FirstOrDefault(c => c.RouteKey == key); } if (!ResponseIsCacheable(filterContext, configuration)) { filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); filterContext.HttpContext.Response.Cache.SetNoStore(); filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0)); return; } // Determine duration and grace time. var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : CacheSettings.DefaultCacheDuration; var cacheGraceTime = configuration != null && configuration.GraceTime.HasValue ? configuration.GraceTime.Value : CacheSettings.DefaultCacheGraceTime; // Include each content item ID as tags for the cache entry. var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray(); // Capture the response output using a custom filter stream. var response = filterContext.HttpContext.Response; var captureStream = new CaptureStream(response.Filter); response.Filter = captureStream; captureStream.Captured += (output) => { try { // Since this is a callback any call to injected dependencies can result in an Autofac exception: "Instances // cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed." // To prevent access to the original lifetime scope a new work context scope should be created here and dependencies // should be resolved from it. using (var scope = _workContextAccessor.CreateWorkContextScope()) { var cacheItem = new CacheItem() { CachedOnUtc = _now, Duration = cacheDuration, GraceTime = cacheGraceTime, Output = output, ContentType = response.ContentType, QueryString = filterContext.HttpContext.Request.Url.Query, CacheKey = _cacheKey, InvariantCacheKey = _invariantCacheKey, Url = filterContext.HttpContext.Request.Url.AbsolutePath, Tenant = scope.Resolve <ShellSettings>().Name, StatusCode = response.StatusCode, Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray() }; // Write the rendered item to the cache. var cacheStorageProvider = scope.Resolve <IOutputCacheStorageProvider>(); cacheStorageProvider.Remove(_cacheKey); cacheStorageProvider.Set(_cacheKey, cacheItem); Logger.Debug("Item '{0}' was written to cache.", _cacheKey); // Also add the item tags to the tag cache. var tagCache = scope.Resolve <ITagCache>(); foreach (var tag in cacheItem.Tags) { tagCache.Tag(tag, _cacheKey); } } } finally { // Always release the cache key lock when the request ends. ReleaseCacheKeyLock(); } }; captureHandlerIsAttached = true; } finally { // If the response filter stream capture handler was attached then we'll trust // it to release the cache key lock at some point in the future when the stream // is flushed; otherwise we'll make sure we'll release it here. if (!captureHandlerIsAttached) { ReleaseCacheKeyLock(); } } }
protected virtual bool ResponseIsCacheable(ResultExecutedContext filterContext, CacheRouteConfig configuration) { if (filterContext.HttpContext.Request.Url == null) { return(false); } // Don't cache non-200 responses or results of a redirect. var response = filterContext.HttpContext.Response; if (response.StatusCode != (int)HttpStatusCode.OK || _transformRedirect) { return(false); } // Don't cache in individual route configuration says no. if (configuration != null && configuration.Duration == 0) { Logger.Debug("Response for item '{0}' will not be cached because route is configured to not be cached.", _cacheKey); return(false); } // Don't cache if request created notifications. var hasNotifications = !String.IsNullOrEmpty(Convert.ToString(filterContext.Controller.TempData["messages"])); if (hasNotifications) { Logger.Debug("Response for item '{0}' will not be cached because one or more notifications were created.", _cacheKey); return(false); } return(true); }
public new void OnActionExecuting(ActionExecutingContext filterContext) { Logger.Debug("Incoming request for URL '{0}'.", filterContext.RequestContext.HttpContext.Request.RawUrl); // This filter is not reentrant (multiple executions within the same request are // not supported) so child actions are ignored completely. if (filterContext.IsChildAction) { Logger.Debug("Action '{0}' ignored because it's a child action.", filterContext.ActionDescriptor.ActionName); return; } _now = _clock.UtcNow; _workContext = _workContextAccessor.GetContext(); var configurations = _cacheService.GetRouteConfigs(); if (configurations.Any()) { var route = filterContext.Controller.ControllerContext.RouteData.Route; var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route); _cacheRouteConfig = configurations.FirstOrDefault(c => c.RouteKey == key); } #region Added var cachedUrl = _cacheAliasServices.GetByUrl(filterContext.RequestContext.HttpContext.Request.RawUrl); var keyToAdd = ""; if (cachedUrl != null) { if (_cacheRouteConfig == null) { keyToAdd = "CacheUrl=" + _tokenizer.Replace(cachedUrl.Url, new Dictionary <string, object>()) + ";";// { { "Content", part.ContentItem } };) _cacheRouteConfig = cachedUrl; } else if (cachedUrl.Priority >= _cacheRouteConfig.Priority) { keyToAdd = "CacheUrl=" + _tokenizer.Replace(cachedUrl.Url, new Dictionary <string, object>()) + ";"; _cacheRouteConfig = cachedUrl; } } #endregion if (!RequestIsCacheable(filterContext)) { return; } // filtro per archiviare il log delle chiamate autenticate loggate e non gestite dalla cache laser. if (_cacheAliasServices.Setting().ActiveLog&& _workContext.CurrentUser != null && CacheSettings.CacheAuthenticatedRequests && keyToAdd == "") { Logger.Information("Cache con chiave => {0}", String.Intern(ComputeCacheKey(filterContext, GetCacheKeyParameters(filterContext)))); } // filtro per PREVENIRE la cache delle chiamate autenticate loggate e non gestite dalla cache laser. if (_cacheAliasServices.Setting().PreventDefaultAuthenticatedCache&& _workContext.CurrentUser != null && CacheSettings.CacheAuthenticatedRequests && keyToAdd == "") { if (_cacheAliasServices.Setting().ActiveLog) { Logger.Information("cache key vuota bloccata "); } return; } if (_cacheAliasServices.Setting().PreventDefaultNotContentItemAuthenticatedCache&& _workContext.CurrentUser != null && CacheSettings.CacheAuthenticatedRequests && keyToAdd == "" && (!((filterContext.ActionDescriptor.ActionName == "Display" && filterContext.ActionDescriptor.ControllerDescriptor.ControllerName == "Item") || filterContext.RequestContext.HttpContext.Request.RawUrl.ToLower().Contains("displayalias")))) { if (_cacheAliasServices.Setting().ActiveLog) { Logger.Information("cache key vuota bloccata "); } return; } // Computing the cache key after we know that the request is cacheable means that we are only performing this calculation on requests that require it _cacheKey = keyToAdd + String.Intern(ComputeCacheKey(filterContext, GetCacheKeyParameters(filterContext))); _invariantCacheKey = ComputeCacheKey(filterContext, null); Logger.Debug("Cache key '{0}' was created.", _cacheKey); try { // Is there a cached item, and are we allowed to serve it? var allowServeFromCache = filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache" || CacheSettings.IgnoreNoCache; var cacheItem = GetCacheItem(_cacheKey); if (allowServeFromCache && cacheItem != null) { Logger.Debug("Item '{0}' was found in cache.", _cacheKey); // Is the cached item in its grace period? if (cacheItem.IsInGracePeriod(_now)) { // Render the content unless another request is already doing so. if (Monitor.TryEnter(_cacheKey)) { Logger.Debug("Item '{0}' is in grace period and not currently being rendered; rendering item...", _cacheKey); BeginRenderItem(filterContext); return; } } // Cached item is not yet in its grace period, or is already being // rendered by another request; serve it from cache. Logger.Debug("Serving item '{0}' from cache.", _cacheKey); ServeCachedItem(filterContext, cacheItem); return; } // No cached item found, or client doesn't want it; acquire the cache key // lock to render the item. Logger.Debug("Item '{0}' was not found in cache or client refuses it. Acquiring cache key lock...", _cacheKey); if (Monitor.TryEnter(_cacheKey)) { Logger.Debug("Cache key lock for item '{0}' was acquired.", _cacheKey); // Item might now have been rendered and cached by another request; if so serve it from cache. if (allowServeFromCache) { cacheItem = GetCacheItem(_cacheKey); if (cacheItem != null) { Logger.Debug("Item '{0}' was now found; releasing cache key lock and serving from cache.", _cacheKey); Monitor.Exit(_cacheKey); ServeCachedItem(filterContext, cacheItem); return; } } } // Either we acquired the cache key lock and the item was still not in cache, or // the lock acquisition timed out. In either case render the item. Logger.Debug("Rendering item '{0}'...", _cacheKey); BeginRenderItem(filterContext); } catch { // Remember to release the cache key lock in the event of an exception! Logger.Debug("Exception occurred for item '{0}'; releasing any acquired lock.", _cacheKey); ReleaseCacheKeyLock(); throw; } }