mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-01 07:35:28 +08:00 
			
		
		
		
	* changed name to cache options to fix issue #146 * Add acceptance test that exposes JSON deserialization bug from issue #146 - Create InMemoryJsonHandle for CacheManager that mimics DictionaryHandle but uses ICacheSerializer to serialize/deserialize values instead of saving references - Add CacheManager.Serialization.Json package - Add StartupWithCustomCacheHandle class that extends Startup and overrides ConfigureServices to register InMemoryJsonHandle - Add GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache method to initiate Ocelot with StartupWithCustomCacheHandle - Add test should_return_response_200_with_simple_url_when_using_jsonserialized_cache * Create Acceptance test that exposes issue #152 - Add GivenOcelotIsRunningUsingJsonSerializedCache() that initializes Ocelot with InMemoryJsonHandle - Add should_return_cached_response_when_using_jsonserialized_cache test * Change Consul port to 9502 on should_return_response_200_with_simple_url_when_using_jsonserialized_cache() test * Implement mapping of HttpResponseMessage to CachedResponse to enable distributed caching - Add CachedResponse class that holds HttpResponse data - Add mapping methods in OutputCacheMiddleware to create HttpResponseMessage from CachedResponse and vice versa - Replace HttpResponseMessage with CachedResponse in services registrations - Replace HttpResponseMessage with CachedResponse in OutputCacheController's IOcelotCache * Fix unit tests for OutputCacheMiddleware and OutputCacheController by replacing HttpResponseMessage with CachedResponse * Add .editorconfig with default identation settings (spaces with size 4) * Re-format broken files with new identation settings * Add Startup_WithConsul_And_CustomCacheHandle class - Use Startup_WithConsul_And_CustomCacheHandle in GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache step * Do minor cleanups - Rename StartupWithCustomCacheHandle to Startup_WithCustomCacheHandle for better readability - Remove cachemanager settings Action in Startup since it is not used anymore * Drop Task in CreateHttpResponseMessage - unnecessary overhead * Make setters private in CachedResponse - Rework CreateCachedResponse to use new CachedResponse constructor
This commit is contained in:
		 Charalampos Chomenidis
					Charalampos Chomenidis
				
			
				
					committed by
					
						 Tom Pallister
						Tom Pallister
					
				
			
			
				
	
			
			
			 Tom Pallister
						Tom Pallister
					
				
			
						parent
						
							3b27bb376e
						
					
				
				
					commit
					48b5a32676
				
			
							
								
								
									
										9
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| end_of_line = crlf | ||||
| insert_final_newline = true | ||||
|  | ||||
| [*.cs] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
							
								
								
									
										25
									
								
								src/Ocelot/Cache/CachedResponse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Ocelot/Cache/CachedResponse.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Net; | ||||
|  | ||||
| namespace Ocelot.Cache | ||||
| { | ||||
|     public class CachedResponse | ||||
|     { | ||||
|         public CachedResponse( | ||||
|             HttpStatusCode statusCode = HttpStatusCode.OK, | ||||
|             Dictionary<string, IEnumerable<string>> headers = null, | ||||
|             string body = null | ||||
|             ) | ||||
|         { | ||||
|             StatusCode = statusCode; | ||||
|             Headers = headers ?? new Dictionary<string, IEnumerable<string>>(); | ||||
|             Body = body ?? ""; | ||||
|         } | ||||
|  | ||||
|         public HttpStatusCode StatusCode { get; private set; } | ||||
|  | ||||
|         public Dictionary<string, IEnumerable<string>> Headers { get; private set; } | ||||
|  | ||||
|         public string Body { get; private set; } | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http; | ||||
| using Ocelot.Infrastructure.RequestData; | ||||
| using Ocelot.Logging; | ||||
| using Ocelot.Middleware; | ||||
| using System.IO; | ||||
|  | ||||
| namespace Ocelot.Cache.Middleware | ||||
| { | ||||
| @@ -13,15 +14,15 @@ namespace Ocelot.Cache.Middleware | ||||
|     { | ||||
|         private readonly RequestDelegate _next; | ||||
|         private readonly IOcelotLogger _logger; | ||||
|         private readonly IOcelotCache<HttpResponseMessage> _outputCache; | ||||
|         private readonly IOcelotCache<CachedResponse> _outputCache; | ||||
|         private readonly IRegionCreator _regionCreator; | ||||
|  | ||||
|         public OutputCacheMiddleware(RequestDelegate next, | ||||
|             IOcelotLoggerFactory loggerFactory, | ||||
|             IRequestScopedDataRepository scopedDataRepository, | ||||
|             IOcelotCache<HttpResponseMessage> outputCache, | ||||
|             IOcelotCache<CachedResponse> outputCache, | ||||
|             IRegionCreator regionCreator) | ||||
|             :base(scopedDataRepository) | ||||
|             : base(scopedDataRepository) | ||||
|         { | ||||
|             _next = next; | ||||
|             _outputCache = outputCache; | ||||
| @@ -40,14 +41,15 @@ namespace Ocelot.Cache.Middleware | ||||
|             var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}"; | ||||
|  | ||||
|             _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey); | ||||
|    | ||||
|  | ||||
|             var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region); | ||||
|  | ||||
|             if (cached != null) | ||||
|             { | ||||
|                 _logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey); | ||||
|  | ||||
|                 SetHttpResponseMessageThisRequest(cached); | ||||
|                 var response = CreateHttpResponseMessage(cached); | ||||
|                 SetHttpResponseMessageThisRequest(response); | ||||
|  | ||||
|                 _logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey); | ||||
|  | ||||
| @@ -65,11 +67,50 @@ namespace Ocelot.Cache.Middleware | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             var response = HttpResponseMessage; | ||||
|             cached = await CreateCachedResponse(HttpResponseMessage); | ||||
|  | ||||
|             _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region); | ||||
|             _outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region); | ||||
|  | ||||
|             _logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey); | ||||
|         } | ||||
|  | ||||
|         internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached) | ||||
|         { | ||||
|             if (cached == null) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             var response = new HttpResponseMessage(cached.StatusCode); | ||||
|             foreach (var header in cached.Headers) | ||||
|             { | ||||
|                 response.Headers.Add(header.Key, header.Value); | ||||
|             } | ||||
|             var content = new MemoryStream(Convert.FromBase64String(cached.Body)); | ||||
|             response.Content = new StreamContent(content); | ||||
|  | ||||
|             return response; | ||||
|         } | ||||
|  | ||||
|         internal async Task<CachedResponse> CreateCachedResponse(HttpResponseMessage response) | ||||
|         { | ||||
|             if (response == null) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             var statusCode = response.StatusCode; | ||||
|             var headers = response.Headers.ToDictionary(v => v.Key, v => v.Value); | ||||
|             string body = null; | ||||
|  | ||||
|             if (response.Content != null) | ||||
|             { | ||||
|                 var content = await response.Content.ReadAsByteArrayAsync(); | ||||
|                 body = Convert.ToBase64String(content); | ||||
|             } | ||||
|  | ||||
|             var cached = new CachedResponse(statusCode, headers, body); | ||||
|             return cached; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ namespace Ocelot.Configuration | ||||
|             List<ClaimToThing> claimsToQueries,  | ||||
|             string requestIdKey,  | ||||
|             bool isCached,  | ||||
|             CacheOptions fileCacheOptions,  | ||||
|             CacheOptions cacheOptions,  | ||||
|             string downstreamScheme,  | ||||
|             string loadBalancer,  | ||||
|             string downstreamHost,  | ||||
| @@ -49,7 +49,7 @@ namespace Ocelot.Configuration | ||||
|             IsAuthorised = isAuthorised; | ||||
|             RequestIdKey = requestIdKey; | ||||
|             IsCached = isCached; | ||||
|             CacheOptions = fileCacheOptions; | ||||
|             CacheOptions = cacheOptions; | ||||
|             ClaimsToQueries = claimsToQueries | ||||
|                 ?? new List<ClaimToThing>(); | ||||
|             ClaimsToClaims = claimsToClaims  | ||||
|   | ||||
| @@ -11,9 +11,9 @@ namespace Ocelot.Controllers | ||||
|     [Route("outputcache")] | ||||
|     public class OutputCacheController : Controller | ||||
|     { | ||||
|         private IOcelotCache<HttpResponseMessage> _cache; | ||||
|         private IOcelotCache<CachedResponse> _cache; | ||||
|  | ||||
|         public OutputCacheController(IOcelotCache<HttpResponseMessage> cache) | ||||
|         public OutputCacheController(IOcelotCache<CachedResponse> cache) | ||||
|         { | ||||
|             _cache = cache; | ||||
|         } | ||||
|   | ||||
| @@ -161,13 +161,13 @@ namespace Ocelot.DependencyInjection | ||||
|  | ||||
|         public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings) | ||||
|         { | ||||
|             var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings); | ||||
|             var ocelotOutputCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache); | ||||
|             var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", settings); | ||||
|             var ocelotOutputCacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache); | ||||
|  | ||||
|             _services.RemoveAll(typeof(ICacheManager<HttpResponseMessage>)); | ||||
|             _services.RemoveAll(typeof(IOcelotCache<HttpResponseMessage>)); | ||||
|             _services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache); | ||||
|             _services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotOutputCacheManager); | ||||
|             _services.RemoveAll(typeof(ICacheManager<CachedResponse>)); | ||||
|             _services.RemoveAll(typeof(IOcelotCache<CachedResponse>)); | ||||
|             _services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache); | ||||
|             _services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager); | ||||
|  | ||||
|             var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings); | ||||
|             var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache); | ||||
|   | ||||
							
								
								
									
										137
									
								
								test/Ocelot.AcceptanceTests/Caching/InMemoryJsonHandle.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								test/Ocelot.AcceptanceTests/Caching/InMemoryJsonHandle.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| using CacheManager.Core; | ||||
| using CacheManager.Core.Internal; | ||||
| using CacheManager.Core.Logging; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Linq; | ||||
| using static CacheManager.Core.Utility.Guard; | ||||
|  | ||||
| namespace Ocelot.AcceptanceTests.Caching | ||||
| { | ||||
|     public class InMemoryJsonHandle<TCacheValue> : BaseCacheHandle<TCacheValue> | ||||
|     { | ||||
|         private readonly ICacheSerializer _serializer; | ||||
|         private readonly ConcurrentDictionary<string, Tuple<Type, byte[]>> _cache; | ||||
|  | ||||
|         public InMemoryJsonHandle( | ||||
|             ICacheManagerConfiguration managerConfiguration, | ||||
|             CacheHandleConfiguration configuration, | ||||
|             ICacheSerializer serializer, | ||||
|             ILoggerFactory loggerFactory) : base(managerConfiguration, configuration) | ||||
|         { | ||||
|             _cache = new ConcurrentDictionary<string, Tuple<Type, byte[]>>(); | ||||
|             _serializer = serializer; | ||||
|             Logger = loggerFactory.CreateLogger(this); | ||||
|         } | ||||
|  | ||||
|         public override int Count => _cache.Count; | ||||
|  | ||||
|         protected override ILogger Logger { get; } | ||||
|  | ||||
|         public override void Clear() => _cache.Clear(); | ||||
|  | ||||
|         public override void ClearRegion(string region) | ||||
|         { | ||||
|             NotNullOrWhiteSpace(region, nameof(region)); | ||||
|  | ||||
|             var key = string.Concat(region, ":"); | ||||
|             foreach (var item in _cache.Where(p => p.Key.StartsWith(key, StringComparison.OrdinalIgnoreCase))) | ||||
|             { | ||||
|                 _cache.TryRemove(item.Key, out Tuple<Type, byte[]> val); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public override bool Exists(string key) | ||||
|         { | ||||
|             NotNullOrWhiteSpace(key, nameof(key)); | ||||
|  | ||||
|             return _cache.ContainsKey(key); | ||||
|         } | ||||
|  | ||||
|         public override bool Exists(string key, string region) | ||||
|         { | ||||
|             NotNullOrWhiteSpace(region, nameof(region)); | ||||
|             var fullKey = GetKey(key, region); | ||||
|             return _cache.ContainsKey(fullKey); | ||||
|         } | ||||
|  | ||||
|         protected override bool AddInternalPrepared(CacheItem<TCacheValue> item) | ||||
|         { | ||||
|             NotNull(item, nameof(item)); | ||||
|  | ||||
|             var key = GetKey(item.Key, item.Region); | ||||
|  | ||||
|             var serializedItem = _serializer.SerializeCacheItem(item); | ||||
|  | ||||
|             return _cache.TryAdd(key, new Tuple<Type, byte[]>(item.Value.GetType(), serializedItem)); | ||||
|         } | ||||
|  | ||||
|         protected override CacheItem<TCacheValue> GetCacheItemInternal(string key) => GetCacheItemInternal(key, null); | ||||
|  | ||||
|         protected override CacheItem<TCacheValue> GetCacheItemInternal(string key, string region) | ||||
|         { | ||||
|             var fullKey = GetKey(key, region); | ||||
|  | ||||
|             CacheItem<TCacheValue> deserializedResult = null; | ||||
|  | ||||
|             if (_cache.TryGetValue(fullKey, out Tuple<Type, byte[]> result)) | ||||
|             { | ||||
|                 deserializedResult = _serializer.DeserializeCacheItem<TCacheValue>(result.Item2, result.Item1); | ||||
|  | ||||
|                 if (deserializedResult.ExpirationMode != ExpirationMode.None && IsExpired(deserializedResult, DateTime.UtcNow)) | ||||
|                 { | ||||
|                     _cache.TryRemove(fullKey, out Tuple<Type, byte[]> removeResult); | ||||
|                     TriggerCacheSpecificRemove(key, region, CacheItemRemovedReason.Expired, deserializedResult.Value); | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return deserializedResult; | ||||
|         } | ||||
|  | ||||
|         protected override void PutInternalPrepared(CacheItem<TCacheValue> item) | ||||
|         { | ||||
|             NotNull(item, nameof(item)); | ||||
|  | ||||
|             var serializedItem = _serializer.SerializeCacheItem<TCacheValue>(item); | ||||
|  | ||||
|             _cache[GetKey(item.Key, item.Region)] = new Tuple<Type, byte[]>(item.Value.GetType(), serializedItem); | ||||
|         } | ||||
|  | ||||
|         protected override bool RemoveInternal(string key) => RemoveInternal(key, null); | ||||
|  | ||||
|         protected override bool RemoveInternal(string key, string region) | ||||
|         { | ||||
|             var fullKey = GetKey(key, region); | ||||
|             return _cache.TryRemove(fullKey, out Tuple<Type, byte[]> val); | ||||
|         } | ||||
|  | ||||
|         private static string GetKey(string key, string region) | ||||
|         { | ||||
|             NotNullOrWhiteSpace(key, nameof(key)); | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(region)) | ||||
|             { | ||||
|                 return key; | ||||
|             } | ||||
|  | ||||
|             return string.Concat(region, ":", key); | ||||
|         } | ||||
|  | ||||
|         private static bool IsExpired(CacheItem<TCacheValue> item, DateTime now) | ||||
|         { | ||||
|             if (item.ExpirationMode == ExpirationMode.Absolute | ||||
|                 && item.CreatedUtc.Add(item.ExpirationTimeout) < now) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|             else if (item.ExpirationMode == ExpirationMode.Sliding | ||||
|                 && item.LastAccessedUtc.Add(item.ExpirationTimeout) < now) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -58,6 +58,42 @@ namespace Ocelot.AcceptanceTests | ||||
|                 .BDDfy(); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void should_return_cached_response_when_using_jsonserialized_cache() | ||||
|         { | ||||
|             var configuration = new FileConfiguration | ||||
|             { | ||||
|                 ReRoutes = new List<FileReRoute> | ||||
|                     { | ||||
|                         new FileReRoute | ||||
|                         { | ||||
|                             DownstreamPathTemplate = "/", | ||||
|                             DownstreamPort = 51879, | ||||
|                             DownstreamScheme = "http", | ||||
|                             DownstreamHost = "localhost", | ||||
|                             UpstreamPathTemplate = "/", | ||||
|                             UpstreamHttpMethod = new List<string> { "Get" }, | ||||
|                             FileCacheOptions = new FileCacheOptions | ||||
|                             { | ||||
|                                 TtlSeconds = 100 | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|             }; | ||||
|  | ||||
|             this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) | ||||
|                 .And(x => _steps.GivenThereIsAConfiguration(configuration)) | ||||
|                 .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache()) | ||||
|                 .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) | ||||
|                 .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) | ||||
|                 .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) | ||||
|                 .Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom")) | ||||
|                 .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) | ||||
|                 .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) | ||||
|                 .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) | ||||
|                 .BDDfy(); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void should_not_return_cached_response_as_ttl_expires() | ||||
|         { | ||||
|   | ||||
| @@ -68,6 +68,45 @@ namespace Ocelot.AcceptanceTests | ||||
|                 .BDDfy(); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() | ||||
|         { | ||||
|             var configuration = new FileConfiguration | ||||
|             { | ||||
|                 ReRoutes = new List<FileReRoute> | ||||
|                     { | ||||
|                         new FileReRoute | ||||
|                         { | ||||
|                             DownstreamPathTemplate = "/", | ||||
|                             DownstreamScheme = "http", | ||||
|                             DownstreamHost = "localhost", | ||||
|                             DownstreamPort = 51779, | ||||
|                             UpstreamPathTemplate = "/", | ||||
|                             UpstreamHttpMethod = new List<string> { "Get" }, | ||||
|                         } | ||||
|                     }, | ||||
|                 GlobalConfiguration = new FileGlobalConfiguration() | ||||
|                 { | ||||
|                     ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() | ||||
|                     { | ||||
|                         Host = "localhost", | ||||
|                         Port = 9502 | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             var fakeConsulServiceDiscoveryUrl = "http://localhost:9502"; | ||||
|  | ||||
|             this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) | ||||
|                 .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) | ||||
|                 .And(x => _steps.GivenThereIsAConfiguration(configuration)) | ||||
|                 .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) | ||||
|                 .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) | ||||
|                 .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) | ||||
|                 .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) | ||||
|                 .BDDfy(); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void should_load_configuration_out_of_consul() | ||||
|         { | ||||
| @@ -236,7 +275,7 @@ namespace Ocelot.AcceptanceTests | ||||
|  | ||||
|                                         var kvp = new FakeConsulGetResponse(base64); | ||||
|  | ||||
|                                         await context.Response.WriteJsonAsync(new FakeConsulGetResponse[]{kvp}); | ||||
|                                         await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp }); | ||||
|                                     } | ||||
|  | ||||
|                                     else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/OcelotConfiguration") | ||||
| @@ -311,4 +350,4 @@ namespace Ocelot.AcceptanceTests | ||||
|             _steps.Dispose(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="CacheManager.Serialization.Json" Version="1.1.1" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" /> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01" /> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; | ||||
| using Ocelot.DependencyInjection; | ||||
| using Ocelot.Middleware; | ||||
| using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; | ||||
| using Ocelot.AcceptanceTests.Caching; | ||||
|  | ||||
| namespace Ocelot.AcceptanceTests | ||||
| { | ||||
| @@ -27,13 +28,8 @@ namespace Ocelot.AcceptanceTests | ||||
|  | ||||
|         public IConfigurationRoot Configuration { get; } | ||||
|  | ||||
|         public void ConfigureServices(IServiceCollection services) | ||||
|         public virtual void ConfigureServices(IServiceCollection services) | ||||
|         { | ||||
|             Action<ConfigurationBuilderCachePart> settings = (x) => | ||||
|             { | ||||
|                 x.WithDictionaryHandle(); | ||||
|             }; | ||||
|  | ||||
|             services.AddOcelot(Configuration); | ||||
|         } | ||||
|  | ||||
| @@ -44,4 +40,43 @@ namespace Ocelot.AcceptanceTests | ||||
|             app.UseOcelot().Wait(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class Startup_WithCustomCacheHandle : Startup | ||||
|     { | ||||
|         public Startup_WithCustomCacheHandle(IHostingEnvironment env) : base(env) { } | ||||
|  | ||||
|         public override void ConfigureServices(IServiceCollection services) | ||||
|         { | ||||
|             services.AddOcelot(Configuration) | ||||
|                 .AddCacheManager((x) => | ||||
|                 { | ||||
|                     x.WithMicrosoftLogging(log => | ||||
|                     { | ||||
|                         log.AddConsole(LogLevel.Debug); | ||||
|                     }) | ||||
|                     .WithJsonSerializer() | ||||
|                     .WithHandle(typeof(InMemoryJsonHandle<>)); | ||||
|                 }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class Startup_WithConsul_And_CustomCacheHandle : Startup | ||||
|     { | ||||
|         public Startup_WithConsul_And_CustomCacheHandle(IHostingEnvironment env) : base(env) { } | ||||
|  | ||||
|         public override void ConfigureServices(IServiceCollection services) | ||||
|         { | ||||
|             services.AddOcelot(Configuration) | ||||
|                 .AddCacheManager((x) => | ||||
|                 { | ||||
|                     x.WithMicrosoftLogging(log => | ||||
|                     { | ||||
|                         log.AddConsole(LogLevel.Debug); | ||||
|                     }) | ||||
|                     .WithJsonSerializer() | ||||
|                     .WithHandle(typeof(InMemoryJsonHandle<>)); | ||||
|                 }) | ||||
|                 .AddStoreOcelotConfigurationInConsul(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,7 @@ using Ocelot.Middleware; | ||||
| using Ocelot.ServiceDiscovery; | ||||
| using Shouldly; | ||||
| using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; | ||||
| using Ocelot.AcceptanceTests.Caching; | ||||
|  | ||||
| namespace Ocelot.AcceptanceTests | ||||
| { | ||||
| @@ -74,9 +75,9 @@ namespace Ocelot.AcceptanceTests | ||||
|         /// </summary> | ||||
|         public void GivenOcelotIsRunning() | ||||
|         { | ||||
|              _webHostBuilder = new WebHostBuilder(); | ||||
|              | ||||
|             _webHostBuilder.ConfigureServices(s =>  | ||||
|             _webHostBuilder = new WebHostBuilder(); | ||||
|  | ||||
|             _webHostBuilder.ConfigureServices(s => | ||||
|             { | ||||
|                 s.AddSingleton(_webHostBuilder); | ||||
|             }); | ||||
| @@ -107,6 +108,21 @@ namespace Ocelot.AcceptanceTests | ||||
|             _ocelotClient = _ocelotServer.CreateClient(); | ||||
|         } | ||||
|  | ||||
|         public void GivenOcelotIsRunningUsingJsonSerializedCache() | ||||
|         { | ||||
|             _webHostBuilder = new WebHostBuilder(); | ||||
|  | ||||
|             _webHostBuilder.ConfigureServices(s => | ||||
|             { | ||||
|                 s.AddSingleton(_webHostBuilder); | ||||
|             }); | ||||
|  | ||||
|             _ocelotServer = new TestServer(_webHostBuilder | ||||
|                 .UseStartup<Startup_WithCustomCacheHandle>()); | ||||
|  | ||||
|             _ocelotClient = _ocelotServer.CreateClient(); | ||||
|         } | ||||
|  | ||||
|         public void GivenOcelotIsRunningUsingConsulToStoreConfig() | ||||
|         { | ||||
|             _webHostBuilder = new WebHostBuilder(); | ||||
| @@ -122,16 +138,31 @@ namespace Ocelot.AcceptanceTests | ||||
|             _ocelotClient = _ocelotServer.CreateClient(); | ||||
|         } | ||||
|  | ||||
|         public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache() | ||||
|         { | ||||
|             _webHostBuilder = new WebHostBuilder(); | ||||
|  | ||||
|             _webHostBuilder.ConfigureServices(s => | ||||
|             { | ||||
|                 s.AddSingleton(_webHostBuilder); | ||||
|             }); | ||||
|  | ||||
|             _ocelotServer = new TestServer(_webHostBuilder | ||||
|                 .UseStartup<Startup_WithConsul_And_CustomCacheHandle>()); | ||||
|  | ||||
|             _ocelotClient = _ocelotServer.CreateClient(); | ||||
|         } | ||||
|  | ||||
|         internal void ThenTheResponseShouldBe(FileConfiguration expected) | ||||
|         { | ||||
|             var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result); | ||||
|              | ||||
|  | ||||
|             response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath); | ||||
|             response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey); | ||||
|             response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host); | ||||
|             response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port); | ||||
|  | ||||
|             for(var i = 0; i < response.ReRoutes.Count; i++) | ||||
|             for (var i = 0; i < response.ReRoutes.Count; i++) | ||||
|             { | ||||
|                 response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost); | ||||
|                 response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate); | ||||
| @@ -155,7 +186,7 @@ namespace Ocelot.AcceptanceTests | ||||
|  | ||||
|             var configuration = builder.Build(); | ||||
|             _webHostBuilder = new WebHostBuilder(); | ||||
|             _webHostBuilder.ConfigureServices(s =>  | ||||
|             _webHostBuilder.ConfigureServices(s => | ||||
|             { | ||||
|                 s.AddSingleton(_webHostBuilder); | ||||
|             }); | ||||
| @@ -172,7 +203,7 @@ namespace Ocelot.AcceptanceTests | ||||
|                         }) | ||||
|                         .WithDictionaryHandle(); | ||||
|                     }; | ||||
|                      | ||||
|  | ||||
|                     s.AddOcelot(configuration); | ||||
|                 }) | ||||
|                 .ConfigureLogging(l => | ||||
| @@ -299,12 +330,12 @@ namespace Ocelot.AcceptanceTests | ||||
|         public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times) | ||||
|         { | ||||
|             var tasks = new Task[times]; | ||||
|              | ||||
|  | ||||
|             for (int i = 0; i < times; i++) | ||||
|             { | ||||
|                 var urlCopy = url; | ||||
|                 tasks[i] = GetForServiceDiscoveryTest(urlCopy); | ||||
|                 Thread.Sleep(_random.Next(40,60)); | ||||
|                 Thread.Sleep(_random.Next(40, 60)); | ||||
|             } | ||||
|  | ||||
|             Task.WaitAll(tasks); | ||||
| @@ -327,7 +358,7 @@ namespace Ocelot.AcceptanceTests | ||||
|                 request.Headers.Add("ClientId", clientId); | ||||
|                 _response = _ocelotClient.SendAsync(request).Result; | ||||
|             } | ||||
|         }  | ||||
|         } | ||||
|  | ||||
|         public void WhenIGetUrlOnTheApiGateway(string url, string requestId) | ||||
|         { | ||||
|   | ||||
| @@ -19,12 +19,12 @@ | ||||
|  | ||||
|     public class OutputCacheMiddlewareTests : ServerHostedMiddlewareTest | ||||
|     { | ||||
|         private readonly Mock<IOcelotCache<HttpResponseMessage>> _cacheManager; | ||||
|         private HttpResponseMessage _response; | ||||
|         private readonly Mock<IOcelotCache<CachedResponse>> _cacheManager; | ||||
|         private CachedResponse _response; | ||||
|  | ||||
|         public OutputCacheMiddlewareTests() | ||||
|         { | ||||
|             _cacheManager = new Mock<IOcelotCache<HttpResponseMessage>>(); | ||||
|             _cacheManager = new Mock<IOcelotCache<CachedResponse>>(); | ||||
|  | ||||
|             ScopedRepository | ||||
|                 .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) | ||||
| @@ -36,7 +36,8 @@ | ||||
|         [Fact] | ||||
|         public void should_returned_cached_item_when_it_is_in_cache() | ||||
|         { | ||||
|             this.Given(x => x.GivenThereIsACachedResponse(new HttpResponseMessage())) | ||||
|             var cachedResponse = new CachedResponse(); | ||||
|             this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) | ||||
|                 .And(x => x.GivenTheDownstreamRouteIs()) | ||||
|                 .And(x => x.GivenThereIsADownstreamUrl()) | ||||
|                 .When(x => x.WhenICallTheMiddleware()) | ||||
| @@ -70,7 +71,7 @@ | ||||
|             app.UseOutputCacheMiddleware(); | ||||
|         } | ||||
|  | ||||
|         private void GivenThereIsACachedResponse(HttpResponseMessage response) | ||||
|         private void GivenThereIsACachedResponse(CachedResponse response) | ||||
|         { | ||||
|             _response = response; | ||||
|             _cacheManager | ||||
| @@ -123,7 +124,7 @@ | ||||
|         private void ThenTheCacheAddIsCalledCorrectly() | ||||
|         { | ||||
|             _cacheManager | ||||
|                 .Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>(), It.IsAny<string>()), Times.Once); | ||||
|                 .Verify(x => x.Add(It.IsAny<string>(), It.IsAny<CachedResponse>(), It.IsAny<TimeSpan>(), It.IsAny<string>()), Times.Once); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,12 +14,12 @@ namespace Ocelot.UnitTests.Controllers | ||||
|     public class OutputCacheControllerTests | ||||
|     { | ||||
|         private OutputCacheController _controller; | ||||
|         private Mock<IOcelotCache<HttpResponseMessage>> _cache; | ||||
|         private Mock<IOcelotCache<CachedResponse>> _cache; | ||||
|         private IActionResult _result; | ||||
|  | ||||
|         public OutputCacheControllerTests() | ||||
|         { | ||||
|             _cache = new Mock<IOcelotCache<HttpResponseMessage>>(); | ||||
|             _cache = new Mock<IOcelotCache<CachedResponse>>(); | ||||
|             _controller = new OutputCacheController(_cache.Object); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -72,9 +72,9 @@ namespace Ocelot.UnitTests.DependencyInjection | ||||
|  | ||||
|         private void OnlyOneVersionOfEachCacheIsRegistered() | ||||
|         { | ||||
|             var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<HttpResponseMessage>)); | ||||
|             var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<HttpResponseMessage>)); | ||||
|             var thing = (CacheManager.Core.ICacheManager<System.Net.Http.HttpResponseMessage>)outputCacheManager.ImplementationInstance; | ||||
|             var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>)); | ||||
|             var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>)); | ||||
|             var thing = (CacheManager.Core.ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance; | ||||
|             thing.Configuration.MaxRetries.ShouldBe(_maxRetries); | ||||
|              | ||||
|             var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IOcelotConfiguration>)); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user