From b4ef277c3e270cc07987706e43d8a1fa10b7ed89 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 07:50:23 +0000 Subject: [PATCH 01/16] added builder for service provider config --- .../Configuration/Builder/ReRouteBuilder.cs | 40 ++---------- .../Creator/FileOcelotConfigurationCreator.cs | 13 ++-- .../ServiceProviderConfiguraionBuilder.cs | 62 +++++++++++++++++++ .../LoadBalancers/LoadBalancerFactory.cs | 13 +--- .../Ocelot.AcceptanceTests/configuration.json | 2 +- .../FileConfigurationCreatorTests.cs | 14 +++-- .../LoadBalancer/LoadBalancerFactoryTests.cs | 20 +++--- .../ServiceProviderFactoryTests.cs | 12 +++- 8 files changed, 110 insertions(+), 66 deletions(-) create mode 100644 src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index caa09d3f..c8bde074 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -26,16 +26,12 @@ namespace Ocelot.Configuration.Builder private string _requestIdHeaderKey; private bool _isCached; private CacheOptions _fileCacheOptions; - private bool _useServiceDiscovery; private string _serviceName; - private string _serviceDiscoveryProvider; - private string _serviceDiscoveryAddress; private string _downstreamScheme; private string _downstreamHost; - private int _dsPort; + private int _downstreamPort; private string _loadBalancer; - private string _serviceProviderHost; - private int _serviceProviderPort; + private ServiceProviderConfiguraion _serviceProviderConfiguraion; public ReRouteBuilder() { @@ -60,30 +56,12 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithServiceDiscoveryAddress(string serviceDiscoveryAddress) - { - _serviceDiscoveryAddress = serviceDiscoveryAddress; - return this; - } - - public ReRouteBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider) - { - _serviceDiscoveryProvider = serviceDiscoveryProvider; - return this; - } - public ReRouteBuilder WithServiceName(string serviceName) { _serviceName = serviceName; return this; } - public ReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) - { - _useServiceDiscovery = useServiceDiscovery; - return this; - } - public ReRouteBuilder WithDownstreamPathTemplate(string input) { _downstreamPathTemplate = input; @@ -198,7 +176,7 @@ namespace Ocelot.Configuration.Builder public ReRouteBuilder WithDownstreamPort(int port) { - _dsPort = port; + _downstreamPort = port; return this; } @@ -208,15 +186,9 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithServiceProviderHost(string serviceProviderHost) + public ReRouteBuilder WithServiceProviderConfiguraion(ServiceProviderConfiguraion serviceProviderConfiguraion) { - _serviceProviderHost = serviceProviderHost; - return this; - } - - public ReRouteBuilder WithServiceProviderPort(int serviceProviderPort) - { - _serviceProviderPort = serviceProviderPort; + _serviceProviderConfiguraion = serviceProviderConfiguraion; return this; } @@ -226,7 +198,7 @@ namespace Ocelot.Configuration.Builder _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _downstreamScheme, _loadBalancer, - _downstreamHost, _dsPort, _loadBalancerKey, new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _dsPort, _useServiceDiscovery, _serviceDiscoveryProvider, _serviceProviderHost, _serviceProviderPort)); + _downstreamHost, _downstreamPort, _loadBalancerKey, _serviceProviderConfiguraion); } } } diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 703239d0..5a265569 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -110,10 +110,15 @@ namespace Ocelot.Configuration.Creator var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; - var serviceProviderConfiguration = new ServiceProviderConfiguraion(fileReRoute.ServiceName, - fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, useServiceDiscovery, - globalConfiguration?.ServiceDiscoveryProvider?.Provider, globalConfiguration?.ServiceDiscoveryProvider?.Host, - serviceProviderPort); + var serviceProviderConfiguration = new ServiceProviderConfiguraionBuilder() + .WithServiceName(fileReRoute.ServiceName) + .WithDownstreamHost(fileReRoute.DownstreamHost) + .WithDownstreamPort(fileReRoute.DownstreamPort) + .WithUseServiceDiscovery(useServiceDiscovery) + .WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider) + .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) + .WithServiceDiscoveryProviderPort(serviceProviderPort) + .Build(); if (isAuthenticated) { diff --git a/src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs b/src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs new file mode 100644 index 00000000..e8609683 --- /dev/null +++ b/src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs @@ -0,0 +1,62 @@ +namespace Ocelot.Configuration +{ + public class ServiceProviderConfiguraionBuilder + { + private string _serviceName; + private string _downstreamHost; + private int _downstreamPort; + private bool _userServiceDiscovery; + private string _serviceDiscoveryProvider; + private string _serviceDiscoveryProviderHost; + private int _serviceDiscoveryProviderPort; + + public ServiceProviderConfiguraionBuilder WithServiceName(string serviceName) + { + _serviceName = serviceName; + return this; + } + + public ServiceProviderConfiguraionBuilder WithDownstreamHost(string downstreamHost) + { + _downstreamHost = downstreamHost; + return this; + } + + public ServiceProviderConfiguraionBuilder WithDownstreamPort(int downstreamPort) + { + _downstreamPort = downstreamPort; + return this; + } + + public ServiceProviderConfiguraionBuilder WithUseServiceDiscovery(bool userServiceDiscovery) + { + _userServiceDiscovery = userServiceDiscovery; + return this; + } + + public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider) + { + _serviceDiscoveryProvider = serviceDiscoveryProvider; + return this; + } + + public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost) + { + _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost; + return this; + } + + public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort) + { + _serviceDiscoveryProviderPort = serviceDiscoveryProviderPort; + return this; + } + + + public ServiceProviderConfiguraion Build() + { + return new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery, + _serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs index 08e45d2b..72ccdba1 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs @@ -13,17 +13,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers } public async Task Get(ReRoute reRoute) - { - var serviceConfig = new ServiceProviderConfiguraion( - reRoute.ServiceProviderConfiguraion.ServiceName, - reRoute.ServiceProviderConfiguraion.DownstreamHost, - reRoute.ServiceProviderConfiguraion.DownstreamPort, - reRoute.ServiceProviderConfiguraion.UseServiceDiscovery, - reRoute.ServiceProviderConfiguraion.ServiceDiscoveryProvider, - reRoute.ServiceProviderConfiguraion.ServiceProviderHost, - reRoute.ServiceProviderConfiguraion.ServiceProviderPort); - - var serviceProvider = _serviceProviderFactory.Get(serviceConfig); + { + var serviceProvider = _serviceProviderFactory.Get(reRoute.ServiceProviderConfiguraion); switch (reRoute.LoadBalancer) { diff --git a/test/Ocelot.AcceptanceTests/configuration.json b/test/Ocelot.AcceptanceTests/configuration.json index 78ab541b..d7db55c7 100755 --- a/test/Ocelot.AcceptanceTests/configuration.json +++ b/test/Ocelot.AcceptanceTests/configuration.json @@ -1 +1 @@ -{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0}}} +{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0}}} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index e1da7de0..d9017da2 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -165,9 +165,11 @@ namespace Ocelot.UnitTests.Configuration .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .WithServiceName("ProductService") - .WithUseServiceDiscovery(true) - .WithServiceDiscoveryProvider("consul") - .WithServiceDiscoveryAddress("127.0.01") + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder() + .WithUseServiceDiscovery(true) + .WithServiceDiscoveryProvider("consul") + .WithServiceDiscoveryProviderHost("127.0.0.1") + .Build()) .Build() })) .BDDfy(); @@ -198,9 +200,9 @@ namespace Ocelot.UnitTests.Configuration .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") - .WithUseServiceDiscovery(false) - .WithServiceDiscoveryProvider(null) - .WithServiceDiscoveryAddress(null) + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder() + .WithUseServiceDiscovery(false) + .Build()) .Build() })) .BDDfy(); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index d030eb99..9d46807e 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -24,18 +24,12 @@ namespace Ocelot.UnitTests.LoadBalancer _factory = new LoadBalancerFactory(_serviceProviderFactory.Object); } - private void GivenTheServiceProviderFactoryReturns() - { - _serviceProviderFactory - .Setup(x => x.Get(It.IsAny())) - .Returns(_serviceProvider.Object); - } - [Fact] public void should_return_no_load_balancer() { var reRoute = new ReRouteBuilder() - .Build(); + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) + .Build(); this.Given(x => x.GivenAReRoute(reRoute)) .And(x => x.GivenTheServiceProviderFactoryReturns()) @@ -49,6 +43,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -63,6 +58,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("LeastConnection") + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -77,6 +73,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") + .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -86,6 +83,13 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } + private void GivenTheServiceProviderFactoryReturns() + { + _serviceProviderFactory + .Setup(x => x.Get(It.IsAny())) + .Returns(_serviceProvider.Object); + } + private void ThenTheServiceProviderIsCalledCorrectly() { _serviceProviderFactory diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs index 7dae5e47..7ba4608f 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs @@ -20,7 +20,11 @@ namespace Ocelot.UnitTests.ServiceDiscovery [Fact] public void should_return_no_service_provider() { - var serviceConfig = new ServiceProviderConfiguraion("product", "127.0.0.1", 80, false, "Does not matter", string.Empty, 0); + var serviceConfig = new ServiceProviderConfiguraionBuilder() + .WithDownstreamHost("127.0.0.1") + .WithDownstreamPort(80) + .WithUseServiceDiscovery(false) + .Build(); this.Given(x => x.GivenTheReRoute(serviceConfig)) .When(x => x.WhenIGetTheServiceProvider()) @@ -31,7 +35,11 @@ namespace Ocelot.UnitTests.ServiceDiscovery [Fact] public void should_return_consul_service_provider() { - var serviceConfig = new ServiceProviderConfiguraion("product", string.Empty, 0, true, "Consul", string.Empty, 0); + var serviceConfig = new ServiceProviderConfiguraionBuilder() + .WithServiceName("product") + .WithUseServiceDiscovery(true) + .WithServiceDiscoveryProvider("Consul") + .Build(); this.Given(x => x.GivenTheReRoute(serviceConfig)) .When(x => x.WhenIGetTheServiceProvider()) From 1671fadea3846cbb0ca7cb90619e97c3d556155a Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 07:53:41 +0000 Subject: [PATCH 02/16] made upstream template use template path object --- src/Ocelot/Configuration/Builder/ReRouteBuilder.cs | 2 +- .../Creator/FileOcelotConfigurationCreator.cs | 8 ++++---- src/Ocelot/Configuration/ReRoute.cs | 8 ++++---- .../DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs | 2 +- src/Ocelot/Values/DownstreamPathTemplate.cs | 4 ++-- .../DownstreamRouteFinder/DownstreamRouteFinderTests.cs | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index c8bde074..78c96fb5 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -194,7 +194,7 @@ namespace Ocelot.Configuration.Builder public ReRoute Build() { - return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, + return new ReRoute(new PathTemplate(_downstreamPathTemplate), new PathTemplate(_upstreamTemplate), _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _downstreamScheme, _loadBalancer, diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 5a265569..e02fccd5 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -131,8 +131,8 @@ namespace Ocelot.Configuration.Creator var claimsToClaims = GetAddThingsToRequest(fileReRoute.AddClaimsToRequest); var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest); - reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate), - fileReRoute.UpstreamTemplate, + reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), + new PathTemplate(fileReRoute.UpstreamTemplate), fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, authOptionsForRoute, claimsToHeaders, claimsToClaims, fileReRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries, @@ -143,8 +143,8 @@ namespace Ocelot.Configuration.Creator } else { - reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate), - fileReRoute.UpstreamTemplate, + reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), + new PathTemplate(fileReRoute.UpstreamTemplate), fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, null, new List(), new List(), fileReRoute.RouteClaimsRequirement, isAuthorised, new List(), diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 278d0746..dbc0d05e 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -6,8 +6,8 @@ namespace Ocelot.Configuration { public class ReRoute { - public ReRoute(DownstreamPathTemplate downstreamPathTemplate, - string upstreamTemplate, string upstreamHttpMethod, + public ReRoute(PathTemplate downstreamPathTemplate, + PathTemplate upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List configurationHeaderExtractorProperties, @@ -44,8 +44,8 @@ namespace Ocelot.Configuration } public string LoadBalancerKey {get;private set;} - public DownstreamPathTemplate DownstreamPathTemplate { get; private set; } - public string UpstreamTemplate { get; private set; } + public PathTemplate DownstreamPathTemplate { get; private set; } + public PathTemplate UpstreamTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } public string UpstreamHttpMethod { get; private set; } public bool IsAuthenticated { get; private set; } diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index eacd6912..910f2bb2 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -34,7 +34,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder if (urlMatch.Data.Match) { - var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate); + var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate.Value); return new OkResponse(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute)); } diff --git a/src/Ocelot/Values/DownstreamPathTemplate.cs b/src/Ocelot/Values/DownstreamPathTemplate.cs index a4c720eb..b0b53418 100644 --- a/src/Ocelot/Values/DownstreamPathTemplate.cs +++ b/src/Ocelot/Values/DownstreamPathTemplate.cs @@ -1,8 +1,8 @@ namespace Ocelot.Values { - public class DownstreamPathTemplate + public class PathTemplate { - public DownstreamPathTemplate(string value) + public PathTemplate(string value) { Value = value; } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index dc9978b3..8abb9a61 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -143,7 +143,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void ThenTheUrlMatcherIsCalledCorrectly() { _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamTemplate), Times.Once); + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamTemplate.Value), Times.Once); } private void GivenTheUrlMatcherReturns(Response match) From 6ad27ec17da0568ea2c9796792fc02a12ccdfa74 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 07:54:03 +0000 Subject: [PATCH 03/16] made upstream template use template path object --- .../DownstreamUrlTemplateVariableReplacer.cs | 2 +- .../IDownstreamUrlPathTemplateVariableReplacer.cs | 2 +- .../DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs index 9e925631..3c42b4f4 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs +++ b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs @@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer { public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer { - public Response Replace(DownstreamPathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues) + public Response Replace(PathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues) { var downstreamPath = new StringBuilder(); diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs index 72d5d4b6..647af63a 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs +++ b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs @@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer { public interface IDownstreamPathPlaceholderReplacer { - Response Replace(DownstreamPathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues); + Response Replace(PathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues); } } \ No newline at end of file diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index a01677b2..c953e264 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -101,7 +101,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator { _downstreamPath = new OkResponse(new DownstreamPath(downstreamUrl)); _downstreamUrlTemplateVariableReplacer - .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) + .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) .Returns(_downstreamPath); } From bbb808eb5129188f74359bfc55d1354234620cc5 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 13:46:22 +0000 Subject: [PATCH 04/16] fixed failing tests --- .../Configuration/FileConfigurationCreatorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index d9017da2..5ecf4810 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -593,7 +593,7 @@ namespace Ocelot.UnitTests.Configuration result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); - result.UpstreamTemplate.ShouldBe(expected.UpstreamTemplate); + result.UpstreamTemplate.Value.ShouldBe(expected.UpstreamTemplate.Value); result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern); } } From 33ce162693bc934fd973cf01024aa63f894fc757 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 18:30:21 +0000 Subject: [PATCH 05/16] changed upstream http method to use httpmethod class in .net --- src/Ocelot/Configuration/ReRoute.cs | 5 +- .../Finder/DownstreamRouteFinder.cs | 2 +- .../AuthenticationMiddlewareTests.cs | 4 +- .../AuthorisationMiddlewareTests.cs | 6 +- .../Cache/OutputCacheMiddlewareTests.cs | 7 ++- .../Claims/ClaimsBuilderMiddlewareTests.cs | 1 + .../InMemoryConfigurationRepositoryTests.cs | 5 +- .../DownstreamRouteFinderMiddlewareTests.cs | 8 ++- .../DownstreamRouteFinderTests.cs | 2 + .../DownstreamUrlCreatorMiddlewareTests.cs | 8 ++- ...eamUrlPathTemplateVariableReplacerTests.cs | 57 ++++++++++++++++--- ...ttpRequestHeadersBuilderMiddlewareTests.cs | 1 + .../LoadBalancer/LoadBalancerFactoryTests.cs | 4 ++ .../LoadBalancerMiddlewareTests.cs | 3 + .../QueryStringBuilderMiddlewareTests.cs | 1 + .../HttpRequestBuilderMiddlewareTests.cs | 4 +- .../RequestId/RequestIdMiddlewareTests.cs | 8 ++- 17 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index dbc0d05e..419096af 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net.Http; using Ocelot.Values; namespace Ocelot.Configuration @@ -25,7 +26,7 @@ namespace Ocelot.Configuration DownstreamPort = downstreamPort; DownstreamPathTemplate = downstreamPathTemplate; UpstreamTemplate = upstreamTemplate; - UpstreamHttpMethod = upstreamHttpMethod; + UpstreamHttpMethod = new HttpMethod(upstreamHttpMethod); UpstreamTemplatePattern = upstreamTemplatePattern; IsAuthenticated = isAuthenticated; AuthenticationOptions = authenticationOptions; @@ -47,7 +48,7 @@ namespace Ocelot.Configuration public PathTemplate DownstreamPathTemplate { get; private set; } public PathTemplate UpstreamTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } - public string UpstreamHttpMethod { get; private set; } + public HttpMethod UpstreamHttpMethod { get; private set; } public bool IsAuthenticated { get; private set; } public bool IsAuthorised { get; private set; } public AuthenticationOptions AuthenticationOptions { get; private set; } diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index 910f2bb2..0e591416 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -26,7 +26,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder { var configuration = await _configProvider.Get(); - var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)); + var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)); foreach (var reRoute in applicableReRoutes) { diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs index ff467aa7..f60eb83c 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs @@ -71,7 +71,9 @@ namespace Ocelot.UnitTests.Authentication [Fact] public void should_call_next_middleware_if_route_is_not_authenticated() { - this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().Build()))) + this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder() + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheUserIsAuthenticated()) .BDDfy(); diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index 8d681e03..35435fc2 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -63,7 +63,11 @@ namespace Ocelot.UnitTests.Authorization [Fact] public void should_call_authorisation_service() { - this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().WithIsAuthorised(true).Build()))) + this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithIsAuthorised(true) + .WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get") + .Build()))) .And(x => x.GivenTheAuthServiceReturns(new OkResponse(true))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheAuthServiceIsCalledCorrectly()) diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 9190dbe9..66532a18 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -87,7 +87,12 @@ namespace Ocelot.UnitTests.Cache private void GivenTheDownstreamRouteIs() { - var reRoute = new ReRouteBuilder().WithIsCached(true).WithCacheOptions(new CacheOptions(100)).Build(); + var reRoute = new ReRouteBuilder() + .WithIsCached(true) + .WithCacheOptions(new CacheOptions(100)) + .WithUpstreamHttpMethod("Get") + .Build(); + var downstreamRoute = new DownstreamRoute(new List(), reRoute); _scopedRepo diff --git a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs index 8822e6b2..90a16b17 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs @@ -72,6 +72,7 @@ namespace Ocelot.UnitTests.Claims { new ClaimToThing("sub", "UserType", "|", 0) }) + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index ec46f914..9f437204 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -84,7 +84,10 @@ namespace Ocelot.UnitTests.Configuration public List ReRoutes => new List { - new ReRouteBuilder().WithDownstreamPathTemplate(_downstreamTemplatePath).Build() + new ReRouteBuilder() + .WithDownstreamPathTemplate(_downstreamTemplatePath) + .WithUpstreamHttpMethod("Get") + .Build() }; } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index a80a3168..e7718f37 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -61,7 +61,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_call_scoped_data_repository_correctly() { - this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build()))) + this.Given(x => x.GivenTheDownStreamRouteFinderReturns( + new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .BDDfy(); diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 8abb9a61..d65024f5 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -58,6 +58,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod("Get") .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) @@ -95,6 +96,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamHttpMethod("Post") .Build() ))) .BDDfy(); diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index c953e264..438710f6 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -72,7 +72,13 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator { var hostAndPort = new HostAndPort("127.0.0.1", 80); - this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build()))) + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod("Get") + .Build()))) .And(x => x.GivenTheHostAndPortIs(hostAndPort)) .And(x => x.TheUrlReplacerReturns("/api/products/1")) .And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1")) diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index a7a5a89b..00445ee6 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -25,7 +25,12 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer [Fact] public void can_replace_no_template_variables() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().Build()))) + this.Given(x => x.GivenThereIsAUrlMatch( + new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("")) .BDDfy(); @@ -34,7 +39,13 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer [Fact] public void can_replace_no_template_variables_with_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch( + new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("/") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) .BDDfy(); @@ -43,7 +54,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer [Fact] public void can_replace_url_no_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("api") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) .BDDfy(); @@ -52,7 +67,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer [Fact] public void can_replace_url_one_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("api/") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) .BDDfy(); @@ -61,7 +80,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer [Fact] public void can_replace_url_multiple_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api/product/products/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("api/product/products/") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) .BDDfy(); @@ -75,7 +98,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new UrlPathPlaceholderNameAndValue("{productId}", "1") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, + new ReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) .BDDfy(); @@ -89,7 +116,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new UrlPathPlaceholderNameAndValue("{productId}", "1") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, + new ReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) .BDDfy(); @@ -104,7 +135,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new UrlPathPlaceholderNameAndValue("{variantId}", "12") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, + new ReRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) .BDDfy(); @@ -120,7 +155,11 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new UrlPathPlaceholderNameAndValue("{categoryId}", "34") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, + new ReRouteBuilder() + .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod("Get") + .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) .BDDfy(); diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index 3516d26b..032a76e9 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -72,6 +72,7 @@ namespace Ocelot.UnitTests.Headers { new ClaimToThing("UserId", "Subject", "", 0) }) + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index 9d46807e..767b4272 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -29,6 +29,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) + .WithUpstreamHttpMethod("Get") .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -43,6 +44,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") + .WithUpstreamHttpMethod("Get") .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); @@ -58,6 +60,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("LeastConnection") + .WithUpstreamHttpMethod("Get") .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); @@ -73,6 +76,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") + .WithUpstreamHttpMethod("Get") .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build()) .Build(); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 5a9eec87..1d544366 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -69,6 +69,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) @@ -85,6 +86,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) @@ -100,6 +102,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index 39b32937..f381ff1b 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -70,6 +70,7 @@ namespace Ocelot.UnitTests.QueryStrings { new ClaimToThing("UserId", "Subject", "", 0) }) + .WithUpstreamHttpMethod("Get") .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index e2f81abe..b46877db 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -67,7 +67,9 @@ namespace Ocelot.UnitTests.Request var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithRequestIdKey("LSRequestId").Build()); + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod("Get") + .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) diff --git a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs index 543613a8..26450ab8 100644 --- a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs @@ -72,7 +72,9 @@ namespace Ocelot.UnitTests.RequestId var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId").Build()); + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod("Get") + .Build()); var requestId = Guid.NewGuid().ToString(); @@ -89,7 +91,9 @@ namespace Ocelot.UnitTests.RequestId var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") - .WithRequestIdKey("LSRequestId").Build()); + .WithRequestIdKey("LSRequestId") + .WithUpstreamHttpMethod("Get") + .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .When(x => x.WhenICallTheMiddleware()) From 7fffc9827abe50618457b3d8b230ccc30c1e637a Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Tue, 7 Feb 2017 20:30:26 +0000 Subject: [PATCH 06/16] auth options builders --- README.md | 10 +-- configuration-explanation.txt | 2 +- .../Middleware/AuthorisationMiddleware.cs | 2 +- .../Builder/AuthenticationOptionsBuilder.cs | 56 ++++++++++++ .../Configuration/Builder/ReRouteBuilder.cs | 83 +++++++---------- .../ServiceProviderConfiguraionBuilder.cs | 2 +- .../Creator/FileOcelotConfigurationCreator.cs | 25 ++++-- src/Ocelot/Configuration/File/FileReRoute.cs | 2 +- src/Ocelot/Configuration/ReRoute.cs | 8 +- .../Validator/FileConfigurationValidator.cs | 8 +- .../Finder/DownstreamRouteFinder.cs | 2 +- .../AuthenticationTests.cs | 10 +-- .../AuthorisationTests.cs | 4 +- test/Ocelot.AcceptanceTests/CachingTests.cs | 4 +- .../CaseSensitiveRoutingTests.cs | 12 +-- .../ClaimsToHeadersForwardingTests.cs | 2 +- .../ClaimsToQueryStringForwardingTests.cs | 2 +- .../CustomMiddlewareTests.cs | 12 +-- test/Ocelot.AcceptanceTests/RequestIdTests.cs | 6 +- .../ReturnsErrorTests.cs | 2 +- test/Ocelot.AcceptanceTests/RoutingTests.cs | 18 ++-- .../ServiceDiscoveryTests.cs | 2 +- .../Ocelot.AcceptanceTests/configuration.json | 2 +- .../ConfigurationValidationTests.cs | 12 +-- .../FileConfigurationCreatorTests.cs | 90 ++++++++++--------- .../DownstreamRouteFinderTests.cs | 10 +-- .../ServiceProviderFactoryTests.cs | 1 + 27 files changed, 222 insertions(+), 167 deletions(-) create mode 100644 src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs rename src/Ocelot/Configuration/{ => Builder}/ServiceProviderConfiguraionBuilder.cs (98%) diff --git a/README.md b/README.md index 738ffbe1..3ef6d6b6 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Ocelot's primary functionality is to take incomeing http requests and forward th to a downstream service. At the moment in the form of another http request (in the future this could be any transport mechanism.). -Ocelot always adds a trailing slash to an UpstreamTemplate. +Ocelot always adds a trailing slash to an UpstreamPathTemplate. Ocelot's describes the routing of one request to another as a ReRoute. In order to get anything working in Ocelot you need to set up a ReRoute in the configuration. @@ -140,16 +140,16 @@ the following. "DownstreamScheme": "https", "DownstreamPort": 80, "DownstreamHost" "localhost" - "UpstreamTemplate": "/posts/{postId}", + "UpstreamPathTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Put" } The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to. -The UpstreamTemplate is the URL that Ocelot will use to identity which +The UpstreamPathTemplate is the URL that Ocelot will use to identity which DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so Ocelot can distinguish between requests to the same URL and is obviously needed to work :) In Ocelot you can add placeholders for variables to your Templates in the form of {something}. -The placeholder needs to be in both the DownstreamPathTemplate and UpstreamTemplate. If it is +The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is Ocelot will attempt to replace the placeholder with the correct variable value from the Upstream URL when the request comes in. @@ -190,7 +190,7 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel { "DownstreamPathTemplate": "/api/posts/{postId}", "DownstreamScheme": "https", - "UpstreamTemplate": "/posts/{postId}", + "UpstreamPathTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Put", "ServiceName": "product" "LoadBalancer": "LeastConnection" diff --git a/configuration-explanation.txt b/configuration-explanation.txt index b6db84a2..d7b4077f 100644 --- a/configuration-explanation.txt +++ b/configuration-explanation.txt @@ -15,7 +15,7 @@ # The path template we are listening on for this re route, Ocelot will add a trailing # slash to this property. Then when a request is made Ocelot makes sure a trailing # slash is added, so everything matches - "UpstreamTemplate": "/identityserverexample", + "UpstreamPathTemplate": "/identityserverexample", # The method we are listening for on this re route "UpstreamHttpMethod": "Get", # Only support identity server at the moment diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index 547fc7f7..760d2a38 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -61,7 +61,7 @@ namespace Ocelot.Authorisation.Middleware SetPipelineError(new List { new UnauthorisedError( - $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamTemplate}") + $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}") }); } } diff --git a/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs new file mode 100644 index 00000000..61d5e847 --- /dev/null +++ b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace Ocelot.Configuration.Builder +{ + public class AuthenticationOptionsBuilder + { + + private string _provider; + private string _providerRootUrl; + private string _scopeName; + private string _scopeSecret; + private bool _requireHttps; + private List _additionalScopes; + + public AuthenticationOptionsBuilder WithProvider(string provider) + { + _provider = provider; + return this; + } + + public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl) + { + _providerRootUrl = providerRootUrl; + return this; + } + + public AuthenticationOptionsBuilder WithScopeName(string scopeName) + { + _scopeName = scopeName; + return this; + } + + public AuthenticationOptionsBuilder WithScopeSecret(string scopeSecret) + { + _scopeSecret = scopeSecret; + return this; + } + + public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps) + { + _requireHttps = requireHttps; + return this; + } + + public AuthenticationOptionsBuilder WithAdditionalScopes(List additionalScopes) + { + _additionalScopes = additionalScopes; + return this; + } + + public AuthenticationOptions Build() + { + return new AuthenticationOptions(_provider, _providerRootUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 78c96fb5..d6615d11 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -1,23 +1,19 @@ using System; using System.Collections.Generic; +using System.Net.Http; using Ocelot.Values; namespace Ocelot.Configuration.Builder { public class ReRouteBuilder { + private AuthenticationOptions _authenticationOptions; private string _loadBalancerKey; private string _downstreamPathTemplate; private string _upstreamTemplate; private string _upstreamTemplatePattern; private string _upstreamHttpMethod; private bool _isAuthenticated; - private string _authenticationProvider; - private string _authenticationProviderUrl; - private string _scopeName; - private List _additionalScopes; - private bool _requireHttps; - private string _scopeSecret; private List _configHeaderExtractorProperties; private List _claimToClaims; private Dictionary _routeClaimRequirement; @@ -33,11 +29,6 @@ namespace Ocelot.Configuration.Builder private string _loadBalancer; private ServiceProviderConfiguraion _serviceProviderConfiguraion; - public ReRouteBuilder() - { - _additionalScopes = new List(); - } - public ReRouteBuilder WithLoadBalancer(string loadBalancer) { _loadBalancer = loadBalancer; @@ -68,7 +59,7 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithUpstreamTemplate(string input) + public ReRouteBuilder WithUpstreamPathTemplate(string input) { _upstreamTemplate = input; return this; @@ -96,42 +87,6 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithAuthenticationProvider(string input) - { - _authenticationProvider = input; - return this; - } - - public ReRouteBuilder WithAuthenticationProviderUrl(string input) - { - _authenticationProviderUrl = input; - return this; - } - - public ReRouteBuilder WithAuthenticationProviderScopeName(string input) - { - _scopeName = input; - return this; - } - - public ReRouteBuilder WithAuthenticationProviderAdditionalScopes(List input) - { - _additionalScopes = input; - return this; - } - - public ReRouteBuilder WithRequireHttps(bool input) - { - _requireHttps = input; - return this; - } - - public ReRouteBuilder WithScopeSecret(string input) - { - _scopeSecret = input; - return this; - } - public ReRouteBuilder WithRequestIdKey(string input) { _requestIdHeaderKey = input; @@ -192,13 +147,35 @@ namespace Ocelot.Configuration.Builder return this; } + public ReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions) + { + _authenticationOptions = authenticationOptions; + return this; + } + public ReRoute Build() { - return new ReRoute(new PathTemplate(_downstreamPathTemplate), new PathTemplate(_upstreamTemplate), _upstreamHttpMethod, _upstreamTemplatePattern, - _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, - _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, - _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _downstreamScheme, _loadBalancer, - _downstreamHost, _downstreamPort, _loadBalancerKey, _serviceProviderConfiguraion); + return new ReRoute( + new PathTemplate(_downstreamPathTemplate), + new PathTemplate(_upstreamTemplate), + new HttpMethod(_upstreamHttpMethod), + _upstreamTemplatePattern, + _isAuthenticated, + _authenticationOptions, + _configHeaderExtractorProperties, + _claimToClaims, + _routeClaimRequirement, + _isAuthorised, + _claimToQueries, + _requestIdHeaderKey, + _isCached, + _fileCacheOptions, + _downstreamScheme, + _loadBalancer, + _downstreamHost, + _downstreamPort, + _loadBalancerKey, + _serviceProviderConfiguraion); } } } diff --git a/src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs b/src/Ocelot/Configuration/Builder/ServiceProviderConfiguraionBuilder.cs similarity index 98% rename from src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs rename to src/Ocelot/Configuration/Builder/ServiceProviderConfiguraionBuilder.cs index e8609683..129acae8 100644 --- a/src/Ocelot/Configuration/ServiceProviderConfiguraionBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ServiceProviderConfiguraionBuilder.cs @@ -1,4 +1,4 @@ -namespace Ocelot.Configuration +namespace Ocelot.Configuration.Builder { public class ServiceProviderConfiguraionBuilder { diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index e02fccd5..cf6a2b54 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Net.Http; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Ocelot.Configuration.Builder; using Ocelot.Configuration.File; using Ocelot.Configuration.Parser; using Ocelot.Configuration.Validator; @@ -88,7 +90,7 @@ namespace Ocelot.Configuration.Creator { var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey); - var upstreamTemplate = BuildUpstreamTemplate(fileReRoute); + var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute); var isAuthenticated = !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); @@ -104,7 +106,7 @@ namespace Ocelot.Configuration.Creator && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider); //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain - var loadBalancerKey = $"{fileReRoute.UpstreamTemplate}{fileReRoute.UpstreamHttpMethod}"; + var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; ReRoute reRoute; @@ -132,20 +134,29 @@ namespace Ocelot.Configuration.Creator var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest); reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), - new PathTemplate(fileReRoute.UpstreamTemplate), - fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, + new PathTemplate(fileReRoute.UpstreamPathTemplate), + new HttpMethod(fileReRoute.UpstreamHttpMethod), upstreamTemplatePattern, isAuthenticated, authOptionsForRoute, claimsToHeaders, claimsToClaims, fileReRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries, requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds) , fileReRoute.DownstreamScheme, fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey, serviceProviderConfiguration); + + //reRoute = new ReRouteBuilder() + // .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) + // .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) + // .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + // .WithUpstreamTemplatePattern(upstreamTemplatePattern) + // .WithIsAuthenticated(isAuthenticated) + //.Build(); + } else { reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), - new PathTemplate(fileReRoute.UpstreamTemplate), - fileReRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, + new PathTemplate(fileReRoute.UpstreamPathTemplate), + new HttpMethod(fileReRoute.UpstreamHttpMethod), upstreamTemplatePattern, isAuthenticated, null, new List(), new List(), fileReRoute.RouteClaimsRequirement, isAuthorised, new List(), requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), @@ -161,7 +172,7 @@ namespace Ocelot.Configuration.Creator private string BuildUpstreamTemplate(FileReRoute reRoute) { - var upstreamTemplate = reRoute.UpstreamTemplate; + var upstreamTemplate = reRoute.UpstreamPathTemplate; upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/'); diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 0d0fc7bd..6d3fea4f 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -15,7 +15,7 @@ namespace Ocelot.Configuration.File } public string DownstreamPathTemplate { get; set; } - public string UpstreamTemplate { get; set; } + public string UpstreamPathTemplate { get; set; } public string UpstreamHttpMethod { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; } public Dictionary AddHeadersToRequest { get; set; } diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 419096af..82d13e02 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -8,7 +8,7 @@ namespace Ocelot.Configuration public class ReRoute { public ReRoute(PathTemplate downstreamPathTemplate, - PathTemplate upstreamTemplate, string upstreamHttpMethod, + PathTemplate upstreamTemplate, HttpMethod upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List configurationHeaderExtractorProperties, @@ -25,8 +25,8 @@ namespace Ocelot.Configuration DownstreamHost = downstreamHost; DownstreamPort = downstreamPort; DownstreamPathTemplate = downstreamPathTemplate; - UpstreamTemplate = upstreamTemplate; - UpstreamHttpMethod = new HttpMethod(upstreamHttpMethod); + UpstreamPathTemplate = upstreamTemplate; + UpstreamHttpMethod = upstreamHttpMethod; UpstreamTemplatePattern = upstreamTemplatePattern; IsAuthenticated = isAuthenticated; AuthenticationOptions = authenticationOptions; @@ -46,7 +46,7 @@ namespace Ocelot.Configuration public string LoadBalancerKey {get;private set;} public PathTemplate DownstreamPathTemplate { get; private set; } - public PathTemplate UpstreamTemplate { get; private set; } + public PathTemplate UpstreamPathTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } public HttpMethod UpstreamHttpMethod { get; private set; } public bool IsAuthenticated { get; private set; } diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs index 412613eb..98301ab4 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs @@ -54,7 +54,7 @@ namespace Ocelot.Configuration.Validator continue; } - var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamTemplate}, upstream method is {reRoute.UpstreamHttpMethod}"); + var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}"); errors.Add(error); } @@ -94,18 +94,18 @@ namespace Ocelot.Configuration.Validator private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration) { var hasDupes = configuration.ReRoutes - .GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any()); + .GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any()); if (!hasDupes) { return new ConfigurationValidationResult(false); } - var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod }) + var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }) .Where(x => x.Skip(1).Any()); var errors = dupes - .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamTemplate))) + .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamPathTemplate))) .Cast() .ToList(); diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index 0e591416..e504a430 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -34,7 +34,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder if (urlMatch.Data.Match) { - var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate.Value); + var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value); return new OkResponse(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute)); } diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 8b14f4f1..87419ae6 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -47,7 +47,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = _downstreamServicePort, DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions { @@ -85,7 +85,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = _downstreamServicePort, DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions { @@ -123,7 +123,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = _downstreamServicePort, DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions { @@ -163,7 +163,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = _downstreamServicePort, DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions { @@ -203,7 +203,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = _downstreamServicePort, DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions { diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 1f86c6ff..a89616b9 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51876, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions { @@ -98,7 +98,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51876, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions { diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index e4e628af..a8364855 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -35,7 +35,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", FileCacheOptions = new FileCacheOptions { @@ -71,7 +71,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", FileCacheOptions = new FileCacheOptions { diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index 81824602..7b1dfc75 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -34,7 +34,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } } @@ -61,7 +61,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false } @@ -89,7 +89,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -117,7 +117,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/PRODUCTS/{productId}", + UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -145,7 +145,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -173,7 +173,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/PRODUCTS/{productId}", + UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 08bbd968..2d6bf163 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -55,7 +55,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 52876, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions { diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index 04dc25db..0c10c3e4 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -55,7 +55,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 57876, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions { diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index f6f6de20..085ce2a5 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -49,7 +49,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -86,7 +86,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -123,7 +123,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -160,7 +160,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -197,7 +197,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -234,7 +234,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 41879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index 9334786b..8b9cd805 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -37,7 +37,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", RequestIdKey = _steps.RequestIdKey } @@ -65,7 +65,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", RequestIdKey = _steps.RequestIdKey } @@ -95,7 +95,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", DownstreamHost = "localhost", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } }, diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index 81307781..f9222b86 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -30,7 +30,7 @@ namespace Ocelot.AcceptanceTests new FileReRoute { DownstreamPathTemplate = "http://localhost:53876/", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get" } } diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 4f97114f..14f5af8a 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -44,7 +44,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -72,7 +72,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -100,7 +100,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost/", DownstreamPort = 51879, - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", } } @@ -128,7 +128,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/products/", + UpstreamPathTemplate = "/products/", UpstreamHttpMethod = "Get", } } @@ -156,7 +156,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/products", + UpstreamPathTemplate = "/products", UpstreamHttpMethod = "Get", } } @@ -184,7 +184,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } } @@ -211,7 +211,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamTemplate = "/products/{productId}", + UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } } @@ -239,7 +239,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, DownstreamScheme = "http", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Post" } } @@ -264,7 +264,7 @@ namespace Ocelot.AcceptanceTests new FileReRoute { DownstreamPathTemplate = "/newThing", - UpstreamTemplate = "/newThing", + UpstreamPathTemplate = "/newThing", DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index ef7e0dd7..fcf75b14 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -68,7 +68,7 @@ namespace Ocelot.AcceptanceTests { DownstreamPathTemplate = "/", DownstreamScheme = "http", - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", UpstreamHttpMethod = "Get", ServiceName = serviceName, LoadBalancer = "LeastConnection", diff --git a/test/Ocelot.AcceptanceTests/configuration.json b/test/Ocelot.AcceptanceTests/configuration.json index d7db55c7..a08843bb 100755 --- a/test/Ocelot.AcceptanceTests/configuration.json +++ b/test/Ocelot.AcceptanceTests/configuration.json @@ -1 +1 @@ -{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0}}} \ No newline at end of file +{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0}}} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs index 8a3e24f9..382471c6 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs @@ -29,7 +29,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}", - UpstreamTemplate = "http://asdf.com" + UpstreamPathTemplate = "http://asdf.com" } } })) @@ -48,7 +48,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamPathTemplate = "/api/products/", - UpstreamTemplate = "http://asdf.com" + UpstreamPathTemplate = "http://asdf.com" } } })) @@ -67,7 +67,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamPathTemplate = "/api/products/", - UpstreamTemplate = "http://asdf.com", + UpstreamPathTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { Provider = "IdentityServer" @@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamPathTemplate = "/api/products/", - UpstreamTemplate = "http://asdf.com", + UpstreamPathTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { Provider = "BootyBootyBottyRockinEverywhere" @@ -114,12 +114,12 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamPathTemplate = "/api/products/", - UpstreamTemplate = "http://asdf.com" + UpstreamPathTemplate = "http://asdf.com" }, new FileReRoute { DownstreamPathTemplate = "http://www.bbc.co.uk", - UpstreamTemplate = "http://asdf.com" + UpstreamPathTemplate = "http://asdf.com" } } })) diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 5ecf4810..2d2a48ef 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -53,7 +53,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamHost = "127.0.0.1", - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } @@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamHost = "127.0.0.1", - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } @@ -91,7 +91,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamHost("127.0.0.1") .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .Build() @@ -109,7 +109,7 @@ namespace Ocelot.UnitTests.Configuration new FileReRoute { DownstreamScheme = "https", - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } @@ -122,7 +122,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamScheme("https") .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .Build() @@ -139,7 +139,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false, @@ -161,7 +161,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .WithServiceName("ProductService") @@ -184,7 +184,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false, @@ -197,7 +197,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder() @@ -217,7 +217,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false @@ -230,7 +230,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .Build() @@ -247,7 +247,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } @@ -259,7 +259,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .Build() @@ -276,7 +276,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -289,7 +289,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") .Build() @@ -306,7 +306,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -323,7 +323,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") .WithRequestIdKey("blahhhh") @@ -341,7 +341,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -354,7 +354,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") .Build() @@ -365,18 +365,23 @@ namespace Ocelot.UnitTests.Configuration [Fact] public void should_create_with_headers_to_extract() { + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithProvider("IdentityServer") + .WithProviderRootUrl("http://localhost:51888") + .WithRequireHttps(false) + .WithScopeSecret("secret") + .WithScopeName("api") + .WithAdditionalScopes(new List()) + .Build(); + var expected = new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") - .WithAuthenticationProvider("IdentityServer") - .WithAuthenticationProviderUrl("http://localhost:51888") - .WithRequireHttps(false) - .WithScopeSecret("secret") - .WithAuthenticationProviderScopeName("api") + .WithAuthenticationOptions(authenticationOptions) .WithClaimsToHeaders(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0), @@ -390,7 +395,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true, @@ -429,18 +434,23 @@ namespace Ocelot.UnitTests.Configuration [Fact] public void should_create_with_authentication_properties() { + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithProvider("IdentityServer") + .WithProviderRootUrl("http://localhost:51888") + .WithRequireHttps(false) + .WithScopeSecret("secret") + .WithScopeName("api") + .WithAdditionalScopes(new List()) + .Build(); + var expected = new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") - .WithAuthenticationProvider("IdentityServer") - .WithAuthenticationProviderUrl("http://localhost:51888") - .WithRequireHttps(false) - .WithScopeSecret("secret") - .WithAuthenticationProviderScopeName("api") + .WithAuthenticationOptions(authenticationOptions) .Build() }; @@ -450,7 +460,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}", + UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true, @@ -483,7 +493,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}/variants/{variantId}", + UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -496,7 +506,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}") + .WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$") .Build() @@ -513,7 +523,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/api/products/{productId}/variants/{variantId}/", + UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -526,7 +536,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}/") + .WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}/") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$") .Build() @@ -543,7 +553,7 @@ namespace Ocelot.UnitTests.Configuration { new FileReRoute { - UpstreamTemplate = "/", + UpstreamPathTemplate = "/", DownstreamPathTemplate = "/api/products/", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -556,7 +566,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate("/api/products/") - .WithUpstreamTemplate("/") + .WithUpstreamPathTemplate("/") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/$") .Build() @@ -593,7 +603,7 @@ namespace Ocelot.UnitTests.Configuration result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); - result.UpstreamTemplate.Value.ShouldBe(expected.UpstreamTemplate.Value); + result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value); result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern); } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index d65024f5..81fc7022 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -45,7 +45,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamTemplate("someUpstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("someUpstreamPath") .Build() @@ -77,13 +77,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamTemplate("someUpstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("") .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamTemplate("someUpstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Post") .WithUpstreamTemplatePattern("") .Build() @@ -110,7 +110,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { new ReRouteBuilder() .WithDownstreamPathTemplate("somPath") - .WithUpstreamTemplate("somePath") + .WithUpstreamPathTemplate("somePath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("somePath") .Build(), @@ -145,7 +145,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void ThenTheUrlMatcherIsCalledCorrectly() { _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamTemplate.Value), Times.Once); + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); } private void GivenTheUrlMatcherReturns(Response match) diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs index 7ba4608f..b3053afa 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceProviderFactoryTests.cs @@ -1,4 +1,5 @@ using Ocelot.Configuration; +using Ocelot.Configuration.Builder; using Ocelot.ServiceDiscovery; using Shouldly; using TestStack.BDDfy; From dbe28d38bca82f9e9ba061bd009c1c3fb8601992 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Wed, 8 Feb 2017 07:37:04 +0000 Subject: [PATCH 07/16] tidying up configuration creation --- .../Creator/FileOcelotConfigurationCreator.cs | 69 ++++++++----------- .../AuthenticationHandlerFactoryTests.cs | 12 +++- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index cf6a2b54..40c2bc02 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -108,8 +108,6 @@ namespace Ocelot.Configuration.Creator //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; - ReRoute reRoute; - var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; var serviceProviderConfiguration = new ServiceProviderConfiguraionBuilder() @@ -122,48 +120,41 @@ namespace Ocelot.Configuration.Creator .WithServiceDiscoveryProviderPort(serviceProviderPort) .Build(); - if (isAuthenticated) - { - var authOptionsForRoute = new AuthenticationOptions(fileReRoute.AuthenticationOptions.Provider, - fileReRoute.AuthenticationOptions.ProviderRootUrl, fileReRoute.AuthenticationOptions.ScopeName, - fileReRoute.AuthenticationOptions.RequireHttps, fileReRoute.AuthenticationOptions.AdditionalScopes, - fileReRoute.AuthenticationOptions.ScopeSecret); + var authOptionsForRoute = new AuthenticationOptionsBuilder() + .WithProvider(fileReRoute.AuthenticationOptions?.Provider) + .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) + .WithScopeName(fileReRoute.AuthenticationOptions?.ScopeName) + .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) + .WithAdditionalScopes(fileReRoute.AuthenticationOptions?.AdditionalScopes) + .WithScopeSecret(fileReRoute.AuthenticationOptions?.ScopeSecret) + .Build(); var claimsToHeaders = GetAddThingsToRequest(fileReRoute.AddHeadersToRequest); var claimsToClaims = GetAddThingsToRequest(fileReRoute.AddClaimsToRequest); var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest); - reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), - new PathTemplate(fileReRoute.UpstreamPathTemplate), - new HttpMethod(fileReRoute.UpstreamHttpMethod), upstreamTemplatePattern, isAuthenticated, - authOptionsForRoute, claimsToHeaders, claimsToClaims, - fileReRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries, - requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds) - , fileReRoute.DownstreamScheme, - fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey, - serviceProviderConfiguration); - - //reRoute = new ReRouteBuilder() - // .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) - // .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) - // .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - // .WithUpstreamTemplatePattern(upstreamTemplatePattern) - // .WithIsAuthenticated(isAuthenticated) - //.Build(); - - } - else - { - reRoute = new ReRoute(new PathTemplate(fileReRoute.DownstreamPathTemplate), - new PathTemplate(fileReRoute.UpstreamPathTemplate), - new HttpMethod(fileReRoute.UpstreamHttpMethod), upstreamTemplatePattern, isAuthenticated, - null, new List(), new List(), - fileReRoute.RouteClaimsRequirement, isAuthorised, new List(), - requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), - fileReRoute.DownstreamScheme, - fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort, loadBalancerKey, - serviceProviderConfiguration); - } + var reRoute = new ReRouteBuilder() + .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) + .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamTemplatePattern(upstreamTemplatePattern) + .WithIsAuthenticated(isAuthenticated) + .WithAuthenticationOptions(authOptionsForRoute) + .WithClaimsToHeaders(claimsToHeaders) + .WithClaimsToClaims(claimsToClaims) + .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) + .WithIsAuthorised(isAuthorised) + .WithClaimsToQueries(claimsToQueries) + .WithRequestIdKey(requestIdKey) + .WithIsCached(isCached) + .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) + .WithDownstreamScheme(fileReRoute.DownstreamScheme) + .WithLoadBalancer(fileReRoute.LoadBalancer) + .WithDownstreamHost(fileReRoute.DownstreamHost) + .WithDownstreamPort(fileReRoute.DownstreamPort) + .WithLoadBalancerKey(loadBalancerKey) + .WithServiceProviderConfiguraion(serviceProviderConfiguration) + .Build(); var loadBalancer = await _loadBalanceFactory.Get(reRoute); _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer); diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs index e76a2b28..229da4a9 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs @@ -6,6 +6,7 @@ using Moq; using Ocelot.Authentication.Handler; using Ocelot.Authentication.Handler.Creator; using Ocelot.Authentication.Handler.Factory; +using Ocelot.Configuration.Builder; using Ocelot.Errors; using Ocelot.Responses; using Shouldly; @@ -33,7 +34,11 @@ namespace Ocelot.UnitTests.Authentication [Fact] public void should_return_identity_server_access_token_handler() { - this.Given(x => x.GivenTheAuthenticationOptionsAre(new AuthenticationOptions("IdentityServer", "","",false, new List(), ""))) + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithProvider("IdentityServer") + .Build(); + + this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions)) .And(x => x.GivenTheCreatorReturns()) .When(x => x.WhenIGetFromTheFactory()) .Then(x => x.ThenTheHandlerIsReturned("IdentityServer")) @@ -43,7 +48,10 @@ namespace Ocelot.UnitTests.Authentication [Fact] public void should_return_error_if_cannot_create_handler() { - this.Given(x => x.GivenTheAuthenticationOptionsAre(new AuthenticationOptions("IdentityServer", "", "", false, new List(), ""))) + var authenticationOptions = new AuthenticationOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions)) .And(x => x.GivenTheCreatorReturnsAnError()) .When(x => x.WhenIGetFromTheFactory()) .Then(x => x.ThenAnErrorResponseIsReturned()) From 26ac0fd3f45b76997ab75f164ba8feea2ba8e2c0 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Wed, 8 Feb 2017 07:49:50 +0000 Subject: [PATCH 08/16] more refactoring --- .../Creator/FileOcelotConfigurationCreator.cs | 117 +++++++++++------- 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 40c2bc02..7439e01c 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -90,8 +90,6 @@ namespace Ocelot.Configuration.Creator { var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey); - var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute); - var isAuthenticated = !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); var isAuthorised = fileReRoute.RouteClaimsRequirement?.Count > 0; @@ -102,15 +100,80 @@ namespace Ocelot.Configuration.Creator ? globalConfiguration.RequestIdKey : fileReRoute.RequestIdKey; + var loadBalancerKey = BuildLoadBalancerKey(fileReRoute); + + var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute); + + var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration); + + var authOptionsForRoute = BuildAuthenticationOptions(fileReRoute); + + var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest); + + var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest); + + var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest); + + var reRoute = new ReRouteBuilder() + .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) + .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamTemplatePattern(upstreamTemplatePattern) + .WithIsAuthenticated(isAuthenticated) + .WithAuthenticationOptions(authOptionsForRoute) + .WithClaimsToHeaders(claimsToHeaders) + .WithClaimsToClaims(claimsToClaims) + .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) + .WithIsAuthorised(isAuthorised) + .WithClaimsToQueries(claimsToQueries) + .WithRequestIdKey(requestIdKey) + .WithIsCached(isCached) + .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) + .WithDownstreamScheme(fileReRoute.DownstreamScheme) + .WithLoadBalancer(fileReRoute.LoadBalancer) + .WithDownstreamHost(fileReRoute.DownstreamHost) + .WithDownstreamPort(fileReRoute.DownstreamPort) + .WithLoadBalancerKey(loadBalancerKey) + .WithServiceProviderConfiguraion(serviceProviderConfiguration) + .Build(); + + await SetupLoadBalancer(reRoute); + return reRoute; + } + + private string BuildLoadBalancerKey(FileReRoute fileReRoute) + { + //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain + var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; + return loadBalancerKey; + } + + private AuthenticationOptions BuildAuthenticationOptions(FileReRoute fileReRoute) + { + return new AuthenticationOptionsBuilder() + .WithProvider(fileReRoute.AuthenticationOptions?.Provider) + .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) + .WithScopeName(fileReRoute.AuthenticationOptions?.ScopeName) + .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) + .WithAdditionalScopes(fileReRoute.AuthenticationOptions?.AdditionalScopes) + .WithScopeSecret(fileReRoute.AuthenticationOptions?.ScopeSecret) + .Build(); + } + + private async Task SetupLoadBalancer(ReRoute reRoute) + { + var loadBalancer = await _loadBalanceFactory.Get(reRoute); + _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer); + } + + private ServiceProviderConfiguraion BuildServiceProviderConfiguration(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + { var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName) && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider); - //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain - var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; - var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; - var serviceProviderConfiguration = new ServiceProviderConfiguraionBuilder() + return new ServiceProviderConfiguraionBuilder() .WithServiceName(fileReRoute.ServiceName) .WithDownstreamHost(fileReRoute.DownstreamHost) .WithDownstreamPort(fileReRoute.DownstreamPort) @@ -119,46 +182,6 @@ namespace Ocelot.Configuration.Creator .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) .WithServiceDiscoveryProviderPort(serviceProviderPort) .Build(); - - var authOptionsForRoute = new AuthenticationOptionsBuilder() - .WithProvider(fileReRoute.AuthenticationOptions?.Provider) - .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) - .WithScopeName(fileReRoute.AuthenticationOptions?.ScopeName) - .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) - .WithAdditionalScopes(fileReRoute.AuthenticationOptions?.AdditionalScopes) - .WithScopeSecret(fileReRoute.AuthenticationOptions?.ScopeSecret) - .Build(); - - var claimsToHeaders = GetAddThingsToRequest(fileReRoute.AddHeadersToRequest); - var claimsToClaims = GetAddThingsToRequest(fileReRoute.AddClaimsToRequest); - var claimsToQueries = GetAddThingsToRequest(fileReRoute.AddQueriesToRequest); - - var reRoute = new ReRouteBuilder() - .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) - .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamTemplatePattern(upstreamTemplatePattern) - .WithIsAuthenticated(isAuthenticated) - .WithAuthenticationOptions(authOptionsForRoute) - .WithClaimsToHeaders(claimsToHeaders) - .WithClaimsToClaims(claimsToClaims) - .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) - .WithIsAuthorised(isAuthorised) - .WithClaimsToQueries(claimsToQueries) - .WithRequestIdKey(requestIdKey) - .WithIsCached(isCached) - .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) - .WithDownstreamScheme(fileReRoute.DownstreamScheme) - .WithLoadBalancer(fileReRoute.LoadBalancer) - .WithDownstreamHost(fileReRoute.DownstreamHost) - .WithDownstreamPort(fileReRoute.DownstreamPort) - .WithLoadBalancerKey(loadBalancerKey) - .WithServiceProviderConfiguraion(serviceProviderConfiguration) - .Build(); - - var loadBalancer = await _loadBalanceFactory.Get(reRoute); - _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer); - return reRoute; } private string BuildUpstreamTemplate(FileReRoute reRoute) @@ -192,7 +215,7 @@ namespace Ocelot.Configuration.Creator return route; } - private List GetAddThingsToRequest(Dictionary thingBeingAdded) + private List BuildAddThingsToRequest(Dictionary thingBeingAdded) { var claimsToTHings = new List(); From 9d790a449b77dd90fb1f240ca3c55708e3e88af4 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Wed, 8 Feb 2017 18:23:07 +0000 Subject: [PATCH 09/16] more refactoring of fileconfig creator --- .../Creator/FileOcelotConfigurationCreator.cs | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 7439e01c..305dfc66 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -88,17 +88,13 @@ namespace Ocelot.Configuration.Creator private async Task SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) { - var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey); + var isAuthenticated = IsAuthenticated(fileReRoute); - var isAuthenticated = !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); + var isAuthorised = IsAuthenticated(fileReRoute); - var isAuthorised = fileReRoute.RouteClaimsRequirement?.Count > 0; + var isCached = IsCached(fileReRoute); - var isCached = fileReRoute.FileCacheOptions.TtlSeconds > 0; - - var requestIdKey = globalRequestIdConfiguration - ? globalConfiguration.RequestIdKey - : fileReRoute.RequestIdKey; + var requestIdKey = BuildRequestId(fileReRoute, globalConfiguration); var loadBalancerKey = BuildLoadBalancerKey(fileReRoute); @@ -141,6 +137,32 @@ namespace Ocelot.Configuration.Creator return reRoute; } + private bool IsAuthenticated(FileReRoute fileReRoute) + { + return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); + } + + private bool IsAuthorised(FileReRoute fileReRoute) + { + return fileReRoute.RouteClaimsRequirement?.Count > 0; + } + + private bool IsCached(FileReRoute fileReRoute) + { + return fileReRoute.FileCacheOptions.TtlSeconds > 0; + } + + private string BuildRequestId(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + { + var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey); + + var requestIdKey = globalRequestIdConfiguration + ? globalConfiguration.RequestIdKey + : fileReRoute.RequestIdKey; + + return requestIdKey; + } + private string BuildLoadBalancerKey(FileReRoute fileReRoute) { //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain From f7fe7c0f497d71e69f22989ca19e5cdfe9ecb2ad Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Wed, 8 Feb 2017 18:47:35 +0000 Subject: [PATCH 10/16] more refactoring --- build.cake | 2 +- src/.DS_Store | Bin 0 -> 6148 bytes src/Ocelot/.DS_Store | Bin 0 -> 8196 bytes src/Ocelot/Authentication/Handler/.DS_Store | Bin 0 -> 6148 bytes .../Creator/AuthenticationHandlerCreator.cs | 2 +- .../Creator/IAuthenticationHandlerCreator.cs | 2 +- .../Factory/AuthenticationHandlerFactory.cs | 2 +- src/Ocelot/Authorisation/ClaimsAuthoriser.cs | 1 - .../Creator/FileOcelotConfigurationCreator.cs | 4 ---- .../AuthenticationHandlerFactoryTests.cs | 4 ++-- .../ErrorsToHttpStatusCodeMapperTests.cs | 3 --- 11 files changed, 6 insertions(+), 14 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/Ocelot/.DS_Store create mode 100644 src/Ocelot/Authentication/Handler/.DS_Store diff --git a/build.cake b/build.cake index 1a1f005d..0f031972 100644 --- a/build.cake +++ b/build.cake @@ -42,7 +42,7 @@ var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; // internal build variables - don't change these. var releaseTag = ""; -var committedVersion = "0.0.0-dev"; +string committedVersion = "0.0.0-dev"; var buildVersion = committedVersion; var target = Argument("target", "Default"); diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e6c4e75f3d6ddeee04f8f8aa9d10d754c345d1f2 GIT binary patch literal 6148 zcmeHK&1%~~5Z-m%M2ce2A)%!-pnG$0+q9uK;Ut$(h(W;pV;+D&>Nex}K?Ve#%8I#~nEod3FA4;eNQ)E!hZCyQyG3Uh#|Yr#sPPZx8I4fT$p^hDy+8YYhqk})v;e^HpUF#Td49qjIVje@>{}2Cs|DR8CPmTe{z<w=R4f?zIu<3s4k{s}0_yfT6cyh{dh=4yYFJM`!@X7Hfmx0dWTbO@nJ3 I1Amo)pQC1So&W#< literal 0 HcmV?d00001 diff --git a/src/Ocelot/.DS_Store b/src/Ocelot/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..996c69e40ae6edfe8644ddf224acf46e45ffdbc9 GIT binary patch literal 8196 zcmeHM-EJF26h0HDu^ro$5viyUNPAg{stQmj+!;4P1Qj*ZZHh!u*tNIGmSs2WuH!~g zBwrw2fE(U{2jBrfP$V7zam$Z*i}0N@n`mb3v^SNgU?$o*oAvjdIp;fPcQXzVss4EM zD$zwEYT#hoZDE=-_;WsH#zJ|;gH_-Yg_KZ15e>;C#Za1#&pn32_DVa|R?|soI;nDHRqjxftPY+ha8f;O-Ru-_3d}0t;nI0}hsM~q z?^^qJ-p8kuY{cm>h|>T#zO~-PaS4YyKF0h6ZZOIBG*`VC?8Q-Dtgim17HW& za;x2Gb-L|Vr}fETH=PU$zZmCzKilrUG>ArF;AeNEQL%eFOxELYKTK|BH~b>ZK4$Rk zG>rk!=YFsig_G^>%JnoU{3r=C@uz*W|Nh6Hq7$j|!9^?j_tW{WC#kqU$dWisc7S6aozrTil%YmB zzH6CZt6zY=bSwjBL^TS4uT)gss-Vu%b$A?dH`ujTzgCb-K*qpE^f~1=R{G={W9<&G z=q9=k^bWc$du-Gu^LbiBU+zh7j$|Iv0d2t71N=6epSxsbdbt2EDNVr2!N|b$5%n!x zAv%XOuJ9!Ozm53Y zCpg5zJ#_`$m4jEgTO<>c_uLGRS02)5XL(SZm+4`dHYGyt@C++Qu$8+>vNSnu`Vf7~ z_yZb%!_NVzUgdF?3m+L-*3t1HeIZ=j2ey`+n}u%LXkO1|DCF{3#s-Zl4Ag55BZrFo zbeMDhNe+*U9CTDCjC@ut$-`uHp%H7f50$u=*I;v;rMMy*R$}C=On#H@NL^y^&a98c zSfVYtd$>lGT5>+Mw~T!1=zspI+*DhkV3O@PtZc_&PybZ6SmF^1?h`UPvR z@CJN2#*&gw?f&m3jqU3X@yaZWwDyJmZM46>BeH>`_5G z>;0)tuv=vm3u2FXow~dhill|ACsr zgcg168So5@Gq7yC6*>P8zrO#EPx3v_fM?)eF= k3M+RUYeSCW4HgxS1=1jf4s(mtQ2ZYOrNLL8fgfey1AVE?-~a#s literal 0 HcmV?d00001 diff --git a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs index 65260d64..67f2ebbd 100644 --- a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs +++ b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs @@ -12,7 +12,7 @@ namespace Ocelot.Authentication.Handler.Creator /// public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator { - public Response CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions) + public Response Create(IApplicationBuilder app, AuthenticationOptions authOptions) { var builder = app.New(); diff --git a/src/Ocelot/Authentication/Handler/Creator/IAuthenticationHandlerCreator.cs b/src/Ocelot/Authentication/Handler/Creator/IAuthenticationHandlerCreator.cs index 6baa0385..9d92c81d 100644 --- a/src/Ocelot/Authentication/Handler/Creator/IAuthenticationHandlerCreator.cs +++ b/src/Ocelot/Authentication/Handler/Creator/IAuthenticationHandlerCreator.cs @@ -8,6 +8,6 @@ namespace Ocelot.Authentication.Handler.Creator public interface IAuthenticationHandlerCreator { - Response CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions); + Response Create(IApplicationBuilder app, AuthenticationOptions authOptions); } } diff --git a/src/Ocelot/Authentication/Handler/Factory/AuthenticationHandlerFactory.cs b/src/Ocelot/Authentication/Handler/Factory/AuthenticationHandlerFactory.cs index 6379cc1f..60253816 100644 --- a/src/Ocelot/Authentication/Handler/Factory/AuthenticationHandlerFactory.cs +++ b/src/Ocelot/Authentication/Handler/Factory/AuthenticationHandlerFactory.cs @@ -19,7 +19,7 @@ namespace Ocelot.Authentication.Handler.Factory public Response Get(IApplicationBuilder app, AuthenticationOptions authOptions) { - var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions); + var handler = _creator.Create(app, authOptions); if (!handler.IsError) { diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs index cb7849e9..96f7acd6 100644 --- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs +++ b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using Ocelot.Errors; using Ocelot.Responses; diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 305dfc66..357d6296 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -55,10 +55,6 @@ namespace Ocelot.Configuration.Creator return new OkResponse(config); } - /// - /// This method is meant to be tempoary to convert a config to an ocelot config...probably wont keep this but we will see - /// will need a refactor at some point as its crap - /// private async Task SetUpConfiguration() { var response = _configurationValidator.IsValid(_options.Value); diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs index 229da4a9..8bf53607 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs @@ -66,7 +66,7 @@ namespace Ocelot.UnitTests.Authentication private void GivenTheCreatorReturnsAnError() { _creator - .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny(), It.IsAny())) + .Setup(x => x.Create(It.IsAny(), It.IsAny())) .Returns(new ErrorResponse(new List { new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx") @@ -76,7 +76,7 @@ namespace Ocelot.UnitTests.Authentication private void GivenTheCreatorReturns() { _creator - .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny(), It.IsAny())) + .Setup(x => x.Create(It.IsAny(), It.IsAny())) .Returns(new OkResponse(x => Task.CompletedTask)); } diff --git a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs index 78f78235..cb8198dc 100644 --- a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs +++ b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs @@ -1,7 +1,4 @@ using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using Microsoft.AspNetCore.Http; using Ocelot.Errors; using Ocelot.Middleware; using Ocelot.Responder; From 48a62382fe5f79e870e7ed809c3cbaffb180df68 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Wed, 8 Feb 2017 18:53:52 +0000 Subject: [PATCH 11/16] changes missed --- .../Authentication/Middleware/AuthenticationMiddleware.cs | 1 - src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index ad30e166..037deeee 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; using Ocelot.Authentication.Handler.Factory; using Ocelot.Configuration; using Ocelot.Errors; diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index 760d2a38..a86643a4 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.Logging; -using Ocelot.Infrastructure.RequestData; +using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Responses; From ee73d3897c2c4c9a2b06313458f285c606d878e2 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 11 Feb 2017 12:18:27 +0000 Subject: [PATCH 12/16] removed second publish of symols as we publish with the nuget package --- build.cake | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build.cake b/build.cake index 1a1f005d..0dca21ec 100644 --- a/build.cake +++ b/build.cake @@ -318,14 +318,6 @@ private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbo ApiKey = feedApiKey, Source = codeFeedUrl }); - - NuGetPush( - symbolsPackage, - new NuGetPushSettings { - ApiKey = feedApiKey, - Source = symbolFeedUrl - }); - } /// gets the resource from the specified url From 2757fe25eb3d23f678827c0fdc2adeb01347d7df Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 11 Feb 2017 12:29:07 +0000 Subject: [PATCH 13/16] made changes to only push nuget package for some reason still trying to push symbols again --- build.cake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 0dca21ec..93cf8428 100644 --- a/build.cake +++ b/build.cake @@ -310,7 +310,8 @@ private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbo .ToDictionary(v => v[0], v => v[1]); var codePackage = packagesDir + File(artifacts["nuget"]); - var symbolsPackage = packagesDir + File(artifacts["nugetSymbols"]); + + Information("Pushing package"); NuGetPush( codePackage, From ce8da4c92d55f53ad4ed9204279003ef2b968291 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 11 Feb 2017 13:14:25 +0000 Subject: [PATCH 14/16] removed qos by accident, added back in --- src/Ocelot/Configuration/Builder/ReRouteBuilder.cs | 7 ------- .../Creator/FileOcelotConfigurationCreator.cs | 4 +++- .../Configuration/FileConfigurationCreatorTests.cs | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 42e4e5bf..382cf374 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -22,7 +22,6 @@ namespace Ocelot.Configuration.Builder private string _requestIdHeaderKey; private bool _isCached; private CacheOptions _fileCacheOptions; - private string _serviceName; private string _downstreamScheme; private string _downstreamHost; private int _downstreamPort; @@ -49,12 +48,6 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithServiceName(string serviceName) - { - _serviceName = serviceName; - return this; - } - public ReRouteBuilder WithDownstreamPathTemplate(string input) { _downstreamPathTemplate = input; diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 740a2b95..25888681 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -95,6 +95,7 @@ namespace Ocelot.Configuration.Creator var loadBalancerKey = BuildLoadBalancerKey(fileReRoute); var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute); + var isQos = fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions.TimeoutValue >0; var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration); @@ -103,7 +104,6 @@ namespace Ocelot.Configuration.Creator var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest); - var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest); var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest); @@ -129,6 +129,8 @@ namespace Ocelot.Configuration.Creator .WithDownstreamPort(fileReRoute.DownstreamPort) .WithLoadBalancerKey(loadBalancerKey) .WithServiceProviderConfiguraion(serviceProviderConfiguration) + .WithIsQos(isQos) + .WithQosOptions(new QoSOptions(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking, fileReRoute.QoSOptions.DurationOfBreak, fileReRoute.QoSOptions.TimeoutValue)) .Build(); await SetupLoadBalancer(reRoute); diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 2d2a48ef..5080c9e0 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -164,11 +164,11 @@ namespace Ocelot.UnitTests.Configuration .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") - .WithServiceName("ProductService") .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder() .WithUseServiceDiscovery(true) .WithServiceDiscoveryProvider("consul") .WithServiceDiscoveryProviderHost("127.0.0.1") + .WithServiceName("ProductService") .Build()) .Build() })) From 820673dda8b25ecb4ae3098e3bef5600d51fec93 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 11 Feb 2017 15:11:10 +0000 Subject: [PATCH 15/16] added docs but qos acceptance test not working seems circuit never opens but not sure if it is meant to with timeouts..investigating --- README.md | 40 ++++-- src/Ocelot/Configuration/QoSOptions.cs | 6 +- src/Ocelot/Errors/OcelotErrorCode.cs | 3 +- .../CircuitBreakingDelegatingHandler.cs | 27 ++-- .../Requester/HttpClientHttpRequester.cs | 12 +- src/Ocelot/Requester/RequestTimedOutError.cs | 13 ++ .../Responder/ErrorsToHttpStatusCodeMapper.cs | 5 + src/Ocelot/Responses/ErrorResponseGeneric.cs | 8 +- test/Ocelot.AcceptanceTests/QoSTests.cs | 136 ++++++++++++++++++ .../ErrorsToHttpStatusCodeMapperTests.cs | 16 ++- 10 files changed, 235 insertions(+), 31 deletions(-) create mode 100644 src/Ocelot/Requester/RequestTimedOutError.cs create mode 100644 test/Ocelot.AcceptanceTests/QoSTests.cs diff --git a/README.md b/README.md index 3ef6d6b6..dfbdfd1e 100644 --- a/README.md +++ b/README.md @@ -305,19 +305,25 @@ Below is an example configuration that will transforms claims to query string pa This shows a transform where Ocelot looks at the users LocationId claim and add its as a query string parameter to be forwarded onto the downstream service. -## Logging +## Quality of Service -Ocelot uses the standard logging interfaces ILoggerFactory / ILogger at the moment. -This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation -for the standard asp.net core logging stuff at the moment. +Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you +want to use a circuit breaker when making requests to a downstream service. This uses the an awesome +.NET library called Polly check them out [here](https://github.com/App-vNext/Polly). -There are a bunch of debugging logs in the ocelot middlewares however I think the -system probably needs more logging in the code it calls into. Other than the debugging -there is a global error handler that should catch any errors thrown and log them as errors. +Add the following section to a ReRoute configuration. -The reason for not just using bog standard framework logging is that I could not -work out how to override the request id that get's logged when setting IncludeScopes -to true for logging settings. Nicely onto the next feature. + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking":3, + "DurationOfBreak":5, + "TimeoutValue":5000 + } + +You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be +implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped. +TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out. + +If you do not add a QoS section QoS will not be used. ## RequestId / CorrelationId @@ -404,6 +410,20 @@ http request before it is passed to Ocelots request creator. Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added after as Ocelot does not call the next middleware. +## Logging + +Ocelot uses the standard logging interfaces ILoggerFactory / ILogger at the moment. +This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation +for the standard asp.net core logging stuff at the moment. + +There are a bunch of debugging logs in the ocelot middlewares however I think the +system probably needs more logging in the code it calls into. Other than the debugging +there is a global error handler that should catch any errors thrown and log them as errors. + +The reason for not just using bog standard framework logging is that I could not +work out how to override the request id that get's logged when setting IncludeScopes +to true for logging settings. Nicely onto the next feature. + ## Not supported Ocelot does not support... diff --git a/src/Ocelot/Configuration/QoSOptions.cs b/src/Ocelot/Configuration/QoSOptions.cs index 8584c57e..9deaaeda 100644 --- a/src/Ocelot/Configuration/QoSOptions.cs +++ b/src/Ocelot/Configuration/QoSOptions.cs @@ -8,7 +8,11 @@ namespace Ocelot.Configuration { public class QoSOptions { - public QoSOptions(int exceptionsAllowedBeforeBreaking, int durationofBreak, int timeoutValue, TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic) + public QoSOptions( + int exceptionsAllowedBeforeBreaking, + int durationofBreak, + int timeoutValue, + TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic) { ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; DurationOfBreak = TimeSpan.FromMilliseconds(durationofBreak); diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index d24988b9..e2fe4ada 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -25,6 +25,7 @@ ServicesAreNullError, ServicesAreEmptyError, UnableToFindServiceDiscoveryProviderError, - UnableToFindLoadBalancerError + UnableToFindLoadBalancerError, + RequestTimedOutError } } diff --git a/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs b/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs index 4adc0eeb..b4091296 100644 --- a/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs +++ b/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs @@ -18,11 +18,17 @@ namespace Ocelot.Requester private readonly Policy _circuitBreakerPolicy; private readonly TimeoutPolicy _timeoutPolicy; - public CircuitBreakingDelegatingHandler(int exceptionsAllowedBeforeBreaking, TimeSpan durationOfBreak,TimeSpan timeoutValue - ,TimeoutStrategy timeoutStrategy, IOcelotLogger logger, HttpMessageHandler innerHandler) + public CircuitBreakingDelegatingHandler( + int exceptionsAllowedBeforeBreaking, + TimeSpan durationOfBreak, + TimeSpan timeoutValue, + TimeoutStrategy timeoutStrategy, + IOcelotLogger logger, + HttpMessageHandler innerHandler) : base(innerHandler) { this._exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; + this._durationOfBreak = durationOfBreak; _circuitBreakerPolicy = Policy @@ -39,30 +45,27 @@ namespace Ocelot.Requester onReset: () => _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."), onHalfOpen: () => _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.") ); + _timeoutPolicy = Policy.TimeoutAsync(timeoutValue, timeoutStrategy); + _logger = logger; } - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - Task responseTask = null; - try { - responseTask = Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync(() => - { - return base.SendAsync(request,cancellationToken); - }); - return responseTask; + return await Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync(() => base.SendAsync(request,cancellationToken)); } catch (BrokenCircuitException ex) { _logger.LogError($"Reached to allowed number of exceptions. Circuit is open. AllowedExceptionCount: {_exceptionsAllowedBeforeBreaking}, DurationOfBreak: {_durationOfBreak}",ex); throw; } - catch (HttpRequestException) + catch (HttpRequestException ex) { - return responseTask; + _logger.LogError($"Error in CircuitBreakingDelegatingHandler.SendAync", ex); + throw; } } diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index 77ab90cc..cc5332f3 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -27,6 +27,7 @@ namespace Ocelot.Requester { builder.WithCircuitBreaker(request.Qos, _logger, handler); } + using (var httpClient = builder.Build(handler)) { try @@ -34,13 +35,14 @@ namespace Ocelot.Requester var response = await httpClient.SendAsync(request.HttpRequestMessage); return new OkResponse(response); } - catch (Exception exception) + catch (Polly.Timeout.TimeoutRejectedException exception) { return - new ErrorResponse(new List - { - new UnableToCompleteRequestError(exception) - }); + new ErrorResponse(new RequestTimedOutError(exception)); + } + catch (Exception exception) + { + return new ErrorResponse(new UnableToCompleteRequestError(exception)); } } } diff --git a/src/Ocelot/Requester/RequestTimedOutError.cs b/src/Ocelot/Requester/RequestTimedOutError.cs new file mode 100644 index 00000000..38afce1a --- /dev/null +++ b/src/Ocelot/Requester/RequestTimedOutError.cs @@ -0,0 +1,13 @@ +using System; +using Ocelot.Errors; + +namespace Ocelot.Requester +{ + public class RequestTimedOutError : Error + { + public RequestTimedOutError(Exception exception) + : base($"Timeout making http request, exception: {exception.Message}", OcelotErrorCode.RequestTimedOutError) + { + } + } +} diff --git a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs index 6ecc20eb..b9fcb299 100644 --- a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs +++ b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs @@ -22,6 +22,11 @@ namespace Ocelot.Responder return new OkResponse(403); } + if (errors.Any(e => e.Code == OcelotErrorCode.RequestTimedOutError)) + { + return new OkResponse(408); + } + return new OkResponse(404); } } diff --git a/src/Ocelot/Responses/ErrorResponseGeneric.cs b/src/Ocelot/Responses/ErrorResponseGeneric.cs index 77230503..044d6f61 100644 --- a/src/Ocelot/Responses/ErrorResponseGeneric.cs +++ b/src/Ocelot/Responses/ErrorResponseGeneric.cs @@ -5,7 +5,13 @@ namespace Ocelot.Responses { public class ErrorResponse : Response { - public ErrorResponse(List errors) : base(errors) + public ErrorResponse(Error error) + : base(new List {error}) + { + + } + public ErrorResponse(List errors) + : base(errors) { } } diff --git a/test/Ocelot.AcceptanceTests/QoSTests.cs b/test/Ocelot.AcceptanceTests/QoSTests.cs new file mode 100644 index 00000000..50c1fb66 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/QoSTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration.File; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class QoSTests : IDisposable + { + private IWebHost _builder; + private readonly Steps _steps; + private int _requestCount; + + public QoSTests() + { + _steps = new Steps(); + } + + + [Fact] + public void should_open_circuit_breaker_then_close() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = "Get", + QoSOptions = new FileQoSOptions + { + ExceptionsAllowedBeforeBreaking = 1, + TimeoutValue = 500, + DurationOfBreak = 1000 + } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "Hello from Laura")) + .Given(x => _steps.GivenThereIsAConfiguration(configuration)) + .Given(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) + .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) + .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) + .Given(x => x.GivenIWaitMilliSeconds(2000)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void GivenIWaitMilliSeconds(int ms) + { + Thread.Sleep(ms); + } + + private void GivenThereIsAServiceRunningOn(string url, string responseBody) + { + _builder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + //circuit starts closed + if (_requestCount == 0) + { + _requestCount++; + context.Response.StatusCode = 200; + await context.Response.WriteAsync(responseBody); + return; + } + + //request one times out and polly throws exception + if (_requestCount == 1) + { + _requestCount++; + await Task.Delay(1000); + context.Response.StatusCode = 200; + return; + } + + //request two times out and polly throws exception circuit opens + if (_requestCount == 2) + { + _requestCount++; + await Task.Delay(1000); + context.Response.StatusCode = 200; + return; + } + + //after break closes we return 200 OK + if (_requestCount == 3) + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync(responseBody); + return; + } + }); + }) + .Build(); + + _builder.Start(); + } + + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs index cb8198dc..d2b7e386 100644 --- a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs +++ b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Ocelot.Errors; using Ocelot.Middleware; +using Ocelot.Requester; using Ocelot.Responder; using Ocelot.Responses; using Shouldly; @@ -20,6 +22,18 @@ namespace Ocelot.UnitTests.Responder _codeMapper = new ErrorsToHttpStatusCodeMapper(); } + [Fact] + public void should_return_timeout() + { + this.Given(x => x.GivenThereAreErrors(new List + { + new RequestTimedOutError(new Exception()) + })) + .When(x => x.WhenIGetErrorStatusCode()) + .Then(x => x.ThenTheResponseIsStatusCodeIs(408)) + .BDDfy(); + } + [Fact] public void should_create_unauthenticated_response_code() { From 286c7f848813d58d4cc7ca3ee386c8e1af5a29d9 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 11 Feb 2017 18:56:36 +0000 Subject: [PATCH 16/16] Fixed issue where qos was being created for each request so circuit breaker was never stopping traffic going to downstream service. --- .../Builder/QoSOptionsBuilder.cs | 34 ++++ .../Creator/FileOcelotConfigurationCreator.cs | 58 +++++-- src/Ocelot/Configuration/QoSOptions.cs | 7 +- src/Ocelot/Configuration/ReRoute.cs | 6 +- .../ServiceCollectionExtensions.cs | 3 + .../Finder/DownstreamRouteFinder.cs | 7 + src/Ocelot/Errors/OcelotErrorCode.cs | 3 +- .../Middleware/LoadBalancingMiddleware.cs | 3 +- .../Request/Builder/HttpRequestCreator.cs | 5 +- src/Ocelot/Request/Builder/IRequestCreator.cs | 4 +- src/Ocelot/Request/Builder/RequestBuilder.cs | 10 +- .../HttpRequestBuilderMiddleware.cs | 33 +++- src/Ocelot/Request/Request.cs | 11 +- .../CircuitBreakingDelegatingHandler.cs | 77 ---------- src/Ocelot/Requester/HttpClientBuilder.cs | 26 ++-- .../Requester/HttpClientHttpRequester.cs | 21 ++- .../PollyCircuitBreakingDelegatingHandler.cs | 47 ++++++ .../Requester/QoS/IQoSProviderFactory.cs | 145 ++++++++++++++++++ .../QoS/UnableToFindQoSProviderError.cs | 16 ++ .../Responder/ErrorsToHttpStatusCodeMapper.cs | 2 +- test/Ocelot.AcceptanceTests/QoSTests.cs | 116 +++++++++++--- .../FileConfigurationCreatorTests.cs | 62 +++++++- .../DownstreamRouteFinderTests.cs | 41 ++++- .../UrlMatcher/RegExUrlMatcherTests.cs | 20 +++ .../HttpRequestBuilderMiddlewareTests.cs | 17 +- .../Request/RequestBuilderTests.cs | 27 ++-- .../Requester/HttpRequesterMiddlewareTests.cs | 3 +- .../Requester/QoSProviderFactoryTests.cs | 80 ++++++++++ .../Requester/QosProviderHouseTests.cs | 117 ++++++++++++++ .../ErrorsToHttpStatusCodeMapperTests.cs | 2 +- 30 files changed, 819 insertions(+), 184 deletions(-) create mode 100644 src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs delete mode 100644 src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs create mode 100644 src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs create mode 100644 src/Ocelot/Requester/QoS/IQoSProviderFactory.cs create mode 100644 src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs create mode 100644 test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs create mode 100644 test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs diff --git a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs new file mode 100644 index 00000000..8d953d5c --- /dev/null +++ b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs @@ -0,0 +1,34 @@ +namespace Ocelot.Configuration.Builder +{ + public class QoSOptionsBuilder + { + private int _exceptionsAllowedBeforeBreaking; + + private int _durationOfBreak; + + private int _timeoutValue; + + public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking) + { + _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; + return this; + } + + public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak) + { + _durationOfBreak = durationOfBreak; + return this; + } + + public QoSOptionsBuilder WithTimeoutValue(int timeoutValue) + { + _timeoutValue = timeoutValue; + return this; + } + + public QoSOptions Build() + { + return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 25888681..380e193f 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Net.Http; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -10,9 +9,9 @@ using Ocelot.Configuration.File; using Ocelot.Configuration.Parser; using Ocelot.Configuration.Validator; using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.Requester.QoS; using Ocelot.Responses; using Ocelot.Utilities; -using Ocelot.Values; namespace Ocelot.Configuration.Creator { @@ -26,11 +25,14 @@ namespace Ocelot.Configuration.Creator private const string RegExMatchEverything = ".*"; private const string RegExMatchEndString = "$"; private const string RegExIgnoreCase = "(?i)"; + private const string RegExForwardSlashOnly = "^/$"; private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser; private readonly ILogger _logger; private readonly ILoadBalancerFactory _loadBalanceFactory; private readonly ILoadBalancerHouse _loadBalancerHouse; + private readonly IQoSProviderFactory _qoSProviderFactory; + private readonly IQosProviderHouse _qosProviderHouse; public FileOcelotConfigurationCreator( IOptions options, @@ -38,10 +40,14 @@ namespace Ocelot.Configuration.Creator IClaimToThingConfigurationParser claimToThingConfigurationParser, ILogger logger, ILoadBalancerFactory loadBalancerFactory, - ILoadBalancerHouse loadBalancerHouse) + ILoadBalancerHouse loadBalancerHouse, + IQoSProviderFactory qoSProviderFactory, + IQosProviderHouse qosProviderHouse) { _loadBalanceFactory = loadBalancerFactory; _loadBalancerHouse = loadBalancerHouse; + _qoSProviderFactory = qoSProviderFactory; + _qosProviderHouse = qosProviderHouse; _options = options; _configurationValidator = configurationValidator; _claimToThingConfigurationParser = claimToThingConfigurationParser; @@ -86,17 +92,17 @@ namespace Ocelot.Configuration.Creator { var isAuthenticated = IsAuthenticated(fileReRoute); - var isAuthorised = IsAuthenticated(fileReRoute); + var isAuthorised = IsAuthorised(fileReRoute); var isCached = IsCached(fileReRoute); var requestIdKey = BuildRequestId(fileReRoute, globalConfiguration); - var loadBalancerKey = BuildLoadBalancerKey(fileReRoute); + var reRouteKey = BuildReRouteKey(fileReRoute); - var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute); + var upstreamTemplatePattern = BuildUpstreamTemplatePattern(fileReRoute); - var isQos = fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions.TimeoutValue >0; + var isQos = IsQoS(fileReRoute); var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration); @@ -108,6 +114,8 @@ namespace Ocelot.Configuration.Creator var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest); + var qosOptions = BuildQoSOptions(fileReRoute); + var reRoute = new ReRouteBuilder() .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) @@ -127,16 +135,31 @@ namespace Ocelot.Configuration.Creator .WithLoadBalancer(fileReRoute.LoadBalancer) .WithDownstreamHost(fileReRoute.DownstreamHost) .WithDownstreamPort(fileReRoute.DownstreamPort) - .WithLoadBalancerKey(loadBalancerKey) + .WithLoadBalancerKey(reRouteKey) .WithServiceProviderConfiguraion(serviceProviderConfiguration) .WithIsQos(isQos) - .WithQosOptions(new QoSOptions(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking, fileReRoute.QoSOptions.DurationOfBreak, fileReRoute.QoSOptions.TimeoutValue)) + .WithQosOptions(qosOptions) .Build(); await SetupLoadBalancer(reRoute); + SetupQosProvider(reRoute); return reRoute; } + private QoSOptions BuildQoSOptions(FileReRoute fileReRoute) + { + return new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking) + .WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak) + .WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue) + .Build(); + } + + private bool IsQoS(FileReRoute fileReRoute) + { + return fileReRoute.QoSOptions?.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions?.TimeoutValue > 0; + } + private bool IsAuthenticated(FileReRoute fileReRoute) { return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); @@ -163,7 +186,7 @@ namespace Ocelot.Configuration.Creator return requestIdKey; } - private string BuildLoadBalancerKey(FileReRoute fileReRoute) + private string BuildReRouteKey(FileReRoute fileReRoute) { //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; @@ -185,7 +208,13 @@ namespace Ocelot.Configuration.Creator private async Task SetupLoadBalancer(ReRoute reRoute) { var loadBalancer = await _loadBalanceFactory.Get(reRoute); - _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer); + _loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer); + } + + private void SetupQosProvider(ReRoute reRoute) + { + var loadBalancer = _qoSProviderFactory.Get(reRoute); + _qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer); } private ServiceProviderConfiguraion BuildServiceProviderConfiguration(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) @@ -206,7 +235,7 @@ namespace Ocelot.Configuration.Creator .Build(); } - private string BuildUpstreamTemplate(FileReRoute reRoute) + private string BuildUpstreamTemplatePattern(FileReRoute reRoute) { var upstreamTemplate = reRoute.UpstreamPathTemplate; @@ -230,6 +259,11 @@ namespace Ocelot.Configuration.Creator upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything); } + if (upstreamTemplate == "/") + { + return RegExForwardSlashOnly; + } + var route = reRoute.ReRouteIsCaseSensitive ? $"{upstreamTemplate}{RegExMatchEndString}" : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; diff --git a/src/Ocelot/Configuration/QoSOptions.cs b/src/Ocelot/Configuration/QoSOptions.cs index 9deaaeda..29145888 100644 --- a/src/Ocelot/Configuration/QoSOptions.cs +++ b/src/Ocelot/Configuration/QoSOptions.cs @@ -1,8 +1,5 @@ -using Polly.Timeout; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System; +using Polly.Timeout; namespace Ocelot.Configuration { diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 45b31d38..ab9405d0 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -25,12 +25,12 @@ namespace Ocelot.Configuration string loadBalancer, string downstreamHost, int downstreamPort, - string loadBalancerKey, + string reRouteKey, ServiceProviderConfiguraion serviceProviderConfiguraion, bool isQos, QoSOptions qos) { - LoadBalancerKey = loadBalancerKey; + ReRouteKey = reRouteKey; ServiceProviderConfiguraion = serviceProviderConfiguraion; LoadBalancer = loadBalancer; DownstreamHost = downstreamHost; @@ -57,7 +57,7 @@ namespace Ocelot.Configuration QosOptions = qos; } - public string LoadBalancerKey {get;private set;} + public string ReRouteKey {get;private set;} public PathTemplate DownstreamPathTemplate { get; private set; } public PathTemplate UpstreamPathTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 0a6bd42c..e9be9841 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ using Ocelot.Logging; using Ocelot.QueryStrings; using Ocelot.Request.Builder; using Ocelot.Requester; +using Ocelot.Requester.QoS; using Ocelot.Responder; using Ocelot.ServiceDiscovery; @@ -61,6 +62,8 @@ namespace Ocelot.DependencyInjection { services.AddMvcCore().AddJsonFormatters(); services.AddLogging(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index e504a430..8b6447c7 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -30,6 +30,13 @@ namespace Ocelot.DownstreamRouteFinder.Finder foreach (var reRoute in applicableReRoutes) { + if (upstreamUrlPath == reRoute.UpstreamTemplatePattern) + { + var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value); + + return new OkResponse(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute)); + } + var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern); if (urlMatch.Data.Match) diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index e2fe4ada..373ac441 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -26,6 +26,7 @@ ServicesAreEmptyError, UnableToFindServiceDiscoveryProviderError, UnableToFindLoadBalancerError, - RequestTimedOutError + RequestTimedOutError, + UnableToFindQoSProviderError } } diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index ce37f828..8e26cbf2 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -6,7 +6,6 @@ using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.QueryStrings.Middleware; -using Ocelot.ServiceDiscovery; namespace Ocelot.LoadBalancer.Middleware { @@ -31,7 +30,7 @@ namespace Ocelot.LoadBalancer.Middleware { _logger.LogDebug("started calling load balancing middleware"); - var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.LoadBalancerKey); + var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.ReRouteKey); if(loadBalancer.IsError) { SetPipelineError(loadBalancer.Errors); diff --git a/src/Ocelot/Request/Builder/HttpRequestCreator.cs b/src/Ocelot/Request/Builder/HttpRequestCreator.cs index f326933c..3264db59 100644 --- a/src/Ocelot/Request/Builder/HttpRequestCreator.cs +++ b/src/Ocelot/Request/Builder/HttpRequestCreator.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Ocelot.Responses; using Ocelot.Configuration; +using Ocelot.Requester.QoS; namespace Ocelot.Request.Builder { @@ -18,7 +19,7 @@ namespace Ocelot.Request.Builder string contentType, RequestId.RequestId requestId, bool isQos, - QoSOptions qos) + IQoSProvider qosProvider) { var request = await new RequestBuilder() .WithHttpMethod(httpMethod) @@ -30,7 +31,7 @@ namespace Ocelot.Request.Builder .WithRequestId(requestId) .WithCookies(cookies) .WithIsQos(isQos) - .WithQos(qos) + .WithQos(qosProvider) .Build(); return new OkResponse(request); diff --git a/src/Ocelot/Request/Builder/IRequestCreator.cs b/src/Ocelot/Request/Builder/IRequestCreator.cs index 3417d933..7db999b1 100644 --- a/src/Ocelot/Request/Builder/IRequestCreator.cs +++ b/src/Ocelot/Request/Builder/IRequestCreator.cs @@ -1,8 +1,8 @@ using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Ocelot.Requester.QoS; using Ocelot.Responses; -using Ocelot.Configuration; namespace Ocelot.Request.Builder { @@ -17,6 +17,6 @@ namespace Ocelot.Request.Builder string contentType, RequestId.RequestId requestId, bool isQos, - QoSOptions qos); + IQoSProvider qosProvider); } } diff --git a/src/Ocelot/Request/Builder/RequestBuilder.cs b/src/Ocelot/Request/Builder/RequestBuilder.cs index e0201bc8..ea6511ce 100644 --- a/src/Ocelot/Request/Builder/RequestBuilder.cs +++ b/src/Ocelot/Request/Builder/RequestBuilder.cs @@ -8,7 +8,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; -using Ocelot.Configuration; +using Ocelot.Requester.QoS; namespace Ocelot.Request.Builder { @@ -24,7 +24,7 @@ namespace Ocelot.Request.Builder private IRequestCookieCollection _cookies; private readonly string[] _unsupportedHeaders = {"host"}; private bool _isQos; - private QoSOptions _qos; + private IQoSProvider _qoSProvider; public RequestBuilder WithHttpMethod(string httpMethod) { @@ -80,9 +80,9 @@ namespace Ocelot.Request.Builder return this; } - public RequestBuilder WithQos(QoSOptions qos) + public RequestBuilder WithQos(IQoSProvider qoSProvider) { - _qos = qos; + _qoSProvider = qoSProvider; return this; } @@ -105,7 +105,7 @@ namespace Ocelot.Request.Builder var cookieContainer = CreateCookieContainer(uri); - return new Request(httpRequestMessage, cookieContainer,_isQos,_qos); + return new Request(httpRequestMessage, cookieContainer,_isQos, _qoSProvider); } private Uri CreateUri() diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs index aafa2f3d..991e84da 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs @@ -1,10 +1,10 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.Request.Builder; +using Ocelot.Requester.QoS; namespace Ocelot.Request.Middleware { @@ -13,15 +13,18 @@ namespace Ocelot.Request.Middleware private readonly RequestDelegate _next; private readonly IRequestCreator _requestCreator; private readonly IOcelotLogger _logger; + private readonly IQosProviderHouse _qosProviderHouse; public HttpRequestBuilderMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, IRequestScopedDataRepository requestScopedDataRepository, - IRequestCreator requestCreator) + IRequestCreator requestCreator, + IQosProviderHouse qosProviderHouse) :base(requestScopedDataRepository) { _next = next; _requestCreator = requestCreator; + _qosProviderHouse = qosProviderHouse; _logger = loggerFactory.CreateLogger(); } @@ -29,17 +32,35 @@ namespace Ocelot.Request.Middleware { _logger.LogDebug("started calling request builder middleware"); + var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute.ReRouteKey); + + if (qosProvider.IsError) + { + _logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error"); + + SetPipelineError(qosProvider.Errors); + + return; + } + var buildResult = await _requestCreator - .Build(context.Request.Method, DownstreamUrl, context.Request.Body, - context.Request.Headers, context.Request.Cookies, context.Request.QueryString, - context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier), - DownstreamRoute.ReRoute.IsQos,DownstreamRoute.ReRoute.QosOptions); + .Build(context.Request.Method, + DownstreamUrl, + context.Request.Body, + context.Request.Headers, + context.Request.Cookies, + context.Request.QueryString, + context.Request.ContentType, + new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier), + DownstreamRoute.ReRoute.IsQos, + qosProvider.Data); if (buildResult.IsError) { _logger.LogDebug("IRequestCreator returned an error, setting pipeline error"); SetPipelineError(buildResult.Errors); + return; } _logger.LogDebug("setting upstream request"); diff --git a/src/Ocelot/Request/Request.cs b/src/Ocelot/Request/Request.cs index a1e62834..680ab757 100644 --- a/src/Ocelot/Request/Request.cs +++ b/src/Ocelot/Request/Request.cs @@ -2,22 +2,27 @@ using Ocelot.Values; using System.Net; using System.Net.Http; +using Ocelot.Requester.QoS; namespace Ocelot.Request { public class Request { - public Request(HttpRequestMessage httpRequestMessage, CookieContainer cookieContainer,bool isQos, QoSOptions qos) + public Request( + HttpRequestMessage httpRequestMessage, + CookieContainer cookieContainer, + bool isQos, + IQoSProvider qosProvider) { HttpRequestMessage = httpRequestMessage; CookieContainer = cookieContainer; IsQos = isQos; - Qos = qos; + QosProvider = qosProvider; } public HttpRequestMessage HttpRequestMessage { get; private set; } public CookieContainer CookieContainer { get; private set; } public bool IsQos { get; private set; } - public QoSOptions Qos { get; private set; } + public IQoSProvider QosProvider { get; private set; } } } diff --git a/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs b/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs deleted file mode 100644 index b4091296..00000000 --- a/src/Ocelot/Requester/CircuitBreakingDelegatingHandler.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Ocelot.Logging; -using Polly; -using Polly.CircuitBreaker; -using Polly.Timeout; -using System; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace Ocelot.Requester -{ - public class CircuitBreakingDelegatingHandler : DelegatingHandler - { - private readonly IOcelotLogger _logger; - private readonly int _exceptionsAllowedBeforeBreaking; - private readonly TimeSpan _durationOfBreak; - private readonly Policy _circuitBreakerPolicy; - private readonly TimeoutPolicy _timeoutPolicy; - - public CircuitBreakingDelegatingHandler( - int exceptionsAllowedBeforeBreaking, - TimeSpan durationOfBreak, - TimeSpan timeoutValue, - TimeoutStrategy timeoutStrategy, - IOcelotLogger logger, - HttpMessageHandler innerHandler) - : base(innerHandler) - { - this._exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; - - this._durationOfBreak = durationOfBreak; - - _circuitBreakerPolicy = Policy - .Handle() - .Or() - .Or() - .CircuitBreakerAsync( - exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking, - durationOfBreak: durationOfBreak, - onBreak: (ex, breakDelay) => - { - _logger.LogError(".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex); - }, - onReset: () => _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."), - onHalfOpen: () => _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.") - ); - - _timeoutPolicy = Policy.TimeoutAsync(timeoutValue, timeoutStrategy); - - _logger = logger; - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - try - { - return await Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync(() => base.SendAsync(request,cancellationToken)); - } - catch (BrokenCircuitException ex) - { - _logger.LogError($"Reached to allowed number of exceptions. Circuit is open. AllowedExceptionCount: {_exceptionsAllowedBeforeBreaking}, DurationOfBreak: {_durationOfBreak}",ex); - throw; - } - catch (HttpRequestException ex) - { - _logger.LogError($"Error in CircuitBreakingDelegatingHandler.SendAync", ex); - throw; - } - } - - private static bool IsTransientFailure(HttpResponseMessage result) - { - return result.StatusCode >= HttpStatusCode.InternalServerError; - } - } -} diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index c3c98ac0..da37ee02 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -1,35 +1,39 @@ -using Ocelot.Configuration; -using Ocelot.Logging; -using Ocelot.Values; -using Polly.Timeout; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Threading.Tasks; +using Ocelot.Logging; +using Ocelot.Requester.QoS; namespace Ocelot.Requester { internal class HttpClientBuilder { - private readonly Dictionary> handlers = new Dictionary>(); + private readonly Dictionary> _handlers = new Dictionary>(); - public HttpClientBuilder WithCircuitBreaker(QoSOptions qos, IOcelotLogger logger, HttpMessageHandler innerHandler) + public HttpClientBuilder WithQoS(IQoSProvider qoSProvider, IOcelotLogger logger, HttpMessageHandler innerHandler) { - handlers.Add(5000, () => new CircuitBreakingDelegatingHandler(qos.ExceptionsAllowedBeforeBreaking, qos.DurationOfBreak, qos.TimeoutValue, qos.TimeoutStrategy, logger, innerHandler)); + _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qoSProvider, logger, innerHandler)); return this; } internal HttpClient Build(HttpMessageHandler innerHandler) { - return handlers.Any() ? new HttpClient(CreateHttpMessageHandler()) : new HttpClient(innerHandler); + return _handlers.Any() ? + new HttpClient(CreateHttpMessageHandler()) : + new HttpClient(innerHandler); } private HttpMessageHandler CreateHttpMessageHandler() { HttpMessageHandler httpMessageHandler = new HttpClientHandler(); - handlers.OrderByDescending(handler => handler.Key).Select(handler => handler.Value).Reverse().ToList().ForEach(handler => + _handlers + .OrderByDescending(handler => handler.Key) + .Select(handler => handler.Value) + .Reverse() + .ToList() + .ForEach(handler => { var delegatingHandler = handler(); delegatingHandler.InnerHandler = httpMessageHandler; diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index cc5332f3..5a2c86c7 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -1,17 +1,17 @@ using System; -using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; -using Ocelot.Errors; -using Ocelot.Responses; using Ocelot.Logging; +using Ocelot.Responses; +using Polly.CircuitBreaker; +using Polly.Timeout; namespace Ocelot.Requester { public class HttpClientHttpRequester : IHttpRequester { private readonly IOcelotLogger _logger; - + public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); @@ -19,13 +19,13 @@ namespace Ocelot.Requester public async Task> GetResponse(Request.Request request) { - HttpClientBuilder builder = new HttpClientBuilder(); + var builder = new HttpClientBuilder(); using (var handler = new HttpClientHandler { CookieContainer = request.CookieContainer }) { if (request.IsQos) { - builder.WithCircuitBreaker(request.Qos, _logger, handler); + builder.WithQoS(request.QosProvider, _logger, handler); } using (var httpClient = builder.Build(handler)) @@ -35,10 +35,15 @@ namespace Ocelot.Requester var response = await httpClient.SendAsync(request.HttpRequestMessage); return new OkResponse(response); } - catch (Polly.Timeout.TimeoutRejectedException exception) + catch (TimeoutRejectedException exception) { return - new ErrorResponse(new RequestTimedOutError(exception)); + new ErrorResponse(new RequestTimedOutError(exception)); + } + catch (BrokenCircuitException exception) + { + return + new ErrorResponse(new RequestTimedOutError(exception)); } catch (Exception exception) { diff --git a/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs b/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs new file mode 100644 index 00000000..a50ec91b --- /dev/null +++ b/src/Ocelot/Requester/PollyCircuitBreakingDelegatingHandler.cs @@ -0,0 +1,47 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Ocelot.Logging; +using Ocelot.Requester.QoS; +using Polly; +using Polly.CircuitBreaker; +using Polly.Timeout; + +namespace Ocelot.Requester +{ + public class PollyCircuitBreakingDelegatingHandler : DelegatingHandler + { + private readonly IQoSProvider _qoSProvider; + private readonly IOcelotLogger _logger; + + public PollyCircuitBreakingDelegatingHandler( + IQoSProvider qoSProvider, + IOcelotLogger logger, + HttpMessageHandler innerHandler) + : base(innerHandler) + { + _qoSProvider = qoSProvider; + _logger = logger; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { + return await Policy + .WrapAsync(_qoSProvider.CircuitBreaker.CircuitBreakerPolicy, _qoSProvider.CircuitBreaker.TimeoutPolicy) + .ExecuteAsync(() => base.SendAsync(request,cancellationToken)); + } + catch (BrokenCircuitException ex) + { + _logger.LogError($"Reached to allowed number of exceptions. Circuit is open",ex); + throw; + } + catch (HttpRequestException ex) + { + _logger.LogError($"Error in CircuitBreakingDelegatingHandler.SendAync", ex); + throw; + } + } + } +} diff --git a/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs b/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs new file mode 100644 index 00000000..d1f63ea1 --- /dev/null +++ b/src/Ocelot/Requester/QoS/IQoSProviderFactory.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Ocelot.Configuration; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.Logging; +using Ocelot.Responses; +using Polly; +using Polly.CircuitBreaker; +using Polly.Timeout; + +namespace Ocelot.Requester.QoS +{ + public interface IQoSProviderFactory + { + IQoSProvider Get(ReRoute reRoute); + } + + public class QoSProviderFactory : IQoSProviderFactory + { + private readonly IOcelotLoggerFactory _loggerFactory; + + public QoSProviderFactory(IOcelotLoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + + public IQoSProvider Get(ReRoute reRoute) + { + if (reRoute.IsQos) + { + return new PollyQoSProvider(reRoute, _loggerFactory); + } + + return new NoQoSProvider(); + } + } + + public interface IQoSProvider + { + CircuitBreaker CircuitBreaker { get; } + } + + public class NoQoSProvider : IQoSProvider + { + public CircuitBreaker CircuitBreaker { get; } + } + + public class PollyQoSProvider : IQoSProvider + { + private readonly CircuitBreakerPolicy _circuitBreakerPolicy; + private readonly TimeoutPolicy _timeoutPolicy; + private readonly IOcelotLogger _logger; + private readonly CircuitBreaker _circuitBreaker; + + public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + + _timeoutPolicy = Policy.TimeoutAsync(reRoute.QosOptions.TimeoutValue, reRoute.QosOptions.TimeoutStrategy); + + _circuitBreakerPolicy = Policy + .Handle() + .Or() + .Or() + .CircuitBreakerAsync( + exceptionsAllowedBeforeBreaking: reRoute.QosOptions.ExceptionsAllowedBeforeBreaking, + durationOfBreak: reRoute.QosOptions.DurationOfBreak, + onBreak: (ex, breakDelay) => + { + _logger.LogError( + ".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex); + }, + onReset: () => + { + _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."); + }, + onHalfOpen: () => + { + _logger.LogDebug(".Breaker logging: Half-open; next call is a trial."); + } + ); + + _circuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy); + } + + public CircuitBreaker CircuitBreaker => _circuitBreaker; + } + + public class CircuitBreaker + { + public CircuitBreaker(CircuitBreakerPolicy circuitBreakerPolicy, TimeoutPolicy timeoutPolicy) + { + CircuitBreakerPolicy = circuitBreakerPolicy; + TimeoutPolicy = timeoutPolicy; + } + + public CircuitBreakerPolicy CircuitBreakerPolicy { get; private set; } + public TimeoutPolicy TimeoutPolicy { get; private set; } + } + + + public interface IQosProviderHouse + { + Response Get(string key); + Response Add(string key, IQoSProvider loadBalancer); + } + + public class QosProviderHouse : IQosProviderHouse + { + private readonly Dictionary _qoSProviders; + + public QosProviderHouse() + { + _qoSProviders = new Dictionary(); + } + + public Response Get(string key) + { + IQoSProvider qoSProvider; + + if (_qoSProviders.TryGetValue(key, out qoSProvider)) + { + return new OkResponse(_qoSProviders[key]); + } + + return new ErrorResponse(new List() + { + new UnableToFindQoSProviderError($"unabe to find qos provider for {key}") + }); + } + + public Response Add(string key, IQoSProvider loadBalancer) + { + if (!_qoSProviders.ContainsKey(key)) + { + _qoSProviders.Add(key, loadBalancer); + } + + _qoSProviders.Remove(key); + _qoSProviders.Add(key, loadBalancer); + return new OkResponse(); + } + } +} diff --git a/src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs b/src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs new file mode 100644 index 00000000..46ec65f3 --- /dev/null +++ b/src/Ocelot/Requester/QoS/UnableToFindQoSProviderError.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ocelot.Errors; + +namespace Ocelot.Requester.QoS +{ + public class UnableToFindQoSProviderError : Error + { + public UnableToFindQoSProviderError(string message) + : base(message, OcelotErrorCode.UnableToFindQoSProviderError) + { + } + } +} diff --git a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs index b9fcb299..45aeafd1 100644 --- a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs +++ b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs @@ -24,7 +24,7 @@ namespace Ocelot.Responder if (errors.Any(e => e.Code == OcelotErrorCode.RequestTimedOutError)) { - return new OkResponse(408); + return new OkResponse(503); } return new OkResponse(404); diff --git a/test/Ocelot.AcceptanceTests/QoSTests.cs b/test/Ocelot.AcceptanceTests/QoSTests.cs index 50c1fb66..0eb3d8a4 100644 --- a/test/Ocelot.AcceptanceTests/QoSTests.cs +++ b/test/Ocelot.AcceptanceTests/QoSTests.cs @@ -15,16 +15,16 @@ namespace Ocelot.AcceptanceTests { public class QoSTests : IDisposable { - private IWebHost _builder; + private IWebHost _brokenService; private readonly Steps _steps; private int _requestCount; + private IWebHost _workingService; public QoSTests() { _steps = new Steps(); } - [Fact] public void should_open_circuit_breaker_then_close() { @@ -50,33 +50,90 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "Hello from Laura")) + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura")) .Given(x => _steps.GivenThereIsAConfiguration(configuration)) .Given(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.RequestTimeout)) - .Given(x => x.GivenIWaitMilliSeconds(2000)) + .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) + .Given(x => x.GivenIWaitMilliseconds(3000)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } - private void GivenIWaitMilliSeconds(int ms) + [Fact] + public void open_circuit_should_not_effect_different_reRoute() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = "Get", + QoSOptions = new FileQoSOptions + { + ExceptionsAllowedBeforeBreaking = 1, + TimeoutValue = 500, + DurationOfBreak = 1000 + } + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51880, + UpstreamPathTemplate = "working", + UpstreamHttpMethod = "Get", + } + } + }; + + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/working")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom")) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable)) + .And(x => x.GivenIWaitMilliseconds(3000)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void GivenIWaitMilliseconds(int ms) { Thread.Sleep(ms); } - private void GivenThereIsAServiceRunningOn(string url, string responseBody) + private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody) { - _builder = new WebHostBuilder() + _brokenService = new WebHostBuilder() .UseUrls(url) .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) @@ -95,7 +152,7 @@ namespace Ocelot.AcceptanceTests return; } - //request one times out and polly throws exception + //request one times out and polly throws exception, circuit opens if (_requestCount == 1) { _requestCount++; @@ -104,17 +161,8 @@ namespace Ocelot.AcceptanceTests return; } - //request two times out and polly throws exception circuit opens - if (_requestCount == 2) - { - _requestCount++; - await Task.Delay(1000); - context.Response.StatusCode = 200; - return; - } - //after break closes we return 200 OK - if (_requestCount == 3) + if (_requestCount == 2) { context.Response.StatusCode = 200; await context.Response.WriteAsync(responseBody); @@ -124,12 +172,34 @@ namespace Ocelot.AcceptanceTests }) .Build(); - _builder.Start(); + _brokenService.Start(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) + { + _workingService = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _workingService.Start(); } public void Dispose() { - _builder?.Dispose(); + _workingService?.Dispose(); + _brokenService?.Dispose(); _steps.Dispose(); } } diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 5080c9e0..fa1bbda3 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -9,6 +9,7 @@ using Ocelot.Configuration.File; using Ocelot.Configuration.Parser; using Ocelot.Configuration.Validator; using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.Requester.QoS; using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; @@ -28,9 +29,15 @@ namespace Ocelot.UnitTests.Configuration private readonly Mock _loadBalancerFactory; private readonly Mock _loadBalancerHouse; private readonly Mock _loadBalancer; + private readonly Mock _qosProviderFactory; + private readonly Mock _qosProviderHouse; + private readonly Mock _qosProvider; public FileConfigurationCreatorTests() { + _qosProviderFactory = new Mock(); + _qosProviderHouse = new Mock(); + _qosProvider = new Mock(); _logger = new Mock>(); _configParser = new Mock(); _validator = new Mock(); @@ -40,7 +47,8 @@ namespace Ocelot.UnitTests.Configuration _loadBalancer = new Mock(); _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object, - _loadBalancerFactory.Object, _loadBalancerHouse.Object); + _loadBalancerFactory.Object, _loadBalancerHouse.Object, + _qosProviderFactory.Object, _qosProviderHouse.Object); } [Fact] @@ -64,10 +72,39 @@ namespace Ocelot.UnitTests.Configuration .When(x => x.WhenICreateTheConfig()) .Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly()) .And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly()) - .BDDfy(); } + [Fact] + public void should_create_qos_provider() + { + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = "Get", + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + DurationOfBreak = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheQosProviderFactoryReturns()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.TheQosProviderFactoryIsCalledCorrectly()) + .And(x => x.ThenTheQosProviderHouseIsCalledCorrectly()) + .BDDfy(); + } + [Fact] public void should_use_downstream_host() { @@ -568,7 +605,7 @@ namespace Ocelot.UnitTests.Configuration .WithDownstreamPathTemplate("/api/products/") .WithUpstreamPathTemplate("/") .WithUpstreamHttpMethod("Get") - .WithUpstreamTemplatePattern("/$") + .WithUpstreamTemplatePattern("^/$") .Build() })) .BDDfy(); @@ -643,5 +680,24 @@ namespace Ocelot.UnitTests.Configuration _loadBalancerHouse .Verify(x => x.Add(It.IsAny(), _loadBalancer.Object), Times.Once); } + + private void GivenTheQosProviderFactoryReturns() + { + _qosProviderFactory + .Setup(x => x.Get(It.IsAny())) + .Returns(_qosProvider.Object); + } + + private void TheQosProviderFactoryIsCalledCorrectly() + { + _qosProviderFactory + .Verify(x => x.Get(It.IsAny()), Times.Once); + } + + private void ThenTheQosProviderHouseIsCalledCorrectly() + { + _qosProviderHouse + .Verify(x => x.Add(It.IsAny(), _qosProvider.Object), Times.Once); + } } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 81fc7022..2fb8f28f 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -35,6 +35,37 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_return_route() + { + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) + .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod("Get") + .WithUpstreamTemplatePattern("someUpstreamPath") + .Build() + })) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod("Get") + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() { this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( @@ -61,7 +92,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithUpstreamHttpMethod("Get") .Build() ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) .BDDfy(); } @@ -105,7 +136,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_not_return_route() { - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("somePath")) + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath")) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() @@ -148,6 +179,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); } + private void ThenTheUrlMatcherIsNotCalled() + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never); + } + private void GivenTheUrlMatcherReturns(Response match) { _match = match; diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs index ea069593..70dd747d 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs @@ -18,6 +18,26 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher _urlMatcher = new RegExUrlMatcher(); } + [Fact] + public void should_not_match_forward_slash_only_regex() + { + this.Given(x => x.GivenIHaveAUpstreamPath("/working/")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$")) + .When(x => x.WhenIMatchThePaths()) + .And(x => x.ThenTheResultIsFalse()) + .BDDfy(); + } + + [Fact] + public void should_match_forward_slash_only_regex() + { + this.Given(x => x.GivenIHaveAUpstreamPath("/")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$")) + .When(x => x.WhenIMatchThePaths()) + .And(x => x.ThenTheResultIsTrue()) + .BDDfy(); + } + [Fact] public void should_find_match_when_template_smaller_than_valid_path() { diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index da5d2031..b674b015 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -20,6 +20,7 @@ using Ocelot.Responses; using TestStack.BDDfy; using Xunit; using Ocelot.Configuration; +using Ocelot.Requester.QoS; namespace Ocelot.UnitTests.Request { @@ -27,6 +28,7 @@ namespace Ocelot.UnitTests.Request { private readonly Mock _requestBuilder; private readonly Mock _scopedRepository; + private readonly Mock _qosProviderHouse; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; @@ -38,6 +40,7 @@ namespace Ocelot.UnitTests.Request public HttpRequestBuilderMiddlewareTests() { _url = "http://localhost:51879"; + _qosProviderHouse = new Mock(); _requestBuilder = new Mock(); _scopedRepository = new Mock(); var builder = new WebHostBuilder() @@ -45,6 +48,7 @@ namespace Ocelot.UnitTests.Request { x.AddSingleton(); x.AddLogging(); + x.AddSingleton(_qosProviderHouse.Object); x.AddSingleton(_requestBuilder.Object); x.AddSingleton(_scopedRepository.Object); }) @@ -72,15 +76,22 @@ namespace Ocelot.UnitTests.Request .WithUpstreamHttpMethod("Get") .Build()); - this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) + .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse(new NoQoSProvider()))) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new QoSOptions(3, 8 ,5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))) + .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new NoQoSProvider()))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .BDDfy(); } + private void GivenTheQosProviderHouseReturns(Response qosProvider) + { + _qosProviderHouse + .Setup(x => x.Get(It.IsAny())) + .Returns(qosProvider); + } + private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { _downstreamRoute = new OkResponse(downstreamRoute); @@ -94,7 +105,7 @@ namespace Ocelot.UnitTests.Request _request = new OkResponse(request); _requestBuilder .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(), It.IsAny())) + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(),It.IsAny(), It.IsAny())) .ReturnsAsync(_request); } diff --git a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs b/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs index c317c8d7..9d1c2ed5 100644 --- a/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs +++ b/test/Ocelot.UnitTests/Request/RequestBuilderTests.cs @@ -11,6 +11,7 @@ using Shouldly; using TestStack.BDDfy; using Xunit; using Ocelot.Configuration; +using Ocelot.Requester.QoS; namespace Ocelot.UnitTests.Request { @@ -27,7 +28,7 @@ namespace Ocelot.UnitTests.Request private Response _result; private Ocelot.RequestId.RequestId _requestId; private bool _isQos; - private QoSOptions _qos; + private IQoSProvider _qoSProvider; public RequestBuilderTests() { @@ -40,7 +41,7 @@ namespace Ocelot.UnitTests.Request { this.Given(x => x.GivenIHaveHttpMethod("GET")) .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x=> x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x=> x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectDownstreamUrlIsUsed("http://www.bbc.co.uk/")) .BDDfy(); @@ -51,7 +52,7 @@ namespace Ocelot.UnitTests.Request { this.Given(x => x.GivenIHaveHttpMethod("POST")) .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) - .And(x => x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectHttpMethodIsUsed(HttpMethod.Post)) @@ -65,7 +66,7 @@ namespace Ocelot.UnitTests.Request .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) .And(x => x.GivenTheContentTypeIs("application/json")) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectContentIsUsed(new StringContent("Hi from Tom"))) @@ -79,7 +80,7 @@ namespace Ocelot.UnitTests.Request .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) .And(x => x.GivenTheContentTypeIs("application/json")) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary @@ -98,7 +99,7 @@ namespace Ocelot.UnitTests.Request .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) .And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom"))) .And(x => x.GivenTheContentTypeIs("application/json; charset=utf-8")) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary @@ -119,7 +120,7 @@ namespace Ocelot.UnitTests.Request { {"ChopSticks", "Bubbles" } })) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary @@ -138,7 +139,7 @@ namespace Ocelot.UnitTests.Request .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary())) .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId))) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary { @@ -157,7 +158,7 @@ namespace Ocelot.UnitTests.Request {"RequestId", "534534gv54gv45g" } })) .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString()))) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary { @@ -177,7 +178,7 @@ namespace Ocelot.UnitTests.Request .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk")) .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary())) .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue))) - .And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))) + .And(x => x.GivenTheQos(true, new NoQoSProvider())) .When(x => x.WhenICreateARequest()) .And(x => x.ThenTheRequestIdIsNotInTheHeaders()) .BDDfy(); @@ -188,10 +189,10 @@ namespace Ocelot.UnitTests.Request _requestId = requestId; } - private void GivenTheQos(bool isQos, QoSOptions qos) + private void GivenTheQos(bool isQos, IQoSProvider qoSProvider) { _isQos = isQos; - _qos = qos; + _qoSProvider = qoSProvider; } [Fact] @@ -304,7 +305,7 @@ namespace Ocelot.UnitTests.Request private void WhenICreateARequest() { _result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers, - _cookies, _query, _contentType, _requestId,_isQos,_qos).Result; + _cookies, _query, _contentType, _requestId,_isQos,_qoSProvider).Result; } diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index d2d62923..37815a64 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -13,6 +13,7 @@ using Ocelot.Logging; using Ocelot.QueryStrings.Middleware; using Ocelot.Requester; using Ocelot.Requester.Middleware; +using Ocelot.Requester.QoS; using Ocelot.Responder; using Ocelot.Responses; using TestStack.BDDfy; @@ -61,7 +62,7 @@ namespace Ocelot.UnitTests.Requester [Fact] public void should_call_scoped_data_repository_correctly() { - this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new Ocelot.Configuration.QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))) + this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new NoQoSProvider()))) .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage())) .And(x => x.GivenTheScopedRepoReturns()) .When(x => x.WhenICallTheMiddleware()) diff --git a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs new file mode 100644 index 00000000..f4beb0f1 --- /dev/null +++ b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs @@ -0,0 +1,80 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Logging; +using Ocelot.Requester.QoS; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Requester +{ + public class QoSProviderFactoryTests + { + private readonly IQoSProviderFactory _factory; + private ReRoute _reRoute; + private IQoSProvider _result; + private Mock _loggerFactory; + private Mock _logger; + + public QoSProviderFactoryTests() + { + _logger = new Mock(); + _loggerFactory = new Mock(); + _loggerFactory + .Setup(x => x.CreateLogger()) + .Returns(_logger.Object); + _factory = new QoSProviderFactory(_loggerFactory.Object); + } + + [Fact] + public void should_return_no_qos_provider() + { + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod("get") + .WithIsQos(false) + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheQoSProvider()) + .Then(x => x.ThenTheQoSProviderIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_polly_qos_provider() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(100) + .WithDurationOfBreak(100) + .WithExceptionsAllowedBeforeBreaking(100) + .Build(); + + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod("get") + .WithIsQos(true) + .WithQosOptions(qosOptions) + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheQoSProvider()) + .Then(x => x.ThenTheQoSProviderIsReturned()) + .BDDfy(); + } + + private void GivenAReRoute(ReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIGetTheQoSProvider() + { + _result = _factory.Get(_reRoute); + } + + private void ThenTheQoSProviderIsReturned() + { + _result.ShouldBeOfType(); + } + } +} diff --git a/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs new file mode 100644 index 00000000..1e76a027 --- /dev/null +++ b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs @@ -0,0 +1,117 @@ +using Ocelot.Requester.QoS; +using Ocelot.Responses; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Requester +{ + public class QosProviderHouseTests + { + private IQoSProvider _qoSProvider; + private readonly QosProviderHouse _qosProviderHouse; + private Response _addResult; + private Response _getResult; + private string _key; + + public QosProviderHouseTests() + { + _qosProviderHouse = new QosProviderHouse(); + } + + [Fact] + public void should_store_qos_provider() + { + var key = "test"; + + this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider())) + .When(x => x.WhenIAddTheQoSProvider()) + .Then(x => x.ThenItIsAdded()) + .BDDfy(); + } + + [Fact] + public void should_get_qos_provider() + { + var key = "test"; + + this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider())) + .When(x => x.WhenWeGetTheQoSProvider(key)) + .Then(x => x.ThenItIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_store_qos_providers_by_key() + { + var key = "test"; + var keyTwo = "testTwo"; + + this.Given(x => x.GivenThereIsAQoSProvider(key, new FakeQoSProvider())) + .And(x => x.GivenThereIsAQoSProvider(keyTwo, new FakePollyQoSProvider())) + .When(x => x.WhenWeGetTheQoSProvider(key)) + .Then(x => x.ThenTheQoSProviderIs()) + .When(x => x.WhenWeGetTheQoSProvider(keyTwo)) + .Then(x => x.ThenTheQoSProviderIs()) + .BDDfy(); + } + + [Fact] + public void should_return_error_if_no_qos_provider_with_key() + { + this.When(x => x.WhenWeGetTheQoSProvider("test")) + .Then(x => x.ThenAnErrorIsReturned()) + .BDDfy(); + } + + private void ThenAnErrorIsReturned() + { + _getResult.IsError.ShouldBeTrue(); + _getResult.Errors[0].ShouldBeOfType(); + } + + private void ThenTheQoSProviderIs() + { + _getResult.Data.ShouldBeOfType(); + } + + private void ThenItIsAdded() + { + _addResult.IsError.ShouldBe(false); + _addResult.ShouldBeOfType(); + } + + private void WhenIAddTheQoSProvider() + { + _addResult = _qosProviderHouse.Add(_key, _qoSProvider); + } + + + private void GivenThereIsAQoSProvider(string key, IQoSProvider qoSProvider) + { + _key = key; + _qoSProvider = qoSProvider; + WhenIAddTheQoSProvider(); + } + + private void WhenWeGetTheQoSProvider(string key) + { + _getResult = _qosProviderHouse.Get(key); + } + + private void ThenItIsReturned() + { + _getResult.Data.ShouldBe(_qoSProvider); + } + + class FakeQoSProvider : IQoSProvider + { + public CircuitBreaker CircuitBreaker { get; } + } + + class FakePollyQoSProvider : IQoSProvider + { + public CircuitBreaker CircuitBreaker { get; } + } + } +} diff --git a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs index d2b7e386..d2ac91e0 100644 --- a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs +++ b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs @@ -30,7 +30,7 @@ namespace Ocelot.UnitTests.Responder new RequestTimedOutError(new Exception()) })) .When(x => x.WhenIGetErrorStatusCode()) - .Then(x => x.ThenTheResponseIsStatusCodeIs(408)) + .Then(x => x.ThenTheResponseIsStatusCodeIs(503)) .BDDfy(); }