예제 #1
0
        public void Should_not_create_phoenix_for_http_method_not_GET()
        {
            var key = "theCacheKey" + Guid.NewGuid();
            // Arrange
            var objCacheItem = new WebApiCacheItem
            {
                MaxAge = 5,
                StaleWhileRevalidate = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
                Key         = key
            };

            _request.Method = HttpMethod.Post;


            var att = new OutputCacheAttributeWithPublicMethods {
                MaxAge = 5, CacheStoreId = 1000, StaleWhileRevalidate = 5
            };

            // Action
            att.CreatePhoenixPublic(_invocation, objCacheItem, _request);

            // Assert
            Assert.That(!Global.Cache.PhoenixFireCage.ContainsKey(key));
        }
예제 #2
0
        public void Should_return_null_if_stale()
        {
            // Arrange
            var cacheControl = new CacheControlHeaderValue
            {
                MaxStale      = true,
                MaxStaleLimit = TimeSpan.FromSeconds(15)
            };

            var cacheItem = new WebApiCacheItem
            {
                CreatedTime               = DateTime.UtcNow.AddSeconds(-10).AddMilliseconds(-1), // should stale just by 1 milisecond
                MaxAge                    = 10,
                StaleWhileRevalidate      = 5,
                IgnoreRevalidationRequest = false,
                ResponseCharSet           = "UTF8",
                ResponseMediaType         = "text/json",
                Content                   = new byte[0],
                Key = "CacheKey" + Guid.NewGuid()
            };

            Global.Cache.PhoenixFireCage[cacheItem.Key] = new Phoenix(NSubstitute.Substitute.For <_IInvocation>(), new CacheItem());

            var request = UnitTestHelper.GetMessage();
            var svc     = new CacheResponseBuilder {
            };

            // Action
            var response = svc.GetResponse(cacheControl, cacheItem, request);

            // Assert
            Assert.IsNull(response);
        }
        public void Should_dispose_existing_phoenix()
        {
            var key = "theCacheKey" + Guid.NewGuid();
            // Arrange
            var objCacheItem = new WebApiCacheItem
            {
                MaxAge = 5,
                StaleWhileRevalidate = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
                Key         = key
            };

            var existingPhoenix = Substitute.For <WebApiPhoenix>(_invocation, objCacheItem, _request);

            var att = new OutputCacheAttributeWithPublicMethods {
                MaxAge = 5, CacheStoreId = 1000, StaleWhileRevalidate = 5
            };

            Global.Cache.PhoenixFireCage[key] = existingPhoenix;

            // Action
            att.DisposeOldPhoenixAndCreateNew_Public(_invocation, objCacheItem, _request);

            // Assert
            Assert.That(Global.Cache.PhoenixFireCage[key] is WebApiPhoenix);
            existingPhoenix.Received(1).Dispose();
        }
예제 #4
0
        public async Task FireAsync_should_send_a_request_to_original_endpoint_when_loopback_is_not_set()
        {
            // Arrange
            var currentCacheItem = new WebApiCacheItem
            {
                CreatedTime          = DateTime.UtcNow,
                MaxAge               = 2,
                StaleWhileRevalidate = 3,
                StaleIfError         = 4,
                Key = "1"
            };
            var invocation = Substitute.For <_IInvocation>();


            using (var phoenix = new WebApiPhoenixWithPublicMethods(invocation, currentCacheItem, _requestMessage))
            {
                phoenix.HttpClient = _client;


                // Action
                var state = await phoenix.FireAsyncPublic();

                // Assert
                Assert.IsTrue(state is InActivePhoenix);
                _client.Received(1).Timeout = Arg.Is <TimeSpan>(x => x.TotalSeconds > 4);
                await _client
                .Received(1)
                .SendAsync(Arg.Is <HttpRequestMessage>(msg => msg.Properties.Count == 0 &&
                                                       msg.Headers.CacheControl.Extensions.Any(e => e.Name == WebApiExtensions.__cacheControl_flatwhite_force_refresh) &&
                                                       msg.RequestUri.ToString() == "http://localhost/api/method/id")
                           , HttpCompletionOption.ResponseHeadersRead);
            }
        }
예제 #5
0
        public async Task Should_create_phoenix_and_try_refresh_cache_when_cache_item_is_stale()
        {
            // Arrange
            var store = Substitute.For <IAsyncCacheStore>();

            store.StoreId.Returns(1000);
            var objCacheItem = new WebApiCacheItem
            {
                MaxAge = 5,
                StaleWhileRevalidate = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
            };

            store.GetAsync(Arg.Any <string>()).Returns(c =>
            {
                objCacheItem.Key = c.Arg <string>();
                return(Task.FromResult((object)objCacheItem));
            });

            Global.CacheStoreProvider.RegisterAsyncStore(store);
            var att = new Flatwhite.WebApi.OutputCacheAttribute {
                MaxAge = 5, CacheStoreId = 1000, StaleWhileRevalidate = 5
            };

            // Action
            await att.OnActionExecutingAsync(_actionContext, CancellationToken.None);

            // Assert
            Assert.IsTrue(Global.Cache.PhoenixFireCage.ContainsKey(objCacheItem.Key));
        }
예제 #6
0
        public void Should_return_null_if_cache_not_mature_as_min_fresh_request()
        {
            // Arrange
            var cacheControl = new CacheControlHeaderValue
            {
                MinFresh = TimeSpan.FromSeconds(100)
            };

            var cacheItem = new WebApiCacheItem
            {
                CreatedTime               = DateTime.UtcNow.AddSeconds(-20),
                MaxAge                    = 1000,
                StaleWhileRevalidate      = 5,
                IgnoreRevalidationRequest = false
            };

            var request = UnitTestHelper.GetMessage();
            var svc     = new CacheResponseBuilder {
            };

            // Action
            var response = svc.GetResponse(cacheControl, cacheItem, request);

            // Assert
            Assert.IsNull(response);
        }
예제 #7
0
        public async Task Should_hook_up_change_monitors_if_StaleWhileRevalidate_greater_than_0()
        {
            // Arrange
            var wait = new AutoResetEvent(false);

            _request.Headers.CacheControl =
                new CacheControlHeaderValue
            {
                MaxAge     = TimeSpan.FromSeconds(10),
                Extensions = { new NameValueHeaderValue(WebApiExtensions.__cacheControl_flatwhite_force_refresh, "1") }
            };

            var att = new Flatwhite.WebApi.OutputCacheAttribute
            {
                MaxAge = 10,
                IgnoreRevalidationRequest = true,
                StaleIfError         = 2,
                StaleWhileRevalidate = 5
            };

            _actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent("{}")
            };

            var objCacheItem = new WebApiCacheItem
            {
                Key    = CacheKey,
                MaxAge = 5,
                StaleWhileRevalidate = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
            };
            var existingPhoenix = Substitute.For <WebApiPhoenix>(_invocation, objCacheItem, _request);

            Global.Cache.PhoenixFireCage[CacheKey] = existingPhoenix;
            existingPhoenix.When(x => x.Reborn()).Do(c =>
            {
                wait.Set();
            });

            var strategy      = Substitute.For <ICacheStrategy>();
            var changeMonitor = Substitute.For <IChangeMonitor>();

            strategy.GetChangeMonitors(Arg.Any <_IInvocation>(), Arg.Any <IDictionary <string, object> >()).Returns(new[] { changeMonitor });
            _actionExecutedContext.Request.Properties[Global.__flatwhite_outputcache_strategy] = strategy;


            // Action
            await att.OnActionExecutedAsync(_actionExecutedContext, CancellationToken.None);

            changeMonitor.CacheMonitorChanged += Raise.Event <CacheMonitorChangeEvent>(new object());

            // Assert
            await Task.Delay(1000);

            Assert.IsTrue(wait.WaitOne(TimeSpan.FromSeconds(2)));
        }
예제 #8
0
        /// <summary>
        /// Após a action ser executada, é verificado se no Cache da API ja contém a _cacheKey corrente, caso nao tenha
        /// é adicionado a mesma com seu respectivo tempo de duração.
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
        {
            if (!Cache.Contains(_cachekey))
            {
                var body = await actionExecutedContext.Response.Content.ReadAsByteArrayAsync();

                var cacheItem = new WebApiCacheItem(actionExecutedContext.Response.Content.Headers.ContentType, body);
                Cache.Add(_cachekey, cacheItem, DateTime.Now.AddSeconds(Server));
            }

            if (IsCacheable(Client, actionExecutedContext.ActionContext))
            {
                actionExecutedContext.ActionContext.Response.Headers.CacheControl = GetClientCache();
            }
        }
        public async Task Phoenix_action_should_display_all_phoenix_in_cache()
        {
            Global.Cache.PhoenixFireCage.Add("item1", Substitute.For <Phoenix>(Substitute.For <_IInvocation>(), new CacheItem {
                Data = "data", Key = "item1"
            }));
            Global.Cache.PhoenixFireCage.Add("item2", Substitute.For <Phoenix>(Substitute.For <_IInvocation>(), new CacheItem {
                Data = new MediaTypeHeaderValue("text/json"), Key = "item2"
            }));
            Global.Cache.PhoenixFireCage.Add("item5", Substitute.For <Phoenix>(Substitute.For <_IInvocation>(), new CacheItem()));
            Global.Cache.PhoenixFireCage.Add("item6", Substitute.For <Phoenix>(Substitute.For <_IInvocation>(), new CacheItem()));

            var syncStore = Substitute.For <ICacheStore>();

            syncStore.Get(Arg.Any <string>()).Returns(c => new CacheItem
            {
                Key  = c.Arg <string>(),
                Data = "data"
            });

            var asyncStore = Substitute.For <IAsyncCacheStore>();

            asyncStore.GetAsync(Arg.Any <string>()).Returns(c =>
            {
                object obj = new WebApiCacheItem
                {
                    Key     = c.Arg <string>(),
                    Content = new byte[0]
                };
                return(Task.FromResult(obj));
            });

            var provider = Substitute.For <ICacheStoreProvider>();

            provider.GetCacheStore(Arg.Any <int>()).Returns(syncStore);
            provider.GetAsyncCacheStore(Arg.Any <int>()).Returns(asyncStore);
            var controller = new FlatwhiteStatusController(provider);

            // Action
            var result = await controller.Phoenixes();

            var jsonResult = (JsonResult <List <FlatwhiteStatusController.CacheItemStatus> >)result;

            // Assert
            Assert.AreEqual(4, jsonResult.Content.Count);
        }
예제 #10
0
        public async Task Should_return_new_etag_if_cache_item_found_but_doesnt_match_checksum(string cacheChecksum, HttpStatusCode resultCode)
        {
            // Arrange
            var cacheControl = new CacheControlHeaderValue
            {
                MaxStale      = true,
                MaxStaleLimit = TimeSpan.FromSeconds(15),
                MinFresh      = TimeSpan.FromSeconds(20)
            };

            var oldCacheItem = new WebApiCacheItem
            {
                CreatedTime               = DateTime.UtcNow.AddSeconds(-11),
                MaxAge                    = 10,
                StaleWhileRevalidate      = 5,
                IgnoreRevalidationRequest = true,
                ResponseCharSet           = "UTF8",
                ResponseMediaType         = "text/json",
                Content                   = new byte[0],
                Key      = "fw-0-HASHEDKEY",
                Checksum = cacheChecksum
            };

            var request = UnitTestHelper.GetMessage();

            request.Headers.Add("If-None-Match", "\"fw-0-HASHEDKEY-OLDCHECKSUM\"");
            var builder = new CacheResponseBuilder();
            var handler = new EtagHeaderHandler(builder);
            await Global.CacheStoreProvider.GetAsyncCacheStore().SetAsync("fw-0-HASHEDKEY", oldCacheItem, DateTimeOffset.Now.AddDays(1)).ConfigureAwait(false);


            // Action
            Global.Cache.PhoenixFireCage["fw-0-HASHEDKEY"] = new WebApiPhoenix(Substitute.For <_IInvocation>(), oldCacheItem, request);
            var response = await handler.HandleAsync(cacheControl, request, CancellationToken.None).ConfigureAwait(false);

            Assert.AreEqual(resultCode, response.StatusCode);
            if (resultCode == HttpStatusCode.OK)
            {
                Assert.AreEqual($"\"fw-0-HASHEDKEY-{cacheChecksum}\"", response.Headers.ETag.Tag);
            }
            else
            {
                Assert.IsNull(response.Headers.ETag);
            }
        }
예제 #11
0
        public async Task Should_save_result_to_cache_and_create_new_phoenix()
        {
            // Arrange
            _request.Headers.CacheControl =
                new CacheControlHeaderValue
            {
                MaxAge     = TimeSpan.FromSeconds(10),
                Extensions = { new NameValueHeaderValue(WebApiExtensions.__cacheControl_flatwhite_force_refresh, "1") }
            };

            var att = new Flatwhite.WebApi.OutputCacheAttribute
            {
                MaxAge = 10,
                IgnoreRevalidationRequest = true,
                StaleIfError = 2,
                AutoRefresh  = true
            };

            _actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent("{}")
            };

            var objCacheItem = new WebApiCacheItem
            {
                Key         = CacheKey,
                MaxAge      = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
            };
            var existingPhoenix = Substitute.For <WebApiPhoenix>(_invocation, objCacheItem, _request);

            Global.Cache.PhoenixFireCage[CacheKey] = existingPhoenix;

            // Action
            await att.OnActionExecutedAsync(_actionExecutedContext, CancellationToken.None);

            // Assert
            await _store.Received(2).SetAsync(Arg.Any <string>(), Arg.Any <object>(), Arg.Any <DateTimeOffset>());

            Assert.AreNotSame(existingPhoenix, Global.Cache.PhoenixFireCage[CacheKey]);
        }
예제 #12
0
        public async Task Should_response_current_cache_if_there_is_error_but_StaleIfError_is_not_zero()
        {
            // Arrange
            _request.Headers.CacheControl =
                new CacheControlHeaderValue
            {
                MaxAge     = TimeSpan.FromSeconds(10),
                Extensions = { new NameValueHeaderValue(WebApiExtensions.__cacheControl_flatwhite_force_refresh, "1") }
            };

            var att = new Flatwhite.WebApi.OutputCacheAttribute
            {
                MaxAge = 10,
                IgnoreRevalidationRequest = true,
                StaleIfError = 2
            };

            _actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);

            var objCacheItem = new WebApiCacheItem
            {
                MaxAge = 10,
                Key    = CacheKey,
                StaleWhileRevalidate = 5,
                StoreId                   = _store.StoreId,
                StaleIfError              = 2,
                CreatedTime               = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
                Content                   = new byte[] { 0, 1, 2 },
                ResponseMediaType         = "application/json",
                ResponseCharSet           = "utf-8",
                IgnoreRevalidationRequest = true
            };

            _store.GetAsync(CacheKey).Returns(objCacheItem);

            // Action
            await att.OnActionExecutedAsync(_actionExecutedContext, CancellationToken.None);

            // Assert
            Assert.IsTrue(_actionExecutedContext.Response.IsSuccessStatusCode);
            await _store.DidNotReceive().SetAsync(Arg.Any <string>(), Arg.Any <object>(), Arg.Any <DateTimeOffset>());
        }
예제 #13
0
        public void Should_return_Stale_header_if_stale_by_max_age()
        {
            // Arrange
            var cacheControl = new CacheControlHeaderValue
            {
                MaxStale      = true,
                MaxStaleLimit = TimeSpan.FromSeconds(15),
                MinFresh      = TimeSpan.FromSeconds(20)
            };

            var cacheItem = new WebApiCacheItem
            {
                CreatedTime               = DateTime.UtcNow.AddSeconds(-11),
                MaxAge                    = 10,
                StaleWhileRevalidate      = 5,
                IgnoreRevalidationRequest = true,
                ResponseCharSet           = "UTF8",
                ResponseMediaType         = "text/json",
                Content                   = new byte[0],
                Key = "CacheKey" + Guid.NewGuid()
            };

            Global.Cache.PhoenixFireCage[cacheItem.Key] = new Phoenix(NSubstitute.Substitute.For <_IInvocation>(), new CacheItem());

            var request = UnitTestHelper.GetMessage();
            var svc     = new CacheResponseBuilder {
            };

            // Action
            var response = svc.GetResponse(cacheControl, cacheItem, request);

            // Assert
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.AreEqual("Cache freshness lifetime not qualified", response.Headers.GetValues("X-Flatwhite-Warning").First());
            Assert.AreEqual("Response is Stale", response.Headers.GetValues("X-Flatwhite-Warning").Last());
            Assert.AreEqual($"110 - \"Response is Stale\"", response.Headers.GetValues("Warning").Last());
            Assert.AreEqual("stale-while-revalidate", response.Headers.CacheControl.Extensions.ToList().First().Name);
            Assert.AreEqual(cacheItem.ResponseMediaType, response.Content.Headers.ContentType.MediaType);
            Assert.AreEqual(cacheItem.ResponseCharSet, response.Content.Headers.ContentType.CharSet);
        }
예제 #14
0
        public async Task Should_call_reborn_on_existing_phoenix_and_try_refresh_cache_when_cache_item_is_stale()
        {
            // Arrange
            var store = Substitute.For <IAsyncCacheStore>();

            store.StoreId.Returns(1000);
            var objCacheItem = new WebApiCacheItem
            {
                MaxAge = 5,
                StaleWhileRevalidate = 5,
                StoreId     = 1000,
                CreatedTime = DateTime.UtcNow.AddSeconds(-5).AddMilliseconds(-1),
            };

            var existingPhoenix = Substitute.For <WebApiPhoenix>(_invocation, objCacheItem, _request);

            existingPhoenix.When(x => x.Reborn()).Do(c =>
            {
                Global.Cache.PhoenixFireCage.Remove(objCacheItem.Key);
            });

            store.GetAsync(Arg.Any <string>()).Returns(c =>
            {
                objCacheItem.Key = c.Arg <string>();
                Global.Cache.PhoenixFireCage[objCacheItem.Key] = existingPhoenix;
                return(Task.FromResult((object)objCacheItem));
            });

            Global.CacheStoreProvider.RegisterAsyncStore(store);
            var att = new Flatwhite.WebApi.OutputCacheAttribute {
                MaxAge = 5, CacheStoreId = 1000, StaleWhileRevalidate = 5
            };

            // Action
            await att.OnActionExecutingAsync(_actionContext, CancellationToken.None);

            // Assert
            existingPhoenix.Received(1).Reborn();
        }
예제 #15
0
        /// <summary>
        /// Occurs after the action method is invoked.
        /// </summary>
        /// <param name="actionExecutedContext">The action executed context.</param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (actionExecutedContext.Exception != null)
            {
                return;
            }

            if (!this.IsCacheable(actionExecutedContext.ActionContext))
            {
                return;
            }

            if (!Cache.Exists(this._cacheKey))
            {
                var cachedHttpResponseContent = new WebApiCacheItem();
                cachedHttpResponseContent.Content    = actionExecutedContext.Response.Content.ReadAsByteArrayAsync().Result;
                cachedHttpResponseContent.MediaType  = actionExecutedContext.Response.Content.Headers.ContentType.MediaType;
                cachedHttpResponseContent.StatusCode = actionExecutedContext.Response.StatusCode.GetHashCode();

                Cache.Add(this._cacheKey, cachedHttpResponseContent, this._serverTimeSpan, this._forceOverWrite);

                actionExecutedContext.ActionContext.Response.Headers.CacheControl = this.SetClientCache();
            }
        }
        public void CreatePhoenixPublic(_IInvocation invocation, WebApiCacheItem cacheItem, HttpRequestMessage request)
        {
            var methodInfo = typeof(Flatwhite.WebApi.OutputCacheAttribute).GetMethod("CreatePhoenix", BindingFlags.Instance | BindingFlags.NonPublic);

            methodInfo.Invoke(this, new object[] { invocation, cacheItem, request });
        }
예제 #17
0
 public WebApiPhoenixWithPublicMethods(_IInvocation invocation, WebApiCacheItem cacheItem, HttpRequestMessage requestMessage)
     : base(invocation, cacheItem, requestMessage)
 {
 }