コード例 #1
0
        public void SetUp()
        {
            _invocation.Method.Returns(typeof(DummyController).GetMethod(nameof(DummyController.Object)));
            _request = UnitTestHelper.GetMessage();
            var discriptor = Substitute.For <ReflectedHttpActionDescriptor>();

            discriptor.MethodInfo = _invocation.Method;
            discriptor.ReturnType.Returns(typeof(object));


            _actionContext = new HttpActionContext(
                new HttpControllerContext(
                    new HttpConfiguration(),
                    Substitute.For <IHttpRouteData>(), _request), discriptor);

            _store = Substitute.For <IAsyncCacheStore>();
            _store.StoreId.Returns(1000);

            _actionExecutedContext = new HttpActionExecutedContext(_actionContext, null);
            _actionExecutedContext.Request.Properties[Global.__flatwhite_outputcache_store] = _store;
            _actionExecutedContext.Request.Properties[Global.__flatwhite_outputcache_key]   = CacheKey;

            Global.Init();
            Global.CacheStoreProvider.RegisterAsyncStore(_store);
        }
コード例 #2
0
        /// <inheritdoc cref="IAsyncCachedQuery{TCacheEntryOptions}.UpdateCachedResult"/>
        public virtual Task UpdateCachedResult(IAsyncCacheStore <TCacheEntryOptions> cacheStore, CancellationToken cancellationToken)
        {
            if (cacheStore == null)
            {
                throw new ArgumentNullException(nameof(cacheStore));
            }

            return(cacheStore.SetEntryAsync(State.CacheKey, State.CachedResult.ToCacheEntry(), State.CacheEntryOptions, cancellationToken));
        }
コード例 #3
0
        /// <inheritdoc cref="IAsyncCachedQuery{TCacheEntryOptions}.EvictCachedResult"/>
        public virtual Task EvictCachedResult(IAsyncCacheStore <TCacheEntryOptions> cacheStore, CancellationToken cancellationToken)
        {
            if (cacheStore == null)
            {
                throw new ArgumentNullException(nameof(cacheStore));
            }

            return(cacheStore.RemoveEntryAsync(State.CacheKey, cancellationToken));
        }
コード例 #4
0
 /// <summary>
 /// Register the <see cref="IAsyncCacheStore" />
 /// </summary>
 /// <param name="store"></param>
 /// <returns></returns>
 public void RegisterAsyncStore(IAsyncCacheStore store)
 {
     if (store == null)
     {
         throw new ArgumentNullException(nameof(store));
     }
     if (store.StoreId > 0 && _cacheStore.ContainsKey(store.StoreId))
     {
         throw new InvalidOperationException($"There is a registered ICacheStore with id {store.StoreId}: {_cacheStore[store.StoreId].GetType().Name}");
     }
     _asyncCacheStore[store.StoreId]        = store;
     _asyncCacheStoreTypes[store.GetType()] = store;
 }
コード例 #5
0
        /// <summary>
        /// Try to get the cache from etag and build the response if cache is available
        /// </summary>
        /// <param name="cacheControl"></param>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task <HttpResponseMessage> HandleAsync(CacheControlHeaderValue cacheControl, HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.Headers.IfNoneMatch != null)
            {
                // Etag format: fw-StoreId-HashedKey-Checksum
                var requestEtags = request.Headers.IfNoneMatch.Where(t => t.Tag != null && t.Tag.StartsWith("\"fw-")).ToList();
                if (requestEtags.Count > 0)
                {
                    foreach (var etag in requestEtags)
                    {
                        var etagString    = etag.Tag.Trim('"');
                        var index         = etagString.IndexOf("-", 4, StringComparison.Ordinal);
                        var storeIdString = index > 0 ? etagString.Substring(3, index - 3) : "0";
                        index = etagString.LastIndexOf("-", StringComparison.Ordinal);

                        int storeId;
                        IAsyncCacheStore cacheStore = null;
                        if (int.TryParse(storeIdString, out storeId))
                        {
                            cacheStore = Global.CacheStoreProvider.GetAsyncCacheStore(storeId) ?? Global.CacheStoreProvider.GetAsyncCacheStore();
                        }

                        if (cacheStore != null && index > 0)
                        {
                            var hashedKey = etagString.Substring(0, index);
                            var checkSum  = etagString.Substring(index + 1);
                            var cacheItem = (await cacheStore.GetAsync(hashedKey)) as WebApiCacheItem;

                            if (cacheItem != null && cacheItem.Checksum == checkSum)
                            {
                                request.Properties[WebApiExtensions.__webApi_etag_matched] = true;
                            }
                            return(_builder.GetResponse(cacheControl, cacheItem, request));
                        }
                    }
                }
            }
            return(null);
        }
コード例 #6
0
        private async Task <bool> AttemptToResponseTheCacheWhenError(HttpActionExecutedContext actionExecutedContext, IAsyncCacheStore cacheStore, string storedKey)
        {
            if (actionExecutedContext.ActionContext.Response == null || !actionExecutedContext.ActionContext.Response.IsSuccessStatusCode)
            {
                var cacheItem = await cacheStore.GetAsync(storedKey).ConfigureAwait(false) as WebApiCacheItem;

                if (cacheItem != null && StaleIfError > 0)
                {
                    var builder  = actionExecutedContext.Request.GetConfiguration().GetFlatwhiteCacheConfiguration().ResponseBuilder;
                    var response = builder.GetResponse(actionExecutedContext.Request.Headers.CacheControl, cacheItem, actionExecutedContext.Request);

                    if (response != null)
                    {
                        //NOTE: Override error response
                        actionExecutedContext.Response = response;
                    }
                    return(true);
                }
            }
            return(false);
        }
コード例 #7
0
        private async Task SaveTheResultToCache(HttpActionExecutedContext actionExecutedContext, string storedKey, IAsyncCacheStore cacheStore)
        {
            var responseContent = actionExecutedContext.Response.Content;

            if (responseContent != null)
            {
                var cacheItem = new WebApiCacheItem
                {
                    Key                       = storedKey,
                    Content                   = await responseContent.ReadAsByteArrayAsync().ConfigureAwait(false),
                    ResponseHeaders           = responseContent.Headers,
                    ResponseMediaType         = responseContent.Headers.ContentType.MediaType,
                    ResponseCharSet           = responseContent.Headers.ContentType.CharSet,
                    StoreId                   = cacheStore.StoreId,
                    StaleWhileRevalidate      = StaleWhileRevalidate,
                    MaxAge                    = MaxAge,
                    CreatedTime               = DateTime.UtcNow,
                    IgnoreRevalidationRequest = IgnoreRevalidationRequest,
                    StaleIfError              = StaleIfError,
                    AutoRefresh               = AutoRefresh,
                    Path                      = $"{actionExecutedContext.Request.Method} {actionExecutedContext.Request.RequestUri.PathAndQuery}"
                };

                var invocation = GetInvocation(actionExecutedContext.ActionContext);

                if (AutoRefresh)
                {
                    // Create if not there
                    DisposeOldPhoenixAndCreateNew(invocation, cacheItem, actionExecutedContext.Request);
                }

                if (StaleWhileRevalidate > 0) // Only auto refresh when revalidate if this value > 0, creating a Phoenix is optional
                {
                    var context        = GetInvocationContext(actionExecutedContext.ActionContext);
                    var strategy       = (ICacheStrategy)actionExecutedContext.Request.Properties[Global.__flatwhite_outputcache_strategy];
                    var changeMonitors = strategy.GetChangeMonitors(invocation, context);
                    foreach (var mon in changeMonitors)
                    {
                        mon.CacheMonitorChanged += state => { TryToRefreshCacheWhileRevalidate(storedKey); };
                    }
                }

                actionExecutedContext.Response.Headers.ETag = new EntityTagHeaderValue($"\"{cacheItem.Key}-{cacheItem.Checksum}\"");

                var absoluteExpiration = DateTime.UtcNow.AddSeconds(MaxAge + Math.Max(StaleWhileRevalidate, StaleIfError));

                await Task.WhenAll(new[]
                {
                    //Save url base cache key that can map to the real cache key which will be used by EvaluateServerCacheHandler
                    cacheStore.SetAsync(actionExecutedContext.Request.GetUrlBaseCacheKey(), cacheItem.Key, absoluteExpiration),
                    //Save the actual cache
                    cacheStore.SetAsync(cacheItem.Key, cacheItem, absoluteExpiration)
                }).ConfigureAwait(false);
            }
        }
コード例 #8
0
        /// <inheritdoc cref="IAsyncCachedQuery{TContext,TCacheEntryOptions,TResult}.Execute"/>
        public virtual async Task <TTransformedResult> Execute(TContext context, IAsyncCacheStore <TCacheEntryOptions> cacheStore, CacheOption cacheOption, CancellationToken cancellationToken)
        {
            var cachedResult = await GetCachedResult(context, cacheStore, cacheOption, cancellationToken).ConfigureAwait(false);

            return(await TransformCachedResult(cachedResult, cancellationToken).ConfigureAwait(false));
        }
コード例 #9
0
 /// <inheritdoc cref="IAsyncCachedQuery{TContext,TCacheEntryOptions,TResult}.Execute"/>
 public virtual Task <TResult> Execute(TContext context, IAsyncCacheStore <TCacheEntryOptions> cacheStore, CacheOption cacheOption, CancellationToken cancellationToken) =>
 GetCachedResult(context, cacheStore, cacheOption, cancellationToken);
コード例 #10
0
 /// <summary>
 /// Register the <see cref="IAsyncCacheStore" />
 /// </summary>
 /// <param name="store"></param>
 /// <returns></returns>
 public void RegisterAsyncStore(IAsyncCacheStore store)
 {
     if (store == null)
     {
         throw new ArgumentNullException(nameof(store));
     }
     if (store.StoreId > 0 && _cacheStore.ContainsKey(store.StoreId))
     {
         throw new InvalidOperationException($"There is a registered ICacheStore with id {store.StoreId}: {_cacheStore[store.StoreId].GetType().Name}");
     }
     _asyncCacheStore[store.StoreId] = store;
     _asyncCacheStoreTypes[store.GetType()] = store;
 }
コード例 #11
0
        /// <inheritdoc cref="IAsyncCachedQuery{TContext,TCacheEntryOptions,TResult}.Execute"/>
        protected virtual async Task <TCachedResult> GetCachedResult(TContext context, IAsyncCacheStore <TCacheEntryOptions> cacheStore, CacheOption cacheOption, CancellationToken cancellationToken)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (cacheStore == null)
            {
                throw new ArgumentNullException(nameof(cacheStore));
            }

            State.SetContext(context);

            if (cacheOption == CacheOption.Default)
            {
                var cacheEntry = await cacheStore.GetEntryAsync <TCachedResult>(State.CacheKey, cancellationToken).ConfigureAwait(false);

                if (cacheEntry != null)
                {
                    return(State.SetCachedResult(cacheEntry.Value));
                }
            }

            var result = await Query(context, cancellationToken).ConfigureAwait(false);

            await cacheStore.SetEntryAsync(State.CacheKey, result.ToCacheEntry(), State.CacheEntryOptions, cancellationToken).ConfigureAwait(false);

            return(State.SetCachedResult(result));
        }