mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 08:55:28 +08:00 
			
		
		
		
	Feature/#574 look at httpclient cache key (#589)
* #574 consolidate some code, man the config stuff is a mess! * #574 just use the downstream re route and the key for caching http clients * #574 added benchmark, i was suprised to learn using a complex type was faster than a string in benchmark .net dictionary tests, hey ho probably dont have enough data in the type...
This commit is contained in:
		
							
								
								
									
										163
									
								
								test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
namespace Ocelot.AcceptanceTests
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using System.Collections.Concurrent;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.Net;
 | 
			
		||||
    using Configuration;
 | 
			
		||||
    using Microsoft.AspNetCore.Http;
 | 
			
		||||
    using Ocelot.Configuration.File;
 | 
			
		||||
    using Requester;
 | 
			
		||||
    using Shouldly;
 | 
			
		||||
    using TestStack.BDDfy;
 | 
			
		||||
    using Xunit;
 | 
			
		||||
 | 
			
		||||
    public class HttpClientCachingTests : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Steps _steps;
 | 
			
		||||
        private string _downstreamPath;
 | 
			
		||||
        private readonly ServiceHandler _serviceHandler;
 | 
			
		||||
 | 
			
		||||
        public HttpClientCachingTests()
 | 
			
		||||
        {
 | 
			
		||||
            _serviceHandler = new ServiceHandler();
 | 
			
		||||
            _steps = new Steps();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_cache_one_http_client_same_re_route()
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                    {
 | 
			
		||||
                        new FileReRoute
 | 
			
		||||
                        {
 | 
			
		||||
                            DownstreamPathTemplate = "/",
 | 
			
		||||
                            DownstreamScheme = "http",
 | 
			
		||||
                            DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                            {
 | 
			
		||||
                                new FileHostAndPort
 | 
			
		||||
                                {
 | 
			
		||||
                                    Host = "localhost",
 | 
			
		||||
                                    Port = 51879,
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            UpstreamPathTemplate = "/",
 | 
			
		||||
                            UpstreamHttpMethod = new List<string> { "Get" },
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var cache = new FakeHttpClientCache();
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
 | 
			
		||||
                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
 | 
			
		||||
                .And(x => cache.Count.ShouldBe(1))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_cache_two_http_client_different_re_route()
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                {
 | 
			
		||||
                    new FileReRoute
 | 
			
		||||
                    {
 | 
			
		||||
                        DownstreamPathTemplate = "/",
 | 
			
		||||
                        DownstreamScheme = "http",
 | 
			
		||||
                        DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                        {
 | 
			
		||||
                            new FileHostAndPort
 | 
			
		||||
                            {
 | 
			
		||||
                                Host = "localhost",
 | 
			
		||||
                                Port = 51879,
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        UpstreamPathTemplate = "/",
 | 
			
		||||
                        UpstreamHttpMethod = new List<string> { "Get" },
 | 
			
		||||
                    },
 | 
			
		||||
                    new FileReRoute
 | 
			
		||||
                    {
 | 
			
		||||
                        DownstreamPathTemplate = "/two",
 | 
			
		||||
                        DownstreamScheme = "http",
 | 
			
		||||
                        DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                        {
 | 
			
		||||
                            new FileHostAndPort
 | 
			
		||||
                            {
 | 
			
		||||
                                Host = "localhost",
 | 
			
		||||
                                Port = 51879,
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        UpstreamPathTemplate = "/two",
 | 
			
		||||
                        UpstreamHttpMethod = new List<string> { "Get" },
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var cache = new FakeHttpClientCache();
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
			
		||||
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
 | 
			
		||||
                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
 | 
			
		||||
                .And(x => cache.Count.ShouldBe(2))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenThereIsAServiceRunningOn(string baseUrl,  int statusCode, string responseBody)
 | 
			
		||||
        {
 | 
			
		||||
            _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
 | 
			
		||||
            {
 | 
			
		||||
                context.Response.StatusCode = statusCode;
 | 
			
		||||
                await context.Response.WriteAsync(responseBody);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            _serviceHandler.Dispose();
 | 
			
		||||
            _steps.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class FakeHttpClientCache : IHttpClientCache
 | 
			
		||||
        {
 | 
			
		||||
            private readonly ConcurrentDictionary<DownstreamReRoute, IHttpClient> _httpClientsCache;
 | 
			
		||||
 | 
			
		||||
            public FakeHttpClientCache()
 | 
			
		||||
            {
 | 
			
		||||
                _httpClientsCache = new ConcurrentDictionary<DownstreamReRoute, IHttpClient>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime)
 | 
			
		||||
            {
 | 
			
		||||
                _httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public IHttpClient Get(DownstreamReRoute key)
 | 
			
		||||
            {
 | 
			
		||||
                //todo handle error?
 | 
			
		||||
                return _httpClientsCache.TryGetValue(key, out var client) ? client : null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public int Count => _httpClientsCache.Count;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
    using static Ocelot.Infrastructure.Wait;
 | 
			
		||||
    using Configuration.Repository;
 | 
			
		||||
    using Ocelot.Configuration.Creator;
 | 
			
		||||
    using Requester;
 | 
			
		||||
    using CookieHeaderValue = System.Net.Http.Headers.CookieHeaderValue;
 | 
			
		||||
    using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
 | 
			
		||||
 | 
			
		||||
@@ -182,6 +183,35 @@
 | 
			
		||||
            _ocelotClient = _ocelotServer.CreateClient();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void GivenOcelotIsRunningWithFakeHttpClientCache(IHttpClientCache cache)
 | 
			
		||||
        {
 | 
			
		||||
            _webHostBuilder = new WebHostBuilder();
 | 
			
		||||
 | 
			
		||||
            _webHostBuilder
 | 
			
		||||
                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
			
		||||
                {
 | 
			
		||||
                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
			
		||||
                    var env = hostingContext.HostingEnvironment;
 | 
			
		||||
                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
 | 
			
		||||
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                    config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                    config.AddEnvironmentVariables();
 | 
			
		||||
                })
 | 
			
		||||
                .ConfigureServices(s =>
 | 
			
		||||
                {
 | 
			
		||||
                    s.AddSingleton<IHttpClientCache>(cache);
 | 
			
		||||
                    s.AddOcelot();
 | 
			
		||||
                })
 | 
			
		||||
                .Configure(app =>
 | 
			
		||||
                {
 | 
			
		||||
                    app.UseOcelot().Wait();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            _ocelotServer = new TestServer(_webHostBuilder);
 | 
			
		||||
 | 
			
		||||
            _ocelotClient = _ocelotServer.CreateClient();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal void GivenIWait(int wait)
 | 
			
		||||
        {
 | 
			
		||||
            Thread.Sleep(wait);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user