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)); }
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(); }
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); } }
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)); }
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); }
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))); }
/// <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); }
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); } }
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]); }
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>()); }
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); }
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(); }
/// <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 }); }
public WebApiPhoenixWithPublicMethods(_IInvocation invocation, WebApiCacheItem cacheItem, HttpRequestMessage requestMessage) : base(invocation, cacheItem, requestMessage) { }