diff --git a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs index dbc4b9155..4e8b90cea 100644 --- a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs +++ b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs @@ -25,13 +25,16 @@ public static class ConsulProviderFactory // TODO : IServiceDiscoveryProviderFac public static ServiceDiscoveryFinderDelegate Get { get; } = CreateProvider; private static IServiceDiscoveryProvider CreateProvider(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamRoute route) { + // Singleton services var factory = provider.GetService(); var consulFactory = provider.GetService(); var contextAccessor = provider.GetService(); + // Scoped services + var context = contextAccessor.HttpContext; var configuration = new ConsulRegistryConfiguration(config.Scheme, config.Host, config.Port, route.ServiceName, config.Token); // TODO Why not to pass 2 args only: config, route? LoL - contextAccessor.HttpContext.Items[nameof(ConsulRegistryConfiguration)] = configuration; // initialize data - var serviceBuilder = provider.GetService(); // consume data in default/custom builder + context.Items[nameof(ConsulRegistryConfiguration)] = configuration; // initialize data + var serviceBuilder = context.RequestServices.GetService(); // consume data in default/custom builder var consulProvider = new Consul(configuration, factory, consulFactory, serviceBuilder); // TODO It must be added to DI-container! diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index 2aa54fc88..cf38c6648 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -578,7 +578,7 @@ public void Should_return_response_200_with_user_forwarding() .And(x => x.GivenServiceIsRunning(1, port2, "/", 200, "{Hello from Tom}")) .And(x => auth.GivenIHaveAToken(identityServerUrl)) .And(x => auth.GivenThereIsAConfiguration(configuration)) - .And(x => auth.GivenOcelotIsRunningWithServices(configureServices, configureApp)) + .And(x => auth.GivenOcelotIsRunningWithServices(configureServices, configureApp, true)) .And(x => auth.GivenIHaveAddedATokenToMyRequest()) .When(x => auth.WhenIGetUrlOnTheApiGateway("/")) .Then(x => auth.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) diff --git a/test/Ocelot.AcceptanceTests/Configuration/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/Configuration/ConfigurationInConsulTests.cs index 52b496524..9c86c4363 100644 --- a/test/Ocelot.AcceptanceTests/Configuration/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/Configuration/ConfigurationInConsulTests.cs @@ -1,180 +1,215 @@ +using CacheManager.Core; using Consul; using IdentityServer4.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; +using Ocelot.AcceptanceTests.Caching; +using Ocelot.Cache.CacheManager; using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Ocelot.Provider.Consul; using System.Text; -namespace Ocelot.AcceptanceTests.Configuration +namespace Ocelot.AcceptanceTests.Configuration; + +public sealed class ConfigurationInConsulTests : Steps, IDisposable { - public class ConfigurationInConsulTests : IDisposable + private IHost _builder; + private IHost _fakeConsulBuilder; + private FileConfiguration _config; + private readonly List _consulServices; + + public ConfigurationInConsulTests() { - private IHost _builder; - private readonly Steps _steps; - private IHost _fakeConsulBuilder; - private FileConfiguration _config; - private readonly List _consulServices; + _consulServices = new List(); + } - public ConfigurationInConsulTests() - { - _consulServices = new List(); - _steps = new Steps(); - } + public override void Dispose() + { + _builder?.Dispose(); + _fakeConsulBuilder?.Dispose(); + base.Dispose(); + } - [Fact] - public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() - { - var consulPort = PortFinder.GetRandomPort(); - var servicePort = PortFinder.GetRandomPort(); + [Fact] + public void Should_return_response_200_with_simple_url_when_using_jsonserialized_cache() + { + var consulPort = PortFinder.GetRandomPort(); + var servicePort = PortFinder.GetRandomPort(); - var configuration = new FileConfiguration - { - Routes = new List + var configuration = new FileConfiguration + { + Routes = new List + { + new() { - new() + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List + new() { - new() - { - Host = "localhost", - Port = servicePort, - }, + Host = "localhost", + Port = servicePort, }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, }, - GlobalConfiguration = new FileGlobalConfiguration + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort, - }, + Scheme = "http", + Host = "localhost", + Port = consulPort, }, - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, string.Empty)) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", string.Empty, 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(); - } + }, + }; + + var fakeConsulServiceDiscoveryUrl = DownstreamUrl(consulPort); + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, string.Empty)) + .And(x => x.GivenThereIsAServiceRunningOn(DownstreamUrl(servicePort), string.Empty, 200, "Hello from Laura")) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => x.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) + .When(x => WhenIGetUrlOnTheApiGateway("/")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } - private Task GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _fakeConsulBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => + private void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache() + { + _webHostBuilder = new WebHostBuilder() + .UseDefaultServiceProvider(_ => _.ValidateScopes = true) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", true, false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); + config.AddJsonFile(_ocelotConfigFileName, true, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddOcelot() + .AddCacheManager(x => x + .WithMicrosoftLogging(_ => { /*log.AddConsole(LogLevel.Debug);*/ }) + .WithJsonSerializer() + .WithHandle(typeof(InMemoryJsonHandle<>))) + .AddConsul() + .AddConfigStoredInConsul(); + }) + .Configure(app => app.UseOcelot().GetAwaiter().GetResult()); // Turning as async/await some tests got broken + + _ocelotServer = new TestServer(_webHostBuilder); + _ocelotClient = _ocelotServer.CreateClient(); + } + + private Task GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _fakeConsulBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => { - app.Run(async context => + if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") { - if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - var json = JsonConvert.SerializeObject(_config); + var json = JsonConvert.SerializeObject(_config); - var bytes = Encoding.UTF8.GetBytes(json); + var bytes = Encoding.UTF8.GetBytes(json); - var base64 = Convert.ToBase64String(bytes); + var base64 = Convert.ToBase64String(bytes); - var kvp = new FakeConsulGetResponse(base64); + var kvp = new FakeConsulGetResponse(base64); - await context.Response.WriteJsonAsync(new[] { kvp }); - } - else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + await context.Response.WriteJsonAsync(new[] { kvp }); + } + else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + try { - try - { - var reader = new StreamReader(context.Request.Body); + var reader = new StreamReader(context.Request.Body); - // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. - // var json = reader.ReadToEnd(); - var json = await reader.ReadToEndAsync(); + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // var json = reader.ReadToEnd(); + var json = await reader.ReadToEndAsync(); - _config = JsonConvert.DeserializeObject(json); + _config = JsonConvert.DeserializeObject(json); - var response = JsonConvert.SerializeObject(true); + var response = JsonConvert.SerializeObject(true); - await context.Response.WriteAsync(response); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } + await context.Response.WriteAsync(response); } - else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + catch (Exception e) { - await context.Response.WriteJsonAsync(_consulServices); + Console.WriteLine(e); + throw; } - }); + } + else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + await context.Response.WriteJsonAsync(_consulServices); + } }); - }).Build(); - return _fakeConsulBuilder.StartAsync(); - } + }); + }).Build(); + return _fakeConsulBuilder.StartAsync(); + } - public class FakeConsulGetResponse + public class FakeConsulGetResponse + { + public FakeConsulGetResponse(string value) { - public FakeConsulGetResponse(string value) - { - Value = value; - } - - public int CreateIndex => 100; - public int ModifyIndex => 200; - public int LockIndex => 200; - public string Key => "InternalConfiguration"; - public int Flags => 0; - public string Value { get; } - public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; + Value = value; } - private Task GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) - { - _builder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => + public int CreateIndex => 100; + public int ModifyIndex => 200; + public int LockIndex => 200; + public string Key => "InternalConfiguration"; + public int Flags => 0; + public string Value { get; } + public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; + } + + private Task GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) + { + _builder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => + app.UsePathBase(basePath); + app.Run(async context => { - app.UsePathBase(basePath); - app.Run(async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); }); - }) - .Build(); - return _builder.StartAsync(); - } - - public void Dispose() - { - _builder?.Dispose(); - _steps.Dispose(); - } + }); + }) + .Build(); + return _builder.StartAsync(); } } diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulConfigurationInConsulTests.cs index d0b1c0ab6..d234c2ab6 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulConfigurationInConsulTests.cs @@ -2,17 +2,21 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using Ocelot.Cache; using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Ocelot.Provider.Consul; using System.Text; namespace Ocelot.AcceptanceTests.ServiceDiscovery { - public class ConsulConfigurationInConsulTests : IDisposable + public sealed class ConsulConfigurationInConsulTests : Steps, IDisposable { private IWebHost _builder; - private readonly Steps _steps; private IWebHost _fakeConsulBuilder; private FileConfiguration _config; private readonly List _consulServices; @@ -20,11 +24,10 @@ public class ConsulConfigurationInConsulTests : IDisposable public ConsulConfigurationInConsulTests() { _consulServices = new List(); - _steps = new Steps(); } [Fact] - public void should_return_response_200_with_simple_url() + public void Should_return_response_200_with_simple_url() { var consulPort = PortFinder.GetRandomPort(); var servicePort = PortFinder.GetRandomPort(); @@ -64,16 +67,16 @@ public void should_return_response_200_with_simple_url() this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", string.Empty, 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => x.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => WhenIGetUrlOnTheApiGateway("/")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] - public void should_load_configuration_out_of_consul() + public void Should_load_configuration_out_of_consul() { var consulPort = PortFinder.GetRandomPort(); var servicePort = PortFinder.GetRandomPort(); @@ -127,16 +130,16 @@ public void should_load_configuration_out_of_consul() this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => x.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => WhenIGetUrlOnTheApiGateway("/cs/status")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] - public void should_load_configuration_out_of_consul_if_it_is_changed() + public void Should_load_configuration_out_of_consul_if_it_is_changed() { var consulPort = PortFinder.GetRandomPort(); var servicePort = PortFinder.GetRandomPort(); @@ -221,18 +224,18 @@ public void should_load_configuration_out_of_consul_if_it_is_changed() this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => x.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .And(x => WhenIGetUrlOnTheApiGateway("/cs/status")) + .And(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) .When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) .Then(x => ThenTheConfigIsUpdatedInOcelot()) .BDDfy(); } [Fact] - public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit() + public void Should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit() { var consulPort = PortFinder.GetRandomPort(); const string serviceName = "web"; @@ -305,14 +308,14 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ .And(x => GivenTheConsulConfigurationIs(consulConfig)) .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 2)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => x.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) + .Then(x => ThenTheStatusCodeShouldBe(200)) + .When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 2)) + .Then(x => ThenTheStatusCodeShouldBe(200)) + .When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) + .Then(x => ThenTheStatusCodeShouldBe(428)) .BDDfy(); } @@ -322,9 +325,9 @@ private async Task ThenTheConfigIsUpdatedInOcelot() { try { - await _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); - _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); - _steps.ThenTheResponseBodyShouldBe("Hello from Laura"); + await WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); + ThenTheStatusCodeShouldBe(HttpStatusCode.OK); + ThenTheResponseBodyShouldBe("Hello from Laura"); return true; } catch (Exception) @@ -348,6 +351,27 @@ private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] servi } } + private void GivenOcelotIsRunningUsingConsulToStoreConfig() + { + _webHostBuilder = new WebHostBuilder() + .UseDefaultServiceProvider(_ => _.ValidateScopes = true) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", true, false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); + config.AddJsonFile(_ocelotConfigFileName, true, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(s => { s.AddOcelot().AddConsul().AddConfigStoredInConsul(); }) + .Configure(app => app.UseOcelot().GetAwaiter().GetResult()); // Turning as async/await some tests got broken + + _ocelotServer = new TestServer(_webHostBuilder); + _ocelotClient = _ocelotServer.CreateClient(); + Thread.Sleep(1000); + } + private Task GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) { _fakeConsulBuilder = new WebHostBuilder() @@ -445,33 +469,19 @@ private Task GivenThereIsAServiceRunningOn(string url, string basePath, int stat return _builder.StartAsync(); } - public void Dispose() + public override void Dispose() { _builder?.Dispose(); - _steps.Dispose(); + _fakeConsulBuilder?.Dispose(); + base.Dispose(); } private class FakeCache : IOcelotCache { - public void Add(string key, FileConfiguration value, TimeSpan ttl, string region) - { - throw new NotImplementedException(); - } - - public FileConfiguration Get(string key, string region) - { - throw new NotImplementedException(); - } - - public void ClearRegion(string region) - { - throw new NotImplementedException(); - } - - public void AddAndDelete(string key, FileConfiguration value, TimeSpan ttl, string region) - { - throw new NotImplementedException(); - } + public void Add(string key, FileConfiguration value, TimeSpan ttl, string region) => throw new NotImplementedException(); + public FileConfiguration Get(string key, string region) => throw new NotImplementedException(); + public void ClearRegion(string region) => throw new NotImplementedException(); + public void AddAndDelete(string key, FileConfiguration value, TimeSpan ttl, string region) => throw new NotImplementedException(); } } } diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulServiceDiscoveryTests.cs index 922fc244d..6a388a5df 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulServiceDiscoveryTests.cs @@ -59,7 +59,7 @@ public void ShouldDiscoverServicesInConsulAndLoadBalanceByLeastConnectionWhenCon .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntries)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGatewayConcurrently("/", 50)) .Then(x => ThenAllServicesShouldHaveBeenCalledTimes(50)) .And(x => ThenAllServicesCalledRealisticAmountOfTimes(/*25*/24, /*25*/26)) // TODO Check strict assertion @@ -84,7 +84,7 @@ public void ShouldHandleRequestToConsulForDownstreamServiceAndMakeRequest() .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGateway("/home")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -114,7 +114,7 @@ public void ShouldHandleRequestToConsulForDownstreamServiceAndMakeRequestWhenDyn .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntry)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGateway("/web/something")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -139,7 +139,7 @@ public void ShouldUseConsulServiceDiscoveryAndLoadBalanceRequestWhenDynamicRouti .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntries)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGatewayConcurrently($"/{serviceName}/", 50)) .Then(x => ThenAllServicesShouldHaveBeenCalledTimes(50)) .And(x => ThenAllServicesCalledRealisticAmountOfTimes(/*25*/24, /*25*/26)) // TODO Check strict assertion @@ -164,7 +164,7 @@ public void ShouldUseAclTokenToMakeRequestToConsul() .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntry)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGateway("/home")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -187,7 +187,7 @@ public void ShouldSendRequestToServiceAfterItBecomesAvailableInConsul() .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntries)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .And(x => WhenIGetUrlOnTheApiGatewayConcurrently("/", 10)) .And(x => ThenAllServicesShouldHaveBeenCalledTimes(10)) .And(x => ThenAllServicesCalledRealisticAmountOfTimes(/*5*/4, /*5*/6)) // TODO Check strict assertion @@ -223,7 +223,7 @@ public void ShouldPollConsulForDownstreamServiceAndMakeRequest() .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntry)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -271,7 +271,7 @@ public void ShouldUseConsulServiceDiscoveryWhenThereAreTwoUpstreamHosts(string l .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryUS, serviceEntryEU)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) .When(x => x.WhenIGetUrl(publicUrlUS, sessionCookieUS), "When I get US shop for the first time") .Then(x => x.ThenConsulShouldHaveBeenCalledTimes(1)) .And(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) @@ -313,7 +313,7 @@ public void ShouldReturnServiceAddressByOverriddenServiceBuilderWhenThereIsANode .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntry)) .And(x => x.GivenTheServiceNodesAreRegisteredWithConsul(serviceNode)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) // default services registration results with the bug: "n1" host issue + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) // default services registration results with the bug: "n1" host issue .When(x => WhenIGetUrlOnTheApiGateway("/open/home")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway)) .And(x => ThenTheResponseBodyShouldBe("")) @@ -321,7 +321,7 @@ public void ShouldReturnServiceAddressByOverriddenServiceBuilderWhenThereIsANode .And(x => ThenConsulNodesShouldHaveBeenCalledTimes(1)) // Override default service builder - .Given(x => GivenOcelotIsRunningWithServices(WithOverriddenConsulServiceBuilder)) + .Given(x => GivenOcelotIsRunningWithServices(WithConsulServiceBuilder, true)) .When(x => WhenIGetUrlOnTheApiGateway("/open/home")) .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => ThenTheResponseBodyShouldBe("Hello from Raman")) @@ -368,7 +368,7 @@ public void ShouldReturnDifferentServicesWhenThereAre2SequentialRequestsToDiffer .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(service1, service2)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(WithConsul, true)) // Step 1 .When(x => WhenIGetUrlOnTheApiGateway("/projects/api/projects")) @@ -428,7 +428,7 @@ public void ShouldReturnDifferentServicesWhenSequentiallylyRequestingToDifferent .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(service1, service2)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(withAnalyzer ? WithLbAnalyzer(loadBalancer) : WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(withAnalyzer ? WithLbAnalyzer(loadBalancer) : WithConsul, true)) .When(x => WhenIDoActionMultipleTimes(50, requestToProjectsAndThenRequestToCustomersAndAssert)) .Then(x => ThenAllStatusCodesShouldBe(HttpStatusCode.OK)) .And(x => x.ThenResponsesShouldHaveBodyFromDifferentServices(ports, Bug2119ServiceNames)) // !!! @@ -461,7 +461,7 @@ public void ShouldReturnDifferentServicesWhenConcurrentlyRequestingToDifferentSe .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(DownstreamUrl(consulPort))) .And(x => x.GivenTheServicesAreRegisteredWithConsul(service1, service2)) .And(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithServices(withAnalyzer ? WithLbAnalyzer(loadBalancer) : WithConsul)) + .And(x => GivenOcelotIsRunningWithServices(withAnalyzer ? WithLbAnalyzer(loadBalancer) : WithConsul, true)) .When(x => WhenIGetUrlOnTheApiGatewayConcurrently(total, "/projects/api/projects", "/customers/api/customers")) .Then(x => ThenAllStatusCodesShouldBe(HttpStatusCode.OK)) .And(x => x.ThenResponsesShouldHaveBodyFromDifferentServices(ports, Bug2119ServiceNames)) // !!! @@ -509,7 +509,7 @@ private void ThenResponsesShouldHaveBodyFromDifferentServices(int[] ports, strin private static void WithConsul(IServiceCollection services) => services .AddOcelot().AddConsul(); - private static void WithOverriddenConsulServiceBuilder(IServiceCollection services) => services + private static void WithConsulServiceBuilder(IServiceCollection services) => services .AddOcelot().AddConsul(); public class MyConsulServiceBuilder : DefaultConsulServiceBuilder diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulWebSocketTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulWebSocketTests.cs index 9afa1b154..8ad56288e 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulWebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscovery/ConsulWebSocketTests.cs @@ -1,347 +1,389 @@ using Consul; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Ocelot.Provider.Consul; using Ocelot.WebSockets; using System.Net.WebSockets; using System.Text; -namespace Ocelot.AcceptanceTests.ServiceDiscovery +namespace Ocelot.AcceptanceTests.ServiceDiscovery; + +public sealed class ConsulWebSocketTests : Steps, IDisposable { - public class ConsulWebSocketTests : IDisposable + private readonly List _secondRecieved; + private readonly List _firstRecieved; + private readonly List _serviceEntries; + private readonly ServiceHandler _serviceHandler; + private IWebHost _ocelotHost; + + public ConsulWebSocketTests() { - private readonly List _secondRecieved; - private readonly List _firstRecieved; - private readonly List _serviceEntries; - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; + _serviceHandler = new ServiceHandler(); + _firstRecieved = new List(); + _secondRecieved = new List(); + _serviceEntries = new List(); + } - public ConsulWebSocketTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _firstRecieved = new List(); - _secondRecieved = new List(); - _serviceEntries = new List(); - } + public override void Dispose() + { + _serviceHandler?.Dispose(); + _ocelotHost?.Dispose(); + base.Dispose(); + } - [Fact] - public void ShouldProxyWebsocketInputToDownstreamServiceAndUseServiceDiscoveryAndLoadBalancer() - { - var downstreamPort = PortFinder.GetRandomPort(); - var downstreamHost = "localhost"; + [Fact] + public void ShouldProxyWebsocketInputToDownstreamServiceAndUseServiceDiscoveryAndLoadBalancer() + { + var downstreamPort = PortFinder.GetRandomPort(); + var downstreamHost = "localhost"; - var secondDownstreamPort = PortFinder.GetRandomPort(); - var secondDownstreamHost = "localhost"; + var secondDownstreamPort = PortFinder.GetRandomPort(); + var secondDownstreamHost = "localhost"; - var serviceName = "websockets"; - var consulPort = PortFinder.GetRandomPort(); - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry + var serviceName = "websockets"; + var consulPort = PortFinder.GetRandomPort(); + var serviceEntryOne = new ServiceEntry + { + Service = new AgentService + { + Service = serviceName, + Address = downstreamHost, + Port = downstreamPort, + ID = Guid.NewGuid().ToString(), + Tags = Array.Empty(), + }, + }; + var serviceEntryTwo = new ServiceEntry + { + Service = new AgentService { - Service = new AgentService + Service = serviceName, + Address = secondDownstreamHost, + Port = secondDownstreamPort, + ID = Guid.NewGuid().ToString(), + Tags = Array.Empty(), + }, + }; + + var config = new FileConfiguration + { + Routes = new List + { + new() { - Service = serviceName, - Address = downstreamHost, - Port = downstreamPort, - ID = Guid.NewGuid().ToString(), - Tags = Array.Empty(), + UpstreamPathTemplate = "/", + DownstreamPathTemplate = "/ws", + DownstreamScheme = "ws", + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" }, + ServiceName = serviceName, }, - }; - var serviceEntryTwo = new ServiceEntry + }, + GlobalConfiguration = new FileGlobalConfiguration { - Service = new AgentService + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider { - Service = serviceName, - Address = secondDownstreamHost, - Port = secondDownstreamPort, - ID = Guid.NewGuid().ToString(), - Tags = Array.Empty(), + Scheme = "http", + Host = "localhost", + Port = consulPort, + Type = "consul", }, - }; + }, + }; + + this.Given(_ => GivenThereIsAConfiguration(config)) + .And(_ => StartFakeOcelotWithWebSocketsWithConsul()) + .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(consulPort, serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) + .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) + .When(_ => WhenIStartTheClients()) + .Then(_ => ThenBothDownstreamServicesAreCalled()) + .BDDfy(); + } - var config = new FileConfiguration + private async Task StartFakeOcelotWithWebSocketsWithConsul() + { + _ocelotBuilder = new WebHostBuilder() + .UseDefaultServiceProvider(_ => _.ValidateScopes = true) + .ConfigureServices(s => { - Routes = new List - { - new() - { - UpstreamPathTemplate = "/", - DownstreamPathTemplate = "/ws", - DownstreamScheme = "ws", - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" }, - ServiceName = serviceName, - }, - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort, - Type = "consul", - }, - }, - }; - - this.Given(_ => _steps.GivenThereIsAConfiguration(config)) - .And(_ => _steps.StartFakeOcelotWithWebSocketsWithConsul()) - .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) - .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) - .When(_ => WhenIStartTheClients()) - .Then(_ => ThenBothDownstreamServicesAreCalled()) - .BDDfy(); - } + s.AddSingleton(_ocelotBuilder); + s.AddOcelot().AddConsul(); + }) + .UseKestrel() + .UseUrls(DownstreamUrl(5000)) // TODO not good to have constant hardcoded port + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", true, false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); + config.AddJsonFile(_ocelotConfigFileName, false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + }) + .Configure(async app => + { + app.UseWebSockets(); + await app.UseOcelot(); + }) + .UseIISIntegration(); + _ocelotHost = _ocelotBuilder.Build(); // new TestServer(_webHostBuilder); ??? + await _ocelotHost.StartAsync(); + } - private void ThenBothDownstreamServicesAreCalled() + private void ThenBothDownstreamServicesAreCalled() + { + _firstRecieved.Count.ShouldBe(10); + _firstRecieved.ForEach(x => { - _firstRecieved.Count.ShouldBe(10); - _firstRecieved.ForEach(x => - { - x.ShouldBe("test"); - }); + x.ShouldBe("test"); + }); - _secondRecieved.Count.ShouldBe(10); - _secondRecieved.ForEach(x => - { - x.ShouldBe("chocolate"); - }); - } + _secondRecieved.Count.ShouldBe(10); + _secondRecieved.ForEach(x => + { + x.ShouldBe("chocolate"); + }); + } - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) { - foreach (var serviceEntry in serviceEntries) - { - _serviceEntries.Add(serviceEntry); - } + _serviceEntries.Add(serviceEntry); } + } - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(int port, string serviceName) + { + var url = DownstreamUrl(port); + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") { - if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - var json = JsonConvert.SerializeObject(_serviceEntries); - context.Response.Headers.Append("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - } + var json = JsonConvert.SerializeObject(_serviceEntries); + context.Response.Headers.Append("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + } - private async Task WhenIStartTheClients() - { - var firstClient = StartClient("ws://localhost:5000/"); + private async Task WhenIStartTheClients() + { + var firstClient = StartClient("ws://localhost:5000/"); - var secondClient = StartSecondClient("ws://localhost:5000/"); + var secondClient = StartSecondClient("ws://localhost:5000/"); - await Task.WhenAll(firstClient, secondClient); - } + await Task.WhenAll(firstClient, secondClient); + } - private async Task StartClient(string url) - { - IClientWebSocket client = new ClientWebSocketProxy(); + private async Task StartClient(string url) + { + IClientWebSocket client = new ClientWebSocketProxy(); - await client.ConnectAsync(new Uri(url), CancellationToken.None); + await client.ConnectAsync(new Uri(url), CancellationToken.None); - var sending = Task.Run(async () => + var sending = Task.Run(async () => + { + var line = "test"; + for (var i = 0; i < 10; i++) { - var line = "test"; - for (var i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); + var bytes = Encoding.UTF8.GetBytes(line); - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - }); + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + }); + + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; - var receiving = Task.Run(async () => + while (true) { - var buffer = new byte[1024 * 4]; + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (true) + if (result.MessageType == WebSocketMessageType.Text) { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) + _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - } - - break; + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } + + break; } - }); + } + }); - await Task.WhenAll(sending, receiving); - } + await Task.WhenAll(sending, receiving); + } - private async Task StartSecondClient(string url) - { - await Task.Delay(500); + private async Task StartSecondClient(string url) + { + await Task.Delay(500); - IClientWebSocket client = new ClientWebSocketProxy(); + IClientWebSocket client = new ClientWebSocketProxy(); - await client.ConnectAsync(new Uri(url), CancellationToken.None); + await client.ConnectAsync(new Uri(url), CancellationToken.None); - var sending = Task.Run(async () => + var sending = Task.Run(async () => + { + var line = "test"; + for (var i = 0; i < 10; i++) { - var line = "test"; - for (var i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); + var bytes = Encoding.UTF8.GetBytes(line); - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - }); + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + }); - var receiving = Task.Run(async () => + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; + + while (true) { - var buffer = new byte[1024 * 4]; + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (true) + if (result.MessageType == WebSocketMessageType.Text) { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) + _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - } - - break; + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } + + break; } - }); + } + }); - await Task.WhenAll(sending, receiving); - } + await Task.WhenAll(sending, receiving); + } - private async Task StartFakeDownstreamService(string url, string path) + private async Task StartFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, async (context, next) => { - await _serviceHandler.StartFakeDownstreamService(url, async (context, next) => + if (context.Request.Path == path) { - if (context.Request.Path == path) + if (context.WebSockets.IsWebSocketRequest) { - if (context.WebSockets.IsWebSocketRequest) - { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - } - else - { - context.Response.StatusCode = 400; - } + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Echo(webSocket); } else { - await next(); + context.Response.StatusCode = 400; } - }); - } + } + else + { + await next(); + } + }); + } - private async Task StartSecondFakeDownstreamService(string url, string path) + private async Task StartSecondFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, async (context, next) => { - await _serviceHandler.StartFakeDownstreamService(url, async (context, next) => + if (context.Request.Path == path) { - if (context.Request.Path == path) + if (context.WebSockets.IsWebSocketRequest) { - if (context.WebSockets.IsWebSocketRequest) - { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Message(webSocket); - } - else - { - context.Response.StatusCode = 400; - } + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Message(webSocket); } else { - await next(); + context.Response.StatusCode = 400; } - }); - } - - private static async Task Echo(WebSocket webSocket) - { - try + } + else { - var buffer = new byte[1024 * 4]; - - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + await next(); + } + }); + } - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + private static async Task Echo(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) + while (!result.CloseStatus.HasValue) { - Console.WriteLine(e); + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); } - } - private static async Task Message(WebSocket webSocket) + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) { - try - { - var buffer = new byte[1024 * 4]; - - var bytes = Encoding.UTF8.GetBytes("chocolate"); + Console.WriteLine(e); + } + } - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + private static async Task Message(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); + var bytes = Encoding.UTF8.GetBytes("chocolate"); - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) + while (!result.CloseStatus.HasValue) { - Console.WriteLine(e); + await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); } - } - public void Dispose() + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) { - _serviceHandler?.Dispose(); - _steps.Dispose(); - GC.SuppressFinalize(this); + Console.WriteLine(e); } } } diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index a01791990..44eb6d5c1 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -18,7 +18,6 @@ using Ocelot.DependencyInjection; using Ocelot.Logging; using Ocelot.Middleware; -using Ocelot.Provider.Consul; using Ocelot.Provider.Eureka; using Ocelot.Provider.Polly; using Ocelot.Tracing.Butterfly; @@ -46,9 +45,12 @@ public class Steps : IDisposable private readonly Random _random; protected readonly Guid _testId; protected readonly string _ocelotConfigFileName; + + // TODO Merge both members protected IWebHostBuilder _webHostBuilder; - private WebHostBuilder _ocelotBuilder; - private IWebHost _ocelotHost; + protected IWebHostBuilder _ocelotBuilder; + + private IWebHost _ocelotHost; // TODO remove because of one reference private IOcelotConfigurationChangeTokenSource _changeToken; public Steps() @@ -105,7 +107,11 @@ public async Task ThenConfigShouldBeWithTimeout(FileConfiguration fileConfig, in }); result.ShouldBe(true); } - + + /// + /// TODO Move to . See references. + /// + /// Task. public async Task StartFakeOcelotWithWebSockets() { _ocelotBuilder = new WebHostBuilder(); @@ -140,41 +146,6 @@ public async Task StartFakeOcelotWithWebSockets() _ocelotHost = _ocelotBuilder.Build(); await _ocelotHost.StartAsync(); } - - public async Task StartFakeOcelotWithWebSocketsWithConsul() - { - _ocelotBuilder = new WebHostBuilder(); - _ocelotBuilder.ConfigureServices(s => - { - s.AddSingleton(_ocelotBuilder); - s.AddOcelot().AddConsul(); - }); - _ocelotBuilder.UseKestrel() - .UseUrls("http://localhost:5000") - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", true, false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); - config.AddJsonFile(_ocelotConfigFileName, false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostingContext, logging) => - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - }) - .Configure(async app => - { - app.UseWebSockets(); - await app.UseOcelot(); - }) - .UseIISIntegration(); - _ocelotHost = _ocelotBuilder.Build(); - await _ocelotHost.StartAsync(); - } public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) => GivenThereIsAConfiguration(fileConfiguration, _ocelotConfigFileName); @@ -320,63 +291,6 @@ internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl) _ocelotClient = _ocelotServer.CreateClient(); } - public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", true, false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); - config.AddJsonFile(_ocelotConfigFileName, true, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot() - .AddCacheManager((x) => - { - x.WithMicrosoftLogging(_ => - { - //log.AddConsole(LogLevel.Debug); - }) - .WithJsonSerializer() - .WithHandle(typeof(InMemoryJsonHandle<>)); - }) - .AddConsul() - .AddConfigStoredInConsul(); - }) - .Configure(app => app.UseOcelot().GetAwaiter().GetResult()); // Turning as async/await some tests got broken - - _ocelotServer = new TestServer(_webHostBuilder); - _ocelotClient = _ocelotServer.CreateClient(); - } - - public void GivenOcelotIsRunningUsingConsulToStoreConfig() - { - _webHostBuilder = new WebHostBuilder(); - - _webHostBuilder - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", true, false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false); - config.AddJsonFile(_ocelotConfigFileName, true, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(s => { s.AddOcelot().AddConsul().AddConfigStoredInConsul(); }) - .Configure(app => app.UseOcelot().GetAwaiter().GetResult()); // Turning as async/await some tests got broken - - _ocelotServer = new TestServer(_webHostBuilder); - _ocelotClient = _ocelotServer.CreateClient(); - Thread.Sleep(1000); - } - public async Task WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url) { var result = await Wait.WaitFor(2000).UntilAsync(async () => @@ -633,15 +547,22 @@ public void ThenTheResponseHeaderIs(string key, string value) public void ThenTheReasonPhraseIs(string expected) { _response.ReasonPhrase.ShouldBe(expected); - } + } public void GivenOcelotIsRunningWithServices(Action configureServices) - => GivenOcelotIsRunningWithServices(configureServices, null); + => GivenOcelotIsRunningWithServices(configureServices, null, validateScopes: false); public void GivenOcelotIsRunningWithServices(Action configureServices, Action configureApp) + => GivenOcelotIsRunningWithServices(configureServices, null, validateScopes: false); + + public void GivenOcelotIsRunningWithServices(Action configureServices, bool validateScopes) + => GivenOcelotIsRunningWithServices(configureServices, null, validateScopes); + + public void GivenOcelotIsRunningWithServices(Action configureServices, Action configureApp, bool validateScopes) { _webHostBuilder = new WebHostBuilder() .ConfigureAppConfiguration(WithBasicConfiguration) + .UseDefaultServiceProvider(opts => opts.ValidateScopes = validateScopes) .ConfigureServices(configureServices ?? WithAddOcelot) .Configure(configureApp ?? WithUseOcelot); _ocelotServer = new TestServer(_webHostBuilder); @@ -1090,7 +1011,7 @@ protected virtual void Dispose(bool disposing) } if (disposing) - { + { _ocelotClient?.Dispose(); _ocelotServer?.Dispose(); _ocelotHost?.Dispose(); diff --git a/test/Ocelot.UnitTests/Consul/ConsulProviderFactoryTests.cs b/test/Ocelot.UnitTests/Consul/ConsulProviderFactoryTests.cs new file mode 100644 index 000000000..6496ff3ce --- /dev/null +++ b/test/Ocelot.UnitTests/Consul/ConsulProviderFactoryTests.cs @@ -0,0 +1,155 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Logging; +using Ocelot.Provider.Consul; +using Ocelot.Provider.Consul.Interfaces; +using Ocelot.ServiceDiscovery.Providers; + +namespace Ocelot.UnitTests.Consul; + +public sealed class ConsulProviderFactoryTests : UnitTest, IDisposable +{ + private readonly ServiceProvider _provider; + private readonly IServiceScope _scope; + private readonly HttpContext _context = new DefaultHttpContext(); + + public ConsulProviderFactoryTests() + { + var contextAccessor = new Mock(); + _context.Items.Add(nameof(ConsulRegistryConfiguration), new ConsulRegistryConfiguration(null, null, 0, null, null)); + contextAccessor.SetupGet(x => x.HttpContext).Returns(_context); + + var loggerFactory = new Mock(); + var logger = new Mock(); + loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); + loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); + + var consulFactory = new Mock(); + var consulServiceBuilder = new Mock(); + + var services = new ServiceCollection(); + services.AddSingleton(contextAccessor.Object); + services.AddSingleton(consulFactory.Object); + services.AddSingleton(loggerFactory.Object); + services.AddScoped(_ => consulServiceBuilder.Object); + + _provider = services.BuildServiceProvider(true); // validate scopes!!! + _scope = _provider.CreateScope(); + _context.RequestServices = _scope.ServiceProvider; + } + + public void Dispose() + { + _scope.Dispose(); + _provider.Dispose(); + } + + [Fact] + public void Get_EmptyTypeName_ReturnedConsul() + { + // Arrange + var emptyType = string.Empty; + var route = GivenRoute(string.Empty); + + // Act + var actual = CreateProvider(route, emptyType); + + // Assert + actual.ShouldNotBeNull().ShouldBeOfType(); + } + + [Fact] + public void Get_PollConsulTypeName_ReturnedPollConsul() + { + // Arrange, Act + var route = GivenRoute(string.Empty); + var actual = CreateProvider(route, nameof(PollConsul)); + + // Assert + actual.ShouldNotBeNull().ShouldBeOfType(); + } + + [Fact] + public void Get_RoutesWithTheSameServiceName_ReturnedSameProvider() + { + // Arrange, Act: 1 + var route1 = GivenRoute("test"); + var actual1 = CreateProvider(route1); + + // Arrange, Act: 2 + var route2 = GivenRoute("test"); + var actual2 = CreateProvider(route2); + + // Assert + actual1.ShouldNotBeNull().ShouldBeOfType(); + actual2.ShouldNotBeNull().ShouldBeOfType(); + actual1.ShouldBeEquivalentTo(actual2); + var provider1 = actual1 as PollConsul; + var provider2 = actual2 as PollConsul; + provider1.ServiceName.ShouldBeEquivalentTo(provider2.ServiceName); + } + + [Fact] + public void Get_MultipleServiceNames_ShouldReturnProviderAccordingToServiceName() + { + string[] serviceNames = new[] { "service1", "service2", "service3", "service4" }; + var providersList = serviceNames.Select(DummyPollingConsulServiceFactory).ToList(); + + foreach (var serviceName in serviceNames) + { + var currentProvider = DummyPollingConsulServiceFactory(serviceName); + providersList.ShouldContain(currentProvider); + } + + var convertedProvidersList = providersList.Select(x => x as PollConsul).ToList(); + convertedProvidersList.ForEach(x => x.ShouldNotBeNull()); + + foreach (var serviceName in serviceNames) + { + var cProvider = DummyPollingConsulServiceFactory(serviceName); + var convertedCProvider = cProvider as PollConsul; + convertedCProvider.ShouldNotBeNull(); + + var matchingProviders = convertedProvidersList + .Where(x => x.ServiceName == convertedCProvider.ServiceName) + .ToList(); + matchingProviders.ShouldHaveSingleItem(); + + matchingProviders.First() + .ShouldNotBeNull() + .ServiceName.ShouldBeEquivalentTo(convertedCProvider.ServiceName); + } + } + + [Fact] + [Trait("Bug", "2178")] + public void Get_RootProvider_ShouldThrowInvalidOperationException() + { + // Arrange + var route = GivenRoute(string.Empty); + _context.RequestServices = _provider; // given service provider is root provider + + // Act + Func consulProviderFactoryCall = () => CreateProvider(route); + + // Assert + consulProviderFactoryCall.ShouldThrow(); + } + + private IServiceDiscoveryProvider DummyPollingConsulServiceFactory(string serviceName) => CreateProvider(GivenRoute(serviceName)); + + private static DownstreamRoute GivenRoute(string serviceName) => new DownstreamRouteBuilder() + .WithServiceName(serviceName) + .Build(); + + private IServiceDiscoveryProvider CreateProvider(DownstreamRoute route, string providerType = ConsulProviderFactory.PollConsul) + { + var stopsFromPolling = 10000; + return ConsulProviderFactory.Get.Invoke( + _provider, + new ServiceProviderConfiguration(providerType, Uri.UriSchemeHttp, string.Empty, 1, string.Empty, string.Empty, stopsFromPolling), + route); + } +} diff --git a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs deleted file mode 100644 index 247af37e9..000000000 --- a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Logging; -using Ocelot.Provider.Consul; -using Ocelot.Provider.Consul.Interfaces; -using Ocelot.ServiceDiscovery.Providers; - -namespace Ocelot.UnitTests.Consul; - -public class ProviderFactoryTests -{ - private readonly IServiceProvider _provider; - - public ProviderFactoryTests() - { - var contextAccessor = new Mock(); - var context = new DefaultHttpContext(); - context.Items.Add(nameof(ConsulRegistryConfiguration), new ConsulRegistryConfiguration(null, null, 0, null, null)); - contextAccessor.SetupGet(x => x.HttpContext).Returns(context); - - var loggerFactory = new Mock(); - var logger = new Mock(); - loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); - loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); - - var consulFactory = new Mock(); - - var services = new ServiceCollection(); - services.AddSingleton(contextAccessor.Object); - services.AddSingleton(consulFactory.Object); - services.AddSingleton(loggerFactory.Object); - _provider = services.BuildServiceProvider(); - } - - [Fact] - public void should_return_consul_service_discovery_provider() - { - var route = new DownstreamRouteBuilder() - .WithServiceName(string.Empty) - .Build(); - - var provider = ConsulProviderFactory.Get(_provider, - new ServiceProviderConfiguration(string.Empty, string.Empty, string.Empty, 1, string.Empty, string.Empty, - 1), route); - provider.ShouldBeOfType(); - } - - [Fact] - public void should_return_polling_consul_service_discovery_provider() - { - var provider = DummyPollingConsulServiceFactory(string.Empty); - var pollProvider = provider as PollConsul; - pollProvider.ShouldNotBeNull(); - } - - [Fact] - public void should_return_same_provider_for_given_service_name() - { - var provider = DummyPollingConsulServiceFactory("test"); - var provider2 = DummyPollingConsulServiceFactory("test"); - - provider.ShouldBeEquivalentTo(provider2); - - var pollProvider = provider as PollConsul; - pollProvider.ShouldNotBeNull(); - - var pollProvider2 = provider2 as PollConsul; - pollProvider2.ShouldNotBeNull(); - - pollProvider.ServiceName.ShouldBeEquivalentTo(pollProvider2.ServiceName); - } - - [Theory] - [InlineData(new object[] { new[] { "service1", "service2", "service3", "service4" } })] - public void should_return_provider_according_to_service_name(string[] serviceNames) - { - var providersList = serviceNames.Select(DummyPollingConsulServiceFactory).ToList(); - - foreach (var serviceName in serviceNames) - { - var currentProvider = DummyPollingConsulServiceFactory(serviceName); - providersList.ShouldContain(currentProvider); - } - - var convertedProvidersList = providersList.Select(x => x as PollConsul).ToList(); - - foreach (var convertedProvider in convertedProvidersList) - { - convertedProvider.ShouldNotBeNull(); - } - - foreach (var serviceName in serviceNames) - { - var cProvider = DummyPollingConsulServiceFactory(serviceName); - var convertedCProvider = cProvider as PollConsul; - - convertedCProvider.ShouldNotBeNull(); - - var matchingProviders = convertedProvidersList - .Where(x => x.ServiceName == convertedCProvider.ServiceName) - .ToList(); - matchingProviders.ShouldHaveSingleItem(); - - matchingProviders.First() - .ShouldNotBeNull() - .ServiceName.ShouldBeEquivalentTo(convertedCProvider.ServiceName); - } - } - - private IServiceDiscoveryProvider DummyPollingConsulServiceFactory(string serviceName) - { - var stopsFromPolling = 10000; - - var route = new DownstreamRouteBuilder() - .WithServiceName(serviceName) - .Build(); - - return ConsulProviderFactory.Get?.Invoke( - _provider, - new ServiceProviderConfiguration(ConsulProviderFactory.PollConsul, Uri.UriSchemeHttp, string.Empty, 1, string.Empty, string.Empty, stopsFromPolling), - route); - } -}