public void Displayed(ShapeDisplayedContext context)
        {
            // TODO: Configure duration of sliding expiration

            var cacheContext = context.ShapeMetadata.Cache();

            // If the shape is not cached, evaluate the ESIs
            if (cacheContext == null)
            {
                string content;
                using (var sw = new StringWriter())
                {
                    context.ChildContent.WriteTo(sw, HtmlEncoder.Default);
                    content = sw.ToString();
                }

                ProcessESIs(ref content, GetDistributedCache);
                context.ChildContent = new HtmlString(content);
            }
            else if (!_cached.Contains(cacheContext) && context.ChildContent != null)
            {
                var    cacheEntries = GetCacheEntries(cacheContext).ToList();
                string cacheKey     = GetCacheKey(cacheContext.CacheId, cacheEntries);

                using (var sw = new StringWriter())
                {
                    context.ChildContent.WriteTo(sw, HtmlEncoder.Default);
                    var content = sw.ToString();

                    _cached.Add(cacheContext);
                    _cache[cacheKey] = content;
                    var contexts = String.Join(ContextSeparator.ToString(), cacheContext.Contexts.ToArray());
                    context.ChildContent = new HtmlString($"[[cache id='{cacheContext.CacheId}' contexts='{contexts}']]");

                    var bytes = Encoding.UTF8.GetBytes(content);

                    // Default duration is sliding expiration (permanent as long as it's used)
                    DistributedCacheEntryOptions options = new DistributedCacheEntryOptions
                    {
                        SlidingExpiration = new TimeSpan(0, 1, 0)
                    };

                    // If a custom duration is specified, replace the default options
                    if (cacheContext.Duration.HasValue)
                    {
                        options = new DistributedCacheEntryOptions
                        {
                            AbsoluteExpirationRelativeToNow = cacheContext.Duration
                        };
                    }

                    _dynamicCache.SetAsync(cacheKey, bytes, options).Wait();
                    _tagCache.Tag(cacheKey, cacheContext.Tags.ToArray());
                }
            }
        }
        public void CacheItem(IContent contentItem, string displayType, ItemLevelCacheItem itemLevelCacheItem)
        {
            if (contentItem == null)
            {
                return;
            }

            var itemLevelCachePart = contentItem.As <ItemLevelCachePart>();

            if (itemLevelCachePart == null || !itemLevelCachePart.ItemLevelCacheSettings.ContainsKey(displayType) || itemLevelCachePart.ItemLevelCacheSettings[displayType].Mode != ItemLevelCacheMode.CacheItem)
            {
                return;
            }

            var settings = itemLevelCachePart.ItemLevelCacheSettings[displayType];
            var cacheKey = GetCacheKey(contentItem, displayType);

            var cachedItem = mOutputCacheStorageProvider.GetCacheItem(cacheKey);

            if (cachedItem == null || cachedItem.IsInGracePeriod(mClock.UtcNow)) // TODO: Should this method check the cache first, or just blindly insert?
            {
                var serializedCacheItem = mJsonConverter.Serialize(itemLevelCacheItem);

                var cacheItem = new CacheItem()
                {
                    CachedOnUtc = mClock.UtcNow,
                    Duration    = settings.CacheDurationSeconds,
                    GraceTime   = settings.CacheGraceTimeSeconds,
                    Output      = mHttpContextAccessor.Current().Request.ContentEncoding.GetBytes(serializedCacheItem),
                    Tags        = new[]
                    {
                        ItemLevelCacheTag.GenericTag,
                        ItemLevelCacheTag.For(contentItem),
                        ItemLevelCacheTag.For(contentItem, displayType),
                        ItemLevelCacheTag.For(contentItem.ContentItem.TypeDefinition)
                    },
                    Tenant            = mShellSettings.Name,
                    CacheKey          = cacheKey,
                    InvariantCacheKey = cacheKey,
                    Url = ""
                };

                mOutputCacheStorageProvider.Set(cacheKey, cacheItem);

                // Also add the item tags to the tag cache.
                foreach (var tag in cacheItem.Tags)
                {
                    mTagCache.Tag(tag, cacheKey);
                }
            }
        }
示例#3
0
        private dynamic BeginRenderItem(IContent content, string displayType, string groupId)
        {
            WorkContext workContext = _workContextAccessor.GetContext();
            var         output      = _shapeDisplay.Display(BuildShape(content, displayType, groupId));

            _cacheSettings     = GetOutputCacheSettings(content);
            _cacheKey          = ComputeCacheKey(content, displayType, groupId);
            _invariantCacheKey = string.Format("tenant={0};id={1};", _shellSettings.Name, content.ContentItem.Id);

            var cacheItem = new CacheItem()
            {
                CachedOnUtc = _now,
                ValidFor    = _cacheSettings.CacheDuration,
                Output      = _jsonConverter.Serialize(new OutputCacheItem
                {
                    Id        = content.ContentItem.Id,
                    Output    = output,
                    Resources = _resourceManager.GetRequiredResources("script")
                                .Concat(_resourceManager.GetRequiredResources("stylesheet")).Select(GetCacheItemResource).ToList()
                }),
                ContentType       = content.ContentItem.ContentType,
                QueryString       = workContext.HttpContext.Request.Url.Query,
                CacheKey          = _cacheKey,
                InvariantCacheKey = _invariantCacheKey,
                Url        = workContext.HttpContext.Request.Url.AbsolutePath,
                Tenant     = _shellSettings.Name,
                StatusCode = workContext.HttpContext.Response.StatusCode,
                Tags       = new[] { _invariantCacheKey, content.ContentItem.Id.ToString(CultureInfo.InvariantCulture) }
            };

            _cacheStorageProvider.Remove(_cacheKey);
            _cacheStorageProvider.Set(_cacheKey, cacheItem);

            foreach (var tag in cacheItem.Tags)
            {
                _tagCache.Tag(tag, _cacheKey);
            }

            return(ServeCachedItem(cacheItem, content, displayType, groupId));
        }
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            string capturedResponse = null;

            if (_completeResponse != null)
            {
                capturedResponse = _completeResponse(filterContext);
            }

            var response = filterContext.HttpContext.Response;

            // ignore error results from cache
            if (response.StatusCode != (int)HttpStatusCode.OK ||
                _transformRedirect)
            {
                // Never cache non-200 responses.
                filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                filterContext.HttpContext.Response.Cache.SetNoStore();
                filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0));

                return;
            }

            if (capturedResponse == null)
            {
                return;
            }

            // check if there is a specific rule not to cache the whole route
            RouteConfiguration configuration = null;
            var configurations = _cacheService.GetRouteConfigurations();

            if (configurations.Any())
            {
                var route = filterContext.Controller.ControllerContext.RouteData.Route;
                var key   = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route);
                configuration = configurations.FirstOrDefault(c => c.RouteKey == key);
            }

            // do not cache ?
            if (configuration != null && configuration.Duration == 0)
            {
                return;
            }

            // don't cache the result if there were some notifications
            if (_notificationManager.GetNotifications().Any())
            {
                return;
            }

            // default duration of specific one ?
            var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : _cacheDuration;

            // include each of the content item ids as tags for the cache entry
            var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray();

            if (filterContext.HttpContext.Request.Url == null)
            {
                return;
            }

            _cacheItem.ContentType       = response.ContentType;
            _cacheItem.StatusCode        = response.StatusCode;
            _cacheItem.CachedOnUtc       = _now;
            _cacheItem.ValidFor          = cacheDuration;
            _cacheItem.QueryString       = filterContext.HttpContext.Request.Url.Query;
            _cacheItem.Output            = capturedResponse;
            _cacheItem.CacheKey          = _cacheKey;
            _cacheItem.InvariantCacheKey = _invariantCacheKey;
            _cacheItem.Tenant            = _shellSettings.Name;
            _cacheItem.Url  = filterContext.HttpContext.Request.Url.AbsolutePath;
            _cacheItem.Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray();

            Logger.Debug("Cache item added: " + _cacheItem.CacheKey);

            // remove only the current version of the page
            _cacheService.RemoveByTag(_cacheKey);

            // add data to cache
            _cacheStorageProvider.Set(_cacheKey, _cacheItem);

            // add to the tags index
            foreach (var tag in _cacheItem.Tags)
            {
                _tagCache.Tag(tag, _cacheKey);
            }
        }
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            var response = filterContext.HttpContext.Response;

            if (!_cacheControlStrategy.IsCacheable(filterContext.Result, response))
            {
                _filter = null;
                if (_previousFilter != null)
                {
                    response.Filter = _previousFilter;
                }
            }

            // ignore error results from cache
            if (response.StatusCode != (int)HttpStatusCode.OK)
            {
                // Never cache non-200 responses.
                filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                filterContext.HttpContext.Response.Cache.SetNoStore();
                filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0));

                _filter = null;
                if (_previousFilter != null)
                {
                    response.Filter = _previousFilter;
                }
                return;
            }

            // if the result of a POST is a Redirect, remove any Cache Item for this url
            // so that the redirected client gets a fresh result
            // also add a random token to the query string so that public cachers (IIS, proxies, ...) don't return cached content
            // i.e., Comment creation

            // ignore in admin
            if (AdminFilter.IsApplied(new RequestContext(filterContext.HttpContext, new RouteData())))
            {
                _filter = null;
                if (_previousFilter != null)
                {
                    response.Filter = _previousFilter;
                }
                return;
            }

            _workContext = _workContextAccessor.GetContext();

            // ignore authenticated requests
            if (_workContext.CurrentUser != null)
            {
                _filter = null;
                if (_previousFilter != null)
                {
                    response.Filter = _previousFilter;
                }
                return;
            }

            // save the result only if the content can be intercepted
            if (_filter == null)
            {
                return;
            }

            // flush here to force the Filter to get the rendered content
            if (response.IsClientConnected)
            {
                response.Flush();
            }

            var output = _filter.GetContents(response.ContentEncoding);

            if (String.IsNullOrWhiteSpace(output))
            {
                return;
            }

            response.Filter = null;
            response.Write(output);

            // check if there is a specific rule not to cache the whole route
            var configurations = _cacheService.GetRouteConfigurations();
            var route          = filterContext.Controller.ControllerContext.RouteData.Route;
            var key            = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route);
            var configuration  = configurations.FirstOrDefault(c => c.RouteKey == key);


            // do not cache ?
            if (configuration != null && configuration.Duration == 0)
            {
                return;
            }

            // don't cache the result of a POST redirection as it could contain notifications
            if (_transformRedirect)
            {
                return;
            }

            // don't cache the result if there were some notifications
            var messagesZone     = _workContextAccessor.GetContext(filterContext).Layout.Zones["Messages"];
            var hasNotifications = messagesZone != null && ((IEnumerable <dynamic>)messagesZone).Any();

            if (hasNotifications)
            {
                return;
            }

            // default duration of specific one ?
            var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : _cacheDuration;

            if (cacheDuration <= 0)
            {
                return;
            }

            // include each of the content item ids as tags for the cache entry
            var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray();

            if (filterContext.HttpContext.Request.Url == null)
            {
                return;
            }

            _cacheItem.ContentType       = response.ContentType;
            _cacheItem.StatusCode        = response.StatusCode;
            _cacheItem.CachedOnUtc       = _now;
            _cacheItem.ValidFor          = cacheDuration;
            _cacheItem.QueryString       = filterContext.HttpContext.Request.Url.Query;
            _cacheItem.Output            = output;
            _cacheItem.CacheKey          = _cacheKey;
            _cacheItem.InvariantCacheKey = _invariantCacheKey;
            _cacheItem.Tenant            = _shellSettings.Name;
            _cacheItem.Url  = filterContext.HttpContext.Request.Url.AbsolutePath;
            _cacheItem.Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray();

            Logger.Debug("Cache item added: " + _cacheItem.CacheKey);

            // remove old cache data
            _cacheService.RemoveByTag(_invariantCacheKey);

            // add data to cache
            _cacheStorageProvider.Set(_cacheKey, _cacheItem);

            // add to the tags index
            foreach (var tag in _cacheItem.Tags)
            {
                _tagCache.Tag(tag, _cacheKey);
            }
        }
示例#6
0
        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 {
                        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     = _shellSettings.Name,
                            StatusCode = response.StatusCode,
                            Tags       = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray()
                        };

                        // Write the rendered item to the cache.
                        _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.
                        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();
                }
            }
        }
示例#7
0
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            var response = filterContext.HttpContext.Response;

            // save the result only if the content can be intercepted
            if (_filter == null)
            {
                return;
            }

            // check if there is a specific rule not to cache the whole route
            var configurations = _cacheService.GetRouteConfigurations();
            var route          = filterContext.Controller.ControllerContext.RouteData.Route;
            var key            = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route);
            var configuration  = configurations.FirstOrDefault(c => c.RouteKey == key);

            // flush here to force the Filter to get the rendered content
            if (response.IsClientConnected)
            {
                response.Flush();
            }

            var output = _filter.GetContents(response.ContentEncoding);

            if (String.IsNullOrWhiteSpace(output))
            {
                return;
            }

            response.Filter = null;
            response.Write(output);

            // do not cache ?
            if (configuration != null && configuration.Duration == 0)
            {
                return;
            }

            // default duration of specific one ?
            var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : _cacheDuration;

            // include each of the content item ids as tags for the cache entry
            var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray();

            _cacheItem.ContentType       = response.ContentType;
            _cacheItem.StatusCode        = response.StatusCode;
            _cacheItem.CachedOnUtc       = _now;
            _cacheItem.ValidFor          = cacheDuration;
            _cacheItem.QueryString       = filterContext.HttpContext.Request.Url.Query;
            _cacheItem.Output            = output;
            _cacheItem.CacheKey          = _cacheKey;
            _cacheItem.InvariantCacheKey = _invariantCacheKey;
            _cacheItem.Tenant            = _shellSettings.Name;
            _cacheItem.Url  = filterContext.HttpContext.Request.Url.AbsolutePath;
            _cacheItem.Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray();

            Logger.Debug("Cache item added: " + _cacheItem.CacheKey);

            // remove old cache data
            _cacheService.RemoveByTag(_invariantCacheKey);

            // add data to cache
            _cacheStorageProvider.Set(_cacheKey, _cacheItem);

            // add to the tags index
            foreach (var tag in _cacheItem.Tags)
            {
                _tagCache.Tag(tag, _cacheKey);
            }
        }