From 044b609ea9d08af99fc5baeb5c5ccee6fa54edc8 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Fri, 20 Jan 2017 19:03:18 +0000 Subject: [PATCH 1/3] started implementing service discovery integration --- run-acceptance-tests.bat | 8 + run-tests.bat | 19 +-- run-unit-tests.bat | 8 + .../Configuration/Builder/ReRouteBuilder.cs | 50 ++++++- .../Creator/FileOcelotConfigurationCreator.cs | 15 +- .../File/FileGlobalConfiguration.cs | 5 + src/Ocelot/Configuration/File/FileReRoute.cs | 3 + .../File/FileServiceDiscoveryProvider.cs | 8 + src/Ocelot/Configuration/ReRoute.cs | 18 ++- .../DownstreamTemplateContainsHostError.cs | 12 ++ .../DownstreamTemplateContainsSchemeError.cs | 12 ++ .../Validator/FileConfigurationValidator.cs | 28 ++++ src/Ocelot/Errors/OcelotErrorCode.cs | 4 +- .../ConfigurationValidationTests.cs | 46 +++++- .../FileConfigurationCreatorTests.cs | 137 ++++++++++++++++++ .../FileConfigurationProviderTests.cs | 5 +- 16 files changed, 346 insertions(+), 32 deletions(-) create mode 100755 run-acceptance-tests.bat create mode 100755 run-unit-tests.bat create mode 100644 src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs create mode 100644 src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs create mode 100644 src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs diff --git a/run-acceptance-tests.bat b/run-acceptance-tests.bat new file mode 100755 index 000000000..ba8a3489d --- /dev/null +++ b/run-acceptance-tests.bat @@ -0,0 +1,8 @@ +echo Running Ocelot.AcceptanceTests +cd test/Ocelot.AcceptanceTests/ +dotnet restore +dotnet test +cd ../../ + +echo Restoring Ocelot.ManualTest +dotnet restore test/Ocelot.ManualTest/ \ No newline at end of file diff --git a/run-tests.bat b/run-tests.bat index ae4856fc5..39532229a 100755 --- a/run-tests.bat +++ b/run-tests.bat @@ -1,17 +1,2 @@ -echo ------------------------- - -echo Restoring Ocelot -dotnet restore src/Ocelot - -echo Restoring Ocelot.ManualTest -dotnet restore test/Ocelot.ManualTest/ - -echo Running Ocelot.UnitTests -dotnet restore test/Ocelot.UnitTests/ -dotnet test test/Ocelot.UnitTests/ - -echo Running Ocelot.AcceptanceTests -cd test/Ocelot.AcceptanceTests/ -dotnet restore -dotnet test -cd ../../ \ No newline at end of file +./run-unit-tests.bat +./run-acceptance-tests.bat \ No newline at end of file diff --git a/run-unit-tests.bat b/run-unit-tests.bat new file mode 100755 index 000000000..9ad6a4f2d --- /dev/null +++ b/run-unit-tests.bat @@ -0,0 +1,8 @@ +echo ------------------------- + +echo Restoring Ocelot +dotnet restore src/Ocelot + +echo Running Ocelot.UnitTests +dotnet restore test/Ocelot.UnitTests/ +dotnet test test/Ocelot.UnitTests/ diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index f77ae66b9..3f5ef40b5 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Ocelot.Configuration.Builder { @@ -23,12 +24,54 @@ public class ReRouteBuilder 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; public ReRouteBuilder() { _additionalScopes = new List(); } + public ReRouteBuilder WithDownstreamScheme(string downstreamScheme) + { + _downstreamScheme = downstreamScheme; + return this; + } + + public ReRouteBuilder WithDownstreamHost(string downstreamHost) + { + _downstreamHost = downstreamHost; + 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 WithDownstreamTemplate(string input) { _downstreamTemplate = input; @@ -143,10 +186,13 @@ public ReRouteBuilder WithCacheOptions(CacheOptions input) public ReRoute Build() { + Func downstreamHostFunc = () => { return _downstreamHost; }; + return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, - _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions); + _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName, + _useServiceDiscovery, _serviceDiscoveryAddress, _serviceDiscoveryProvider, downstreamHostFunc, _downstreamScheme); } } } diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index f74f880bf..c71aba23e 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -91,6 +91,13 @@ private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration global ? globalConfiguration.RequestIdKey : reRoute.RequestIdKey; + var useServiceDiscovery = !string.IsNullOrEmpty(reRoute.ServiceName) + && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Address) + && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider); + + + Func downstreamHostFunc = () => { return reRoute.DownstreamHost; }; + if (isAuthenticated) { var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider, @@ -106,14 +113,18 @@ private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration global reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, authOptionsForRoute, claimsToHeaders, claimsToClaims, reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries, - requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds)); + requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds), + reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, + globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostFunc, reRoute.DownstreamScheme); } return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, null, new List(), new List(), reRoute.RouteClaimsRequirement, isAuthorised, new List(), - requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds)); + requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds), + reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, + globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostFunc, reRoute.DownstreamScheme); } private string BuildUpstreamTemplate(FileReRoute reRoute) diff --git a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs index 07c171883..f414bc831 100644 --- a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs +++ b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs @@ -2,6 +2,11 @@ { public class FileGlobalConfiguration { + public FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider(); + } public string RequestIdKey { get; set; } + public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;} } } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 3773dd9d3..3afa03ce3 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -25,5 +25,8 @@ public FileReRoute() public string RequestIdKey { get; set; } public FileCacheOptions FileCacheOptions { get; set; } public bool ReRouteIsCaseSensitive { get; set; } + public string ServiceName { get; set; } + public string DownstreamScheme {get;set;} + public string DownstreamHost {get;set;} } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs new file mode 100644 index 000000000..47efc6dfd --- /dev/null +++ b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs @@ -0,0 +1,8 @@ +namespace Ocelot.Configuration.File +{ + public class FileServiceDiscoveryProvider + { + public string Provider {get;set;} + public string Address {get;set;} + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 433250b3e..8778e5f72 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Ocelot.Configuration { @@ -7,7 +8,8 @@ public class ReRoute public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List configurationHeaderExtractorProperties, List claimsToClaims, Dictionary routeClaimsRequirement, bool isAuthorised, List claimsToQueries, - string requestIdKey, bool isCached, CacheOptions fileCacheOptions) + string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery, + string serviceDiscoveryProvider, string serviceDiscoveryAddress, Func downstreamHost, string downstreamScheme) { DownstreamTemplate = downstreamTemplate; UpstreamTemplate = upstreamTemplate; @@ -26,6 +28,12 @@ public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstre ?? new List(); ClaimsToHeaders = configurationHeaderExtractorProperties ?? new List(); + ServiceName = serviceName; + UseServiceDiscovery = useServiceDiscovery; + ServiceDiscoveryProvider = serviceDiscoveryProvider; + ServiceDiscoveryAddress = serviceDiscoveryAddress; + DownstreamHost = downstreamHost; + DownstreamScheme = downstreamScheme; } public string DownstreamTemplate { get; private set; } @@ -42,5 +50,11 @@ public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstre public string RequestIdKey { get; private set; } public bool IsCached { get; private set; } public CacheOptions FileCacheOptions { get; private set; } + public string ServiceName { get; private set;} + public bool UseServiceDiscovery { get; private set;} + public string ServiceDiscoveryProvider { get; private set;} + public string ServiceDiscoveryAddress { get; private set;} + public Func DownstreamHost {get;private set;} + public string DownstreamScheme {get;private set;} } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs new file mode 100644 index 000000000..8d9dba92a --- /dev/null +++ b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.Configuration.Validator +{ + public class DownstreamTemplateContainsHostError : Error + { + public DownstreamTemplateContainsHostError(string message) + : base(message, OcelotErrorCode.DownstreamTemplateContainsHostError) + { + } + } +} diff --git a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs new file mode 100644 index 000000000..1901fc88d --- /dev/null +++ b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.Configuration.Validator +{ + public class DownstreamTemplateContainsSchemeError : Error + { + public DownstreamTemplateContainsSchemeError(string message) + : base(message, OcelotErrorCode.DownstreamTemplateContainsSchemeError) + { + } + } +} diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs index fb55a10b3..3a675d419 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs @@ -26,6 +26,13 @@ public Response IsValid(FileConfiguration configu return new OkResponse(result); } + result = CheckForReRoutesContainingDownstreamScheme(configuration); + + if (result.IsError) + { + return new OkResponse(result); + } + return new OkResponse(result); } @@ -63,6 +70,27 @@ private bool IsSupportedAuthenticationProvider(string provider) return Enum.TryParse(provider, true, out supportedProvider); } + private ConfigurationValidationResult CheckForReRoutesContainingDownstreamScheme(FileConfiguration configuration) + { + var errors = new List(); + + foreach(var reRoute in configuration.ReRoutes) + { + if(reRoute.DownstreamTemplate.Contains("https://") + || reRoute.DownstreamTemplate.Contains("http://")) + { + errors.Add(new DownstreamTemplateContainsSchemeError($"{reRoute.DownstreamTemplate} contains scheme")); + } + } + + if(errors.Any()) + { + return new ConfigurationValidationResult(false, errors); + } + + return new ConfigurationValidationResult(true, errors); + } + private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration) { var hasDupes = configuration.ReRoutes diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index a4864d1a3..f0a336f03 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -17,6 +17,8 @@ public enum OcelotErrorCode InstructionNotForClaimsError, UnauthorizedError, ClaimValueNotAuthorisedError, - UserDoesNotHaveClaimError + UserDoesNotHaveClaimError, + DownstreamTemplateContainsSchemeError, + DownstreamTemplateContainsHostError } } diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs index 7f6f40056..174ea8bc1 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs @@ -19,6 +19,44 @@ public ConfigurationValidationTests() _configurationValidator = new FileConfigurationValidator(); } + [Fact] + public void configuration_is_invalid_if_scheme_in_downstream_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration() + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamTemplate = "http://www.bbc.co.uk/api/products/{productId}", + UpstreamTemplate = "http://asdf.com" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_host_in_downstream_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration() + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamTemplate = "www.bbc.co.uk/api/products/{productId}", + UpstreamTemplate = "http://asdf.com" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .BDDfy(); + } + [Fact] public void configuration_is_valid_with_one_reroute() { @@ -28,7 +66,7 @@ public void configuration_is_valid_with_one_reroute() { new FileReRoute { - DownstreamTemplate = "http://www.bbc.co.uk", + DownstreamTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com" } } @@ -47,7 +85,7 @@ public void configuration_is_valid_with_valid_authentication_provider() { new FileReRoute { - DownstreamTemplate = "http://www.bbc.co.uk", + DownstreamTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { @@ -70,7 +108,7 @@ public void configuration_is_invalid_with_invalid_authentication_provider() { new FileReRoute { - DownstreamTemplate = "http://www.bbc.co.uk", + DownstreamTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { @@ -94,7 +132,7 @@ public void configuration_is_not_valid_with_duplicate_reroutes() { new FileReRoute { - DownstreamTemplate = "http://www.bbc.co.uk", + DownstreamTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com" }, new FileReRoute diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 4a4a53450..4207a333f 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -35,6 +35,143 @@ public FileConfigurationCreatorTests() _fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object); } + [Fact] + public void should_use_downstream_host() + { + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamTemplate = "/api/products/{productId}", + DownstreamTemplate = "/products/{productId}", + UpstreamHttpMethod = "Get", + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamHost("127.0.0.1") + .WithDownstreamTemplate("/products/{productId}") + .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod("Get") + .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") + .Build() + })) + .BDDfy(); + } + + public void should_use_downstream_scheme() + { + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamScheme = "https", + UpstreamTemplate = "/api/products/{productId}", + DownstreamTemplate = "/products/{productId}", + UpstreamHttpMethod = "Get", + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamScheme("https") + .WithDownstreamTemplate("/products/{productId}") + .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod("Get") + .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_use_service_discovery_for_downstream_service_host() + { + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamTemplate = "/api/products/{productId}", + DownstreamTemplate = "/products/{productId}", + UpstreamHttpMethod = "Get", + ReRouteIsCaseSensitive = false, + ServiceName = "ProductService" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Provider = "consul", + Address = "127.0.0.1" + } + } + })) + .And(x => x.GivenTheConfigIsValid()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamTemplate("/products/{productId}") + .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod("Get") + .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") + .WithServiceName("ProductService") + .WithUseServiceDiscovery(true) + .WithServiceDiscoveryProvider("consul") + .WithServiceDiscoveryAddress("127.0.01") + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name() + { + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamTemplate = "/api/products/{productId}", + DownstreamTemplate = "/products/{productId}", + UpstreamHttpMethod = "Get", + ReRouteIsCaseSensitive = false, + } + } + })) + .And(x => x.GivenTheConfigIsValid()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamTemplate("/products/{productId}") + .WithUpstreamTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod("Get") + .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") + .WithUseServiceDiscovery(false) + .WithServiceDiscoveryProvider(null) + .WithServiceDiscoveryAddress(null) + .Build() + })) + .BDDfy(); + } + [Fact] public void should_use_reroute_case_sensitivity_value() { diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationProviderTests.cs index 1bdb32791..56fb64879 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationProviderTests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Creator; From 0f71c040d9e098d2d495e81384df41a5fdba868c Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 21 Jan 2017 09:59:47 +0000 Subject: [PATCH 2/3] split DownstreamTemplate into DownstreamPathTemplate, DownstreamScheme, DownstreamHost and DownstreamPort in order to prepare for service discovery --- Ocelot.sln | 2 + README.md | 11 +- configuration-explanation.txt | 19 ++- .../Configuration/Builder/ReRouteBuilder.cs | 18 ++- .../Creator/FileOcelotConfigurationCreator.cs | 11 +- src/Ocelot/Configuration/File/FileReRoute.cs | 3 +- src/Ocelot/Configuration/ReRoute.cs | 13 +- .../DownstreamPathTemplateAlreadyUsedError.cs | 11 ++ ...wnstreamPathTemplateContainsSchemeError.cs | 12 ++ .../DownstreamTemplateAlreadyUsedError.cs | 11 -- .../DownstreamTemplateContainsHostError.cs | 12 -- .../DownstreamTemplateContainsSchemeError.cs | 12 -- .../Validator/FileConfigurationValidator.cs | 16 +-- .../ServiceCollectionExtensions.cs | 4 +- .../DownstreamRouteFinderMiddleware.cs | 2 +- .../DownstreamHostNullOrEmptyError.cs | 12 ++ .../DownstreamPathNullOrEmptyError.cs | 12 ++ .../DownstreamSchemeNullOrEmptyError.cs | 12 ++ .../DownstreamUrlCreator/IUrlBuilder.cs | 12 ++ .../DownstreamUrlCreatorMiddleware.cs | 39 ++++-- src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs | 47 +++++++ .../DownstreamUrlTemplateVariableReplacer.cs | 14 +- ...wnstreamUrlPathTemplateVariableReplacer.cs | 5 +- src/Ocelot/Errors/OcelotErrorCode.cs | 8 +- src/Ocelot/Values/DownstreamPath.cs | 12 ++ src/Ocelot/Values/DownstreamPathTemplate.cs | 12 ++ .../DownstreamUrl.cs | 4 +- src/Ocelot/Values/HostAndPort.cs | 14 ++ .../AuthenticationTests.cs | 41 ++++-- .../AuthorisationTests.cs | 10 +- test/Ocelot.AcceptanceTests/CachingTests.cs | 10 +- .../CaseSensitiveRoutingTests.cs | 30 ++++- .../ClaimsToHeadersForwardingTests.cs | 5 +- .../ClaimsToQueryStringForwardingTests.cs | 5 +- .../CustomMiddlewareTests.cs | 30 ++++- test/Ocelot.AcceptanceTests/RequestIdTests.cs | 15 ++- .../ReturnsErrorTests.cs | 2 +- test/Ocelot.AcceptanceTests/RoutingTests.cs | 91 ++++++++++++- .../Ocelot.AcceptanceTests/configuration.json | 2 +- test/Ocelot.ManualTest/Program.cs | 1 - test/Ocelot.ManualTest/configuration.json | 100 +++++++++++--- .../Claims/ClaimsBuilderMiddlewareTests.cs | 2 +- .../ConfigurationValidationTests.cs | 47 ++----- .../FileConfigurationCreatorTests.cs | 58 ++++---- .../InMemoryConfigurationRepositoryTests.cs | 10 +- .../DownstreamRouteFinderMiddlewareTests.cs | 2 +- .../DownstreamRouteFinderTests.cs | 14 +- .../DownstreamUrlCreatorMiddlewareTests.cs | 33 +++-- .../DownstreamUrlCreator/UrlBuilderTests.cs | 124 ++++++++++++++++++ ...eamUrlPathTemplateVariableReplacerTests.cs | 25 ++-- ...ttpRequestHeadersBuilderMiddlewareTests.cs | 2 +- .../QueryStringBuilderMiddlewareTests.cs | 2 +- .../RequestId/RequestIdMiddlewareTests.cs | 4 +- 53 files changed, 767 insertions(+), 258 deletions(-) create mode 100644 src/Ocelot/Configuration/Validator/DownstreamPathTemplateAlreadyUsedError.cs create mode 100644 src/Ocelot/Configuration/Validator/DownstreamPathTemplateContainsSchemeError.cs delete mode 100644 src/Ocelot/Configuration/Validator/DownstreamTemplateAlreadyUsedError.cs delete mode 100644 src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs delete mode 100644 src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs create mode 100644 src/Ocelot/DownstreamUrlCreator/DownstreamHostNullOrEmptyError.cs create mode 100644 src/Ocelot/DownstreamUrlCreator/DownstreamPathNullOrEmptyError.cs create mode 100644 src/Ocelot/DownstreamUrlCreator/DownstreamSchemeNullOrEmptyError.cs create mode 100644 src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs create mode 100644 src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs create mode 100644 src/Ocelot/Values/DownstreamPath.cs create mode 100644 src/Ocelot/Values/DownstreamPathTemplate.cs rename src/Ocelot/{DownstreamUrlCreator/UrlTemplateReplacer => Values}/DownstreamUrl.cs (74%) create mode 100644 src/Ocelot/Values/HostAndPort.cs create mode 100644 test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs diff --git a/Ocelot.sln b/Ocelot.sln index 5ebd0660a..0165beb06 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -17,8 +17,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Ocelot.nuspec = Ocelot.nuspec push-to-nuget.bat = push-to-nuget.bat README.md = README.md + run-acceptance-tests.bat = run-acceptance-tests.bat run-benchmarks.bat = run-benchmarks.bat run-tests.bat = run-tests.bat + run-unit-tests.bat = run-unit-tests.bat EndProjectSection EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot", "src\Ocelot\Ocelot.xproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}" diff --git a/README.md b/README.md index dd5f1043a..249a99a31 100644 --- a/README.md +++ b/README.md @@ -136,17 +136,20 @@ In order to set up a ReRoute you need to add one to the json array called ReRout the following. { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", + "DownstreamPathTemplate": "/api/posts/{postId}", + "DownstreamScheme": "https", + "DownstreamPort": 80, + "DownstreamHost" "localhost" "UpstreamTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Put" } -The DownstreamTemplate is the URL that this request will be forwarded to. +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 -DownstreamTemplate to use for a given request. Finally the UpstreamHttpMethod is used so +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 DownstreamTemplate and UpstreamTemplate. If it is +The placeholder needs to be in both the DownstreamPathTemplate and UpstreamTemplate. If it is Ocelot will attempt to replace the placeholder with the correct variable value from the Upstream URL when the request comes in. diff --git a/configuration-explanation.txt b/configuration-explanation.txt index 898be89fb..ad0204699 100644 --- a/configuration-explanation.txt +++ b/configuration-explanation.txt @@ -1,11 +1,20 @@ { "ReRoutes": [ { - # The url we are forwarding the request to, ocelot will not add a trailing slash - "DownstreamTemplate": "http://somehost.com/identityserverexample", - # The path 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 - # to that so everything matches + # The downstream path we are forwarding the request to, ocelot will not add a trailing slash. + # Ocelot replaces any placeholders {etc} with matched values from the incoming request. + "DownstreamPathTemplate": "/identityserverexample/{someid}/something", + # The scheme you want Ocelot to use when making the downstream request + "DownstreamScheme": "https", + # The port you want Ocelot to use when making the downstream request, will default to + # scheme if nothing set + "DownstreamPort": 80, + # The host address of the downstream service, should not have a trailing slash or scheme + # if there is a trailing slash Ocelot will remove it. + "DownstreamHost" "localhost" + # 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", # The method we are listening for on this re route "UpstreamHttpMethod": "Get", diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 3f5ef40b5..1e06a4407 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using Ocelot.Values; namespace Ocelot.Configuration.Builder { public class ReRouteBuilder { - private string _downstreamTemplate; + private string _downstreamPathTemplate; private string _upstreamTemplate; private string _upstreamTemplatePattern; private string _upstreamHttpMethod; @@ -30,6 +31,7 @@ public class ReRouteBuilder private string _serviceDiscoveryAddress; private string _downstreamScheme; private string _downstreamHost; + private int _dsPort; public ReRouteBuilder() { @@ -72,9 +74,9 @@ public ReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) return this; } - public ReRouteBuilder WithDownstreamTemplate(string input) + public ReRouteBuilder WithDownstreamPathTemplate(string input) { - _downstreamTemplate = input; + _downstreamPathTemplate = input; return this; } @@ -184,11 +186,17 @@ public ReRouteBuilder WithCacheOptions(CacheOptions input) return this; } + public ReRouteBuilder WithDownstreamPort(int port) + { + _dsPort = port; + return this; + } + public ReRoute Build() { - Func downstreamHostFunc = () => { return _downstreamHost; }; + Func downstreamHostFunc = () => new HostAndPort(_downstreamHost, _dsPort); - return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, + return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName, diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index c71aba23e..8884f0d9f 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -8,6 +8,7 @@ using Ocelot.Configuration.Validator; using Ocelot.Responses; using Ocelot.Utilities; +using Ocelot.Values; namespace Ocelot.Configuration.Creator { @@ -96,7 +97,7 @@ private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration global && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider); - Func downstreamHostFunc = () => { return reRoute.DownstreamHost; }; + Func downstreamHostAndPortFunc = () => new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort); if (isAuthenticated) { @@ -109,22 +110,22 @@ private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration global var claimsToClaims = GetAddThingsToRequest(reRoute.AddClaimsToRequest); var claimsToQueries = GetAddThingsToRequest(reRoute.AddQueriesToRequest); - return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, + return new ReRoute(new DownstreamPathTemplate(reRoute.DownstreamPathTemplate), reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, authOptionsForRoute, claimsToHeaders, claimsToClaims, reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries, requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds), reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, - globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostFunc, reRoute.DownstreamScheme); + globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostAndPortFunc, reRoute.DownstreamScheme); } - return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, + return new ReRoute(new DownstreamPathTemplate(reRoute.DownstreamPathTemplate), reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, null, new List(), new List(), reRoute.RouteClaimsRequirement, isAuthorised, new List(), requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds), reRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, - globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostFunc, reRoute.DownstreamScheme); + globalConfiguration?.ServiceDiscoveryProvider?.Address, downstreamHostAndPortFunc, reRoute.DownstreamScheme); } private string BuildUpstreamTemplate(FileReRoute reRoute) diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 3afa03ce3..a653224a5 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -14,7 +14,7 @@ public FileReRoute() FileCacheOptions = new FileCacheOptions(); } - public string DownstreamTemplate { get; set; } + public string DownstreamPathTemplate { get; set; } public string UpstreamTemplate { get; set; } public string UpstreamHttpMethod { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; } @@ -28,5 +28,6 @@ public FileReRoute() public string ServiceName { get; set; } public string DownstreamScheme {get;set;} public string DownstreamHost {get;set;} + public int DownstreamPort { get; set; } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 8778e5f72..960374cc2 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -1,17 +1,18 @@ using System; using System.Collections.Generic; +using Ocelot.Values; namespace Ocelot.Configuration { public class ReRoute { - public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, + public ReRoute(DownstreamPathTemplate downstreamPathTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List configurationHeaderExtractorProperties, List claimsToClaims, Dictionary routeClaimsRequirement, bool isAuthorised, List claimsToQueries, string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery, - string serviceDiscoveryProvider, string serviceDiscoveryAddress, Func downstreamHost, string downstreamScheme) + string serviceDiscoveryProvider, string serviceDiscoveryAddress, Func downstreamHostAndPort, string downstreamScheme) { - DownstreamTemplate = downstreamTemplate; + DownstreamPathTemplate = downstreamPathTemplate; UpstreamTemplate = upstreamTemplate; UpstreamHttpMethod = upstreamHttpMethod; UpstreamTemplatePattern = upstreamTemplatePattern; @@ -32,11 +33,11 @@ public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstre UseServiceDiscovery = useServiceDiscovery; ServiceDiscoveryProvider = serviceDiscoveryProvider; ServiceDiscoveryAddress = serviceDiscoveryAddress; - DownstreamHost = downstreamHost; + DownstreamHostAndPort = downstreamHostAndPort; DownstreamScheme = downstreamScheme; } - public string DownstreamTemplate { get; private set; } + public DownstreamPathTemplate DownstreamPathTemplate { get; private set; } public string UpstreamTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } public string UpstreamHttpMethod { get; private set; } @@ -54,7 +55,7 @@ public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstre public bool UseServiceDiscovery { get; private set;} public string ServiceDiscoveryProvider { get; private set;} public string ServiceDiscoveryAddress { get; private set;} - public Func DownstreamHost {get;private set;} + public Func DownstreamHostAndPort {get;private set;} public string DownstreamScheme {get;private set;} } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Validator/DownstreamPathTemplateAlreadyUsedError.cs b/src/Ocelot/Configuration/Validator/DownstreamPathTemplateAlreadyUsedError.cs new file mode 100644 index 000000000..e350753c8 --- /dev/null +++ b/src/Ocelot/Configuration/Validator/DownstreamPathTemplateAlreadyUsedError.cs @@ -0,0 +1,11 @@ +using Ocelot.Errors; + +namespace Ocelot.Configuration.Validator +{ + public class DownstreamPathTemplateAlreadyUsedError : Error + { + public DownstreamPathTemplateAlreadyUsedError(string message) : base(message, OcelotErrorCode.DownstreampathTemplateAlreadyUsedError) + { + } + } +} diff --git a/src/Ocelot/Configuration/Validator/DownstreamPathTemplateContainsSchemeError.cs b/src/Ocelot/Configuration/Validator/DownstreamPathTemplateContainsSchemeError.cs new file mode 100644 index 000000000..a3dfa3094 --- /dev/null +++ b/src/Ocelot/Configuration/Validator/DownstreamPathTemplateContainsSchemeError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.Configuration.Validator +{ + public class DownstreamPathTemplateContainsSchemeError : Error + { + public DownstreamPathTemplateContainsSchemeError(string message) + : base(message, OcelotErrorCode.DownstreamPathTemplateContainsSchemeError) + { + } + } +} diff --git a/src/Ocelot/Configuration/Validator/DownstreamTemplateAlreadyUsedError.cs b/src/Ocelot/Configuration/Validator/DownstreamTemplateAlreadyUsedError.cs deleted file mode 100644 index b836b1ebc..000000000 --- a/src/Ocelot/Configuration/Validator/DownstreamTemplateAlreadyUsedError.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ocelot.Errors; - -namespace Ocelot.Configuration.Validator -{ - public class DownstreamTemplateAlreadyUsedError : Error - { - public DownstreamTemplateAlreadyUsedError(string message) : base(message, OcelotErrorCode.DownstreamTemplateAlreadyUsedError) - { - } - } -} diff --git a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs deleted file mode 100644 index 8d9dba92a..000000000 --- a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsHostError.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ocelot.Errors; - -namespace Ocelot.Configuration.Validator -{ - public class DownstreamTemplateContainsHostError : Error - { - public DownstreamTemplateContainsHostError(string message) - : base(message, OcelotErrorCode.DownstreamTemplateContainsHostError) - { - } - } -} diff --git a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs b/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs deleted file mode 100644 index 1901fc88d..000000000 --- a/src/Ocelot/Configuration/Validator/DownstreamTemplateContainsSchemeError.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ocelot.Errors; - -namespace Ocelot.Configuration.Validator -{ - public class DownstreamTemplateContainsSchemeError : Error - { - public DownstreamTemplateContainsSchemeError(string message) - : base(message, OcelotErrorCode.DownstreamTemplateContainsSchemeError) - { - } - } -} diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs index 3a675d419..412613ebf 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs @@ -26,7 +26,7 @@ public Response IsValid(FileConfiguration configu return new OkResponse(result); } - result = CheckForReRoutesContainingDownstreamScheme(configuration); + result = CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(configuration); if (result.IsError) { @@ -70,25 +70,25 @@ private bool IsSupportedAuthenticationProvider(string provider) return Enum.TryParse(provider, true, out supportedProvider); } - private ConfigurationValidationResult CheckForReRoutesContainingDownstreamScheme(FileConfiguration configuration) + private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration) { var errors = new List(); foreach(var reRoute in configuration.ReRoutes) { - if(reRoute.DownstreamTemplate.Contains("https://") - || reRoute.DownstreamTemplate.Contains("http://")) + if(reRoute.DownstreamPathTemplate.Contains("https://") + || reRoute.DownstreamPathTemplate.Contains("http://")) { - errors.Add(new DownstreamTemplateContainsSchemeError($"{reRoute.DownstreamTemplate} contains scheme")); + errors.Add(new DownstreamPathTemplateContainsSchemeError($"{reRoute.DownstreamPathTemplate} contains scheme")); } } if(errors.Any()) { - return new ConfigurationValidationResult(false, errors); + return new ConfigurationValidationResult(true, errors); } - return new ConfigurationValidationResult(true, errors); + return new ConfigurationValidationResult(false, errors); } private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration) @@ -105,7 +105,7 @@ private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration .Where(x => x.Skip(1).Any()); var errors = dupes - .Select(d => new DownstreamTemplateAlreadyUsedError(string.Format("Duplicate DownstreamTemplate: {0}", d.Key.UpstreamTemplate))) + .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamTemplate))) .Cast() .ToList(); diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 04839abba..9f40b0098 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -18,6 +18,7 @@ using Ocelot.Configuration.Validator; using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.DownstreamUrlCreator; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Headers; using Ocelot.Infrastructure.Claims.Parser; @@ -59,6 +60,7 @@ public static IServiceCollection AddOcelot(this IServiceCollection services) services.AddMvcCore().AddJsonFormatters(); services.AddLogging(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -69,7 +71,7 @@ public static IServiceCollection AddOcelot(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs index a74b6316b..f445b46bf 100644 --- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs +++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs @@ -44,7 +44,7 @@ public async Task Invoke(HttpContext context) return; } - _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamTemplate}", downstreamRoute.Data.ReRoute.DownstreamTemplate); + _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamPathTemplate); SetDownstreamRouteForThisRequest(downstreamRoute.Data); diff --git a/src/Ocelot/DownstreamUrlCreator/DownstreamHostNullOrEmptyError.cs b/src/Ocelot/DownstreamUrlCreator/DownstreamHostNullOrEmptyError.cs new file mode 100644 index 000000000..8978f6650 --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/DownstreamHostNullOrEmptyError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.DownstreamUrlCreator +{ + public class DownstreamHostNullOrEmptyError : Error + { + public DownstreamHostNullOrEmptyError() + : base("downstream host was null or empty", OcelotErrorCode.DownstreamHostNullOrEmptyError) + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot/DownstreamUrlCreator/DownstreamPathNullOrEmptyError.cs b/src/Ocelot/DownstreamUrlCreator/DownstreamPathNullOrEmptyError.cs new file mode 100644 index 000000000..fbc1a5f53 --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/DownstreamPathNullOrEmptyError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.DownstreamUrlCreator +{ + public class DownstreamPathNullOrEmptyError : Error + { + public DownstreamPathNullOrEmptyError() + : base("downstream path was null or empty", OcelotErrorCode.DownstreamPathNullOrEmptyError) + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot/DownstreamUrlCreator/DownstreamSchemeNullOrEmptyError.cs b/src/Ocelot/DownstreamUrlCreator/DownstreamSchemeNullOrEmptyError.cs new file mode 100644 index 000000000..e52d3488b --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/DownstreamSchemeNullOrEmptyError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.DownstreamUrlCreator +{ + public class DownstreamSchemeNullOrEmptyError : Error + { + public DownstreamSchemeNullOrEmptyError() + : base("downstream scheme was null or empty", OcelotErrorCode.DownstreamSchemeNullOrEmptyError) + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs b/src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs new file mode 100644 index 000000000..18683e625 --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs @@ -0,0 +1,12 @@ +using Ocelot.Configuration; +using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; +using Ocelot.Responses; +using Ocelot.Values; + +namespace Ocelot.DownstreamUrlCreator +{ + public interface IUrlBuilder + { + Response Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort); + } +} diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index 8d0af0bda..8144b42b9 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using Ocelot.Configuration; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; @@ -11,17 +12,20 @@ namespace Ocelot.DownstreamUrlCreator.Middleware public class DownstreamUrlCreatorMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; - private readonly IDownstreamUrlPathPlaceholderReplacer _urlReplacer; + private readonly IDownstreamPathPlaceholderReplacer _replacer; private readonly IOcelotLogger _logger; + private readonly IUrlBuilder _urlBuilder; public DownstreamUrlCreatorMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, - IDownstreamUrlPathPlaceholderReplacer urlReplacer, - IRequestScopedDataRepository requestScopedDataRepository) + IDownstreamPathPlaceholderReplacer replacer, + IRequestScopedDataRepository requestScopedDataRepository, + IUrlBuilder urlBuilder) :base(requestScopedDataRepository) { _next = next; - _urlReplacer = urlReplacer; + _replacer = replacer; + _urlBuilder = urlBuilder; _logger = loggerFactory.CreateLogger(); } @@ -29,19 +33,34 @@ public async Task Invoke(HttpContext context) { _logger.LogDebug("started calling downstream url creator middleware"); - var downstreamUrl = _urlReplacer.Replace(DownstreamRoute.ReRoute.DownstreamTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues); + var dsPath = _replacer + .Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues); - if (downstreamUrl.IsError) + if (dsPath.IsError) { - _logger.LogDebug("IDownstreamUrlPathPlaceholderReplacer returned an error, setting pipeline error"); + _logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error"); - SetPipelineError(downstreamUrl.Errors); + SetPipelineError(dsPath.Errors); return; } - _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", downstreamUrl.Data.Value); + var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; - SetDownstreamUrlForThisRequest(downstreamUrl.Data.Value); + var dsHostAndPort = DownstreamRoute.ReRoute.DownstreamHostAndPort(); + + var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); + + if (dsUrl.IsError) + { + _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); + + SetPipelineError(dsUrl.Errors); + return; + } + + _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", dsUrl.Data.Value); + + SetDownstreamUrlForThisRequest(dsUrl.Data.Value); _logger.LogDebug("calling next middleware"); diff --git a/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs b/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs new file mode 100644 index 000000000..2124ce3ba --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Ocelot.Configuration; +using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; +using Ocelot.Errors; +using Ocelot.Responses; +using Ocelot.Values; + +namespace Ocelot.DownstreamUrlCreator +{ + public class UrlBuilder : IUrlBuilder + { + public Response Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort) + { + if (string.IsNullOrEmpty(downstreamPath)) + { + return new ErrorResponse(new List {new DownstreamPathNullOrEmptyError()}); + } + + if (string.IsNullOrEmpty(downstreamScheme)) + { + return new ErrorResponse(new List { new DownstreamSchemeNullOrEmptyError() }); + } + + if (string.IsNullOrEmpty(downstreamHostAndPort.DownstreamHost)) + { + return new ErrorResponse(new List { new DownstreamHostNullOrEmptyError() }); + } + + var builder = new UriBuilder + { + Host = downstreamHostAndPort.DownstreamHost, + Path = downstreamPath, + Scheme = downstreamScheme + }; + + if (downstreamHostAndPort.DownstreamPort > 0) + { + builder.Port = downstreamHostAndPort.DownstreamPort; + } + + var url = builder.Uri.ToString(); + + return new OkResponse(new DownstreamUrl(url)); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs index 9c19f2f99..9e9256315 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs +++ b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrlTemplateVariableReplacer.cs @@ -1,25 +1,25 @@ using System.Collections.Generic; using System.Text; -using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; +using Ocelot.Values; namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer { - public class DownstreamUrlPathPlaceholderReplacer : IDownstreamUrlPathPlaceholderReplacer + public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer { - public Response Replace(string downstreamTemplate, List urlPathPlaceholderNameAndValues) + public Response Replace(DownstreamPathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues) { - var upstreamUrl = new StringBuilder(); + var downstreamPath = new StringBuilder(); - upstreamUrl.Append(downstreamTemplate); + downstreamPath.Append(downstreamPathTemplate.Value); foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues) { - upstreamUrl.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue); + downstreamPath.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue); } - return new OkResponse(new DownstreamUrl(upstreamUrl.ToString())); + return new OkResponse(new DownstreamPath(downstreamPath.ToString())); } } } \ No newline at end of file diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs index 164c42eff..72d5d4b65 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs +++ b/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamUrlPathTemplateVariableReplacer.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; +using Ocelot.Values; namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer { - public interface IDownstreamUrlPathPlaceholderReplacer + public interface IDownstreamPathPlaceholderReplacer { - Response Replace(string downstreamTemplate, List urlPathPlaceholderNameAndValues); + Response Replace(DownstreamPathTemplate downstreamPathTemplate, List urlPathPlaceholderNameAndValues); } } \ No newline at end of file diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index f0a336f03..5de770cdd 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -4,7 +4,7 @@ public enum OcelotErrorCode { UnauthenticatedError, UnknownError, - DownstreamTemplateAlreadyUsedError, + DownstreampathTemplateAlreadyUsedError, UnableToFindDownstreamRouteError, CannotAddDataError, CannotFindDataError, @@ -18,7 +18,9 @@ public enum OcelotErrorCode UnauthorizedError, ClaimValueNotAuthorisedError, UserDoesNotHaveClaimError, - DownstreamTemplateContainsSchemeError, - DownstreamTemplateContainsHostError + DownstreamPathTemplateContainsSchemeError, + DownstreamPathNullOrEmptyError, + DownstreamSchemeNullOrEmptyError, + DownstreamHostNullOrEmptyError } } diff --git a/src/Ocelot/Values/DownstreamPath.cs b/src/Ocelot/Values/DownstreamPath.cs new file mode 100644 index 000000000..90f2e83eb --- /dev/null +++ b/src/Ocelot/Values/DownstreamPath.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Values +{ + public class DownstreamPath + { + public DownstreamPath(string value) + { + Value = value; + } + + public string Value { get; private set; } + } +} diff --git a/src/Ocelot/Values/DownstreamPathTemplate.cs b/src/Ocelot/Values/DownstreamPathTemplate.cs new file mode 100644 index 000000000..a4c720eb3 --- /dev/null +++ b/src/Ocelot/Values/DownstreamPathTemplate.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Values +{ + public class DownstreamPathTemplate + { + public DownstreamPathTemplate(string value) + { + Value = value; + } + + public string Value { get; private set; } + } +} diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrl.cs b/src/Ocelot/Values/DownstreamUrl.cs similarity index 74% rename from src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrl.cs rename to src/Ocelot/Values/DownstreamUrl.cs index ea90179eb..f809c84b9 100644 --- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamUrl.cs +++ b/src/Ocelot/Values/DownstreamUrl.cs @@ -1,4 +1,4 @@ -namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer +namespace Ocelot.Values { public class DownstreamUrl { @@ -9,4 +9,4 @@ public DownstreamUrl(string value) public string Value { get; private set; } } -} +} \ No newline at end of file diff --git a/src/Ocelot/Values/HostAndPort.cs b/src/Ocelot/Values/HostAndPort.cs new file mode 100644 index 000000000..cd336deca --- /dev/null +++ b/src/Ocelot/Values/HostAndPort.cs @@ -0,0 +1,14 @@ +namespace Ocelot.Values +{ + public class HostAndPort + { + public HostAndPort(string downstreamHost, int downstreamPort) + { + DownstreamHost = downstreamHost; + DownstreamPort = downstreamPort; + } + + public string DownstreamHost { get; private set; } + public int DownstreamPort { get; private set; } + } +} \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 874afe59c..8b14f4f14 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -23,7 +23,11 @@ public class AuthenticationTests : IDisposable private readonly Steps _steps; private IWebHost _identityServerBuilder; private string _identityServerRootUrl = "http://localhost:51888"; - private string _downstreamServiceRootUrl = "http://localhost:51876/"; + private string _downstreamServicePath = "/"; + private string _downstreamServiceHost = "localhost"; + private int _downstreamServicePort = 51876; + private string _downstreamServiceScheme = "http"; + private string _downstreamServiceUrl = "http://localhost:51876"; public AuthenticationTests() { @@ -39,7 +43,10 @@ public void should_return_401_using_identity_server_access_token() { new FileReRoute { - DownstreamTemplate = _downstreamServiceRootUrl, + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, UpstreamTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions @@ -56,7 +63,7 @@ public void should_return_401_using_identity_server_access_token() }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceRootUrl, 201, string.Empty)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -74,7 +81,10 @@ public void should_return_401_using_identity_server_reference_token() { new FileReRoute { - DownstreamTemplate = _downstreamServiceRootUrl, + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, UpstreamTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions @@ -91,7 +101,7 @@ public void should_return_401_using_identity_server_reference_token() }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceRootUrl, 201, string.Empty)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -109,7 +119,10 @@ public void should_return_response_200_using_identity_server() { new FileReRoute { - DownstreamTemplate = _downstreamServiceRootUrl, + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, UpstreamTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions @@ -126,7 +139,7 @@ public void should_return_response_200_using_identity_server() }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceRootUrl, 200, "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) @@ -146,7 +159,10 @@ public void should_return_201_using_identity_server_access_token() { new FileReRoute { - DownstreamTemplate = _downstreamServiceRootUrl, + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, UpstreamTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions @@ -163,7 +179,7 @@ public void should_return_201_using_identity_server_access_token() }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceRootUrl, 201, string.Empty)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) @@ -183,7 +199,10 @@ public void should_return_201_using_identity_server_reference_token() { new FileReRoute { - DownstreamTemplate = _downstreamServiceRootUrl, + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, UpstreamTemplate = "/", UpstreamHttpMethod = "Post", AuthenticationOptions = new FileAuthenticationOptions @@ -200,7 +219,7 @@ public void should_return_201_using_identity_server_reference_token() }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceRootUrl, 201, string.Empty)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 4ceb03f6f..1f86c6ff8 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -37,7 +37,10 @@ public void should_return_response_200_authorising_route() { new FileReRoute { - DownstreamTemplate = "http://localhost:51876/", + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions @@ -91,7 +94,10 @@ public void should_return_response_403_authorising_route() { new FileReRoute { - DownstreamTemplate = "http://localhost:51876/", + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index 34f10b7aa..e4e628afa 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -31,7 +31,10 @@ public void should_return_cached_response() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", FileCacheOptions = new FileCacheOptions @@ -64,7 +67,10 @@ public void should_not_return_cached_response_as_ttl_expires() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", FileCacheOptions = new FileCacheOptions diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index 81a12381b..81824602b 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -30,7 +30,10 @@ public void should_return_response_200_when_global_ignore_case_sensitivity_set() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } @@ -54,7 +57,10 @@ public void should_return_response_200_when_reroute_ignore_case_sensitivity_set( { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false @@ -79,7 +85,10 @@ public void should_return_response_404_when_reroute_respect_case_sensitivity_set { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -104,7 +113,10 @@ public void should_return_response_200_when_reroute_respect_case_sensitivity_set { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -129,7 +141,10 @@ public void should_return_response_404_when_global_respect_case_sensitivity_set( { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true @@ -154,7 +169,10 @@ public void should_return_response_200_when_global_respect_case_sensitivity_set( { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 160686d56..08bbd968c 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -51,7 +51,10 @@ public void should_return_response_200_and_foward_claim_as_header() { new FileReRoute { - DownstreamTemplate = "http://localhost:52876/", + DownstreamPathTemplate = "/", + DownstreamPort = 52876, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index 36583018f..04dc25db4 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -51,7 +51,10 @@ public void should_return_response_200_and_foward_claim_as_query_string() { new FileReRoute { - DownstreamTemplate = "http://localhost:57876/", + DownstreamPathTemplate = "/", + DownstreamPort = 57876, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", AuthenticationOptions = new FileAuthenticationOptions diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 1a267a98a..f6f6de204 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -45,7 +45,10 @@ public void should_call_pre_query_string_builder_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -79,7 +82,10 @@ public void should_call_authorisation_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -113,7 +119,10 @@ public void should_call_authentication_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "41879/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -147,7 +156,10 @@ public void should_call_pre_error_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -181,7 +193,10 @@ public void should_call_pre_authorisation_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -215,7 +230,10 @@ public void should_call_pre_http_authentication_middleware() { new FileReRoute { - DownstreamTemplate = "http://localhost:41879/", + DownstreamPathTemplate = "/", + DownstreamPort = 41879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index 512736ae0..9334786ba 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -33,7 +33,10 @@ public void should_use_default_request_id_and_forward() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", RequestIdKey = _steps.RequestIdKey @@ -58,7 +61,10 @@ public void should_use_request_id_and_forward() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", RequestIdKey = _steps.RequestIdKey @@ -85,7 +91,10 @@ public void should_use_global_request_id_and_forward() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamPort = 51879, + DownstreamScheme = "http", + DownstreamHost = "localhost", UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index 902de6623..813077813 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -29,7 +29,7 @@ public void should_return_response_200_and_foward_claim_as_header() { new FileReRoute { - DownstreamTemplate = "http://localhost:53876/", + DownstreamPathTemplate = "http://localhost:53876/", UpstreamTemplate = "/", UpstreamHttpMethod = "Get" } diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 84e00d5b8..4f97114f9 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -40,7 +40,10 @@ public void should_return_response_200_with_simple_url() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamTemplate = "/", UpstreamHttpMethod = "Get", } @@ -56,6 +59,62 @@ public void should_return_response_200_with_simple_url() .BDDfy(); } + [Fact] + public void should_return_response_200_when_path_missing_forward_slash_as_first_char() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "api/products", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamTemplate = "/", + UpstreamHttpMethod = "Get", + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_host_has_trailing_slash() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/products", + DownstreamScheme = "http", + DownstreamHost = "localhost/", + DownstreamPort = 51879, + UpstreamTemplate = "/", + UpstreamHttpMethod = "Get", + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + [Fact] public void should_not_care_about_no_trailing() { @@ -65,7 +124,10 @@ public void should_not_care_about_no_trailing() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/products", + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamTemplate = "/products/", UpstreamHttpMethod = "Get", } @@ -90,7 +152,10 @@ public void should_not_care_about_trailing() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/products", + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamTemplate = "/products", UpstreamHttpMethod = "Get", } @@ -115,7 +180,10 @@ public void should_return_not_found() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/products", + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } @@ -139,7 +207,10 @@ public void should_return_response_200_with_complex_url() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/api/products/{productId}", + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } @@ -164,7 +235,10 @@ public void should_return_response_201_with_simple_url() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/", + DownstreamPathTemplate = "/", + DownstreamHost = "localhost", + DownstreamPort = 51879, + DownstreamScheme = "http", UpstreamTemplate = "/", UpstreamHttpMethod = "Post" } @@ -189,8 +263,11 @@ public void should_return_response_201_with_complex_query_string() { new FileReRoute { - DownstreamTemplate = "http://localhost:51879/newThing", + DownstreamPathTemplate = "/newThing", UpstreamTemplate = "/newThing", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, UpstreamHttpMethod = "Get", } } diff --git a/test/Ocelot.AcceptanceTests/configuration.json b/test/Ocelot.AcceptanceTests/configuration.json index 984f851cf..f28abefe3 100755 --- a/test/Ocelot.AcceptanceTests/configuration.json +++ b/test/Ocelot.AcceptanceTests/configuration.json @@ -1 +1 @@ -{"ReRoutes":[{"DownstreamTemplate":"http://localhost: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}],"GlobalConfiguration":{"RequestIdKey":null}} \ No newline at end of file +{"ReRoutes":[{"DownstreamPathTemplate":"/","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}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Address":null}}} \ No newline at end of file diff --git a/test/Ocelot.ManualTest/Program.cs b/test/Ocelot.ManualTest/Program.cs index 7b6b8535c..a049d3eab 100644 --- a/test/Ocelot.ManualTest/Program.cs +++ b/test/Ocelot.ManualTest/Program.cs @@ -10,7 +10,6 @@ public static void Main(string[] args) var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() .UseStartup() .Build(); diff --git a/test/Ocelot.ManualTest/configuration.json b/test/Ocelot.ManualTest/configuration.json index 15276d6e3..f7e2bb75f 100644 --- a/test/Ocelot.ManualTest/configuration.json +++ b/test/Ocelot.ManualTest/configuration.json @@ -1,7 +1,10 @@ { "ReRoutes": [ { - "DownstreamTemplate": "http://localhost:52876/", + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHost": "localhost", + "DownstreamPort": 52876, "UpstreamTemplate": "/identityserverexample", "UpstreamHttpMethod": "Get", "AuthenticationOptions": { @@ -38,108 +41,165 @@ "RequestIdKey": "OcRequestId" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts", + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Get" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}/comments", + "DownstreamPathTemplate": "/posts/{postId}/comments", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/{postId}/comments", "UpstreamHttpMethod": "Get" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/comments", + "DownstreamPathTemplate": "/comments", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/comments", "UpstreamHttpMethod": "Get" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts", + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts", "UpstreamHttpMethod": "Post" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Put" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Patch" }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/{postId}", "UpstreamHttpMethod": "Delete" }, { - "DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products", + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/products", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}", + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/products/{productId}", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products", + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/products", "UpstreamHttpMethod": "Post", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}", + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/products/{productId}", "UpstreamHttpMethod": "Put", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}", + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/products/{productId}", "UpstreamHttpMethod": "Delete", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers", + "DownstreamPathTemplate": "/api/customers", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/customers", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}", + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/customers/{customerId}", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers", + "DownstreamPathTemplate": "/api/customers", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/customers", "UpstreamHttpMethod": "Post", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}", + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/customers/{customerId}", "UpstreamHttpMethod": "Put", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}", + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, "UpstreamTemplate": "/customers/{customerId}", "UpstreamHttpMethod": "Delete", "FileCacheOptions": { "TtlSeconds": 15 } }, { - "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts", + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, "UpstreamTemplate": "/posts/", "UpstreamHttpMethod": "Get", "FileCacheOptions": { "TtlSeconds": 15 } diff --git a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs index e3cccdc0b..8822e6b2f 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs @@ -67,7 +67,7 @@ public void should_call_claims_to_request_correctly() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("any old string") + .WithDownstreamPathTemplate("any old string") .WithClaimsToClaims(new List { new ClaimToThing("sub", "UserType", "|", 0) diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs index 174ea8bc1..8a3e24f98 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationValidationTests.cs @@ -10,8 +10,8 @@ namespace Ocelot.UnitTests.Configuration { public class ConfigurationValidationTests { - private FileConfiguration _fileConfiguration; private readonly IConfigurationValidator _configurationValidator; + private FileConfiguration _fileConfiguration; private Response _result; public ConfigurationValidationTests() @@ -22,32 +22,13 @@ public ConfigurationValidationTests() [Fact] public void configuration_is_invalid_if_scheme_in_downstream_template() { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamTemplate = "http://www.bbc.co.uk/api/products/{productId}", - UpstreamTemplate = "http://asdf.com" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_host_in_downstream_template() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() + this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { - DownstreamTemplate = "www.bbc.co.uk/api/products/{productId}", + DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}", UpstreamTemplate = "http://asdf.com" } } @@ -60,13 +41,13 @@ public void configuration_is_invalid_if_host_in_downstream_template() [Fact] public void configuration_is_valid_with_one_reroute() { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() + this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { - DownstreamTemplate = "/api/products/", + DownstreamPathTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com" } } @@ -79,13 +60,13 @@ public void configuration_is_valid_with_one_reroute() [Fact] public void configuration_is_valid_with_valid_authentication_provider() { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() + this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { - DownstreamTemplate = "/api/products/", + DownstreamPathTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { @@ -102,13 +83,13 @@ public void configuration_is_valid_with_valid_authentication_provider() [Fact] public void configuration_is_invalid_with_invalid_authentication_provider() { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() + this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { - DownstreamTemplate = "/api/products/", + DownstreamPathTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com", AuthenticationOptions = new FileAuthenticationOptions { @@ -126,25 +107,25 @@ public void configuration_is_invalid_with_invalid_authentication_provider() [Fact] public void configuration_is_not_valid_with_duplicate_reroutes() { - this.Given(x => x.GivenAConfiguration(new FileConfiguration() + this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { - DownstreamTemplate = "/api/products/", + DownstreamPathTemplate = "/api/products/", UpstreamTemplate = "http://asdf.com" }, new FileReRoute { - DownstreamTemplate = "http://www.bbc.co.uk", + DownstreamPathTemplate = "http://www.bbc.co.uk", UpstreamTemplate = "http://asdf.com" } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorIs()) .BDDfy(); } @@ -173,4 +154,4 @@ private void ThenTheErrorIs() _result.Data.Errors[0].ShouldBeOfType(); } } -} +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 4207a333f..fc80a4787 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -46,7 +46,7 @@ public void should_use_downstream_host() { DownstreamHost = "127.0.0.1", UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } }, @@ -57,7 +57,7 @@ public void should_use_downstream_host() { new ReRouteBuilder() .WithDownstreamHost("127.0.0.1") - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -76,7 +76,7 @@ public void should_use_downstream_scheme() { DownstreamScheme = "https", UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", } }, @@ -87,7 +87,7 @@ public void should_use_downstream_scheme() { new ReRouteBuilder() .WithDownstreamScheme("https") - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -106,7 +106,7 @@ public void should_use_service_discovery_for_downstream_service_host() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false, ServiceName = "ProductService" @@ -126,7 +126,7 @@ public void should_use_service_discovery_for_downstream_service_host() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -149,7 +149,7 @@ public void should_not_use_service_discovery_for_downstream_host_url_when_no_ser new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false, } @@ -160,7 +160,7 @@ public void should_not_use_service_discovery_for_downstream_host_url_when_no_ser .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -182,7 +182,7 @@ public void should_use_reroute_case_sensitivity_value() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = false } @@ -193,7 +193,7 @@ public void should_use_reroute_case_sensitivity_value() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -212,7 +212,7 @@ public void should_set_upstream_template_pattern_to_ignore_case_sensitivity() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get" } } @@ -222,7 +222,7 @@ public void should_set_upstream_template_pattern_to_ignore_case_sensitivity() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") @@ -241,7 +241,7 @@ public void should_set_upstream_template_pattern_to_respect_case_sensitivity() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -252,7 +252,7 @@ public void should_set_upstream_template_pattern_to_respect_case_sensitivity() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") @@ -271,7 +271,7 @@ public void should_set_global_request_id_key() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -286,7 +286,7 @@ public void should_set_global_request_id_key() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") @@ -306,7 +306,7 @@ public void should_create_template_pattern_that_matches_anything_to_end_of_strin new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -317,7 +317,7 @@ public void should_create_template_pattern_that_matches_anything_to_end_of_strin .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") @@ -332,7 +332,7 @@ public void should_create_with_headers_to_extract() var expected = new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") @@ -355,7 +355,7 @@ public void should_create_with_headers_to_extract() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true, AuthenticationOptions = new FileAuthenticationOptions @@ -395,7 +395,7 @@ public void should_create_with_authentication_properties() var expected = new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/$") @@ -414,7 +414,7 @@ public void should_create_with_authentication_properties() new FileReRoute { UpstreamTemplate = "/api/products/{productId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true, AuthenticationOptions = new FileAuthenticationOptions @@ -446,7 +446,7 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde new FileReRoute { UpstreamTemplate = "/api/products/{productId}/variants/{variantId}", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -457,7 +457,7 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$") @@ -476,7 +476,7 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde new FileReRoute { UpstreamTemplate = "/api/products/{productId}/variants/{variantId}/", - DownstreamTemplate = "/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -487,7 +487,7 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/products/{productId}") + .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}/") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$") @@ -506,7 +506,7 @@ public void should_create_template_pattern_that_matches_to_end_of_string() new FileReRoute { UpstreamTemplate = "/", - DownstreamTemplate = "/api/products/", + DownstreamPathTemplate = "/api/products/", UpstreamHttpMethod = "Get", ReRouteIsCaseSensitive = true } @@ -517,7 +517,7 @@ public void should_create_template_pattern_that_matches_to_end_of_string() .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() - .WithDownstreamTemplate("/api/products/") + .WithDownstreamPathTemplate("/api/products/") .WithUpstreamTemplate("/") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("/$") @@ -553,7 +553,7 @@ private void ThenTheReRoutesAre(List expectedReRoutes) var result = _config.Data.ReRoutes[i]; var expected = expectedReRoutes[i]; - result.DownstreamTemplate.ShouldBe(expected.DownstreamTemplate); + result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); result.UpstreamTemplate.ShouldBe(expected.UpstreamTemplate); result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern); diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 8f7af24e3..ec46f914f 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -44,7 +44,7 @@ public void can_get_config() private void ThenTheConfigurationIsReturned() { - _getResult.Data.ReRoutes[0].DownstreamTemplate.ShouldBe("initial"); + _getResult.Data.ReRoutes[0].DownstreamPathTemplate.Value.ShouldBe("initial"); } private void WhenIGetTheConfiguration() @@ -75,16 +75,16 @@ private void ThenNoErrorsAreReturned() class FakeConfig : IOcelotConfiguration { - private readonly string _downstreamTemplate; + private readonly string _downstreamTemplatePath; - public FakeConfig(string downstreamTemplate) + public FakeConfig(string downstreamTemplatePath) { - _downstreamTemplate = downstreamTemplate; + _downstreamTemplatePath = downstreamTemplatePath; } public List ReRoutes => new List { - new ReRouteBuilder().WithDownstreamTemplate(_downstreamTemplate).Build() + new ReRouteBuilder().WithDownstreamPathTemplate(_downstreamTemplatePath).Build() }; } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index 22dfea80b..0d5a6d48a 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -61,7 +61,7 @@ public DownstreamRouteFinderMiddlewareTests() [Fact] public void should_call_scoped_data_repository_correctly() { - this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("any old string").Build()))) + this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").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 bb390d327..c0afca426 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -44,7 +44,7 @@ public void should_return_route() .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamTemplate("someDownstreamPath") + .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("someUpstreamPath") @@ -57,7 +57,7 @@ public void should_return_route() .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("someDownstreamPath") + .WithDownstreamPathTemplate("someDownstreamPath") .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) @@ -75,13 +75,13 @@ public void should_return_correct_route_for_http_verb() .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamTemplate("someDownstreamPath") + .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("") .Build(), new ReRouteBuilder() - .WithDownstreamTemplate("someDownstreamPathForAPost") + .WithDownstreamPathTemplate("someDownstreamPathForAPost") .WithUpstreamTemplate("someUpstreamPath") .WithUpstreamHttpMethod("Post") .WithUpstreamTemplatePattern("") @@ -94,7 +94,7 @@ public void should_return_correct_route_for_http_verb() .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("someDownstreamPathForAPost") + .WithDownstreamPathTemplate("someDownstreamPathForAPost") .Build() ))) .BDDfy(); @@ -107,7 +107,7 @@ public void should_not_return_route() .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() - .WithDownstreamTemplate("somPath") + .WithDownstreamPathTemplate("somPath") .WithUpstreamTemplate("somePath") .WithUpstreamHttpMethod("Get") .WithUpstreamTemplatePattern("somePath") @@ -174,7 +174,7 @@ private void WhenICallTheFinder() private void ThenTheFollowingIsReturned(DownstreamRoute expected) { - _result.Data.ReRoute.DownstreamTemplate.ShouldBe(expected.ReRoute.DownstreamTemplate); + _result.Data.ReRoute.DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamPathTemplate.Value); for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) { diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 98bc5f0b6..5581a32ee 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -7,15 +7,18 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; +using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.DownstreamUrlCreator; using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Responses; +using Ocelot.Values; using TestStack.BDDfy; using Xunit; @@ -23,21 +26,23 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator { public class DownstreamUrlCreatorMiddlewareTests : IDisposable { - private readonly Mock _downstreamUrlTemplateVariableReplacer; + private readonly Mock _downstreamUrlTemplateVariableReplacer; private readonly Mock _scopedRepository; + private readonly Mock _urlBuilder; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; private Response _downstreamRoute; private HttpResponseMessage _result; + private OkResponse _downstreamPath; private OkResponse _downstreamUrl; public DownstreamUrlCreatorMiddlewareTests() { _url = "http://localhost:51879"; - _downstreamUrlTemplateVariableReplacer = new Mock(); + _downstreamUrlTemplateVariableReplacer = new Mock(); _scopedRepository = new Mock(); - + _urlBuilder = new Mock(); var builder = new WebHostBuilder() .ConfigureServices(x => { @@ -45,6 +50,7 @@ public DownstreamUrlCreatorMiddlewareTests() x.AddLogging(); x.AddSingleton(_downstreamUrlTemplateVariableReplacer.Object); x.AddSingleton(_scopedRepository.Object); + x.AddSingleton(_urlBuilder.Object); }) .UseUrls(_url) .UseKestrel() @@ -61,21 +67,30 @@ public DownstreamUrlCreatorMiddlewareTests() } [Fact] - public void should_call_scoped_data_repository_correctly() + public void should_call_dependencies_correctly() { - this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("any old string").Build()))) - .And(x => x.TheUrlReplacerReturns("any old string")) + this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build()))) + .And(x => x.TheUrlReplacerReturns("/api/products/1")) + .And(x => x.TheUrlBuilderReturns("http://www.bbc.co.uk/api/products/1")) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .BDDfy(); } + private void TheUrlBuilderReturns(string dsUrl) + { + _downstreamUrl = new OkResponse(new DownstreamUrl(dsUrl)); + _urlBuilder + .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_downstreamUrl); + } + private void TheUrlReplacerReturns(string downstreamUrl) { - _downstreamUrl = new OkResponse(new DownstreamUrl(downstreamUrl)); + _downstreamPath = new OkResponse(new DownstreamPath(downstreamUrl)); _downstreamUrlTemplateVariableReplacer - .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) - .Returns(_downstreamUrl); + .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) + .Returns(_downstreamPath); } private void ThenTheScopedDataRepositoryIsCalledCorrectly() diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs new file mode 100644 index 000000000..7e512798b --- /dev/null +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlBuilderTests.cs @@ -0,0 +1,124 @@ +using System; +using Ocelot.Configuration; +using Ocelot.DownstreamUrlCreator; +using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; +using Ocelot.Responses; +using Ocelot.Values; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamUrlCreator +{ + public class UrlBuilderTests + { + private readonly IUrlBuilder _urlBuilder; + private string _dsPath; + private string _dsScheme; + private string _dsHost; + private int _dsPort; + + private Response _result; + + public UrlBuilderTests() + { + _urlBuilder = new UrlBuilder(); + } + + [Fact] + public void should_return_error_when_downstream_path_is_null() + { + this.Given(x => x.GivenADownstreamPath(null)) + .When(x => x.WhenIBuildTheUrl()) + .Then(x => x.ThenThereIsAnErrorOfType()) + .BDDfy(); + } + + [Fact] + public void should_return_error_when_downstream_scheme_is_null() + { + this.Given(x => x.GivenADownstreamScheme(null)) + .And(x => x.GivenADownstreamPath("test")) + .When(x => x.WhenIBuildTheUrl()) + .Then(x => x.ThenThereIsAnErrorOfType()) + .BDDfy(); + } + + [Fact] + public void should_return_error_when_downstream_host_is_null() + { + this.Given(x => x.GivenADownstreamScheme(null)) + .And(x => x.GivenADownstreamPath("test")) + .And(x => x.GivenADownstreamScheme("test")) + .When(x => x.WhenIBuildTheUrl()) + .Then(x => x.ThenThereIsAnErrorOfType()) + .BDDfy(); + } + + [Fact] + public void should_not_use_port_if_zero() + { + this.Given(x => x.GivenADownstreamPath("/api/products/1")) + .And(x => x.GivenADownstreamScheme("http")) + .And(x => x.GivenADownstreamHost("127.0.0.1")) + .And(x => x.GivenADownstreamPort(0)) + .When(x => x.WhenIBuildTheUrl()) + .Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1/api/products/1")) + .And(x => x.ThenTheUrlIsWellFormed()) + .BDDfy(); + } + + [Fact] + public void should_build_well_formed_uri() + { + this.Given(x => x.GivenADownstreamPath("/api/products/1")) + .And(x => x.GivenADownstreamScheme("http")) + .And(x => x.GivenADownstreamHost("127.0.0.1")) + .And(x => x.GivenADownstreamPort(5000)) + .When(x => x.WhenIBuildTheUrl()) + .Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1:5000/api/products/1")) + .And(x => x.ThenTheUrlIsWellFormed()) + .BDDfy(); + } + + private void ThenThereIsAnErrorOfType() + { + _result.Errors[0].ShouldBeOfType(); + } + + private void GivenADownstreamPath(string dsPath) + { + _dsPath = dsPath; + } + + private void GivenADownstreamScheme(string dsScheme) + { + _dsScheme = dsScheme; + } + + private void GivenADownstreamHost(string dsHost) + { + _dsHost = dsHost; + } + + private void GivenADownstreamPort(int dsPort) + { + _dsPort = dsPort; + } + + private void WhenIBuildTheUrl() + { + _result = _urlBuilder.Build(_dsPath, _dsScheme, new HostAndPort(_dsHost, _dsPort)); + } + + private void ThenTheUrlIsReturned(string expected) + { + _result.Data.Value.ShouldBe(expected); + } + + private void ThenTheUrlIsWellFormed() + { + Uri.IsWellFormedUriString(_result.Data.Value, UriKind.Absolute).ShouldBeTrue(); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index b1ad369bf..a7a5a89b0 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -4,6 +4,7 @@ using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Responses; +using Ocelot.Values; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -13,12 +14,12 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer public class UpstreamUrlPathTemplateVariableReplacerTests { private DownstreamRoute _downstreamRoute; - private Response _result; - private readonly IDownstreamUrlPathPlaceholderReplacer _downstreamUrlPathReplacer; + private Response _result; + private readonly IDownstreamPathPlaceholderReplacer _downstreamPathReplacer; public UpstreamUrlPathTemplateVariableReplacerTests() { - _downstreamUrlPathReplacer = new DownstreamUrlPathPlaceholderReplacer(); + _downstreamPathReplacer = new DownstreamTemplatePathPlaceholderReplacer(); } [Fact] @@ -33,7 +34,7 @@ public void can_replace_no_template_variables() [Fact] public void can_replace_no_template_variables_with_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("/").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) .BDDfy(); @@ -42,7 +43,7 @@ public void can_replace_no_template_variables_with_slash() [Fact] public void can_replace_url_no_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("api").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) .BDDfy(); @@ -51,7 +52,7 @@ public void can_replace_url_no_slash() [Fact] public void can_replace_url_one_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("api/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api/").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) .BDDfy(); @@ -60,7 +61,7 @@ public void can_replace_url_one_slash() [Fact] public void can_replace_url_multiple_slash() { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamTemplate("api/product/products/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("api/product/products/").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) .BDDfy(); @@ -74,7 +75,7 @@ public void can_replace_url_one_template_variable() new UrlPathPlaceholderNameAndValue("{productId}", "1") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) .BDDfy(); @@ -88,7 +89,7 @@ public void can_replace_url_one_template_variable_with_path_after() new UrlPathPlaceholderNameAndValue("{productId}", "1") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/variants").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) .BDDfy(); @@ -103,7 +104,7 @@ public void can_replace_url_two_template_variable() new UrlPathPlaceholderNameAndValue("{variantId}", "12") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/variants/{variantId}").Build()))) + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) .BDDfy(); @@ -119,7 +120,7 @@ public void can_replace_url_three_template_variable() new UrlPathPlaceholderNameAndValue("{categoryId}", "34") }; - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("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}").Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) .BDDfy(); @@ -132,7 +133,7 @@ private void GivenThereIsAUrlMatch(DownstreamRoute downstreamRoute) private void WhenIReplaceTheTemplateVariables() { - _result = _downstreamUrlPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues); + _result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamPathTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues); } private void ThenTheDownstreamUrlPathIsReturned(string expected) diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index b85802afa..3516d26b4 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -67,7 +67,7 @@ public void should_call_add_headers_to_request_correctly() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("any old string") + .WithDownstreamPathTemplate("any old string") .WithClaimsToHeaders(new List { new ClaimToThing("UserId", "Subject", "", 0) diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index e4c7375ec..39b329378 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -65,7 +65,7 @@ public void should_call_add_queries_correctly() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("any old string") + .WithDownstreamPathTemplate("any old string") .WithClaimsToQueries(new List { new ClaimToThing("UserId", "Subject", "", 0) diff --git a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs index 8c0237839..543613a8f 100644 --- a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs @@ -71,7 +71,7 @@ public void should_add_request_id_to_repository() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("any old string") + .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId").Build()); var requestId = Guid.NewGuid().ToString(); @@ -88,7 +88,7 @@ public void should_add_trace_indentifier_to_repository() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithDownstreamTemplate("any old string") + .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId").Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) From d7ad6df582b48726870eeb80b2aef5c88f7b8860 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sat, 21 Jan 2017 10:07:06 +0000 Subject: [PATCH 3/3] removed rc1 status as all dependencies out of pre --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 69ddc117e..831107225 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,6 @@ build_script: test_script: - run-tests.bat after_test: -- push-to-nuget.bat %appveyor_build_version%-rc1 %nugetApiKey% +- push-to-nuget.bat %appveyor_build_version% %nugetApiKey% cache: - '%USERPROFILE%\.nuget\packages' \ No newline at end of file