From 58b82f0fc7a69324d1dcde35e84c4f04fe3647f5 Mon Sep 17 00:00:00 2001 From: Pitming Date: Fri, 8 Nov 2019 12:59:34 +0100 Subject: [PATCH 01/28] Add DownstreamHttpMethodCreatorMiddleware --- .../Builder/DownstreamReRouteBuilder.cs | 10 ++++++- .../Configuration/Creator/ReRoutesCreator.cs | 1 + src/Ocelot/Configuration/DownstreamReRoute.cs | 5 +++- src/Ocelot/Configuration/File/FileReRoute.cs | 1 + .../DownstreamHttpMethodCreatorMiddleware.cs | 27 +++++++++++++++++++ .../Request/Middleware/DownstreamRequest.cs | 4 ++- .../HttpRequestBuilderMiddlewareExtensions.cs | 4 ++- 7 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 4b3e6ea3..ed978dde 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -41,6 +41,7 @@ namespace Ocelot.Configuration.Builder private List _addHeadersToUpstream; private bool _dangerousAcceptAnyServerCertificateValidator; private SecurityOptions _securityOptions; + private string _downstreamHttpMethod; public DownstreamReRouteBuilder() { @@ -56,6 +57,12 @@ namespace Ocelot.Configuration.Builder return this; } + public DownstreamReRouteBuilder WithDownStreamHttpMethod(string method) + { + _downstreamHttpMethod = method; + return this; + } + public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions) { _loadBalancerOptions = loadBalancerOptions; @@ -282,7 +289,8 @@ namespace Ocelot.Configuration.Builder _addHeadersToDownstream, _addHeadersToUpstream, _dangerousAcceptAnyServerCertificateValidator, - _securityOptions); + _securityOptions, + _downstreamHttpMethod); } } } diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index 4443e201..1cc463ef 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -138,6 +138,7 @@ namespace Ocelot.Configuration.Creator .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) .WithSecurityOptions(securityOptions) + .WithDownStreamHttpMethod(fileReRoute.DownstreamHttpMethod) .Build(); return reRoute; diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index e8dfade5..1c41f648 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -38,7 +38,8 @@ namespace Ocelot.Configuration List addHeadersToDownstream, List addHeadersToUpstream, bool dangerousAcceptAnyServerCertificateValidator, - SecurityOptions securityOptions) + SecurityOptions securityOptions, + string downstreamHttpMethod) { DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; AddHeadersToDownstream = addHeadersToDownstream; @@ -72,6 +73,7 @@ namespace Ocelot.Configuration LoadBalancerKey = loadBalancerKey; AddHeadersToUpstream = addHeadersToUpstream; SecurityOptions = securityOptions; + DownstreamHttpMethod = downstreamHttpMethod; } public string Key { get; } @@ -106,5 +108,6 @@ namespace Ocelot.Configuration public List AddHeadersToUpstream { get; } public bool DangerousAcceptAnyServerCertificateValidator { get; } public SecurityOptions SecurityOptions { get; } + public string DownstreamHttpMethod { get; } } } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index b15653ea..1bae31dd 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -29,6 +29,7 @@ namespace Ocelot.Configuration.File public string DownstreamPathTemplate { get; set; } public string UpstreamPathTemplate { get; set; } public List UpstreamHttpMethod { get; set; } + public string DownstreamHttpMethod { get; set; } public Dictionary AddHeadersToRequest { get; set; } public Dictionary UpstreamHeaderTransform { get; set; } public Dictionary DownstreamHeaderTransform { get; set; } diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs new file mode 100644 index 00000000..6689c0c1 --- /dev/null +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs @@ -0,0 +1,27 @@ +using Ocelot.Logging; +using Ocelot.Middleware; +using System.Threading.Tasks; + +namespace Ocelot.DownstreamUrlCreator.Middleware +{ + public class DownstreamHttpMethodCreatorMiddleware : OcelotMiddleware + { + private readonly OcelotRequestDelegate _next; + + public DownstreamHttpMethodCreatorMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory) + : base(loggerFactory.CreateLogger()) + { + _next = next; + } + + public async Task Invoke(DownstreamContext context) + { + if (context.DownstreamReRoute.DownstreamHttpMethod != null) + { + context.DownstreamRequest.Method = context.DownstreamReRoute.DownstreamHttpMethod; + } + + await _next.Invoke(context); + } + } +} diff --git a/src/Ocelot/Request/Middleware/DownstreamRequest.cs b/src/Ocelot/Request/Middleware/DownstreamRequest.cs index 256436e5..1715b387 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequest.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequest.cs @@ -24,7 +24,7 @@ namespace Ocelot.Request.Middleware public HttpRequestHeaders Headers { get; } - public string Method { get; } + public string Method { get; set; } public string OriginalString { get; } @@ -52,6 +52,8 @@ namespace Ocelot.Request.Middleware }; _request.RequestUri = uriBuilder.Uri; + _request.Method = new HttpMethod(Method); + _request.Content = Content; return _request; } diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs index 2d803e1e..6c43507c 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Builder; +using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Middleware.Pipeline; namespace Ocelot.Request.Middleware @@ -7,7 +8,8 @@ namespace Ocelot.Request.Middleware { public static IOcelotPipelineBuilder UseDownstreamRequestInitialiser(this IOcelotPipelineBuilder builder) { - return builder.UseMiddleware(); + return builder.UseMiddleware() + .UseMiddleware(); } } } From ec622dc0add3474e3288c94a328652cbc9e71f8d Mon Sep 17 00:00:00 2001 From: Pitming Date: Fri, 8 Nov 2019 13:08:01 +0100 Subject: [PATCH 02/28] Add DownstreamHttpMethodCreatorMiddleware --- .../Middleware/DownstreamMethodTransformerMiddleware.cs} | 8 ++++---- .../Middleware/Pipeline/OcelotPipelineExtensions.cs | 4 ++++ src/Ocelot/Request/Middleware/DownstreamRequest.cs | 1 - .../Middleware/HttpRequestBuilderMiddlewareExtensions.cs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) rename src/Ocelot/{DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs => DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs} (68%) diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs b/src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs similarity index 68% rename from src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs rename to src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs index 6689c0c1..a17e9167 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamHttpMethodCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs @@ -2,14 +2,14 @@ using Ocelot.Middleware; using System.Threading.Tasks; -namespace Ocelot.DownstreamUrlCreator.Middleware +namespace Ocelot.DownstreamMethodTransformer.Middleware { - public class DownstreamHttpMethodCreatorMiddleware : OcelotMiddleware + public class DownstreamMethodTransformerMiddleware : OcelotMiddleware { private readonly OcelotRequestDelegate _next; - public DownstreamHttpMethodCreatorMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory) - : base(loggerFactory.CreateLogger()) + public DownstreamMethodTransformerMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory) + : base(loggerFactory.CreateLogger()) { _next = next; } diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs index d52cfb0d..02ee07f4 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs @@ -2,6 +2,7 @@ using Ocelot.Authorisation.Middleware; using Ocelot.Cache.Middleware; using Ocelot.Claims.Middleware; +using Ocelot.DownstreamMethodTransformer.Middleware; using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Errors.Middleware; @@ -68,6 +69,9 @@ namespace Ocelot.Middleware.Pipeline // Initialises downstream request builder.UseDownstreamRequestInitialiser(); + //change Http Method + builder.UseMiddleware(); + // We check whether the request is ratelimit, and if there is no continue processing builder.UseRateLimiting(); diff --git a/src/Ocelot/Request/Middleware/DownstreamRequest.cs b/src/Ocelot/Request/Middleware/DownstreamRequest.cs index 1715b387..24d96cfe 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequest.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequest.cs @@ -53,7 +53,6 @@ namespace Ocelot.Request.Middleware _request.RequestUri = uriBuilder.Uri; _request.Method = new HttpMethod(Method); - _request.Content = Content; return _request; } diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs index 6c43507c..e6eff7e0 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs @@ -9,7 +9,7 @@ namespace Ocelot.Request.Middleware public static IOcelotPipelineBuilder UseDownstreamRequestInitialiser(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware() - .UseMiddleware(); + .UseMiddleware(); } } } From ebe662abe6b04ab35a13f3936440aa6904a5fa4d Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Sun, 19 Jan 2020 17:47:57 +0000 Subject: [PATCH 03/28] make rate limiting whitelist a function so users can override with dynamic behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make rate-limiting client whitelist dynamic * Refactor `RateLimitOptions.ClientWhiteList` * Fix typo in variable `enbleRateLimiting` * Fix case in variable `clientIdheader` author Taiwo Otubamowo * fix 1045 * #492 log 500 with error log level, acceptance test, unit test * #492 minor changes * initial commit for new feature #1077 allow to limit the number of concurrent tcp connection to a downstream service * protect code against value not in accurate range add unit test * Do not crash host on Dispose * Add test * Pin GitVersion.CommandLine package version * #683 validate if there are duplicated placeholders in UpstreamPathTemplate * Use registered scheme from Eureka (#1087) * extra test * very brief mention MaxConnectionsPerServer in docs * build develop like a PR * more docs * test Co-authored-by: Taiwo O. <44668623+totubamowo@users.noreply.github.com> Co-authored-by: Catcher Wong Co-authored-by: jlukawska <56401969+jlukawska@users.noreply.github.com> Co-authored-by: buretjph <58700930+buretjph@users.noreply.github.com> Co-authored-by: Jonathan Mezach Co-authored-by: 彭伟 --- Ocelot.sln | 10 -- .../Properties/launchSettings.json | 27 ++++ .../Builder/RateLimitOptionsBuilder.cs | 18 ++- .../Creator/RateLimitOptionsCreator.cs | 2 +- src/Ocelot/Configuration/RateLimitOptions.cs | 30 ++-- .../Requester/HttpExeptionToErrorMapper.cs | 4 +- .../Middleware/HttpRequesterMiddleware.cs | 19 +++ .../Ocelot.AcceptanceTests.csproj | 147 +++++++++--------- .../ResponseCodeTests.cs | 4 +- .../ReturnsErrorTests.cs | 36 ++++- test/Ocelot.AcceptanceTests/Steps.cs | 57 +++++++ .../RateLimitOptionsCreatorTests.cs | 2 +- .../ClientRateLimitMiddlewareTests.cs | 30 ++-- .../HttpExeptionToErrorMapperTests.cs | 11 ++ .../Requester/HttpRequesterMiddlewareTests.cs | 32 +++- 15 files changed, 303 insertions(+), 126 deletions(-) create mode 100644 samples/OcelotBasic/Properties/launchSettings.json diff --git a/Ocelot.sln b/Ocelot.sln index e943d369..60369059 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -9,23 +9,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .dockerignore = .dockerignore .gitignore = .gitignore - build-and-release-unstable.ps1 = build-and-release-unstable.ps1 - build-and-run-tests.ps1 = build-and-run-tests.ps1 build.cake = build.cake build.ps1 = build.ps1 codeanalysis.ruleset = codeanalysis.ruleset - docker-compose.yaml = docker-compose.yaml - Dockerfile = Dockerfile GitVersion.yml = GitVersion.yml - global.json = global.json LICENSE.md = LICENSE.md README.md = README.md - release.ps1 = release.ps1 ReleaseNotes.md = ReleaseNotes.md - run-acceptance-tests.ps1 = run-acceptance-tests.ps1 - run-benchmarks.ps1 = run-benchmarks.ps1 - run-unit-tests.ps1 = run-unit-tests.ps1 - version.ps1 = version.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}" diff --git a/samples/OcelotBasic/Properties/launchSettings.json b/samples/OcelotBasic/Properties/launchSettings.json new file mode 100644 index 00000000..b500ae57 --- /dev/null +++ b/samples/OcelotBasic/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55029/", + "sslPort": 44390 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "OcelotBasic": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs index eba79769..eaaa4dab 100644 --- a/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Ocelot.Configuration.Builder { @@ -7,6 +8,7 @@ namespace Ocelot.Configuration.Builder private bool _enableRateLimiting; private string _clientIdHeader; private List _clientWhitelist; + private Func> _getClientWhitelist; private bool _disableRateLimitHeaders; private string _quotaExceededMessage; private string _rateLimitCounterPrefix; @@ -19,15 +21,15 @@ namespace Ocelot.Configuration.Builder return this; } - public RateLimitOptionsBuilder WithClientIdHeader(string clientIdheader) + public RateLimitOptionsBuilder WithClientIdHeader(string clientIdHeader) { - _clientIdHeader = clientIdheader; + _clientIdHeader = clientIdHeader; return this; } - public RateLimitOptionsBuilder WithClientWhiteList(List clientWhitelist) + public RateLimitOptionsBuilder WithClientWhiteList(Func> getClientWhitelist) { - _clientWhitelist = clientWhitelist; + _getClientWhitelist = getClientWhitelist; return this; } @@ -63,9 +65,9 @@ namespace Ocelot.Configuration.Builder public RateLimitOptions Build() { - return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _clientWhitelist, - _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix, + return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _getClientWhitelist, + _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix, _rateLimitRule, _httpStatusCode); } } -} +} diff --git a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs index 8f300dd2..ba167bfe 100644 --- a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs @@ -11,7 +11,7 @@ namespace Ocelot.Configuration.Creator { return new RateLimitOptionsBuilder() .WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader) - .WithClientWhiteList(fileRateLimitRule.ClientWhitelist) + .WithClientWhiteList(() => fileRateLimitRule.ClientWhitelist) .WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders) .WithEnableRateLimiting(fileRateLimitRule.EnableRateLimiting) .WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode) diff --git a/src/Ocelot/Configuration/RateLimitOptions.cs b/src/Ocelot/Configuration/RateLimitOptions.cs index db1da8eb..28472825 100644 --- a/src/Ocelot/Configuration/RateLimitOptions.cs +++ b/src/Ocelot/Configuration/RateLimitOptions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Ocelot.Configuration { @@ -7,12 +8,14 @@ namespace Ocelot.Configuration /// public class RateLimitOptions { - public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List clientWhitelist, bool disableRateLimitHeaders, + private readonly Func> _getClientWhitelist; + + public RateLimitOptions(bool enableRateLimiting, string clientIdHeader, Func> getClientWhitelist, bool disableRateLimitHeaders, string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode) { - EnableRateLimiting = enbleRateLimiting; + EnableRateLimiting = enableRateLimiting; ClientIdHeader = clientIdHeader; - ClientWhitelist = clientWhitelist ?? new List(); + _getClientWhitelist = getClientWhitelist; DisableRateLimitHeaders = disableRateLimitHeaders; QuotaExceededMessage = quotaExceededMessage; RateLimitCounterPrefix = rateLimitCounterPrefix; @@ -22,18 +25,21 @@ namespace Ocelot.Configuration public RateLimitRule RateLimitRule { get; private set; } - public List ClientWhitelist { get; private set; } + /// + /// Gets the list of white listed clients + /// + public List ClientWhitelist { get => _getClientWhitelist(); } /// /// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId /// - public string ClientIdHeader { get; private set; } - + public string ClientIdHeader { get; private set; } + /// /// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests) /// - public int HttpStatusCode { get; private set; } - + public int HttpStatusCode { get; private set; } + /// /// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message. /// If none specified the default will be: @@ -44,8 +50,8 @@ namespace Ocelot.Configuration /// /// Gets or sets the counter prefix, used to compose the rate limit counter cache key /// - public string RateLimitCounterPrefix { get; private set; } - + public string RateLimitCounterPrefix { get; private set; } + /// /// Enables endpoint rate limiting based URL path and HTTP verb /// @@ -56,4 +62,4 @@ namespace Ocelot.Configuration /// public bool DisableRateLimitHeaders { get; private set; } } -} +} diff --git a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs index 100d01f4..f2fbad2d 100644 --- a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs +++ b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs @@ -1,6 +1,6 @@ namespace Ocelot.Requester { - using Errors; + using Ocelot.Errors; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; @@ -23,7 +23,7 @@ namespace Ocelot.Requester return _mappers[type](exception); } - if (type == typeof(OperationCanceledException)) + if (type == typeof(OperationCanceledException) || type.IsSubclassOf(typeof(OperationCanceledException))) { return new RequestCanceledError(exception.Message); } diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs index 92d3128e..a48aa84d 100644 --- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs +++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs @@ -1,6 +1,9 @@ +using System.Net; +using System.Net.Http; using Ocelot.Logging; using Ocelot.Middleware; using System.Threading.Tasks; +using Ocelot.Responses; namespace Ocelot.Requester.Middleware { @@ -22,6 +25,8 @@ namespace Ocelot.Requester.Middleware { var response = await _requester.GetResponse(context); + CreateLogBasedOnResponse(response); + if (response.IsError) { Logger.LogDebug("IHttpRequester returned an error, setting pipeline error"); @@ -36,5 +41,19 @@ namespace Ocelot.Requester.Middleware await _next.Invoke(context); } + + private void CreateLogBasedOnResponse(Response response) + { + if (response.Data?.StatusCode <= HttpStatusCode.BadRequest) + { + Logger.LogInformation( + $"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}"); + } + else if (response.Data?.StatusCode >= HttpStatusCode.BadRequest) + { + Logger.LogWarning( + $"{(int) response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}"); + } + } } } diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 4782eeb5..356561bb 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -1,74 +1,75 @@ - - - 0.0.0-dev - netcoreapp3.1 - Ocelot.AcceptanceTests - Exe - Ocelot.AcceptanceTests - true - osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 - false - false - false - ..\..\codeanalysis.ruleset - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - - - - - - - - - - - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - + + + 0.0.0-dev + netcoreapp3.1 + Ocelot.AcceptanceTests + Exe + Ocelot.AcceptanceTests + true + osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 + false + false + false + ..\..\codeanalysis.ruleset + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs index ac2b6be8..cd5be7a7 100644 --- a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs +++ b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs @@ -34,7 +34,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 50092, + Port = 51092, } }, UpstreamPathTemplate = "/{everything}", @@ -43,7 +43,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:50092", "/inline.132.bundle.js", 304)) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51092", "/inline.132.bundle.js", 304)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/inline.132.bundle.js")) diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index f72efff8..fd04af61 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -20,7 +20,8 @@ [Fact] public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error() - { + { + var configuration = new FileConfiguration { ReRoutes = new List @@ -49,6 +50,39 @@ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError)) .BDDfy(); + } + + [Fact] + public void should_log_warning_if_downstream_service_returns_internal_server_error() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53876, + }, + }, + DownstreamScheme = "http", + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53876")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithLogger()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenWarningShouldBeLogged()) + .BDDfy(); } private void GivenThereIsAServiceRunningOn(string url) diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 5bddcd92..9bfb2091 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -10,12 +10,14 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; + using Moq; using Newtonsoft.Json; using Ocelot.Cache.CacheManager; using Ocelot.Configuration.Creator; using Ocelot.Configuration.File; using Ocelot.DependencyInjection; using Ocelot.Infrastructure; + using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.Middleware.Multiplexer; using Ocelot.Provider.Consul; @@ -1120,5 +1122,60 @@ _ocelotClient = _ocelotServer.CreateClient(); } + + public void GivenOcelotIsRunningWithLogger() + { + _webHostBuilder = new WebHostBuilder(); + + _webHostBuilder + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + s.AddSingleton(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void ThenWarningShouldBeLogged() + { + MockLoggerFactory loggerFactory = (MockLoggerFactory)_ocelotServer.Host.Services.GetService(); + loggerFactory.Verify(); + } + + internal class MockLoggerFactory : IOcelotLoggerFactory + { + private Mock _logger; + + public IOcelotLogger CreateLogger() + { + if (_logger == null) + { + _logger = new Mock(); + _logger.Setup(x => x.LogWarning(It.IsAny())).Verifiable(); + } + return _logger.Object; + } + + public void Verify() + { + _logger.Verify(x => x.LogWarning(It.IsAny()), Times.Once); + } + } } } diff --git a/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs index 75e9cfca..a2bc1957 100644 --- a/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs @@ -50,7 +50,7 @@ namespace Ocelot.UnitTests.Configuration }; var expected = new RateLimitOptionsBuilder() .WithClientIdHeader("ClientIdHeader") - .WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist) + .WithClientWhiteList(() => fileReRoute.RateLimitOptions.ClientWhitelist) .WithDisableRateLimitHeaders(true) .WithEnableRateLimiting(true) .WithHttpStatusCode(200) diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index 2b54bbfa..d0f69cec 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -49,15 +49,15 @@ namespace Ocelot.UnitTests.RateLimit [Fact] public void should_call_middleware_and_ratelimiting() - { - var upstreamTemplate = new UpstreamPathTemplateBuilder().Build(); + { + var upstreamTemplate = new UpstreamPathTemplateBuilder().Build(); var downstreamReRoute = new DownstreamReRouteBuilder() .WithEnableRateLimiting(true) - .WithRateLimitOptions(new RateLimitOptions(true, "ClientId", new List(), false, "", "", new RateLimitRule("1s", 100, 3), 429)) + .WithRateLimitOptions(new RateLimitOptions(true, "ClientId", () => new List(), false, "", "", new RateLimitRule("1s", 100, 3), 429)) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(upstreamTemplate) - .Build(); + .Build(); var reRoute = new ReRouteBuilder() .WithDownstreamReRoute(downstreamReRoute) @@ -82,7 +82,7 @@ namespace Ocelot.UnitTests.RateLimit .WithDownstreamReRoute(new DownstreamReRouteBuilder() .WithEnableRateLimiting(true) .WithRateLimitOptions( - new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List() { "ocelotclient2" }, false, "", "", new RateLimitRule("1s", 100, 3), 429)) + new Ocelot.Configuration.RateLimitOptions(true, "ClientId", () => new List() { "ocelotclient2" }, false, "", "", new RateLimitRule("1s", 100, 3), 429)) .WithUpstreamHttpMethod(new List { "Get" }) .Build()) .WithUpstreamHttpMethod(new List { "Get" }) @@ -102,8 +102,8 @@ namespace Ocelot.UnitTests.RateLimit private void WhenICallTheMiddlewareMultipleTime(int times) { - var clientId = "ocelotclient1"; - + var clientId = "ocelotclient1"; + for (int i = 0; i < times; i++) { var request = new HttpRequestMessage(new HttpMethod("GET"), _url); @@ -117,8 +117,8 @@ namespace Ocelot.UnitTests.RateLimit private void WhenICallTheMiddlewareWithWhiteClient() { - var clientId = "ocelotclient2"; - + var clientId = "ocelotclient2"; + for (int i = 0; i < 10; i++) { var request = new HttpRequestMessage(new HttpMethod("GET"), _url); @@ -127,10 +127,10 @@ namespace Ocelot.UnitTests.RateLimit _downstreamContext.HttpContext.Request.Headers.TryAdd("ClientId", clientId); _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); - _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode; - } - } - + _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode; + } + } + private void ThenresponseStatusCodeIs429() { _responseStatusCode.ShouldBe(429); @@ -145,7 +145,7 @@ namespace Ocelot.UnitTests.RateLimit internal class FakeStream : Stream { public override void Flush() - { + { //do nothing //throw new System.NotImplementedException(); } @@ -176,4 +176,4 @@ namespace Ocelot.UnitTests.RateLimit public override long Length { get; } public override long Position { get; set; } } -} +} diff --git a/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs b/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs index bad8668e..14413aad 100644 --- a/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs @@ -38,6 +38,14 @@ error.ShouldBeOfType(); } + [Fact] + public void should_return_request_canceled_for_subtype() + { + var error = _mapper.Map(new SomeException()); + + error.ShouldBeOfType(); + } + [Fact] public void should_return_error_from_mapper() { @@ -56,5 +64,8 @@ error.ShouldBeOfType(); } + + private class SomeException : OperationCanceledException + { } } } diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index a597df6b..8a7b1fc8 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -41,9 +41,10 @@ namespace Ocelot.UnitTests.Requester public void should_call_services_correctly() { this.Given(x => x.GivenTheRequestIs()) - .And(x => x.GivenTheRequesterReturns(new OkResponse(new HttpResponseMessage()))) + .And(x => x.GivenTheRequesterReturns(new OkResponse(new HttpResponseMessage(System.Net.HttpStatusCode.OK)))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheDownstreamResponseIsSet()) + .Then(x => InformationIsLogged()) .BDDfy(); } @@ -57,6 +58,17 @@ namespace Ocelot.UnitTests.Requester .BDDfy(); } + [Fact] + public void should_log_downstream_internal_server_error() + { + this.Given(x => x.GivenTheRequestIs()) + .And(x => x.GivenTheRequesterReturns( + new OkResponse(new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)))) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.WarningIsLogged()) + .BDDfy(); + } + private void ThenTheErrorIsSet() { _downstreamContext.IsError.ShouldBeTrue(); @@ -98,5 +110,23 @@ namespace Ocelot.UnitTests.Requester _downstreamContext.DownstreamResponse.Content.ShouldBe(_response.Data.Content); _downstreamContext.DownstreamResponse.StatusCode.ShouldBe(_response.Data.StatusCode); } + + private void WarningIsLogged() + { + _logger.Verify( + x => x.LogWarning( + It.IsAny() + ), + Times.Once); + } + + private void InformationIsLogged() + { + _logger.Verify( + x => x.LogInformation( + It.IsAny() + ), + Times.Once); + } } } From e45071fa1070b026137f51f6fec94f9fa077a4be Mon Sep 17 00:00:00 2001 From: EL Aisati Ahmed Date: Tue, 4 Feb 2020 17:59:39 +0100 Subject: [PATCH 04/28] Fix issue #1088 (Ocelot Administration doesn't work with .NET Core 3.x) Fixed OcelotMiddlewareConfigurationDelegate configuration --- samples/AdministrationApi/AdministrationApi.csproj | 9 +++++---- samples/AdministrationApi/Program.cs | 13 ++++--------- ...IdentityServerMiddlewareConfigurationProvider.cs | 8 +++++++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj index 3fcf5dc8..7f8a351c 100644 --- a/samples/AdministrationApi/AdministrationApi.csproj +++ b/samples/AdministrationApi/AdministrationApi.csproj @@ -3,11 +3,12 @@ netcoreapp3.1 - + - - - + + + + \ No newline at end of file diff --git a/samples/AdministrationApi/Program.cs b/samples/AdministrationApi/Program.cs index a5e46fda..a69f1dc6 100644 --- a/samples/AdministrationApi/Program.cs +++ b/samples/AdministrationApi/Program.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Ocelot.Administration; using Ocelot.DependencyInjection; using Ocelot.Middleware; -using Ocelot.Administration; +using System.IO; namespace AdministrationApi { - public class Program + public class Program { public static void Main(string[] args) { diff --git a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs index b4b8994a..0d263463 100644 --- a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs +++ b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs @@ -27,7 +27,13 @@ } app.UseAuthentication(); - app.UseMvc(); + app.UseRouting(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + }); }); } From 157737ef01b33ba784814392d080d442e273a808 Mon Sep 17 00:00:00 2001 From: EL Aisati Ahmed Date: Tue, 4 Feb 2020 18:11:38 +0100 Subject: [PATCH 05/28] Revert "Merge branch 'feature/issue#1088'" This reverts commit 914a386b0e79612a832a0ddc4c1b7fda7e8cf4a2. --- samples/AdministrationApi/AdministrationApi.csproj | 9 ++++----- samples/AdministrationApi/Program.cs | 13 +++++++++---- ...IdentityServerMiddlewareConfigurationProvider.cs | 8 +------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj index 7f8a351c..3fcf5dc8 100644 --- a/samples/AdministrationApi/AdministrationApi.csproj +++ b/samples/AdministrationApi/AdministrationApi.csproj @@ -3,12 +3,11 @@ netcoreapp3.1 - + - - - - + + + \ No newline at end of file diff --git a/samples/AdministrationApi/Program.cs b/samples/AdministrationApi/Program.cs index a69f1dc6..a5e46fda 100644 --- a/samples/AdministrationApi/Program.cs +++ b/samples/AdministrationApi/Program.cs @@ -1,14 +1,19 @@ -using Microsoft.AspNetCore.Hosting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Ocelot.Administration; using Ocelot.DependencyInjection; using Ocelot.Middleware; -using System.IO; +using Ocelot.Administration; namespace AdministrationApi { - public class Program + public class Program { public static void Main(string[] args) { diff --git a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs index 0d263463..b4b8994a 100644 --- a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs +++ b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs @@ -27,13 +27,7 @@ } app.UseAuthentication(); - app.UseRouting(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - }); + app.UseMvc(); }); } From f4eb93b66ca072d5f1b93a5d95eb64d54d1b5977 Mon Sep 17 00:00:00 2001 From: EL Aisati Ahmed Date: Tue, 4 Feb 2020 18:20:33 +0100 Subject: [PATCH 06/28] Fix issue #1088 (Ocelot Administration doesn't work with .NET Core 3.x) --- samples/AdministrationApi/AdministrationApi.csproj | 12 +++++++----- samples/AdministrationApi/Program.cs | 13 ++++--------- ...IdentityServerMiddlewareConfigurationProvider.cs | 8 +++++++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj index 3fcf5dc8..4366ca06 100644 --- a/samples/AdministrationApi/AdministrationApi.csproj +++ b/samples/AdministrationApi/AdministrationApi.csproj @@ -1,13 +1,15 @@ - + netcoreapp3.1 - + + + + + - - - + \ No newline at end of file diff --git a/samples/AdministrationApi/Program.cs b/samples/AdministrationApi/Program.cs index a5e46fda..a69f1dc6 100644 --- a/samples/AdministrationApi/Program.cs +++ b/samples/AdministrationApi/Program.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Ocelot.Administration; using Ocelot.DependencyInjection; using Ocelot.Middleware; -using Ocelot.Administration; +using System.IO; namespace AdministrationApi { - public class Program + public class Program { public static void Main(string[] args) { diff --git a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs index b4b8994a..0d263463 100644 --- a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs +++ b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs @@ -27,7 +27,13 @@ } app.UseAuthentication(); - app.UseMvc(); + app.UseRouting(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + }); }); } From b5dc618780b362f449ecbf28140053500d4d30b7 Mon Sep 17 00:00:00 2001 From: eitamal <4740959+eitamal@users.noreply.github.com> Date: Wed, 5 Feb 2020 04:22:33 +1000 Subject: [PATCH 07/28] Fix URL for the Ocelot logo in README.md (#1117) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a655d3d2..4de2f08d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[](https://threemammals.com/ocelot) +[](https://threemammals.com/ocelot) [![CircleCI](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master.svg?style=svg)](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master) From 73e56322e0bf469c1f3b9e0d240daa644cb15a4c Mon Sep 17 00:00:00 2001 From: WebMed Date: Tue, 4 Feb 2020 20:50:13 +0100 Subject: [PATCH 08/28] Removed direct reference to Ocelot.Administration project --- samples/AdministrationApi/AdministrationApi.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj index 4366ca06..e634ba2c 100644 --- a/samples/AdministrationApi/AdministrationApi.csproj +++ b/samples/AdministrationApi/AdministrationApi.csproj @@ -5,11 +5,9 @@ - + + - - - \ No newline at end of file From 473d50ff361fc98d131f2e5409f883741e710c6d Mon Sep 17 00:00:00 2001 From: Jonathan Mezach Date: Tue, 4 Feb 2020 21:10:25 +0100 Subject: [PATCH 09/28] Update README.md (#1110) Fix link to Slack ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4de2f08d..57a93697 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?branch=master)](https://coveralls.io/github/ThreeMammals/Ocelot?branch=master) -[Slack](threemammals.slack.com) +[Slack](https://threemammals.slack.com) # Ocelot From 86e8d66daf1365e87ad9ba9b00724e65b129c560 Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Tue, 4 Feb 2020 20:50:40 +0000 Subject: [PATCH 10/28] Activate ChangeToken when Ocelot's configuration changes #1037 * Add configuration change token (#1036) * Add IOptionsMonitor * Activate change token from *ConfigurationRepository instead of FileAndInternalConfigurationSetter; add acceptance & integration tests * Update documentation * Use IWebHostEnvironment as IHostingEnvironment deprecated Co-authored-by: Chris Swinchatt --- docs/features/configuration.rst | 510 ++--- .../IOcelotConfigurationChangeTokenSource.cs | 14 + .../OcelotConfigurationChangeToken.cs | 74 + .../OcelotConfigurationChangeTokenSource.cs | 16 + .../OcelotConfigurationMonitor.cs | 30 + .../DiskFileConfigurationRepository.cs | 6 +- ...InMemoryInternalConfigurationRepository.cs | 10 +- .../DependencyInjection/OcelotBuilder.cs | 9 +- src/Ocelot/Properties/AssemblyInfo.cs | 2 +- .../ConfigurationReloadTests.cs | 28 + test/Ocelot.AcceptanceTests/Steps.cs | 15 +- .../AdministrationTests.cs | 1774 +++++++++-------- ...elotConfigurationChangeTokenSourceTests.cs | 35 + .../OcelotConfigurationChangeTokenTests.cs | 91 + .../DiskFileConfigurationRepositoryTests.cs | 20 +- .../FileConfigurationSetterTests.cs | 8 +- .../InMemoryConfigurationRepositoryTests.cs | 15 +- 17 files changed, 1546 insertions(+), 1111 deletions(-) create mode 100644 src/Ocelot/Configuration/ChangeTracking/IOcelotConfigurationChangeTokenSource.cs create mode 100644 src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeToken.cs create mode 100644 src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSource.cs create mode 100644 src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationMonitor.cs create mode 100644 test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSourceTests.cs create mode 100644 test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenTests.cs diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst index 7983af39..cc4afa0c 100644 --- a/docs/features/configuration.rst +++ b/docs/features/configuration.rst @@ -1,230 +1,280 @@ -Configuration -============ - -An example configuration can be found `here `_. -There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration. -The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global -configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful -if you don't want to manage lots of ReRoute specific settings. - -.. code-block:: json - - { - "ReRoutes": [], - "GlobalConfiguration": {} - } - -Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment: - -.. code-block:: json - - { - "DownstreamPathTemplate": "/", - "UpstreamPathTemplate": "/", - "UpstreamHttpMethod": [ - "Get" - ], - "AddHeadersToRequest": {}, - "AddClaimsToRequest": {}, - "RouteClaimsRequirement": {}, - "AddQueriesToRequest": {}, - "RequestIdKey": "", - "FileCacheOptions": { - "TtlSeconds": 0, - "Region": "" - }, - "ReRouteIsCaseSensitive": false, - "ServiceName": "", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 51876, - } - ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 0, - "DurationOfBreak": 0, - "TimeoutValue": 0 - }, - "LoadBalancer": "", - "RateLimitOptions": { - "ClientWhitelist": [], - "EnableRateLimiting": false, - "Period": "", - "PeriodTimespan": 0, - "Limit": 0 - }, - "AuthenticationOptions": { - "AuthenticationProviderKey": "", - "AllowedScopes": [] - }, - "HttpHandlerOptions": { - "AllowAutoRedirect": true, - "UseCookieContainer": true, - "UseTracing": true, - "MaxConnectionsPerServer": 100 - }, - "DangerousAcceptAnyServerCertificateValidator": false - } - -More information on how to use these options is below.. - -Multiple environments -^^^^^^^^^^^^^^^^^^^^^ - -Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following -to you - -.. code-block:: csharp - - .ConfigureAppConfiguration((hostingContext, config) => - { - config - .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) - .AddJsonFile("ocelot.json") - .AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json") - .AddEnvironmentVariables(); - }) - -Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isn't one. - -You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs `_. - -Merging configuration files -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This feature was requested in `Issue 296 `_ and allows users to have multiple configuration files to make managing large configurations easier. - -Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below. - -.. code-block:: csharp - - .ConfigureAppConfiguration((hostingContext, config) => - { - config - .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) - .AddOcelot(hostingContext.HostingEnvironment) - .AddEnvironmentVariables(); - }) - -In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json. - -The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running. - -At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems. - -You can also give Ocelot a specific path to look in for the configuration files like below. - -.. code-block:: csharp - - .ConfigureAppConfiguration((hostingContext, config) => - { - config - .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) - .AddOcelot("/foo/bar", hostingContext.HostingEnvironment) - .AddEnvironmentVariables(); - }) - -Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm. - -Store configuration in consul -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first thing you need to do is install the NuGet package that provides Consul support in Ocelot. - -``Install-Package Ocelot.Provider.Consul`` - -Then you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store. - -.. code-block:: csharp - - services - .AddOcelot() - .AddConsul() - .AddConfigStoredInConsul(); - -You also need to add the following to your ocelot.json. This is how Ocelot -finds your Consul agent and interacts to load and store the configuration from Consul. - -.. code-block:: json - - "GlobalConfiguration": { - "ServiceDiscoveryProvider": { - "Host": "localhost", - "Port": 9500 - } - } - -I decided to create this feature after working on the Raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this! -I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now. - -This feature has a 3 second ttl cache before making a new request to your local consul agent. - -Reload JSON config on change -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Ocelot supports reloading the json configuration file on change. e.g. the following will recreate Ocelots internal configuration when the ocelot.json file is updated -manually. - -.. code-block:: json - - config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); - -Configuration Key ------------------ - -If you are using Consul for configuration (or other providers in the future) you might want to key your configurations so you can have multiple configurations :) This feature was requested in `issue 346 `_! In order to specify the key you need to set the ConfigurationKey property in the ServiceDiscoveryProvider section of the configuration json file e.g. - -.. code-block:: json - - "GlobalConfiguration": { - "ServiceDiscoveryProvider": { - "Host": "localhost", - "Port": 9500, - "ConfigurationKey": "Oceolot_A" - } - } - -In this example Ocelot will use Oceolot_A as the key for your configuration when looking it up in Consul. - -If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key. - -Follow Redirects / Use CookieContainer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: - -1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically -follow redirection responses from the Downstream resource; otherwise false. The default value is false. - -2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer -property to store server cookies and uses these cookies when sending requests. The default value is false. Please note -that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests -to that DownstreamService will share the same cookies. `Issue 274 `_ was created because a user -noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients -that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight -requests. This would also mean that subsequent requests don't use the cookies from the previous response! All in all not a great situation. I would avoid setting -UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request! - -SSL Errors -^^^^^^^^^^ - -If you want to ignore SSL warnings / errors set the following in your ReRoute config. - -.. code-block:: json - - "DangerousAcceptAnyServerCertificateValidator": true - -I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can. - -MaxConnectionsPerServer -^^^^^^^^^^^^^^^^^^^^^^^ - -This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level. \ No newline at end of file +Configuration +============ + +An example configuration can be found `here `_. +There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration. +The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global +configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful +if you don't want to manage lots of ReRoute specific settings. + +.. code-block:: json + + { + "ReRoutes": [], + "GlobalConfiguration": {} + } + +Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment: + +.. code-block:: json + + { + "DownstreamPathTemplate": "/", + "UpstreamPathTemplate": "/", + "UpstreamHttpMethod": [ + "Get" + ], + "AddHeadersToRequest": {}, + "AddClaimsToRequest": {}, + "RouteClaimsRequirement": {}, + "AddQueriesToRequest": {}, + "RequestIdKey": "", + "FileCacheOptions": { + "TtlSeconds": 0, + "Region": "" + }, + "ReRouteIsCaseSensitive": false, + "ServiceName": "", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 51876, + } + ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 0, + "DurationOfBreak": 0, + "TimeoutValue": 0 + }, + "LoadBalancer": "", + "RateLimitOptions": { + "ClientWhitelist": [], + "EnableRateLimiting": false, + "Period": "", + "PeriodTimespan": 0, + "Limit": 0 + }, + "AuthenticationOptions": { + "AuthenticationProviderKey": "", + "AllowedScopes": [] + }, + "HttpHandlerOptions": { + "AllowAutoRedirect": true, + "UseCookieContainer": true, + "UseTracing": true, + "MaxConnectionsPerServer": 100 + }, + "DangerousAcceptAnyServerCertificateValidator": false + } + +More information on how to use these options is below.. + +Multiple environments +^^^^^^^^^^^^^^^^^^^^^ + +Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following +to you + +.. code-block:: csharp + + .ConfigureAppConfiguration((hostingContext, config) => + { + config + .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) + .AddJsonFile("ocelot.json") + .AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json") + .AddEnvironmentVariables(); + }) + +Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isn't one. + +You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs `_. + +Merging configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This feature was requested in `Issue 296 `_ and allows users to have multiple configuration files to make managing large configurations easier. + +Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below. + +.. code-block:: csharp + + .ConfigureAppConfiguration((hostingContext, config) => + { + config + .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) + .AddOcelot(hostingContext.HostingEnvironment) + .AddEnvironmentVariables(); + }) + +In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json. + +The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running. + +At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems. + +You can also give Ocelot a specific path to look in for the configuration files like below. + +.. code-block:: csharp + + .ConfigureAppConfiguration((hostingContext, config) => + { + config + .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) + .AddOcelot("/foo/bar", hostingContext.HostingEnvironment) + .AddEnvironmentVariables(); + }) + +Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm. + +Store configuration in consul +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The first thing you need to do is install the NuGet package that provides Consul support in Ocelot. + +``Install-Package Ocelot.Provider.Consul`` + +Then you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store. + +.. code-block:: csharp + + services + .AddOcelot() + .AddConsul() + .AddConfigStoredInConsul(); + +You also need to add the following to your ocelot.json. This is how Ocelot +finds your Consul agent and interacts to load and store the configuration from Consul. + +.. code-block:: json + + "GlobalConfiguration": { + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 9500 + } + } + +I decided to create this feature after working on the Raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this! +I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now. + +This feature has a 3 second ttl cache before making a new request to your local consul agent. + +Reload JSON config on change +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ocelot supports reloading the json configuration file on change. e.g. the following will recreate Ocelots internal configuration when the ocelot.json file is updated +manually. + +.. code-block:: json + + config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); + +Configuration Key +----------------- + +If you are using Consul for configuration (or other providers in the future) you might want to key your configurations so you can have multiple configurations :) This feature was requested in `issue 346 `_! In order to specify the key you need to set the ConfigurationKey property in the ServiceDiscoveryProvider section of the configuration json file e.g. + +.. code-block:: json + + "GlobalConfiguration": { + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 9500, + "ConfigurationKey": "Oceolot_A" + } + } + +In this example Ocelot will use Oceolot_A as the key for your configuration when looking it up in Consul. + +If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key. + +Follow Redirects / Use CookieContainer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: + +1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically +follow redirection responses from the Downstream resource; otherwise false. The default value is false. + +2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer +property to store server cookies and uses these cookies when sending requests. The default value is false. Please note +that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests +to that DownstreamService will share the same cookies. `Issue 274 `_ was created because a user +noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients +that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight +requests. This would also mean that subsequent requests don't use the cookies from the previous response! All in all not a great situation. I would avoid setting +UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request! + +SSL Errors +^^^^^^^^^^ + +If you want to ignore SSL warnings / errors set the following in your ReRoute config. + +.. code-block:: json + + "DangerousAcceptAnyServerCertificateValidator": true + +I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can. + +MaxConnectionsPerServer +^^^^^^^^^^^^^^^^^^^^^^^ + +This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level. + +React to Configuration Changes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Resolve IOcelotConfigurationChangeTokenSource from the DI container if you wish to react to changes to the Ocelot configuration via the Ocelot.Administration API or ocelot.json being reloaded from the disk. You may either poll the change token's HasChanged property, or register a callback with the RegisterChangeCallback method. + +Polling the HasChanged property +------------------------------- + +.. code-block:: csharp + public class ConfigurationNotifyingService : BackgroundService + { + private readonly IOcelotConfigurationChangeTokenSource _tokenSource; + private readonly ILogger _logger; + public ConfigurationNotifyingService(IOcelotConfigurationChangeTokenSource tokenSource, ILogger logger) + { + _tokenSource = tokenSource; + _logger = logger; + } + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + if (_tokenSource.ChangeToken.HasChanged) + { + _logger.LogInformation("Configuration updated"); + } + await Task.Delay(1000, stoppingToken); + } + } + } + +Registering a callback +---------------------- + +.. code-block:: csharp + public class MyDependencyInjectedClass : IDisposable + { + private readonly IOcelotConfigurationChangeTokenSource _tokenSource; + private readonly IDisposable _callbackHolder; + public MyClass(IOcelotConfigurationChangeTokenSource tokenSource) + { + _tokenSource = tokenSource; + _callbackHolder = tokenSource.ChangeToken.RegisterChangeCallback(_ => Console.WriteLine("Configuration changed"), null); + } + public void Dispose() + { + _callbackHolder.Dispose(); + } + } \ No newline at end of file diff --git a/src/Ocelot/Configuration/ChangeTracking/IOcelotConfigurationChangeTokenSource.cs b/src/Ocelot/Configuration/ChangeTracking/IOcelotConfigurationChangeTokenSource.cs new file mode 100644 index 00000000..f69d91af --- /dev/null +++ b/src/Ocelot/Configuration/ChangeTracking/IOcelotConfigurationChangeTokenSource.cs @@ -0,0 +1,14 @@ +namespace Ocelot.Configuration.ChangeTracking +{ + using Microsoft.Extensions.Primitives; + + /// + /// source which is activated when Ocelot's configuration is changed. + /// + public interface IOcelotConfigurationChangeTokenSource + { + IChangeToken ChangeToken { get; } + + void Activate(); + } +} diff --git a/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeToken.cs b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeToken.cs new file mode 100644 index 00000000..ecefcf9b --- /dev/null +++ b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeToken.cs @@ -0,0 +1,74 @@ +namespace Ocelot.Configuration.ChangeTracking +{ + using System; + using System.Collections.Generic; + using Microsoft.Extensions.Primitives; + + public class OcelotConfigurationChangeToken : IChangeToken + { + public const double PollingIntervalSeconds = 1; + + private readonly ICollection _callbacks = new List(); + private readonly object _lock = new object(); + private DateTime? _timeChanged; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + lock (_lock) + { + var wrapper = new CallbackWrapper(callback, state, _callbacks, _lock); + _callbacks.Add(wrapper); + return wrapper; + } + } + + public void Activate() + { + lock (_lock) + { + _timeChanged = DateTime.UtcNow; + foreach (var wrapper in _callbacks) + { + wrapper.Invoke(); + } + } + } + + // Token stays active for PollingIntervalSeconds after a change (could be parameterised) - otherwise HasChanged would be true forever. + // Taking suggestions for better ways to reset HasChanged back to false. + public bool HasChanged => _timeChanged.HasValue && (DateTime.UtcNow - _timeChanged.Value).TotalSeconds < PollingIntervalSeconds; + + public bool ActiveChangeCallbacks => true; + + private class CallbackWrapper : IDisposable + { + private readonly ICollection _callbacks; + private readonly object _lock; + + public CallbackWrapper(Action callback, object state, ICollection callbacks, object @lock) + { + _callbacks = callbacks; + _lock = @lock; + Callback = callback; + State = state; + } + + public void Invoke() + { + Callback.Invoke(State); + } + + public void Dispose() + { + lock (_lock) + { + _callbacks.Remove(this); + } + } + + public Action Callback { get; } + + public object State { get; } + } + } +} diff --git a/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSource.cs b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSource.cs new file mode 100644 index 00000000..422864d2 --- /dev/null +++ b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSource.cs @@ -0,0 +1,16 @@ +namespace Ocelot.Configuration.ChangeTracking +{ + using Microsoft.Extensions.Primitives; + + public class OcelotConfigurationChangeTokenSource : IOcelotConfigurationChangeTokenSource + { + private readonly OcelotConfigurationChangeToken _changeToken = new OcelotConfigurationChangeToken(); + + public IChangeToken ChangeToken => _changeToken; + + public void Activate() + { + _changeToken.Activate(); + } + } +} diff --git a/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationMonitor.cs b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationMonitor.cs new file mode 100644 index 00000000..4e5536d0 --- /dev/null +++ b/src/Ocelot/Configuration/ChangeTracking/OcelotConfigurationMonitor.cs @@ -0,0 +1,30 @@ +namespace Ocelot.Configuration.ChangeTracking +{ + using System; + using Microsoft.Extensions.Options; + using Ocelot.Configuration.Repository; + + public class OcelotConfigurationMonitor : IOptionsMonitor + { + private readonly IOcelotConfigurationChangeTokenSource _changeTokenSource; + private readonly IInternalConfigurationRepository _repo; + + public OcelotConfigurationMonitor(IInternalConfigurationRepository repo, IOcelotConfigurationChangeTokenSource changeTokenSource) + { + _changeTokenSource = changeTokenSource; + _repo = repo; + } + + public IInternalConfiguration Get(string name) + { + return _repo.Get().Data; + } + + public IDisposable OnChange(Action listener) + { + return _changeTokenSource.ChangeToken.RegisterChangeCallback(_ => listener(CurrentValue, ""), null); + } + + public IInternalConfiguration CurrentValue => _repo.Get().Data; + } +} diff --git a/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs index 770d7b7a..965efaae 100644 --- a/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs @@ -4,18 +4,21 @@ using Ocelot.Configuration.File; using Ocelot.Responses; using System; using System.Threading.Tasks; +using Ocelot.Configuration.ChangeTracking; namespace Ocelot.Configuration.Repository { public class DiskFileConfigurationRepository : IFileConfigurationRepository { + private readonly IOcelotConfigurationChangeTokenSource _changeTokenSource; private readonly string _environmentFilePath; private readonly string _ocelotFilePath; private static readonly object _lock = new object(); private const string ConfigurationFileName = "ocelot"; - public DiskFileConfigurationRepository(IWebHostEnvironment hostingEnvironment) + public DiskFileConfigurationRepository(IWebHostEnvironment hostingEnvironment, IOcelotConfigurationChangeTokenSource changeTokenSource) { + _changeTokenSource = changeTokenSource; _environmentFilePath = $"{AppContext.BaseDirectory}{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json"; _ocelotFilePath = $"{AppContext.BaseDirectory}{ConfigurationFileName}.json"; @@ -56,6 +59,7 @@ namespace Ocelot.Configuration.Repository System.IO.File.WriteAllText(_ocelotFilePath, jsonConfiguration); } + _changeTokenSource.Activate(); return Task.FromResult(new OkResponse()); } } diff --git a/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs index 2ac954e3..9daee78b 100644 --- a/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs @@ -1,4 +1,5 @@ -using Ocelot.Responses; +using Ocelot.Configuration.ChangeTracking; +using Ocelot.Responses; namespace Ocelot.Configuration.Repository { @@ -10,6 +11,12 @@ namespace Ocelot.Configuration.Repository private static readonly object LockObject = new object(); private IInternalConfiguration _internalConfiguration; + private readonly IOcelotConfigurationChangeTokenSource _changeTokenSource; + + public InMemoryInternalConfigurationRepository(IOcelotConfigurationChangeTokenSource changeTokenSource) + { + _changeTokenSource = changeTokenSource; + } public Response Get() { @@ -23,6 +30,7 @@ namespace Ocelot.Configuration.Repository _internalConfiguration = internalConfiguration; } + _changeTokenSource.Activate(); return new OkResponse(); } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 8f120997..7f92d50d 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -1,9 +1,12 @@ +using Ocelot.Configuration.ChangeTracking; + namespace Ocelot.DependencyInjection { using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; + using Microsoft.Extensions.Options; using Ocelot.Authorisation; using Ocelot.Cache; using Ocelot.Claims; @@ -112,6 +115,8 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton, OcelotConfigurationMonitor>(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository @@ -215,7 +220,7 @@ namespace Ocelot.DependencyInjection { // see: https://greatrexpectations.com/2018/10/25/decorators-in-net-core-with-dependency-injection var wrappedDescriptor = Services.First(x => x.ServiceType == typeof(IPlaceholders)); - + var objectFactory = ActivatorUtilities.CreateFactory( typeof(ConfigAwarePlaceholders), new[] { typeof(IPlaceholders) }); @@ -229,7 +234,7 @@ namespace Ocelot.DependencyInjection return this; } - + private static object CreateInstance(IServiceProvider services, ServiceDescriptor descriptor) { if (descriptor.ImplementationInstance != null) diff --git a/src/Ocelot/Properties/AssemblyInfo.cs b/src/Ocelot/Properties/AssemblyInfo.cs index c2a11a5b..cf28a510 100644 --- a/src/Ocelot/Properties/AssemblyInfo.cs +++ b/src/Ocelot/Properties/AssemblyInfo.cs @@ -15,4 +15,4 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d6df4206-0dba-41d8-884d-c3e08290fdbb")] +[assembly: Guid("d6df4206-0dba-41d8-884d-c3e08290fdbb")] diff --git a/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs index 90d57504..58a2c797 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs @@ -1,5 +1,6 @@ using Ocelot.Configuration.File; using System; +using Ocelot.Configuration.ChangeTracking; using TestStack.BDDfy; using Xunit; @@ -54,6 +55,33 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_trigger_change_token_on_change() + { + this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig)) + .And(x => _steps.GivenOcelotIsRunningReloadingConfig(true)) + .And(x => _steps.GivenIHaveAChangeToken()) + .And(x => _steps.GivenThereIsAConfiguration(_anotherConfig)) + .And(x => _steps.GivenIWait(MillisecondsToWaitForChangeToken)) + .Then(x => _steps.TheChangeTokenShouldBeActive(true)) + .BDDfy(); + } + + [Fact] + public void should_not_trigger_change_token_with_no_change() + { + this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig)) + .And(x => _steps.GivenOcelotIsRunningReloadingConfig(false)) + .And(x => _steps.GivenIHaveAChangeToken()) + .And(x => _steps.GivenIWait(MillisecondsToWaitForChangeToken)) // Wait for prior activation to expire. + .And(x => _steps.GivenThereIsAConfiguration(_anotherConfig)) + .And(x => _steps.GivenIWait(MillisecondsToWaitForChangeToken)) + .Then(x => _steps.TheChangeTokenShouldBeActive(false)) + .BDDfy(); + } + + private const int MillisecondsToWaitForChangeToken = (int) (OcelotConfigurationChangeToken.PollingIntervalSeconds*1000) - 100; + public void Dispose() { _steps.Dispose(); diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 9bfb2091..341daa36 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -1,4 +1,6 @@ -namespace Ocelot.AcceptanceTests +using Ocelot.Configuration.ChangeTracking; + +namespace Ocelot.AcceptanceTests { using Caching; using Configuration.Repository; @@ -54,6 +56,7 @@ private IWebHostBuilder _webHostBuilder; private WebHostBuilder _ocelotBuilder; private IWebHost _ocelotHost; + private IOcelotConfigurationChangeTokenSource _changeToken; public Steps() { @@ -216,6 +219,11 @@ _ocelotClient = _ocelotServer.CreateClient(); } + public void GivenIHaveAChangeToken() + { + _changeToken = _ocelotServer.Host.Services.GetRequiredService(); + } + /// /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. /// @@ -1123,6 +1131,11 @@ _ocelotClient = _ocelotServer.CreateClient(); } + public void TheChangeTokenShouldBeActive(bool itShouldBeActive) + { + _changeToken.ChangeToken.HasChanged.ShouldBe(itShouldBeActive); + } + public void GivenOcelotIsRunningWithLogger() { _webHostBuilder = new WebHostBuilder(); diff --git a/test/Ocelot.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index fe18965d..8c378ece 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -1,864 +1,910 @@ -using IdentityServer4.AccessTokenValidation; -using IdentityServer4.Models; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Newtonsoft.Json; -using Ocelot.Administration; -using Ocelot.Cache; -using Ocelot.Configuration.File; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; -using Shouldly; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.IntegrationTests -{ - public class AdministrationTests : IDisposable - { - private HttpClient _httpClient; - private readonly HttpClient _httpClientTwo; - private HttpResponseMessage _response; - private IHost _builder; - private IHostBuilder _webHostBuilder; - private string _ocelotBaseUrl; - private BearerToken _token; - private IHostBuilder _webHostBuilderTwo; - private IHost _builderTwo; - private IHost _identityServerBuilder; - private IHost _fooServiceBuilder; - private IHost _barServiceBuilder; - - public AdministrationTests() - { - _httpClient = new HttpClient(); - _httpClientTwo = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5000"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - } - - [Fact] - public void should_return_response_401_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() - { - _httpClient = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5011"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - BaseUrl = _ocelotBaseUrl - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) - .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_file_configuration() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - RequestIdKey = "RequestId", - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Host = "127.0.0.1", - } - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Geoff" - } - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Dave" - } - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(configuration)) - .BDDfy(); - } - - [Fact] - public void should_get_file_configuration_edit_and_post_updated_version() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test" - } - }, - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) - .BDDfy(); - } - - private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected) - { - var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json"; - var resultText = File.ReadAllText(ocelotJsonPath); - var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); - resultText.ShouldBe(expectedText); - - var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json"; - resultText = File.ReadAllText(environmentSpecificPath); - expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); - resultText.ShouldBe(expectedText); - } - - [Fact] - public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute() - { - var fooPort = 47689; - var barPort = 47690; - - var initialConfiguration = new FileConfiguration - { - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = fooPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/foo", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = barPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/bar", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) - .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) - .And(x => GivenOcelotIsRunning()) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("bar")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(initialConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .BDDfy(); - } - - [Fact] - public void should_clear_region() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - } - } - }; - - var regionToClear = "gettest"; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() - { - var configuration = new FileConfiguration(); - - var identityServerRootUrl = "http://localhost:5123"; - - Action options = o => - { - o.Authority = identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) - .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) - .And(x => GivenIHaveAToken(identityServerRootUrl)) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - private void GivenIHaveAToken(string url) - { - var formData = new List> - { - new KeyValuePair("client_id", "api"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync($"{url}/connect/token", content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName) - { - _identityServerBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = apiName, - Enabled = true, - DisplayName = apiName, - Scopes = new List() - { - new Scope(apiName), - }, - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = apiName, - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List { new Secret("secret".Sha256()) }, - AllowedScopes = new List { apiName }, - AccessTokenType = AccessTokenType.Jwt, - Enabled = true - }, - }) - .AddTestUsers(new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "1231231" - }, - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - } - ); - }).Build(); - - _identityServerBuilder.Start(); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; - response.EnsureSuccessStatusCode(); - } - } - - private void GivenAnotherOcelotIsRunning(string baseUrl) - { - _httpClientTwo.BaseAddress = new Uri(baseUrl); - - _webHostBuilderTwo = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builderTwo = _webHostBuilderTwo.Build(); - - _builderTwo.Start(); - } - - private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); - } - - private void WhenIGetUrlOnTheSecondOcelot(string url) - { - _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - _response = _httpClientTwo.GetAsync(url).Result; - } - - private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) - { - var json = JsonConvert.SerializeObject(updatedConfiguration); - var content = new StringContent(json); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - _response = _httpClient.PostAsync(url, content).Result; - } - - private void ThenTheResponseShouldBe(List expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - var result = JsonConvert.DeserializeObject(content); - result.Value.ShouldBe(expected); - } - - private void ThenTheResponseBodyShouldBe(string expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - content.ShouldBe(expected); - } - - private void ThenTheResponseShouldBe(FileConfiguration expecteds) - { - var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); - - response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.ReRoutes.Count; i++) - { - for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var result = response.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); - response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); - response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate); - response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod); - } - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private void GivenIHaveAnOcelotToken(string adminPath) - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - - var response = _httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = _httpClient.GetAsync(configPath).Result; - response.EnsureSuccessStatusCode(); - } - - private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", configOptions); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunning() - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(s => s.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void WhenIGetUrlOnTheApiGateway(string url) - { - _response = _httpClient.GetAsync(url).Result; - } - - private void WhenIDeleteOnTheApiGateway(string url) - { - _response = _httpClient.DeleteAsync(url).Result; - } - - private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) - { - _response.StatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void Dispose() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); - _builder?.Dispose(); - _httpClient?.Dispose(); - _identityServerBuilder?.Dispose(); - } - - private void GivenThereIsAFooServiceRunningOn(string baseUrl) - { - _fooServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/foo"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("foo"); - }); - }); - }).Build(); - - _fooServiceBuilder.Start(); - } - - private void GivenThereIsABarServiceRunningOn(string baseUrl) - { - _barServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/bar"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("bar"); - }); - }); - }).Build(); - - _barServiceBuilder.Start(); - } - } -} +using IdentityServer4.AccessTokenValidation; +using IdentityServer4.Models; +using IdentityServer4.Test; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using Ocelot.Administration; +using Ocelot.Cache; +using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using TestStack.BDDfy; +using Ocelot.Configuration.ChangeTracking; +using Xunit; + +namespace Ocelot.IntegrationTests +{ + public class AdministrationTests : IDisposable + { + private HttpClient _httpClient; + private readonly HttpClient _httpClientTwo; + private HttpResponseMessage _response; + private IHost _builder; + private IHostBuilder _webHostBuilder; + private string _ocelotBaseUrl; + private BearerToken _token; + private IHostBuilder _webHostBuilderTwo; + private IHost _builderTwo; + private IHost _identityServerBuilder; + private IHost _fooServiceBuilder; + private IHost _barServiceBuilder; + + public AdministrationTests() + { + _httpClient = new HttpClient(); + _httpClientTwo = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5000"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + } + + [Fact] + public void should_return_response_401_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() + { + _httpClient = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5011"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + BaseUrl = _ocelotBaseUrl + } + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) + .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_file_configuration() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "RequestId", + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Host = "127.0.0.1", + } + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Geoff" + } + }, + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Dave" + } + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(configuration)) + .BDDfy(); + } + + [Fact] + public void should_get_file_configuration_edit_and_post_updated_version() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test" + } + }, + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/geoffrey", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.123.123", + Port = 443, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/blooper/{productId}", + UpstreamHttpMethod = new List { "post" }, + UpstreamPathTemplate = "/test" + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) + .BDDfy(); + } + + [Fact] + public void should_activate_change_token_when_configuration_is_updated() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration(), + ReRoutes = new List + { + new FileReRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", configuration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => TheChangeTokenShouldBeActive()) + .And(x => ThenTheResponseShouldBe(configuration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(configuration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(configuration)) + .BDDfy(); + } + + private void TheChangeTokenShouldBeActive() + { + _builder.Services.GetRequiredService().ChangeToken.HasChanged.ShouldBeTrue(); + } + + private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected) + { + var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json"; + var resultText = File.ReadAllText(ocelotJsonPath); + var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); + resultText.ShouldBe(expectedText); + + var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json"; + resultText = File.ReadAllText(environmentSpecificPath); + expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); + resultText.ShouldBe(expectedText); + } + + [Fact] + public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute() + { + var fooPort = 47689; + var barPort = 47690; + + var initialConfiguration = new FileConfiguration + { + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = fooPort, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/foo", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo" + } + } + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = barPort, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/bar", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo" + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) + .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) + .And(x => GivenOcelotIsRunning()) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("bar")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(initialConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .BDDfy(); + } + + [Fact] + public void should_clear_region() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + }, + new FileReRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + } + } + }; + + var regionToClear = "gettest"; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() + { + var configuration = new FileConfiguration(); + + var identityServerRootUrl = "http://localhost:5123"; + + Action options = o => + { + o.Authority = identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) + .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) + .And(x => GivenIHaveAToken(identityServerRootUrl)) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + private void GivenIHaveAToken(string url) + { + var formData = new List> + { + new KeyValuePair("client_id", "api"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync($"{url}/connect/token", content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName) + { + _identityServerBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = apiName, + Enabled = true, + DisplayName = apiName, + Scopes = new List() + { + new Scope(apiName), + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = apiName, + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List { new Secret("secret".Sha256()) }, + AllowedScopes = new List { apiName }, + AccessTokenType = AccessTokenType.Jwt, + Enabled = true + }, + }) + .AddTestUsers(new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "1231231" + }, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + } + ); + }).Build(); + + _identityServerBuilder.Start(); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; + response.EnsureSuccessStatusCode(); + } + } + + private void GivenAnotherOcelotIsRunning(string baseUrl) + { + _httpClientTwo.BaseAddress = new Uri(baseUrl); + + _webHostBuilderTwo = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builderTwo = _webHostBuilderTwo.Build(); + + _builderTwo.Start(); + } + + private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); + } + + private void WhenIGetUrlOnTheSecondOcelot(string url) + { + _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + _response = _httpClientTwo.GetAsync(url).Result; + } + + private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) + { + var json = JsonConvert.SerializeObject(updatedConfiguration); + var content = new StringContent(json); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + _response = _httpClient.PostAsync(url, content).Result; + } + + private void ThenTheResponseShouldBe(List expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + var result = JsonConvert.DeserializeObject(content); + result.Value.ShouldBe(expected); + } + + private void ThenTheResponseBodyShouldBe(string expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + content.ShouldBe(expected); + } + + private void ThenTheResponseShouldBe(FileConfiguration expecteds) + { + var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); + + response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < response.ReRoutes.Count; i++) + { + for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) + { + var result = response.ReRoutes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); + response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); + response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate); + response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod); + } + } + + private void GivenIHaveAddedATokenToMyRequest() + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + private void GivenIHaveAnOcelotToken(string adminPath) + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("grant_type", "client_credentials") + }; + var content = new FormUrlEncodedContent(formData); + + var response = _httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + var configPath = $"{adminPath}/.well-known/openid-configuration"; + response = _httpClient.GetAsync(configPath).Result; + response.EnsureSuccessStatusCode(); + } + + private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", configOptions); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunning() + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(s => s.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + private void WhenIGetUrlOnTheApiGateway(string url) + { + _response = _httpClient.GetAsync(url).Result; + } + + private void WhenIDeleteOnTheApiGateway(string url) + { + _response = _httpClient.DeleteAsync(url).Result; + } + + private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) + { + _response.StatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); + _builder?.Dispose(); + _httpClient?.Dispose(); + _identityServerBuilder?.Dispose(); + } + + private void GivenThereIsAFooServiceRunningOn(string baseUrl) + { + _fooServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/foo"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("foo"); + }); + }); + }).Build(); + + _fooServiceBuilder.Start(); + } + + private void GivenThereIsABarServiceRunningOn(string baseUrl) + { + _barServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/bar"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("bar"); + }); + }); + }).Build(); + + _barServiceBuilder.Start(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSourceTests.cs b/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSourceTests.cs new file mode 100644 index 00000000..dfcf23f2 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenSourceTests.cs @@ -0,0 +1,35 @@ +namespace Ocelot.UnitTests.Configuration.ChangeTracking +{ + using Ocelot.Configuration.ChangeTracking; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class OcelotConfigurationChangeTokenSourceTests + { + private readonly IOcelotConfigurationChangeTokenSource _source; + + public OcelotConfigurationChangeTokenSourceTests() + { + _source = new OcelotConfigurationChangeTokenSource(); + } + + [Fact] + public void should_activate_change_token() + { + this.Given(_ => GivenIActivateTheChangeTokenSource()) + .Then(_ => ThenTheChangeTokenShouldBeActivated()) + .BDDfy(); + } + + private void GivenIActivateTheChangeTokenSource() + { + _source.Activate(); + } + + private void ThenTheChangeTokenShouldBeActivated() + { + _source.ChangeToken.HasChanged.ShouldBeTrue(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenTests.cs b/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenTests.cs new file mode 100644 index 00000000..0b651030 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/ChangeTracking/OcelotConfigurationChangeTokenTests.cs @@ -0,0 +1,91 @@ +using Xunit; + +namespace Ocelot.UnitTests.Configuration.ChangeTracking +{ + using System; + using Shouldly; + using Ocelot.Configuration.ChangeTracking; + using TestStack.BDDfy; + + public class OcelotConfigurationChangeTokenTests + { + [Fact] + public void should_call_callback_with_state() + { + this.Given(_ => GivenIHaveAChangeToken()) + .And(_ => AndIRegisterACallback()) + .Then(_ => ThenIShouldGetADisposableWrapper()) + .Given(_ => GivenIActivateTheToken()) + .Then(_ => ThenTheCallbackShouldBeCalled()) + .BDDfy(); + } + + [Fact] + public void should_not_call_callback_if_it_is_disposed() + { + this.Given(_ => GivenIHaveAChangeToken()) + .And(_ => AndIRegisterACallback()) + .Then(_ => ThenIShouldGetADisposableWrapper()) + .And(_ => GivenIActivateTheToken()) + .And(_ => AndIDisposeTheCallbackWrapper()) + .And(_ => GivenIActivateTheToken()) + .Then(_ => ThenTheCallbackShouldNotBeCalled()) + .BDDfy(); + } + + private OcelotConfigurationChangeToken _changeToken; + private IDisposable _callbackWrapper; + private int _callbackCounter; + private readonly object _callbackInitialState = new object(); + private object _callbackState; + + private void Callback(object state) + { + _callbackCounter++; + _callbackState = state; + _changeToken.HasChanged.ShouldBeTrue(); + } + + private void GivenIHaveAChangeToken() + { + _changeToken = new OcelotConfigurationChangeToken(); + } + + private void AndIRegisterACallback() + { + _callbackWrapper = _changeToken.RegisterChangeCallback(Callback, _callbackInitialState); + } + + private void ThenIShouldGetADisposableWrapper() + { + _callbackWrapper.ShouldNotBeNull(); + } + + private void GivenIActivateTheToken() + { + _callbackCounter = 0; + _callbackState = null; + _changeToken.Activate(); + } + + private void ThenTheCallbackShouldBeCalled() + { + _callbackCounter.ShouldBe(1); + _callbackState.ShouldNotBeNull(); + _callbackState.ShouldBeSameAs(_callbackInitialState); + } + + private void ThenTheCallbackShouldNotBeCalled() + { + _callbackCounter.ShouldBe(0); + _callbackState.ShouldBeNull(); + } + + private void AndIDisposeTheCallbackWrapper() + { + _callbackState = null; + _callbackCounter = 0; + _callbackWrapper.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs index b2eb06b5..7c00c2f4 100644 --- a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs @@ -3,6 +3,7 @@ namespace Ocelot.UnitTests.Configuration using Microsoft.AspNetCore.Hosting; using Moq; using Newtonsoft.Json; + using Ocelot.Configuration.ChangeTracking; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Shouldly; @@ -16,6 +17,7 @@ namespace Ocelot.UnitTests.Configuration public class DiskFileConfigurationRepositoryTests : IDisposable { private readonly Mock _hostingEnvironment; + private readonly Mock _changeTokenSource; private IFileConfigurationRepository _repo; private string _environmentSpecificPath; private string _ocelotJsonPath; @@ -35,7 +37,9 @@ namespace Ocelot.UnitTests.Configuration _semaphore.Wait(); _hostingEnvironment = new Mock(); _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); - _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object); + _changeTokenSource = new Mock(MockBehavior.Strict); + _changeTokenSource.Setup(m => m.Activate()); + _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); } [Fact] @@ -70,6 +74,7 @@ namespace Ocelot.UnitTests.Configuration .When(_ => WhenISetTheConfiguration()) .Then(_ => ThenTheConfigurationIsStoredAs(config)) .And(_ => ThenTheConfigurationJsonIsIndented(config)) + .And(x => AndTheChangeTokenIsActivated()) .BDDfy(); } @@ -117,7 +122,7 @@ namespace Ocelot.UnitTests.Configuration { _environmentName = null; _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); - _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object); + _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); } private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration) @@ -210,6 +215,11 @@ namespace Ocelot.UnitTests.Configuration } } + private void AndTheChangeTokenIsActivated() + { + _changeTokenSource.Verify(m => m.Activate(), Times.Once); + } + private FileConfiguration FakeFileConfigurationForSet() { var reRoutes = new List @@ -222,11 +232,11 @@ namespace Ocelot.UnitTests.Configuration { Host = "123.12.12.12", Port = 80, - } + }, }, DownstreamScheme = "https", - DownstreamPathTemplate = "/asdfs/test/{test}" - } + DownstreamPathTemplate = "/asdfs/test/{test}", + }, }; var globalConfiguration = new FileGlobalConfiguration diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs index 53289434..85fdd53c 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs @@ -9,6 +9,7 @@ using Ocelot.Errors; using Ocelot.Responses; using Shouldly; using System.Collections.Generic; +using Ocelot.Configuration.ChangeTracking; using TestStack.BDDfy; using Xunit; @@ -21,7 +22,7 @@ namespace Ocelot.UnitTests.Configuration private Mock _configRepo; private Mock _configCreator; private Response _configuration; - private object _result; + private object _result; private Mock _repo; public FileConfigurationSetterTests() @@ -104,8 +105,7 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheConfigurationRepositoryIsCalledCorrectly() { - _configRepo - .Verify(x => x.AddOrReplace(_configuration.Data), Times.Once); + _configRepo.Verify(x => x.AddOrReplace(_configuration.Data), Times.Once); } } -} +} diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 30f89916..508f25e0 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -5,6 +5,8 @@ using Ocelot.Responses; using Shouldly; using System; using System.Collections.Generic; +using Moq; +using Ocelot.Configuration.ChangeTracking; using TestStack.BDDfy; using Xunit; @@ -16,10 +18,13 @@ namespace Ocelot.UnitTests.Configuration private IInternalConfiguration _config; private Response _result; private Response _getResult; + private readonly Mock _changeTokenSource; public InMemoryConfigurationRepositoryTests() { - _repo = new InMemoryInternalConfigurationRepository(); + _changeTokenSource = new Mock(MockBehavior.Strict); + _changeTokenSource.Setup(m => m.Activate()); + _repo = new InMemoryInternalConfigurationRepository(_changeTokenSource.Object); } [Fact] @@ -28,6 +33,7 @@ namespace Ocelot.UnitTests.Configuration this.Given(x => x.GivenTheConfigurationIs(new FakeConfig("initial", "adminath"))) .When(x => x.WhenIAddOrReplaceTheConfig()) .Then(x => x.ThenNoErrorsAreReturned()) + .And(x => AndTheChangeTokenIsActivated()) .BDDfy(); } @@ -71,6 +77,11 @@ namespace Ocelot.UnitTests.Configuration _result.IsError.ShouldBeFalse(); } + private void AndTheChangeTokenIsActivated() + { + _changeTokenSource.Verify(m => m.Activate(), Times.Once); + } + private class FakeConfig : IInternalConfiguration { private readonly string _downstreamTemplatePath; @@ -111,4 +122,4 @@ namespace Ocelot.UnitTests.Configuration public HttpHandlerOptions HttpHandlerOptions { get; } } } -} +} From 4147aee587d18b0fc6da6295d2c01ca0128782e2 Mon Sep 17 00:00:00 2001 From: Pavel Date: Sun, 9 Feb 2020 15:05:07 +0100 Subject: [PATCH 11/28] RABC typo fix (#1123) --- docs/features/kubernetes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index 75568ec4..088ea3ba 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -14,7 +14,7 @@ Then add the following to your ConfigureServices method. s.AddOcelot() .AddKubernetes(); -If you have services deployed in kubernetes you will normally use the naming service to access them. Default usePodServiceAccount = True, which means that ServiceAccount using Pod to access the service of the k8s cluster needs to be ServiceAccount based on RABC authorization +If you have services deployed in kubernetes you will normally use the naming service to access them. Default usePodServiceAccount = True, which means that ServiceAccount using Pod to access the service of the k8s cluster needs to be ServiceAccount based on RBAC authorization .. code-block::csharp public static class OcelotBuilderExtensions From 6bd903bc8782eef72230684daaec7b1832d63d35 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sun, 9 Feb 2020 15:37:27 +0000 Subject: [PATCH 12/28] tests passing --- samples/OcelotKube/ApiGateway/Startup.cs | 3 +- .../OcelotKube/DownstreamService/Startup.cs | 3 +- .../DownstreamMethodTransformerMiddleware.cs | 27 --------------- .../Pipeline/OcelotPipelineExtensions.cs | 4 --- .../DownstreamRequestInitialiserMiddleware.cs | 5 +++ .../HttpRequestBuilderMiddlewareExtensions.cs | 5 +-- ...streamRequestInitialiserMiddlewareTests.cs | 34 +++++++++++++++++-- 7 files changed, 41 insertions(+), 40 deletions(-) delete mode 100644 src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs diff --git a/samples/OcelotKube/ApiGateway/Startup.cs b/samples/OcelotKube/ApiGateway/Startup.cs index d7b2473c..3e37ffa3 100644 --- a/samples/OcelotKube/ApiGateway/Startup.cs +++ b/samples/OcelotKube/ApiGateway/Startup.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Ocelot.DependencyInjection; using Ocelot.Middleware; using Ocelot.Provider.Kubernetes; @@ -18,7 +19,7 @@ namespace ApiGateway } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { diff --git a/samples/OcelotKube/DownstreamService/Startup.cs b/samples/OcelotKube/DownstreamService/Startup.cs index 9a927a37..a8abb5d4 100644 --- a/samples/OcelotKube/DownstreamService/Startup.cs +++ b/samples/OcelotKube/DownstreamService/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -28,7 +29,7 @@ namespace DownstreamService } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { diff --git a/src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs b/src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs deleted file mode 100644 index a17e9167..00000000 --- a/src/Ocelot/DownstreamMethodTransformer/Middleware/DownstreamMethodTransformerMiddleware.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Ocelot.Logging; -using Ocelot.Middleware; -using System.Threading.Tasks; - -namespace Ocelot.DownstreamMethodTransformer.Middleware -{ - public class DownstreamMethodTransformerMiddleware : OcelotMiddleware - { - private readonly OcelotRequestDelegate _next; - - public DownstreamMethodTransformerMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory) - : base(loggerFactory.CreateLogger()) - { - _next = next; - } - - public async Task Invoke(DownstreamContext context) - { - if (context.DownstreamReRoute.DownstreamHttpMethod != null) - { - context.DownstreamRequest.Method = context.DownstreamReRoute.DownstreamHttpMethod; - } - - await _next.Invoke(context); - } - } -} diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs index 02ee07f4..d52cfb0d 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs @@ -2,7 +2,6 @@ using Ocelot.Authorisation.Middleware; using Ocelot.Cache.Middleware; using Ocelot.Claims.Middleware; -using Ocelot.DownstreamMethodTransformer.Middleware; using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Errors.Middleware; @@ -69,9 +68,6 @@ namespace Ocelot.Middleware.Pipeline // Initialises downstream request builder.UseDownstreamRequestInitialiser(); - //change Http Method - builder.UseMiddleware(); - // We check whether the request is ratelimit, and if there is no continue processing builder.UseRateLimiting(); diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs index 442bb4b9..c9f6f859 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -34,6 +34,11 @@ namespace Ocelot.Request.Middleware context.DownstreamRequest = _creator.Create(downstreamRequest.Data); + if (!string.IsNullOrEmpty(context.DownstreamReRoute?.DownstreamHttpMethod)) + { + context.DownstreamRequest.Method = context.DownstreamReRoute.DownstreamHttpMethod; + } + await _next.Invoke(context); } } diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs index e6eff7e0..a2931cbb 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddlewareExtensions.cs @@ -1,5 +1,3 @@ -using Microsoft.AspNetCore.Builder; -using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Middleware.Pipeline; namespace Ocelot.Request.Middleware @@ -8,8 +6,7 @@ namespace Ocelot.Request.Middleware { public static IOcelotPipelineBuilder UseDownstreamRequestInitialiser(this IOcelotPipelineBuilder builder) { - return builder.UseMiddleware() - .UseMiddleware(); + return builder.UseMiddleware(); } } } diff --git a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs index 93d963b6..aadc551c 100644 --- a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs @@ -1,6 +1,4 @@ -using Ocelot.Middleware; - -namespace Ocelot.UnitTests.Request +namespace Ocelot.UnitTests.Request { using Microsoft.AspNetCore.Http; using Moq; @@ -9,6 +7,8 @@ namespace Ocelot.UnitTests.Request using Ocelot.Request.Creator; using Ocelot.Request.Mapper; using Ocelot.Request.Middleware; + using Ocelot.Configuration.Builder; + using Ocelot.Middleware; using Ocelot.Responses; using Shouldly; using System.Net.Http; @@ -65,6 +65,24 @@ namespace Ocelot.UnitTests.Request .Then(_ => ThenTheContexRequestIsMappedToADownstreamRequest()) .And(_ => ThenTheDownstreamRequestIsStored()) .And(_ => ThenTheNextMiddlewareIsInvoked()) + .And(_ => ThenTheDownstreamRequestMethodIs("GET")) + .BDDfy(); + } + + [Theory] + [InlineData("POST", "POST")] + [InlineData(null, "GET")] + [InlineData("", "GET")] + public void Should_map_downstream_reroute_method_to_downstream_request(string input, string expected) + { + this.Given(_ => GivenTheHttpContextContainsARequest()) + .And(_ => GivenTheDownstreamReRouteMethodIs(input)) + .And(_ => GivenTheMapperWillReturnAMappedRequest()) + .When(_ => WhenTheMiddlewareIsInvoked()) + .Then(_ => ThenTheContexRequestIsMappedToADownstreamRequest()) + .And(_ => ThenTheDownstreamRequestIsStored()) + .And(_ => ThenTheNextMiddlewareIsInvoked()) + .And(_ => ThenTheDownstreamRequestMethodIs(expected)) .BDDfy(); } @@ -80,6 +98,16 @@ namespace Ocelot.UnitTests.Request .BDDfy(); } + private void GivenTheDownstreamReRouteMethodIs(string input) + { + _downstreamContext.DownstreamReRoute = new DownstreamReRouteBuilder().WithDownStreamHttpMethod(input).Build(); + } + + private void ThenTheDownstreamRequestMethodIs(string expected) + { + _downstreamContext.DownstreamRequest.Method.ShouldBe(expected); + } + private void GivenTheHttpContextContainsARequest() { _httpContext From 0c5e09d18f55d89aa2c279d2bd6c4ac04c480db9 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sun, 9 Feb 2020 15:51:22 +0000 Subject: [PATCH 13/28] moved logic to request mapper and public setter gone --- src/Ocelot/Request/Mapper/IRequestMapper.cs | 3 +- src/Ocelot/Request/Mapper/RequestMapper.cs | 12 +++-- .../Request/Middleware/DownstreamRequest.cs | 2 +- .../DownstreamRequestInitialiserMiddleware.cs | 7 +-- ...streamRequestInitialiserMiddlewareTests.cs | 22 +++------ .../Request/Mapper/RequestMapperTests.cs | 47 ++++++++++++++----- 6 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/Ocelot/Request/Mapper/IRequestMapper.cs b/src/Ocelot/Request/Mapper/IRequestMapper.cs index d16a2d06..cd075c5d 100644 --- a/src/Ocelot/Request/Mapper/IRequestMapper.cs +++ b/src/Ocelot/Request/Mapper/IRequestMapper.cs @@ -1,12 +1,13 @@ namespace Ocelot.Request.Mapper { using Microsoft.AspNetCore.Http; + using Ocelot.Configuration; using Ocelot.Responses; using System.Net.Http; using System.Threading.Tasks; public interface IRequestMapper { - Task> Map(HttpRequest request); + Task> Map(HttpRequest request, DownstreamReRoute downstreamReRoute); } } diff --git a/src/Ocelot/Request/Mapper/RequestMapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs index f669c286..e050f1a6 100644 --- a/src/Ocelot/Request/Mapper/RequestMapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Primitives; + using Ocelot.Configuration; using Ocelot.Responses; using System; using System.Collections.Generic; @@ -15,14 +16,14 @@ { private readonly string[] _unsupportedHeaders = { "host" }; - public async Task> Map(HttpRequest request) + public async Task> Map(HttpRequest request, DownstreamReRoute downstreamReRoute) { try { var requestMessage = new HttpRequestMessage() { Content = await MapContent(request), - Method = MapMethod(request), + Method = MapMethod(request, downstreamReRoute), RequestUri = MapUri(request) }; @@ -71,8 +72,13 @@ } } - private HttpMethod MapMethod(HttpRequest request) + private HttpMethod MapMethod(HttpRequest request, DownstreamReRoute downstreamReRoute) { + if (!string.IsNullOrEmpty(downstreamReRoute?.DownstreamHttpMethod)) + { + return new HttpMethod(downstreamReRoute.DownstreamHttpMethod); + } + return new HttpMethod(request.Method); } diff --git a/src/Ocelot/Request/Middleware/DownstreamRequest.cs b/src/Ocelot/Request/Middleware/DownstreamRequest.cs index 24d96cfe..cf973d94 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequest.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequest.cs @@ -24,7 +24,7 @@ namespace Ocelot.Request.Middleware public HttpRequestHeaders Headers { get; } - public string Method { get; set; } + public string Method { get; } public string OriginalString { get; } diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs index c9f6f859..83a2ecb9 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -24,7 +24,7 @@ namespace Ocelot.Request.Middleware public async Task Invoke(DownstreamContext context) { - var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request); + var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request, context.DownstreamReRoute); if (downstreamRequest.IsError) { @@ -34,11 +34,6 @@ namespace Ocelot.Request.Middleware context.DownstreamRequest = _creator.Create(downstreamRequest.Data); - if (!string.IsNullOrEmpty(context.DownstreamReRoute?.DownstreamHttpMethod)) - { - context.DownstreamRequest.Method = context.DownstreamReRoute.DownstreamHttpMethod; - } - await _next.Invoke(context); } } diff --git a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs index aadc551c..13bf024b 100644 --- a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs @@ -12,6 +12,7 @@ using Ocelot.Responses; using Shouldly; using System.Net.Http; + using Ocelot.Configuration; using TestStack.BDDfy; using Xunit; @@ -69,20 +70,16 @@ .BDDfy(); } - [Theory] - [InlineData("POST", "POST")] - [InlineData(null, "GET")] - [InlineData("", "GET")] - public void Should_map_downstream_reroute_method_to_downstream_request(string input, string expected) + [Fact] + public void Should_map_downstream_reroute_method_to_downstream_request() { this.Given(_ => GivenTheHttpContextContainsARequest()) - .And(_ => GivenTheDownstreamReRouteMethodIs(input)) .And(_ => GivenTheMapperWillReturnAMappedRequest()) .When(_ => WhenTheMiddlewareIsInvoked()) .Then(_ => ThenTheContexRequestIsMappedToADownstreamRequest()) .And(_ => ThenTheDownstreamRequestIsStored()) .And(_ => ThenTheNextMiddlewareIsInvoked()) - .And(_ => ThenTheDownstreamRequestMethodIs(expected)) + .And(_ => ThenTheDownstreamRequestMethodIs("GET")) .BDDfy(); } @@ -98,11 +95,6 @@ .BDDfy(); } - private void GivenTheDownstreamReRouteMethodIs(string input) - { - _downstreamContext.DownstreamReRoute = new DownstreamReRouteBuilder().WithDownStreamHttpMethod(input).Build(); - } - private void ThenTheDownstreamRequestMethodIs(string expected) { _downstreamContext.DownstreamRequest.Method.ShouldBe(expected); @@ -120,7 +112,7 @@ _mappedRequest = new OkResponse(new HttpRequestMessage(HttpMethod.Get, "http://www.bbc.co.uk")); _requestMapper - .Setup(rm => rm.Map(It.IsAny())) + .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) .ReturnsAsync(_mappedRequest); } @@ -129,7 +121,7 @@ _mappedRequest = new ErrorResponse(new UnmappableRequestError(new System.Exception("boooom!"))); _requestMapper - .Setup(rm => rm.Map(It.IsAny())) + .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) .ReturnsAsync(_mappedRequest); } @@ -140,7 +132,7 @@ private void ThenTheContexRequestIsMappedToADownstreamRequest() { - _requestMapper.Verify(rm => rm.Map(_httpRequest.Object), Times.Once); + _requestMapper.Verify(rm => rm.Map(_httpRequest.Object, _downstreamContext.DownstreamReRoute), Times.Once); } private void ThenTheDownstreamRequestIsStored() diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index ac81a4d4..77091fc6 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -13,6 +13,8 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; using TestStack.BDDfy; using Xunit; @@ -27,6 +29,8 @@ private List> _inputHeaders = null; + private DownstreamReRoute _downstreamReRoute; + public RequestMapperTests() { _httpContext = new DefaultHttpContext(); @@ -82,6 +86,21 @@ .BDDfy(); } + [Theory] + [InlineData("", "GET")] + [InlineData(null, "GET")] + [InlineData("POST", "POST")] + public void Should_use_downstream_reroute_method_if_set(string input, string expected) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheDownstreamReRouteMethodIs(input)) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasMethod(expected)) + .BDDfy(); + } + [Fact] public void Should_map_all_headers() { @@ -154,16 +173,6 @@ .BDDfy(); } - private void GivenTheInputRequestHasNoContentLength() - { - _inputRequest.ContentLength = null; - } - - private void GivenTheInputRequestHasNoContentType() - { - _inputRequest.ContentType = null; - } - [Fact] public void Should_map_content_headers() { @@ -212,6 +221,22 @@ .BDDfy(); } + private void GivenTheDownstreamReRouteMethodIs(string input) + { + _downstreamReRoute = new DownstreamReRouteBuilder().WithDownStreamHttpMethod(input).Build(); + } + + private void GivenTheInputRequestHasNoContentLength() + { + _inputRequest.ContentLength = null; + } + + private void GivenTheInputRequestHasNoContentType() + { + _inputRequest.ContentType = null; + } + + private void ThenTheContentHeadersAreNotAddedToNonContentHeaders() { _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition"); @@ -380,7 +405,7 @@ private async Task WhenMapped() { - _mappedRequest = await _requestMapper.Map(_inputRequest); + _mappedRequest = await _requestMapper.Map(_inputRequest, _downstreamReRoute); } private void ThenNoErrorIsReturned() From 7e807208812ffcc9cfb7929b2f2490b606adf39c Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sun, 9 Feb 2020 16:15:39 +0000 Subject: [PATCH 14/28] acceptance tests --- test/Ocelot.AcceptanceTests/MethodTests.cs | 158 +++++++++++++++++++++ test/Ocelot.AcceptanceTests/Steps.cs | 12 ++ 2 files changed, 170 insertions(+) create mode 100644 test/Ocelot.AcceptanceTests/MethodTests.cs diff --git a/test/Ocelot.AcceptanceTests/MethodTests.cs b/test/Ocelot.AcceptanceTests/MethodTests.cs new file mode 100644 index 00000000..abfbfecf --- /dev/null +++ b/test/Ocelot.AcceptanceTests/MethodTests.cs @@ -0,0 +1,158 @@ +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Http; + using TestStack.BDDfy; + using Xunit; + + public class MethodTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public MethodTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_post() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53171, + }, + }, + DownstreamHttpMethod = "POST", + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53171/", "/", "POST")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_post_with_content() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53271, + }, + }, + DownstreamHttpMethod = "POST", + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53271/", "/", "POST")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_get_with_content() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Post" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53272, + }, + }, + DownstreamHttpMethod = "GET", + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53272/", "/", "GET")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string expected) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + if (context.Request.Method == expected) + { + context.Response.StatusCode = 200; + var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + await context.Response.WriteAsync(body); + } + else + { + context.Response.StatusCode = 500; + } + }); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 9bfb2091..60a770f7 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -901,6 +901,18 @@ _response = _ocelotClient.GetAsync(url).Result; } + public void WhenIGetUrlOnTheApiGateway(string url, HttpContent content) + { + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url) {Content = content}; + _response = _ocelotClient.SendAsync(httpRequestMessage).Result; + } + + public void WhenIPostUrlOnTheApiGateway(string url, HttpContent content) + { + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url) { Content = content }; + _response = _ocelotClient.SendAsync(httpRequestMessage).Result; + } + public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value) { var request = _ocelotServer.CreateRequest(url); From 89b2decf5042afe0cc884e0a0f6caaae5e77ca9a Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sun, 9 Feb 2020 16:52:59 +0000 Subject: [PATCH 15/28] added docs for http method transformation --- docs/features/configuration.rst | 1 + docs/features/methodtransformation.rst | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 docs/features/methodtransformation.rst diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst index 7983af39..fe399740 100644 --- a/docs/features/configuration.rst +++ b/docs/features/configuration.rst @@ -24,6 +24,7 @@ Here is an example ReRoute configuration, You don't need to set all of these thi "UpstreamHttpMethod": [ "Get" ], + "DownstreamHttpMethod": "". "AddHeadersToRequest": {}, "AddClaimsToRequest": {}, "RouteClaimsRequirement": {}, diff --git a/docs/features/methodtransformation.rst b/docs/features/methodtransformation.rst new file mode 100644 index 00000000..b94ebce4 --- /dev/null +++ b/docs/features/methodtransformation.rst @@ -0,0 +1,28 @@ +HTTP Method Transformation +========================== + +Ocelot allows the user to change the HTTP request method that will be used when making a request to a downstream service. + +This achieved by setting the following ReRoute configuration: + +.. code-block:: json + +{ + "DownstreamPathTemplate": "/{url}", + "UpstreamPathTemplate": "/{url}", + "UpstreamHttpMethod": [ + "Get" + ], + "DownstreamHttpMethod": "POST", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 53271 + } + ], +} + +The key property here is DownstreamHttpMethod which is set as POST and the ReRoute will only match on GET as set by UpstreamHttpMethod. + +This feature can be useful when interacting with downstream apis that only support POST and you want to present some kind of RESTful interface. \ No newline at end of file From ddf8b01d364dfd17f781c1ce74c586d4ddfa570c Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Sun, 9 Feb 2020 17:12:37 +0000 Subject: [PATCH 16/28] updated docs and readme with Method Transformation info (#1125) --- README.md | 2 +- docs/index.rst | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 57a93697..b59f84f3 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio * Retry policies / QoS * Load Balancing * Logging / Tracing / Correlation -* Headers / Query String / Claims Transformation +* Headers / Method / Query String / Claims Transformation * Custom Middleware / Delegating Handlers * Configuration / Administration REST API * Platform / Cloud Agnostic diff --git a/docs/index.rst b/docs/index.rst index 4aba33fd..7952c91a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n features/requestaggregation features/graphql features/servicediscovery - features/servicefabric + features/servicefabric features/kubernetes features/authentication features/authorisation @@ -33,6 +33,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n features/caching features/qualityofservice features/headerstransformation + features/methodtransformation features/claimstransformation features/logging features/tracing From fc3b6fdb8b7ee00e16e8f9e3d73992de98fab191 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Sun, 9 Feb 2020 18:15:24 +0000 Subject: [PATCH 17/28] fix docs formatting for http method transformation --- docs/features/methodtransformation.rst | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/features/methodtransformation.rst b/docs/features/methodtransformation.rst index b94ebce4..5b1c1518 100644 --- a/docs/features/methodtransformation.rst +++ b/docs/features/methodtransformation.rst @@ -7,21 +7,21 @@ This achieved by setting the following ReRoute configuration: .. code-block:: json -{ - "DownstreamPathTemplate": "/{url}", - "UpstreamPathTemplate": "/{url}", - "UpstreamHttpMethod": [ - "Get" - ], - "DownstreamHttpMethod": "POST", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 53271 - } - ], -} + { + "DownstreamPathTemplate": "/{url}", + "UpstreamPathTemplate": "/{url}", + "UpstreamHttpMethod": [ + "Get" + ], + "DownstreamHttpMethod": "POST", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 53271 + } + ], + } The key property here is DownstreamHttpMethod which is set as POST and the ReRoute will only match on GET as set by UpstreamHttpMethod. From f2f583a88cdb281d4e14fcaf5247f857b4518b12 Mon Sep 17 00:00:00 2001 From: Andrea Tosato Date: Mon, 10 Feb 2020 18:01:54 +0100 Subject: [PATCH 18/28] Add DownstreamHttpVersion property for Http/2 WebServer --- .../Configuration/Builder/DownstreamReRouteBuilder.cs | 11 ++++++++++- src/Ocelot/Configuration/Creator/ReRoutesCreator.cs | 1 + src/Ocelot/Configuration/DownstreamReRoute.cs | 6 +++++- src/Ocelot/Configuration/File/FileReRoute.cs | 1 + .../Configuration/Validator/ReRouteFluentValidator.cs | 5 +++++ src/Ocelot/Request/Mapper/RequestMapper.cs | 8 +++++++- 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index ed978dde..c318b949 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -1,5 +1,6 @@ using Ocelot.Configuration.Creator; using Ocelot.Values; +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -42,6 +43,7 @@ namespace Ocelot.Configuration.Builder private bool _dangerousAcceptAnyServerCertificateValidator; private SecurityOptions _securityOptions; private string _downstreamHttpMethod; + private string _downstreamHttpVersion; public DownstreamReRouteBuilder() { @@ -255,6 +257,12 @@ namespace Ocelot.Configuration.Builder return this; } + public DownstreamReRouteBuilder WithHttpVersion(string httpVersion) + { + _downstreamHttpVersion = httpVersion; + return this; + } + public DownstreamReRoute Build() { return new DownstreamReRoute( @@ -290,7 +298,8 @@ namespace Ocelot.Configuration.Builder _addHeadersToUpstream, _dangerousAcceptAnyServerCertificateValidator, _securityOptions, - _downstreamHttpMethod); + _downstreamHttpMethod, + _downstreamHttpVersion); } } } diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index 1cc463ef..63f42197 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -138,6 +138,7 @@ namespace Ocelot.Configuration.Creator .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) .WithSecurityOptions(securityOptions) + .WithHttpVersion(fileReRoute.DownstreamHttpVersion) .WithDownStreamHttpMethod(fileReRoute.DownstreamHttpMethod) .Build(); diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index 1c41f648..d8a3cb74 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -1,6 +1,7 @@ namespace Ocelot.Configuration { using Creator; + using System; using System.Collections.Generic; using Values; @@ -39,7 +40,8 @@ namespace Ocelot.Configuration List addHeadersToUpstream, bool dangerousAcceptAnyServerCertificateValidator, SecurityOptions securityOptions, - string downstreamHttpMethod) + string downstreamHttpMethod, + string downstreamHttpVersion) { DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; AddHeadersToDownstream = addHeadersToDownstream; @@ -74,6 +76,7 @@ namespace Ocelot.Configuration AddHeadersToUpstream = addHeadersToUpstream; SecurityOptions = securityOptions; DownstreamHttpMethod = downstreamHttpMethod; + DownstreamHttpVersion = downstreamHttpVersion; } public string Key { get; } @@ -109,5 +112,6 @@ namespace Ocelot.Configuration public bool DangerousAcceptAnyServerCertificateValidator { get; } public SecurityOptions SecurityOptions { get; } public string DownstreamHttpMethod { get; } + public string DownstreamHttpVersion { get; } } } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 1bae31dd..b1a6cd4d 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -56,5 +56,6 @@ namespace Ocelot.Configuration.File public int Timeout { get; set; } public bool DangerousAcceptAnyServerCertificateValidator { get; set; } public FileSecurityOptions SecurityOptions { get; set; } + public string DownstreamHttpVersion { get; set; } } } diff --git a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs b/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs index 746c430c..99a5a422 100644 --- a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs @@ -83,6 +83,11 @@ RuleForEach(reRoute => reRoute.DownstreamHostAndPorts) .SetValidator(hostAndPortValidator); }); + + When(reRoute => !string.IsNullOrEmpty(reRoute.DownstreamHttpVersion), () => + { + RuleFor(r => r.DownstreamHttpVersion).Matches("^[0-9]([.,][0-9]{1,1})?$"); + }); } private async Task IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken) diff --git a/src/Ocelot/Request/Mapper/RequestMapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs index e050f1a6..f7ad7c38 100644 --- a/src/Ocelot/Request/Mapper/RequestMapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -20,11 +20,17 @@ { try { + if (!Version.TryParse(downstreamReRoute.DownstreamHttpVersion, out Version version)) + { + version = new Version(1, 1); + } + var requestMessage = new HttpRequestMessage() { Content = await MapContent(request), Method = MapMethod(request, downstreamReRoute), - RequestUri = MapUri(request) + RequestUri = MapUri(request), + Version = version, }; MapHeaders(request, requestMessage); From 42a1395a84eb5e029e7e9d7da3ccaae44ff6b528 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 17 Feb 2020 07:45:44 +0000 Subject: [PATCH 19/28] tests for downstream http version validation --- .../Validation/ReRouteFluentValidatorTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs index 9433bb89..82b3e646 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs @@ -305,6 +305,71 @@ .BDDfy(); } + [Theory] + [InlineData("1.0")] + [InlineData("1.1")] + [InlineData("2.0")] + [InlineData("1,0")] + [InlineData("1,1")] + [InlineData("2,0")] + [InlineData("1")] + [InlineData("2")] + [InlineData("")] + [InlineData(null)] + public void should_be_valid_re_route_using_downstream_http_version(string version) + { + var fileReRoute = new FileReRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000, + }, + }, + DownstreamHttpVersion = version, + }; + + this.Given(_ => GivenThe(fileReRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Theory] + [InlineData("retg1.1")] + [InlineData("re2.0")] + [InlineData("1,0a")] + [InlineData("a1,1")] + [InlineData("12,0")] + [InlineData("asdf")] + public void should_be_invalid_re_route_using_downstream_http_version(string version) + { + var fileReRoute = new FileReRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000, + }, + }, + DownstreamHttpVersion = version, + }; + + this.Given(_ => GivenThe(fileReRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("'Downstream Http Version' is not in the correct format.")) + .BDDfy(); + } + private void GivenAnAuthProvider(string key) { var schemes = new List From b8922cef5f7fdffe5754e6aa40a2eba5f296a1ca Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 17 Feb 2020 08:03:11 +0000 Subject: [PATCH 20/28] tests for version creator --- .../Builder/DownstreamReRouteBuilder.cs | 6 ++-- .../Configuration/Creator/IVersionCreator.cs | 9 +++++ .../Configuration/Creator/ReRoutesCreator.cs | 9 +++-- .../Configuration/Creator/VersionCreator.cs | 17 ++++++++++ src/Ocelot/Configuration/DownstreamReRoute.cs | 4 +-- .../DependencyInjection/OcelotBuilder.cs | 1 + src/Ocelot/Request/Mapper/RequestMapper.cs | 7 +--- .../Configuration/ReRoutesCreatorTests.cs | 10 +++++- .../Configuration/VersionCreatorTests.cs | 34 +++++++++++++++++++ 9 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 src/Ocelot/Configuration/Creator/IVersionCreator.cs create mode 100644 src/Ocelot/Configuration/Creator/VersionCreator.cs create mode 100644 test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index c318b949..09f5c59a 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -43,7 +43,7 @@ namespace Ocelot.Configuration.Builder private bool _dangerousAcceptAnyServerCertificateValidator; private SecurityOptions _securityOptions; private string _downstreamHttpMethod; - private string _downstreamHttpVersion; + private Version _downstreamHttpVersion; public DownstreamReRouteBuilder() { @@ -257,9 +257,9 @@ namespace Ocelot.Configuration.Builder return this; } - public DownstreamReRouteBuilder WithHttpVersion(string httpVersion) + public DownstreamReRouteBuilder WithHttpVersion(Version downstreamHttpVersion) { - _downstreamHttpVersion = httpVersion; + _downstreamHttpVersion = downstreamHttpVersion; return this; } diff --git a/src/Ocelot/Configuration/Creator/IVersionCreator.cs b/src/Ocelot/Configuration/Creator/IVersionCreator.cs new file mode 100644 index 00000000..d810bde3 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IVersionCreator.cs @@ -0,0 +1,9 @@ +namespace Ocelot.Configuration.Creator +{ + using System; + + public interface IVersionCreator + { + Version Create(string downstreamHttpVersion); + } +} diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index 63f42197..ad896ac9 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -22,6 +22,7 @@ namespace Ocelot.Configuration.Creator private readonly IDownstreamAddressesCreator _downstreamAddressesCreator; private readonly IReRouteKeyCreator _reRouteKeyCreator; private readonly ISecurityOptionsCreator _securityOptionsCreator; + private readonly IVersionCreator _versionCreator; public ReRoutesCreator( IClaimsToThingCreator claimsToThingCreator, @@ -37,7 +38,8 @@ namespace Ocelot.Configuration.Creator IDownstreamAddressesCreator downstreamAddressesCreator, ILoadBalancerOptionsCreator loadBalancerOptionsCreator, IReRouteKeyCreator reRouteKeyCreator, - ISecurityOptionsCreator securityOptionsCreator + ISecurityOptionsCreator securityOptionsCreator, + IVersionCreator versionCreator ) { _reRouteKeyCreator = reRouteKeyCreator; @@ -55,6 +57,7 @@ namespace Ocelot.Configuration.Creator _httpHandlerOptionsCreator = httpHandlerOptionsCreator; _loadBalancerOptionsCreator = loadBalancerOptionsCreator; _securityOptionsCreator = securityOptionsCreator; + _versionCreator = versionCreator; } public List Create(FileConfiguration fileConfiguration) @@ -104,6 +107,8 @@ namespace Ocelot.Configuration.Creator var securityOptions = _securityOptionsCreator.Create(fileReRoute.SecurityOptions); + var downstreamHttpVersion = _versionCreator.Create(fileReRoute.DownstreamHttpVersion); + var reRoute = new DownstreamReRouteBuilder() .WithKey(fileReRoute.Key) .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) @@ -138,7 +143,7 @@ namespace Ocelot.Configuration.Creator .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) .WithSecurityOptions(securityOptions) - .WithHttpVersion(fileReRoute.DownstreamHttpVersion) + .WithHttpVersion(downstreamHttpVersion) .WithDownStreamHttpMethod(fileReRoute.DownstreamHttpMethod) .Build(); diff --git a/src/Ocelot/Configuration/Creator/VersionCreator.cs b/src/Ocelot/Configuration/Creator/VersionCreator.cs new file mode 100644 index 00000000..b6fb3ad1 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/VersionCreator.cs @@ -0,0 +1,17 @@ +namespace Ocelot.Configuration.Creator +{ + using System; + + public class VersionCreator : IVersionCreator + { + public Version Create(string downstreamHttpVersion) + { + if (!Version.TryParse(downstreamHttpVersion, out Version version)) + { + version = new Version(1, 1); + } + + return version; + } + } +} diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index d8a3cb74..be044539 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -41,7 +41,7 @@ namespace Ocelot.Configuration bool dangerousAcceptAnyServerCertificateValidator, SecurityOptions securityOptions, string downstreamHttpMethod, - string downstreamHttpVersion) + Version downstreamHttpVersion) { DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; AddHeadersToDownstream = addHeadersToDownstream; @@ -112,6 +112,6 @@ namespace Ocelot.Configuration public bool DangerousAcceptAnyServerCertificateValidator { get; } public SecurityOptions SecurityOptions { get; } public string DownstreamHttpMethod { get; } - public string DownstreamHttpVersion { get; } + public Version DownstreamHttpVersion { get; } } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 7f92d50d..2f1444f6 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -136,6 +136,7 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.TryAddSingleton(); //add security this.AddSecurity(); diff --git a/src/Ocelot/Request/Mapper/RequestMapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs index f7ad7c38..7d383a70 100644 --- a/src/Ocelot/Request/Mapper/RequestMapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -20,17 +20,12 @@ { try { - if (!Version.TryParse(downstreamReRoute.DownstreamHttpVersion, out Version version)) - { - version = new Version(1, 1); - } - var requestMessage = new HttpRequestMessage() { Content = await MapContent(request), Method = MapMethod(request, downstreamReRoute), RequestUri = MapUri(request), - Version = version, + Version = downstreamReRoute.DownstreamHttpVersion, }; MapHeaders(request, requestMessage); diff --git a/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs index 32c235aa..47bc07c8 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs @@ -1,5 +1,6 @@ namespace Ocelot.UnitTests.Configuration { + using System; using Moq; using Ocelot.Cache; using Ocelot.Configuration; @@ -30,6 +31,7 @@ private Mock _lboCreator; private Mock _rrkCreator; private Mock _soCreator; + private Mock _versionCreator; private FileConfiguration _fileConfig; private ReRouteOptions _rro; private string _requestId; @@ -46,6 +48,7 @@ private LoadBalancerOptions _lbo; private List _result; private SecurityOptions _securityOptions; + private Version _expectedVersion; public ReRoutesCreatorTests() { @@ -63,6 +66,7 @@ _lboCreator = new Mock(); _rrkCreator = new Mock(); _soCreator = new Mock(); + _versionCreator = new Mock(); _creator = new ReRoutesCreator( _cthCreator.Object, @@ -78,7 +82,8 @@ _daCreator.Object, _lboCreator.Object, _rrkCreator.Object, - _soCreator.Object + _soCreator.Object, + _versionCreator.Object ); } @@ -155,6 +160,7 @@ private void GivenTheDependenciesAreSetUpCorrectly() { + _expectedVersion = new Version("1.1"); _rro = new ReRouteOptions(false, false, false, false, false); _requestId = "testy"; _rrk = "besty"; @@ -182,6 +188,7 @@ _hfarCreator.Setup(x => x.Create(It.IsAny())).Returns(_ht); _daCreator.Setup(x => x.Create(It.IsAny())).Returns(_dhp); _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); + _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_expectedVersion); } private void ThenTheReRoutesAreCreated() @@ -209,6 +216,7 @@ private void ThenTheReRouteIsSet(FileReRoute expected, int reRouteIndex) { + _result[reRouteIndex].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_expectedVersion); _result[reRouteIndex].DownstreamReRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated); _result[reRouteIndex].DownstreamReRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised); _result[reRouteIndex].DownstreamReRoute[0].IsCached.ShouldBe(_rro.IsCached); diff --git a/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs new file mode 100644 index 00000000..346f62e7 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs @@ -0,0 +1,34 @@ +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration.Creator; + using Shouldly; + using Xunit; + + public class VersionCreatorTests + { + private readonly VersionCreator _creator; + + public VersionCreatorTests() + { + _creator = new VersionCreator(); + } + + [Fact] + public void should_create_version_based_on_input() + { + var input = "2.0"; + var result = _creator.Create(input); + result.Major.ShouldBe(2); + result.Minor.ShouldBe(0); + } + + [Fact] + public void should_default_to_version_one_point_one() + { + var input = ""; + var result = _creator.Create(input); + result.Major.ShouldBe(1); + result.Minor.ShouldBe(1); + } + } +} From e969e9ff0b999a700df9110b8a25d6b2f4ba1cf4 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 18 Feb 2020 20:45:28 +0000 Subject: [PATCH 21/28] unit tests passing --- .../Builder/DownstreamReRouteBuilder.cs | 2 +- .../Configuration/Creator/ReRoutesCreator.cs | 2 +- .../Configuration/VersionCreatorTests.cs | 36 ++++++++++++++----- .../Request/Mapper/RequestMapperTests.cs | 20 ++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 09f5c59a..71049795 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -257,7 +257,7 @@ namespace Ocelot.Configuration.Builder return this; } - public DownstreamReRouteBuilder WithHttpVersion(Version downstreamHttpVersion) + public DownstreamReRouteBuilder WithDownstreamHttpVersion(Version downstreamHttpVersion) { _downstreamHttpVersion = downstreamHttpVersion; return this; diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index ad896ac9..e307b6d3 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -143,7 +143,7 @@ namespace Ocelot.Configuration.Creator .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) .WithSecurityOptions(securityOptions) - .WithHttpVersion(downstreamHttpVersion) + .WithDownstreamHttpVersion(downstreamHttpVersion) .WithDownStreamHttpMethod(fileReRoute.DownstreamHttpMethod) .Build(); diff --git a/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs index 346f62e7..f5119ec8 100644 --- a/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs @@ -1,12 +1,16 @@ namespace Ocelot.UnitTests.Configuration { + using System; using Ocelot.Configuration.Creator; using Shouldly; + using TestStack.BDDfy; using Xunit; public class VersionCreatorTests { private readonly VersionCreator _creator; + private string _input; + private Version _result; public VersionCreatorTests() { @@ -16,19 +20,35 @@ [Fact] public void should_create_version_based_on_input() { - var input = "2.0"; - var result = _creator.Create(input); - result.Major.ShouldBe(2); - result.Minor.ShouldBe(0); + this.Given(_ => GivenTheInput("2.0")) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs(2, 0)) + .BDDfy(); } [Fact] public void should_default_to_version_one_point_one() { - var input = ""; - var result = _creator.Create(input); - result.Major.ShouldBe(1); - result.Minor.ShouldBe(1); + this.Given(_ => GivenTheInput("")) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs(1, 1)) + .BDDfy(); + } + + private void GivenTheInput(string input) + { + _input = input; + } + + private void WhenICreate() + { + _result = _creator.Create(_input); + } + + private void ThenTheResultIs(int major, int minor) + { + _result.Major.ShouldBe(major); + _result.Minor.ShouldBe(minor); } } } diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index 77091fc6..501e290c 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -51,6 +51,7 @@ .And(_ => GivenTheInputRequestHasHost(host)) .And(_ => GivenTheInputRequestHasPath(path)) .And(_ => GivenTheInputRequestHasQueryString(queryString)) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasUri(expectedUri)) @@ -80,6 +81,7 @@ { this.Given(_ => GivenTheInputRequestHasMethod(method)) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasMethod(method)) @@ -107,6 +109,7 @@ this.Given(_ => GivenTheInputRequestHasHeaders()) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasEachHeader()) @@ -119,6 +122,7 @@ this.Given(_ => GivenTheInputRequestHasNoHeaders()) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasNoHeaders()) @@ -131,6 +135,7 @@ this.Given(_ => GivenTheInputRequestHasContent("This is my content")) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasContent("This is my content")) @@ -143,6 +148,7 @@ this.Given(_ => GivenTheInputRequestHasNullContent()) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasNoContent()) @@ -155,6 +161,7 @@ this.Given(_ => GivenTheInputRequestHasNoContentType()) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasNoContent()) @@ -167,6 +174,7 @@ this.Given(_ => GivenTheInputRequestHasNoContentLength()) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasNoContent()) @@ -192,6 +200,7 @@ .And(_ => GivenTheContentMD5Is(md5bytes)) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) @@ -213,6 +222,7 @@ .And(_ => GivenTheContentTypeIs("application/json")) .And(_ => GivenTheInputRequestHasMethod("POST")) .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamReRoute()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) @@ -223,7 +233,15 @@ private void GivenTheDownstreamReRouteMethodIs(string input) { - _downstreamReRoute = new DownstreamReRouteBuilder().WithDownStreamHttpMethod(input).Build(); + _downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownStreamHttpMethod(input) + .WithDownstreamHttpVersion(new Version("1.1")).Build(); + } + + private void GivenTheDownstreamReRoute() + { + _downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamHttpVersion(new Version("1.1")).Build(); } private void GivenTheInputRequestHasNoContentLength() From 2772ce406dcba54a16d2e1bfbe5a82663ae17096 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Wed, 19 Feb 2020 09:18:01 +0000 Subject: [PATCH 22/28] all acceptance tests passing --- .../Creator/ConfigurationCreator.cs | 10 +++++-- .../Configuration/Creator/DynamicsCreator.cs | 7 ++++- .../Configuration/File/FileDynamicReRoute.cs | 1 + .../File/FileGlobalConfiguration.cs | 2 ++ .../Configuration/IInternalConfiguration.cs | 4 +++ .../Configuration/InternalConfiguration.cs | 8 +++++- .../Finder/DownstreamRouteCreator.cs | 2 ++ .../LoadBalancerTests.cs | 2 +- test/Ocelot.AcceptanceTests/Steps.cs | 1 + ...wnstreamRouteFinderMiddlewareBenchmarks.cs | 2 +- .../ConfigurationCreatorTests.cs | 4 ++- .../Configuration/DynamicsCreatorTests.cs | 28 +++++++++++++++++-- .../FileConfigurationSetterTests.cs | 4 ++- .../FileInternalConfigurationCreatorTests.cs | 2 +- .../InMemoryConfigurationRepositoryTests.cs | 1 + .../DownstreamRouteCreatorTests.cs | 23 +++++++-------- .../DownstreamRouteFinderMiddlewareTests.cs | 3 +- .../DownstreamRouteFinderTests.cs | 4 ++- .../DownstreamRouteProviderFactoryTests.cs | 5 ++-- .../DownstreamUrlCreatorMiddlewareTests.cs | 2 +- .../Errors/ExceptionHandlerMiddlewareTests.cs | 8 +++--- ...ekaMiddlewareConfigurationProviderTests.cs | 4 +-- .../LoadBalancerMiddlewareTests.cs | 2 +- 23 files changed, 94 insertions(+), 35 deletions(-) diff --git a/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs index 1d5abc32..b3cced34 100644 --- a/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs @@ -13,13 +13,15 @@ namespace Ocelot.Configuration.Creator private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; private readonly IAdministrationPath _adminPath; private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; + private readonly IVersionCreator _versionCreator; public ConfigurationCreator( IServiceProviderConfigurationCreator serviceProviderConfigCreator, IQoSOptionsCreator qosOptionsCreator, IHttpHandlerOptionsCreator httpHandlerOptionsCreator, IServiceProvider serviceProvider, - ILoadBalancerOptionsCreator loadBalancerOptionsCreator + ILoadBalancerOptionsCreator loadBalancerOptionsCreator, + IVersionCreator versionCreator ) { _adminPath = serviceProvider.GetService(); @@ -27,6 +29,7 @@ namespace Ocelot.Configuration.Creator _serviceProviderConfigCreator = serviceProviderConfigCreator; _qosOptionsCreator = qosOptionsCreator; _httpHandlerOptionsCreator = httpHandlerOptionsCreator; + _versionCreator = versionCreator; } public InternalConfiguration Create(FileConfiguration fileConfiguration, List reRoutes) @@ -41,6 +44,8 @@ namespace Ocelot.Configuration.Creator var adminPath = _adminPath != null ? _adminPath.Path : null; + var version = _versionCreator.Create(fileConfiguration.GlobalConfiguration.DownstreamHttpVersion); + return new InternalConfiguration(reRoutes, adminPath, serviceProviderConfiguration, @@ -48,7 +53,8 @@ namespace Ocelot.Configuration.Creator lbOptions, fileConfiguration.GlobalConfiguration.DownstreamScheme, qosOptions, - httpHandlerOptions + httpHandlerOptions, + version ); } } diff --git a/src/Ocelot/Configuration/Creator/DynamicsCreator.cs b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs index 2b5193c0..57b86746 100644 --- a/src/Ocelot/Configuration/Creator/DynamicsCreator.cs +++ b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs @@ -8,10 +8,12 @@ namespace Ocelot.Configuration.Creator public class DynamicsCreator : IDynamicsCreator { private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; + private readonly IVersionCreator _versionCreator; - public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator) + public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator, IVersionCreator versionCreator) { _rateLimitOptionsCreator = rateLimitOptionsCreator; + _versionCreator = versionCreator; } public List Create(FileConfiguration fileConfiguration) @@ -26,10 +28,13 @@ namespace Ocelot.Configuration.Creator var rateLimitOption = _rateLimitOptionsCreator .Create(fileDynamicReRoute.RateLimitRule, globalConfiguration); + var version = _versionCreator.Create(fileDynamicReRoute.DownstreamHttpVersion); + var downstreamReRoute = new DownstreamReRouteBuilder() .WithEnableRateLimiting(rateLimitOption.EnableRateLimiting) .WithRateLimitOptions(rateLimitOption) .WithServiceName(fileDynamicReRoute.ServiceName) + .WithDownstreamHttpVersion(version) .Build(); var reRoute = new ReRouteBuilder() diff --git a/src/Ocelot/Configuration/File/FileDynamicReRoute.cs b/src/Ocelot/Configuration/File/FileDynamicReRoute.cs index 26d8b4d4..7657aa89 100644 --- a/src/Ocelot/Configuration/File/FileDynamicReRoute.cs +++ b/src/Ocelot/Configuration/File/FileDynamicReRoute.cs @@ -4,5 +4,6 @@ namespace Ocelot.Configuration.File { public string ServiceName { get; set; } public FileRateLimitRule RateLimitRule { get; set; } + public string DownstreamHttpVersion { get; set; } } } diff --git a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs index 9844c15d..6692ba04 100644 --- a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs +++ b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs @@ -26,5 +26,7 @@ public string DownstreamScheme { get; set; } public FileHttpHandlerOptions HttpHandlerOptions { get; set; } + + public string DownstreamHttpVersion { get; set; } } } diff --git a/src/Ocelot/Configuration/IInternalConfiguration.cs b/src/Ocelot/Configuration/IInternalConfiguration.cs index 1780da33..0925265f 100644 --- a/src/Ocelot/Configuration/IInternalConfiguration.cs +++ b/src/Ocelot/Configuration/IInternalConfiguration.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; namespace Ocelot.Configuration { + using System; + public interface IInternalConfiguration { List ReRoutes { get; } @@ -19,5 +21,7 @@ namespace Ocelot.Configuration QoSOptions QoSOptions { get; } HttpHandlerOptions HttpHandlerOptions { get; } + + Version DownstreamHttpVersion { get; } } } diff --git a/src/Ocelot/Configuration/InternalConfiguration.cs b/src/Ocelot/Configuration/InternalConfiguration.cs index 7b7649e4..7a398982 100644 --- a/src/Ocelot/Configuration/InternalConfiguration.cs +++ b/src/Ocelot/Configuration/InternalConfiguration.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; namespace Ocelot.Configuration { + using System; + public class InternalConfiguration : IInternalConfiguration { public InternalConfiguration( @@ -12,7 +14,8 @@ namespace Ocelot.Configuration LoadBalancerOptions loadBalancerOptions, string downstreamScheme, QoSOptions qoSOptions, - HttpHandlerOptions httpHandlerOptions) + HttpHandlerOptions httpHandlerOptions, + Version downstreamHttpVersion) { ReRoutes = reRoutes; AdministrationPath = administrationPath; @@ -22,6 +25,7 @@ namespace Ocelot.Configuration DownstreamScheme = downstreamScheme; QoSOptions = qoSOptions; HttpHandlerOptions = httpHandlerOptions; + DownstreamHttpVersion = downstreamHttpVersion; } public List ReRoutes { get; } @@ -32,5 +36,7 @@ namespace Ocelot.Configuration public string DownstreamScheme { get; } public QoSOptions QoSOptions { get; } public HttpHandlerOptions HttpHandlerOptions { get; } + + public Version DownstreamHttpVersion { get; } } } diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs index ec6821ad..b2b1db95 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs @@ -1,5 +1,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder { + using System; using Configuration; using Configuration.Builder; using Configuration.Creator; @@ -54,6 +55,7 @@ .WithQosOptions(qosOptions) .WithDownstreamScheme(configuration.DownstreamScheme) .WithLoadBalancerOptions(configuration.LoadBalancerOptions) + .WithDownstreamHttpVersion(configuration.DownstreamHttpVersion) .WithUpstreamPathTemplate(upstreamPathTemplate); var rateLimitOptions = configuration.ReRoutes != null diff --git a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs index b3adf531..5ce9c1e2 100644 --- a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs +++ b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs @@ -27,7 +27,7 @@ namespace Ocelot.AcceptanceTests public void should_load_balance_request_with_least_connection() { int portOne = 50591; - int portTwo = 51482; + int portTwo = 54483; var downstreamServiceOneUrl = $"http://localhost:{portOne}"; var downstreamServiceTwoUrl = $"http://localhost:{portTwo}"; diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index e8aee690..976b7422 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -392,6 +392,7 @@ namespace Ocelot.AcceptanceTests _ocelotServer = new TestServer(_webHostBuilder); _ocelotClient = _ocelotServer.CreateClient(); + Thread.Sleep(1000); } public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url) diff --git a/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs b/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs index 57f7386a..55869213 100644 --- a/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs +++ b/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs @@ -59,7 +59,7 @@ namespace Ocelot.Benchmarks _downstreamContext = new DownstreamContext(httpContext) { - Configuration = new InternalConfiguration(new List(), null, null, null, null, null, null, null) + Configuration = new InternalConfiguration(new List(), null, null, null, null, null, null, null, null) }; } diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs index a51ec161..68516ad6 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs @@ -19,6 +19,7 @@ namespace Ocelot.UnitTests.Configuration private readonly Mock _qosCreator; private readonly Mock _hhoCreator; private readonly Mock _lboCreator; + private readonly Mock _vCreator; private FileConfiguration _fileConfig; private List _reRoutes; private ServiceProviderConfiguration _spc; @@ -30,6 +31,7 @@ namespace Ocelot.UnitTests.Configuration public ConfigurationCreatorTests() { + _vCreator = new Mock(); _lboCreator = new Mock(); _hhoCreator = new Mock(); _qosCreator = new Mock(); @@ -117,7 +119,7 @@ namespace Ocelot.UnitTests.Configuration private void WhenICreate() { var serviceProvider = _serviceCollection.BuildServiceProvider(); - _creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object); + _creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object, _vCreator.Object); _result = _creator.Create(_fileConfig, _reRoutes); } } diff --git a/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs index 8a0601d3..b502aa73 100644 --- a/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs @@ -1,5 +1,6 @@ namespace Ocelot.UnitTests.Configuration { + using System; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -14,15 +15,18 @@ { private readonly DynamicsCreator _creator; private readonly Mock _rloCreator; + private readonly Mock _versionCreator; private List _result; private FileConfiguration _fileConfig; private RateLimitOptions _rlo1; private RateLimitOptions _rlo2; + private Version _version; public DynamicsCreatorTests() { + _versionCreator = new Mock(); _rloCreator = new Mock(); - _creator = new DynamicsCreator(_rloCreator.Object); + _creator = new DynamicsCreator(_rloCreator.Object, _versionCreator.Object); } [Fact] @@ -50,7 +54,8 @@ RateLimitRule = new FileRateLimitRule { EnableRateLimiting = false - } + }, + DownstreamHttpVersion = "1.1" }, new FileDynamicReRoute { @@ -58,16 +63,19 @@ RateLimitRule = new FileRateLimitRule { EnableRateLimiting = true - } + }, + DownstreamHttpVersion = "2.0" } } }; this.Given(_ => GivenThe(fileConfig)) .And(_ => GivenTheRloCreatorReturns()) + .And(_ => GivenTheVersionCreatorReturns()) .When(_ => WhenICreate()) .Then(_ => ThenTheReRoutesAreReturned()) .And(_ => ThenTheRloCreatorIsCalledCorrectly()) + .And(_ => ThenTheVersionCreatorIsCalledCorrectly()) .BDDfy(); } @@ -80,18 +88,32 @@ _fileConfig.GlobalConfiguration), Times.Once); } + private void ThenTheVersionCreatorIsCalledCorrectly() + { + _versionCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[0].DownstreamHttpVersion), Times.Once); + _versionCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[1].DownstreamHttpVersion), Times.Once); + } + private void ThenTheReRoutesAreReturned() { _result.Count.ShouldBe(2); _result[0].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeFalse(); _result[0].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo1); + _result[0].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_version); _result[0].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[0].ServiceName); _result[1].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); _result[1].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo2); + _result[1].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_version); _result[1].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[1].ServiceName); } + private void GivenTheVersionCreatorReturns() + { + _version = new Version("1.1"); + _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_version); + } + private void GivenTheRloCreatorReturns() { _rlo1 = new RateLimitOptionsBuilder().Build(); diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs index 85fdd53c..c647f410 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs @@ -15,6 +15,8 @@ using Xunit; namespace Ocelot.UnitTests.Configuration { + using System; + public class FileConfigurationSetterTests { private FileConfiguration _fileConfiguration; @@ -38,7 +40,7 @@ namespace Ocelot.UnitTests.Configuration { var fileConfig = new FileConfiguration(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); this.Given(x => GivenTheFollowingConfiguration(fileConfig)) .And(x => GivenTheRepoReturns(new OkResponse())) diff --git a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs index 6ccc4448..ccf3a677 100644 --- a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs @@ -87,7 +87,7 @@ _reRoutes = new List { new ReRouteBuilder().Build() }; _aggregates = new List { new ReRouteBuilder().Build() }; _dynamics = new List { new ReRouteBuilder().Build() }; - _internalConfig = new InternalConfiguration(null, "", null, "", null, "", null, null); + _internalConfig = new InternalConfiguration(null, "", null, "", null, "", null, null, null); _reRoutesCreator.Setup(x => x.Create(It.IsAny())).Returns(_reRoutes); _aggregatesCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_aggregates); diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 508f25e0..4639837e 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -120,6 +120,7 @@ namespace Ocelot.UnitTests.Configuration public string DownstreamScheme { get; } public QoSOptions QoSOptions { get; } public HttpHandlerOptions HttpHandlerOptions { get; } + public Version DownstreamHttpVersion { get; } } } } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs index a3348627..fd8190ea 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs @@ -1,5 +1,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { + using System; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -44,7 +45,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_create_downstream_route() { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration)) .When(_ => WhenICreate()) @@ -71,7 +72,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder var reRoutes = new List { reRoute }; - var configuration = new InternalConfiguration(reRoutes, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(reRoutes, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration)) .When(_ => WhenICreate()) @@ -83,7 +84,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_cache_downstream_route() { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) .When(_ => WhenICreate()) @@ -96,7 +97,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_not_cache_downstream_route() { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/")) .When(_ => WhenICreate()) @@ -110,7 +111,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public void should_create_downstream_route_with_no_path() { var upstreamUrlPath = "/auth/"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) .When(_ => WhenICreate()) @@ -122,7 +123,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public void should_create_downstream_route_with_only_first_segment_no_traling_slash() { var upstreamUrlPath = "/auth"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) .When(_ => WhenICreate()) @@ -134,7 +135,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public void should_create_downstream_route_with_segments_no_traling_slash() { var upstreamUrlPath = "/auth/test"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) .When(_ => WhenICreate()) @@ -146,7 +147,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public void should_create_downstream_route_and_remove_query_string() { var upstreamUrlPath = "/auth/test?test=1&best=2"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) .When(_ => WhenICreate()) @@ -158,7 +159,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public void should_create_downstream_route_for_sticky_sessions() { var loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(CookieStickySessions)).WithKey("boom").WithExpiryInMs(1).Build(); - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration)) .When(_ => WhenICreate()) @@ -174,7 +175,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithTimeoutValue(1) .Build(); - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration)) .And(_ => GivenTheQosCreatorReturns(qoSOptions)) @@ -186,7 +187,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder [Fact] public void should_create_downstream_route_with_handler_options() { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); this.Given(_ => GivenTheConfiguration(configuration)) .When(_ => WhenICreate()) diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index 3b333258..12e0a804 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -1,5 +1,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { + using System; using Microsoft.AspNetCore.Http; using Moq; using Ocelot.Configuration; @@ -48,7 +49,7 @@ [Fact] public void should_call_scoped_data_repository_correctly() { - var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); var downstreamReRoute = new DownstreamReRouteBuilder() .WithDownstreamPathTemplate("any old string") diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 7419fe55..5f9e1a50 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -13,6 +13,8 @@ using Xunit; namespace Ocelot.UnitTests.DownstreamRouteFinder { + using System; + public class DownstreamRouteFinderTests { private readonly IDownstreamRouteProvider _downstreamRouteFinder; @@ -739,7 +741,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void GivenTheConfigurationIs(List reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) { _reRoutesConfig = reRoutesConfig; - _config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + _config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); } private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs index 437033f7..ac8f5e0a 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs @@ -1,5 +1,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { + using System; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; @@ -140,12 +141,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void GivenTheReRoutes(List reRoutes) { - _config = new InternalConfiguration(reRoutes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + _config = new InternalConfiguration(reRoutes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); } private void GivenTheReRoutes(List reRoutes, ServiceProviderConfiguration config) { - _config = new InternalConfiguration(reRoutes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + _config = new InternalConfiguration(reRoutes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); } } } diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 7ed6a02a..f6e0a2d5 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -382,7 +382,7 @@ private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config) { - var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); + var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null, null); _downstreamContext.Configuration = configuration; } diff --git a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs index 3d86505e..932f40b2 100644 --- a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs @@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Errors [Fact] public void NoDownstreamException() { - var config = new InternalConfiguration(null, null, null, null, null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -65,7 +65,7 @@ namespace Ocelot.UnitTests.Errors [Fact] public void DownstreamException() { - var config = new InternalConfiguration(null, null, null, null, null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -77,7 +77,7 @@ namespace Ocelot.UnitTests.Errors [Fact] public void ShouldSetRequestId() { - var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null); + var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.Errors [Fact] public void ShouldSetAspDotNetRequestId() { - var config = new InternalConfiguration(null, null, null, null, null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) diff --git a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs index fc7f3c82..5a6a5306 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs @@ -20,7 +20,7 @@ { var configRepo = new Mock(); configRepo.Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(null, null, null, null, null, null, null, null))); + .Returns(new OkResponse(new InternalConfiguration(null, null, null, null, null, null, null, null, null))); var services = new ServiceCollection(); services.AddSingleton(configRepo.Object); var sp = services.BuildServiceProvider(); @@ -35,7 +35,7 @@ var client = new Mock(); var configRepo = new Mock(); configRepo.Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(null, null, serviceProviderConfig, null, null, null, null, null))); + .Returns(new OkResponse(new InternalConfiguration(null, null, serviceProviderConfig, null, null, null, null, null, null))); var services = new ServiceCollection(); services.AddSingleton(configRepo.Object); services.AddSingleton(client.Object); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 2705b3b7..1a6202cf 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -139,7 +139,7 @@ namespace Ocelot.UnitTests.LoadBalancer private void GivenTheConfigurationIs(ServiceProviderConfiguration config) { _config = config; - var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); + var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null, null); _downstreamContext.Configuration = configuration; } From ec8a5b6ddf65572d93fdc0030c63fb51123e0732 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 21 Feb 2020 16:49:26 +0000 Subject: [PATCH 23/28] finished tests around http2 --- ...ersionCreator.cs => HttpVersionCreator.cs} | 2 +- .../DependencyInjection/OcelotBuilder.cs | 2 +- test/Ocelot.AcceptanceTests/HttpTests.cs | 205 ++++++++++++++++++ test/Ocelot.AcceptanceTests/ServiceHandler.cs | 27 +++ .../Configuration/VersionCreatorTests.cs | 4 +- 5 files changed, 236 insertions(+), 4 deletions(-) rename src/Ocelot/Configuration/Creator/{VersionCreator.cs => HttpVersionCreator.cs} (86%) create mode 100644 test/Ocelot.AcceptanceTests/HttpTests.cs diff --git a/src/Ocelot/Configuration/Creator/VersionCreator.cs b/src/Ocelot/Configuration/Creator/HttpVersionCreator.cs similarity index 86% rename from src/Ocelot/Configuration/Creator/VersionCreator.cs rename to src/Ocelot/Configuration/Creator/HttpVersionCreator.cs index b6fb3ad1..f80f98ff 100644 --- a/src/Ocelot/Configuration/Creator/VersionCreator.cs +++ b/src/Ocelot/Configuration/Creator/HttpVersionCreator.cs @@ -2,7 +2,7 @@ { using System; - public class VersionCreator : IVersionCreator + public class HttpVersionCreator : IVersionCreator { public Version Create(string downstreamHttpVersion) { diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 2f1444f6..0336f691 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -136,7 +136,7 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); //add security this.AddSecurity(); diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs new file mode 100644 index 00000000..89ab12f4 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/HttpTests.cs @@ -0,0 +1,205 @@ +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Http; + using Microsoft.AspNetCore.Server.Kestrel.Core; + using TestStack.BDDfy; + using Xunit; + + public class HttpTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public HttpTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_when_using_http_one_point_one() + { + const int port = 53279; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.1", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_using_http_two_point_zero() + { + const int port = 53675; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "2.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_response_500_when_using_http_one_to_talk_to_server_running_http_two() + { + const int port = 53677; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.1", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_using_http_two_to_talk_to_server_running_http_one_point_one() + { + const int port = 53679; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "2.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int port, HttpProtocols protocols) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + context.Response.StatusCode = 200; + var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + await context.Response.WriteAsync(body); + }, port, protocols); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ServiceHandler.cs b/test/Ocelot.AcceptanceTests/ServiceHandler.cs index c10c122b..9fc8b0f5 100644 --- a/test/Ocelot.AcceptanceTests/ServiceHandler.cs +++ b/test/Ocelot.AcceptanceTests/ServiceHandler.cs @@ -6,9 +6,11 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System; + using System.ComponentModel; using System.IO; using System.Net; using System.Threading.Tasks; + using Microsoft.AspNetCore.Server.Kestrel.Core; public class ServiceHandler : IDisposable { @@ -47,6 +49,31 @@ _builder.Start(); } + public void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, RequestDelegate del, int port, HttpProtocols protocols) + { + _builder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .ConfigureKestrel(serverOptions => + { + serverOptions.Listen(IPAddress.Loopback, port, listenOptions => + { + listenOptions.UseHttps("idsrv3test.pfx", "idsrv3test"); + listenOptions.Protocols = protocols; + }); + }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(del); + }) + .Build(); + + _builder.Start(); + } + public void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string fileName, string password, int port, RequestDelegate del) { _builder = new WebHostBuilder() diff --git a/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs index f5119ec8..bb440c5c 100644 --- a/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/VersionCreatorTests.cs @@ -8,13 +8,13 @@ public class VersionCreatorTests { - private readonly VersionCreator _creator; + private readonly HttpVersionCreator _creator; private string _input; private Version _result; public VersionCreatorTests() { - _creator = new VersionCreator(); + _creator = new HttpVersionCreator(); } [Fact] From ede5650a3cf01fc37cf39ea5bd66220011ef89af Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 21 Feb 2020 16:53:03 +0000 Subject: [PATCH 24/28] docs --- docs/features/configuration.rst | 8 ++++- test/Ocelot.AcceptanceTests/HttpTests.cs | 38 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst index f9cb7841..ebd82203 100644 --- a/docs/features/configuration.rst +++ b/docs/features/configuration.rst @@ -25,6 +25,7 @@ Here is an example ReRoute configuration, You don't need to set all of these thi "Get" ], "DownstreamHttpMethod": "", + "DownstreamHttpVersion": "", "AddHeadersToRequest": {}, "AddClaimsToRequest": {}, "RouteClaimsRequirement": {}, @@ -278,4 +279,9 @@ Registering a callback { _callbackHolder.Dispose(); } - } \ No newline at end of file + } + +DownstreamHttpVersion +--------------------- + +Ocelot allows you to choose the HTTP version it will use to make the proxy request. It can be set as "1.0", "1.1" or "2.0". \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs index 89ab12f4..b63ad4bb 100644 --- a/test/Ocelot.AcceptanceTests/HttpTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpTests.cs @@ -22,6 +22,44 @@ namespace Ocelot.AcceptanceTests _steps = new Steps(); } + [Fact] + public void should_return_response_200_when_using_http_one() + { + const int port = 53219; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + [Fact] public void should_return_response_200_when_using_http_one_point_one() { From 08512ec1c014c9bae9d54db2b7159173dbcb0b44 Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Sat, 22 Feb 2020 12:30:38 +0000 Subject: [PATCH 25/28] check we are on master before pushing coverage report (#1143) This means that forked PRs can now build as they don't need the coveralls secret --- build.cake | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index 3d4626bd..9b909f63 100644 --- a/build.cake +++ b/build.cake @@ -134,11 +134,12 @@ Task("RunUnitTests") var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); Information(coverageSummaryFile); Information(artifactsForUnitTestsDir); + // todo bring back report generator to get a friendly report // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); // https://github.com/danielpalme/ReportGenerator - - if (IsRunningOnCircleCI()) + + if (IsRunningOnCircleCI() && IsMaster()) { var repoToken = EnvironmentVariable(coverallsRepoToken); if (string.IsNullOrEmpty(repoToken)) @@ -497,4 +498,9 @@ private string GetResource(string url) private bool IsRunningOnCircleCI() { return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); +} + +private bool IsMaster() +{ + return Environment.GetEnvironmentVariable("CIRCLE_BRANCH").ToLower() == "master"; } \ No newline at end of file From 263f26ed51df87ebe5f2a8446be0337c21690cd7 Mon Sep 17 00:00:00 2001 From: jlukawska Date: Fri, 6 Mar 2020 10:18:21 +0100 Subject: [PATCH 26/28] #1115 find available ports to use in acceptance tests --- test/Ocelot.AcceptanceTests/AggregateTests.cs | 90 ++++++----- .../AuthenticationTests.cs | 16 +- .../AuthorisationTests.cs | 36 +++-- .../ButterflyTracingTests.cs | 23 +-- test/Ocelot.AcceptanceTests/CachingTests.cs | 40 +++-- .../CaseSensitiveRoutingTests.cs | 48 +++--- .../ClaimsToDownstreamPathTests.cs | 14 +- .../ClaimsToHeadersForwardingTests.cs | 16 +- .../ClaimsToQueryStringForwardingTests.cs | 26 +-- .../ClientRateLimitTests.cs | 14 +- .../ConfigurationInConsulTests.cs | 11 +- .../ConsulConfigurationInConsulTests.cs | 32 ++-- .../ConsulWebSocketTests.cs | 6 +- test/Ocelot.AcceptanceTests/ContentTests.cs | 18 ++- .../CustomMiddlewareTests.cs | 56 ++++--- .../EurekaServiceDiscoveryTests.cs | 2 +- test/Ocelot.AcceptanceTests/GzipTests.cs | 8 +- test/Ocelot.AcceptanceTests/HeaderTests.cs | 74 +++++---- .../HttpClientCachingTests.cs | 18 ++- .../HttpDelegatingHandlersTests.cs | 24 ++- .../LoadBalancerTests.cs | 8 +- test/Ocelot.AcceptanceTests/PollyQoSTests.cs | 37 +++-- .../RandomPortFinder.cs | 55 +++++++ .../ReasonPhraseTests.cs | 6 +- test/Ocelot.AcceptanceTests/RequestIdTests.cs | 32 ++-- .../ResponseCodeTests.cs | 8 +- .../ReturnsErrorTests.cs | 11 +- test/Ocelot.AcceptanceTests/RoutingTests.cs | 152 ++++++++++++------ .../RoutingWithQueryStringTests.cs | 27 ++-- .../ServiceDiscoveryTests.cs | 52 +++--- .../ServiceFabricTests.cs | 24 ++- test/Ocelot.AcceptanceTests/SslTests.cs | 4 +- test/Ocelot.AcceptanceTests/StartupTests.cs | 6 +- .../StickySessionsTests.cs | 12 +- .../TwoDownstreamServicesTests.cs | 12 +- .../UpstreamHostTests.cs | 10 +- test/Ocelot.AcceptanceTests/WebSocketTests.cs | 6 +- 37 files changed, 653 insertions(+), 381 deletions(-) create mode 100644 test/Ocelot.AcceptanceTests/RandomPortFinder.cs diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index 2bd2f09f..8e2f0b81 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -30,7 +30,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_597() - { + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -46,7 +47,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 8571 + Port = port } }, Key = "key1" @@ -62,7 +63,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 8571 + Port = port } }, Key = "key2" @@ -78,7 +79,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 8571 + Port = port } }, Key = "key3" @@ -94,7 +95,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 8571 + Port = port } }, Key = "key4" @@ -129,7 +130,7 @@ namespace Ocelot.AcceptanceTests var expected = "{\"key1\":some_data,\"key2\":some_data}"; - this.Given(x => x.GivenServiceIsRunning("http://localhost:8571", 200, "some_data")) + this.Given(x => x.GivenServiceIsRunning($"http://localhost:{port}", 200, "some_data")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EmpDetail/US/1")) @@ -140,7 +141,10 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_advanced_aggregate_configs() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); + var port3 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -154,7 +158,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51889, + Port = port1, } }, UpstreamPathTemplate = "/Comments", @@ -170,7 +174,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54030, + Port = port2, } }, UpstreamPathTemplate = "/UserDetails", @@ -186,7 +190,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51887, + Port = port3, } }, UpstreamPathTemplate = "/PostDetails", @@ -221,9 +225,9 @@ namespace Ocelot.AcceptanceTests var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + ",\"PostDetails\":" + postDetailsResponseContent + "}"; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51889", "/", 200, commentsResponseContent)) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:54030", "/users/1", 200, userDetailsResponseContent)) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51887", "/posts/2", 200, postDetailsResponseContent)) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 200, commentsResponseContent)) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/users/1", 200, userDetailsResponseContent)) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port3}", "/posts/2", 200, postDetailsResponseContent)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -234,7 +238,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_user_defined_aggregate() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -248,7 +254,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51885, + Port = port1, } }, UpstreamPathTemplate = "/laura", @@ -264,7 +270,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51886, + Port = port2, } }, UpstreamPathTemplate = "/tom", @@ -290,8 +296,8 @@ namespace Ocelot.AcceptanceTests var expected = "Bye from Laura, Bye from Tom"; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 200, "{Hello from Tom}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -303,7 +309,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -317,7 +325,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51875, + Port = port1, } }, UpstreamPathTemplate = "/laura", @@ -333,7 +341,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 52476, + Port = port2, } }, UpstreamPathTemplate = "/tom", @@ -358,8 +366,8 @@ namespace Ocelot.AcceptanceTests var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:52476", "/", 200, "{Hello from Tom}")) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 200, "{Hello from Tom}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -371,7 +379,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_one_service_404() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -385,7 +395,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51881, + Port = port1, } }, UpstreamPathTemplate = "/laura", @@ -401,7 +411,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51889, + Port = port2, } }, UpstreamPathTemplate = "/tom", @@ -426,8 +436,8 @@ namespace Ocelot.AcceptanceTests var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}"; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, "")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51889", "/", 200, "{Hello from Tom}")) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 200, "{Hello from Tom}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -439,7 +449,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_both_service_404() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -453,7 +465,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51883, + Port = port1, } }, UpstreamPathTemplate = "/laura", @@ -469,7 +481,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51884, + Port = port2, } }, UpstreamPathTemplate = "/tom", @@ -494,8 +506,8 @@ namespace Ocelot.AcceptanceTests var expected = "{\"Laura\":,\"Tom\":}"; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, "")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, "")) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 404, "")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -507,7 +519,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_be_thread_safe() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -521,7 +535,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51878, + Port = port1, } }, UpstreamPathTemplate = "/laura", @@ -537,7 +551,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51880, + Port = port2, } }, UpstreamPathTemplate = "/tom", @@ -560,8 +574,8 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}")) + this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 200, "{Hello from Tom}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway()) diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 65d9a878..5f99c209 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -20,7 +20,7 @@ namespace Ocelot.AcceptanceTests { private readonly Steps _steps; private IWebHost _identityServerBuilder; - private string _identityServerRootUrl = "http://localhost:51888"; + private string _identityServerRootUrl; private string _downstreamServicePath = "/"; private string _downstreamServiceHost = "localhost"; private string _downstreamServiceScheme = "http"; @@ -31,7 +31,9 @@ namespace Ocelot.AcceptanceTests public AuthenticationTests() { _serviceHandler = new ServiceHandler(); - _steps = new Steps(); + _steps = new Steps(); + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; _options = o => { o.Authority = _identityServerRootUrl; @@ -45,7 +47,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_401_using_identity_server_access_token() { - int port = 54329; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -86,7 +88,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_using_identity_server() { - int port = 54099; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -129,7 +131,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() { - int port = 54196; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -171,7 +173,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_201_using_identity_server_access_token() { - int port = 52226; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -214,7 +216,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_201_using_identity_server_reference_token() { - int port = 52222; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 6df6755c..fc9df72b 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -21,13 +21,15 @@ namespace Ocelot.AcceptanceTests private IWebHost _identityServerBuilder; private readonly Steps _steps; private readonly Action _options; - private string _identityServerRootUrl = "http://localhost:51888"; + private string _identityServerRootUrl; private readonly ServiceHandler _serviceHandler; public AuthorisationTests() { _serviceHandler = new ServiceHandler(); - _steps = new Steps(); + _steps = new Steps(); + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; _options = o => { o.Authority = _identityServerRootUrl; @@ -41,7 +43,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_authorising_route() { - int port = 52875; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -86,9 +88,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) @@ -101,7 +103,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_403_authorising_route() { - int port = 59471; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -145,9 +147,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) @@ -159,7 +161,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_using_identity_server_with_allowed_scope() { - int port = 63471; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -188,9 +190,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) @@ -202,7 +204,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_403_using_identity_server_with_scope_not_allowed() { - int port = 60571; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -231,9 +233,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) @@ -245,7 +247,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_240() { - int port = 61071; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -292,9 +294,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt, users)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) diff --git a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs index 1b587cb8..ec102a8d 100644 --- a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs +++ b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs @@ -35,6 +35,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_forward_tracing_information_from_ocelot_and_downstream_services() { + int port1 = RandomPortFinder.GetRandomPort(); + int port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -48,7 +50,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51887, + Port = port1, } }, UpstreamPathTemplate = "/api001/values", @@ -67,7 +69,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51388, + Port = port2, } }, UpstreamPathTemplate = "/api002/values", @@ -79,12 +81,13 @@ namespace Ocelot.AcceptanceTests } } }; - - var butterflyUrl = "http://localhost:9618"; + + var butterflyPort = RandomPortFinder.GetRandomPort(); + var butterflyUrl = $"http://localhost:{butterflyPort}"; this.Given(x => GivenFakeButterfly(butterflyUrl)) - .And(x => GivenServiceOneIsRunning("http://localhost:51887", "/api/values", 200, "Hello from Laura", butterflyUrl)) - .And(x => GivenServiceTwoIsRunning("http://localhost:51388", "/api/values", 200, "Hello from Tom", butterflyUrl)) + .And(x => GivenServiceOneIsRunning($"http://localhost:{port1}", "/api/values", 200, "Hello from Laura", butterflyUrl)) + .And(x => GivenServiceTwoIsRunning($"http://localhost:{port2}", "/api/values", 200, "Hello from Tom", butterflyUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) @@ -105,6 +108,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_tracing_header() { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -118,7 +122,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51387, + Port = port, } }, UpstreamPathTemplate = "/api001/values", @@ -136,10 +140,11 @@ namespace Ocelot.AcceptanceTests } }; - var butterflyUrl = "http://localhost:9618"; + var butterflyPort = RandomPortFinder.GetRandomPort(); + var butterflyUrl = $"http://localhost:{butterflyPort}"; this.Given(x => GivenFakeButterfly(butterflyUrl)) - .And(x => GivenServiceOneIsRunning("http://localhost:51387", "/api/values", 200, "Hello from Laura", butterflyUrl)) + .And(x => GivenServiceOneIsRunning($"http://localhost:{port}", "/api/values", 200, "Hello from Laura", butterflyUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index d8b1a05b..d005d2ae 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -22,7 +22,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_cached_response() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -35,7 +37,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57899, + Port = port, } }, DownstreamScheme = "http", @@ -49,13 +51,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57899", 200, "Hello from Laura", null, null)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura", null, null)) .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")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:57899", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns($"http://localhost:{port}", 200, "Hello from Tom")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -65,7 +67,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_cached_response_with_expires_header() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -78,7 +82,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 52839, + Port = port, } }, DownstreamScheme = "http", @@ -92,13 +96,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura", "Expires", "-1")) .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")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns($"http://localhost:{port}", 200, "Hello from Tom")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -109,7 +113,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_cached_response_when_using_jsonserialized_cache() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -122,7 +128,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57879, + Port = port, } }, DownstreamScheme = "http", @@ -136,13 +142,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57879", 200, "Hello from Laura", null, null)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura", null, null)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:57879", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns($"http://localhost:{port}", 200, "Hello from Tom")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) @@ -151,7 +157,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_return_cached_response_as_ttl_expires() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -164,7 +172,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57873, + Port = port, } }, DownstreamScheme = "http", @@ -178,13 +186,13 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57873", 200, "Hello from Laura", null, null)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura", null, null)) .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")) - .Given(x => x.GivenTheServiceNowReturns("http://localhost:57873", 200, "Hello from Tom")) + .Given(x => x.GivenTheServiceNowReturns($"http://localhost:{port}", 200, "Hello from Tom")) .And(x => x.GivenTheCacheExpires()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index 405a7c8e..c56b7d42 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -21,7 +21,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_global_ignore_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -34,7 +36,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -44,7 +46,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -54,7 +56,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_reroute_ignore_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -67,7 +71,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -78,7 +82,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -88,7 +92,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_404_when_reroute_respect_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -101,7 +107,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -112,7 +118,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -122,7 +128,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_reroute_respect_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -135,7 +143,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -146,7 +154,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -156,7 +164,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_404_when_global_respect_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -169,7 +179,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -180,7 +190,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) @@ -190,7 +200,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_global_respect_case_sensitivity_set() - { + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -203,7 +215,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51877, + Port = port, } }, DownstreamScheme = "http", @@ -214,7 +226,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs index 5e3f667f..6d22205c 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs @@ -23,11 +23,13 @@ namespace Ocelot.AcceptanceTests private IWebHost _identityServerBuilder; private readonly Steps _steps; private Action _options; - private string _identityServerRootUrl = "http://localhost:57888"; + private string _identityServerRootUrl; private string _downstreamFinalPath; public ClaimsToDownstreamPathTests() { + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; _steps = new Steps(); _options = o => { @@ -49,6 +51,8 @@ namespace Ocelot.AcceptanceTests SubjectId = "registered|1231231", }; + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -61,7 +65,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57876, + Port = port, }, }, DownstreamScheme = "http", @@ -83,9 +87,9 @@ namespace Ocelot.AcceptanceTests }, }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 5f16c96f..63f79e14 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -25,13 +25,15 @@ namespace Ocelot.AcceptanceTests private IWebHost _identityServerBuilder; private readonly Steps _steps; private Action _options; - private string _identityServerRootUrl = "http://localhost:52888"; + private string _identityServerRootUrl; private readonly ServiceHandler _serviceHandler; public ClaimsToHeadersForwardingTests() { _serviceHandler = new ServiceHandler(); - _steps = new Steps(); + _steps = new Steps(); + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; _options = o => { o.Authority = _identityServerRootUrl; @@ -57,6 +59,8 @@ namespace Ocelot.AcceptanceTests } }; + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -69,7 +73,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 52876, + Port = port, } }, DownstreamScheme = "http", @@ -94,9 +98,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:52888")) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index ad077eb7..7173a6f3 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -25,12 +25,14 @@ namespace Ocelot.AcceptanceTests private IWebHost _identityServerBuilder; private readonly Steps _steps; private Action _options; - private string _identityServerRootUrl = "http://localhost:57888"; + private string _identityServerRootUrl; private string _downstreamQueryString; public ClaimsToQueryStringForwardingTests() { - _steps = new Steps(); + _steps = new Steps(); + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; _options = o => { o.Authority = _identityServerRootUrl; @@ -56,6 +58,8 @@ namespace Ocelot.AcceptanceTests } }; + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -68,7 +72,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57876, + Port = port, } }, DownstreamScheme = "http", @@ -93,9 +97,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) @@ -120,6 +124,8 @@ namespace Ocelot.AcceptanceTests } }; + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -132,7 +138,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57876, + Port = port, } }, DownstreamScheme = "http", @@ -157,9 +163,9 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) diff --git a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs index f598d4ad..c9f556e5 100644 --- a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs +++ b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs @@ -23,6 +23,8 @@ [Fact] public void should_call_withratelimiting() { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -35,7 +37,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51876, + Port = port, } }, DownstreamScheme = "http", @@ -66,7 +68,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) @@ -81,6 +83,8 @@ [Fact] public void should_wait_for_period_timespan_to_elapse_before_making_next_request() { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -93,7 +97,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51926, + Port = port, } }, DownstreamScheme = "http", @@ -125,7 +129,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51926", "/api/ClientRateLimit")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) @@ -146,7 +150,7 @@ [Fact] public void should_call_middleware_withWhitelistClient() { - int port = 61876; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index e72aab33..4d806b05 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -32,6 +32,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() { + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -45,7 +48,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51779, + Port = servicePort, } }, UpstreamPathTemplate = "/", @@ -57,15 +60,15 @@ namespace Ocelot.AcceptanceTests ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 9502 + Port = consulPort } } }; - var fakeConsulServiceDiscoveryUrl = "http://localhost:9502"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs index 976bd909..f9774565 100644 --- a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs @@ -34,6 +34,9 @@ [Fact] public void should_return_response_200_with_simple_url() { + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -47,7 +50,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51779, + Port = servicePort, } }, UpstreamPathTemplate = "/", @@ -59,15 +62,15 @@ ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 9500 + Port = consulPort } } }; - var fakeConsulServiceDiscoveryUrl = "http://localhost:9500"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -79,7 +82,8 @@ [Fact] public void should_load_configuration_out_of_consul() { - var consulPort = 8500; + var consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -108,7 +112,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51779, + Port = servicePort, } }, UpstreamPathTemplate = "/cs/status", @@ -127,7 +131,7 @@ this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) @@ -139,7 +143,9 @@ [Fact] public void should_load_configuration_out_of_consul_if_it_is_changed() { - var consulPort = 8506; + var consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { GlobalConfiguration = new FileGlobalConfiguration() @@ -167,7 +173,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51780, + Port = servicePort, } }, UpstreamPathTemplate = "/cs/status", @@ -197,7 +203,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51780, + Port = servicePort, } }, UpstreamPathTemplate = "/cs/status/awesome", @@ -216,7 +222,7 @@ this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) @@ -230,9 +236,9 @@ [Fact] public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit() { - const int consulPort = 8523; + int consulPort = RandomPortFinder.GetRandomPort(); const string serviceName = "web"; - const int downstreamServicePort = 8187; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() diff --git a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs index 7ebb4737..935a56f7 100644 --- a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs @@ -34,14 +34,14 @@ [Fact] public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer() { - var downstreamPort = 5007; + var downstreamPort = RandomPortFinder.GetRandomPort(); var downstreamHost = "localhost"; - var secondDownstreamPort = 5008; + var secondDownstreamPort = RandomPortFinder.GetRandomPort(); var secondDownstreamHost = "localhost"; var serviceName = "websockets"; - var consulPort = 8509; + var consulPort = RandomPortFinder.GetRandomPort(); var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { diff --git a/test/Ocelot.AcceptanceTests/ContentTests.cs b/test/Ocelot.AcceptanceTests/ContentTests.cs index 7a4d8365..31a16066 100644 --- a/test/Ocelot.AcceptanceTests/ContentTests.cs +++ b/test/Ocelot.AcceptanceTests/ContentTests.cs @@ -26,6 +26,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_add_content_type_or_content_length_headers() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -39,7 +41,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51339, + Port = port, } }, UpstreamPathTemplate = "/", @@ -48,7 +50,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -62,6 +64,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_add_content_type_and_content_length_headers() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -74,7 +78,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51349, + Port = port, } }, DownstreamScheme = "http", @@ -86,7 +90,7 @@ namespace Ocelot.AcceptanceTests var contentType = "application/json"; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51349", "/", 201, string.Empty)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -101,6 +105,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_add_default_content_type_header() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -113,7 +119,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51359, + Port = port, } }, DownstreamScheme = "http", @@ -123,7 +129,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51359", "/", 201, string.Empty)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index bbfce600..2f7a2756 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -37,7 +37,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -51,7 +53,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -61,7 +63,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -80,7 +82,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -94,7 +98,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -104,7 +108,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -123,7 +127,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -137,7 +143,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -147,7 +153,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -166,7 +172,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -180,7 +188,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -190,7 +198,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -209,7 +217,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -223,7 +233,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -233,7 +243,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -252,7 +262,9 @@ _counter++; await next.Invoke(); } - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -266,7 +278,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41879, + Port = port, } }, DownstreamScheme = "http", @@ -276,7 +288,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200, "")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -299,7 +311,9 @@ } return Task.CompletedTask; - }; + }; + + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -313,7 +327,7 @@ new FileHostAndPort { Host = "localhost", - Port = 41880, + Port = port, } }, DownstreamScheme = "http", @@ -323,7 +337,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41880", 200, "/test")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "/test")) .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) .And(x => _steps.GivenOcelotIsRunningWithMiddleareBeforePipeline(callback)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs index 3029249c..d1e63c81 100644 --- a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs @@ -28,7 +28,7 @@ { var eurekaPort = 8761; var serviceName = "product"; - var downstreamServicePort = 50371; + var downstreamServicePort = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; var fakeEurekaServiceDiscoveryUrl = $"http://localhost:{eurekaPort}"; diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs index a2b1e1b7..61237b38 100644 --- a/test/Ocelot.AcceptanceTests/GzipTests.cs +++ b/test/Ocelot.AcceptanceTests/GzipTests.cs @@ -25,7 +25,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -39,7 +41,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51179, + Port = port, } }, UpstreamPathTemplate = "/", @@ -50,7 +52,7 @@ namespace Ocelot.AcceptanceTests var input = "people"; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51179", "/", 200, "Hello from Laura", "\"people\"")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura", "\"people\"")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasGzipContent(input)) diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index e7c0c2ca..82157335 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -24,7 +24,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_transform_upstream_header() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -38,7 +40,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51871, + Port = port, } }, UpstreamPathTemplate = "/", @@ -51,7 +53,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Laz")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenIAddAHeader("Laz", "D")) @@ -63,7 +65,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_transform_downstream_header() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -77,7 +81,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51871, + Port = port, } }, UpstreamPathTemplate = "/", @@ -90,7 +94,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Location", "http://www.bbc.co.uk/")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -101,7 +105,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_190() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -115,14 +121,14 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 6773, + Port = port, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, DownstreamHeaderTransform = new Dictionary { - {"Location", "http://localhost:6773, {BaseUrl}"} + {"Location", $"http://localhost:{port}, {{BaseUrl}}"} }, HttpHandlerOptions = new FileHttpHandlerOptions { @@ -132,7 +138,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 302, "Location", $"http://localhost:{port}/pay/Receive")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -143,7 +149,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_205() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -157,7 +165,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 6773, + Port = port, } }, UpstreamPathTemplate = "/", @@ -174,7 +182,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 302, "Location", $"http://localhost:{port}/pay/Receive")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -185,7 +193,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_417() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -199,7 +209,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 6773, + Port = port, } }, UpstreamPathTemplate = "/", @@ -220,7 +230,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 302, "Location", $"http://localhost:{port}/pay/Receive")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -231,7 +241,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void request_should_reuse_cookies_with_cookie_container() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -245,7 +257,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 6774, + Port = port, } }, UpstreamPathTemplate = "/sso/{everything}", @@ -258,7 +270,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6774", "/sso/test", 200)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/sso/test", 200)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) @@ -272,7 +284,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void request_should_have_own_cookies_no_cookie_container() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -286,7 +300,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 6775, + Port = port, } }, UpstreamPathTemplate = "/sso/{everything}", @@ -299,7 +313,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6775", "/sso/test", 200)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/sso/test", 200)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) @@ -313,7 +327,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void issue_474_should_not_put_spaces_in_header() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -327,7 +343,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 52866, + Port = port, } }, UpstreamPathTemplate = "/", @@ -336,7 +352,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52866", "/", 200, "Accept")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Accept")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenIAddAHeader("Accept", "text/html,application/xhtml+xml,application/xml;")) @@ -348,7 +364,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void issue_474_should_put_spaces_in_header() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -362,7 +380,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51874, + Port = port, } }, UpstreamPathTemplate = "/", @@ -371,7 +389,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/", 200, "Accept")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Accept")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenIAddAHeader("Accept", "text/html")) diff --git a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs index a3b8f49e..58dca0af 100644 --- a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs @@ -26,7 +26,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_cache_one_http_client_same_re_route() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -40,7 +42,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 58814, + Port = port, } }, UpstreamPathTemplate = "/", @@ -51,7 +53,7 @@ namespace Ocelot.AcceptanceTests var cache = new FakeHttpClientCache(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58814", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -66,7 +68,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_cache_two_http_client_different_re_route() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -80,7 +84,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 58817, + Port = port, } }, UpstreamPathTemplate = "/", @@ -95,7 +99,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 58817, + Port = port, } }, UpstreamPathTemplate = "/two", @@ -106,7 +110,7 @@ namespace Ocelot.AcceptanceTests var cache = new FakeHttpClientCache(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58817", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs index dc985e42..57c2dc27 100644 --- a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs @@ -27,6 +27,8 @@ [Fact] public void should_call_re_route_ordered_specific_handlers() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -40,7 +42,7 @@ new FileHostAndPort { Host = "localhost", - Port = 7197, + Port = port, } }, UpstreamPathTemplate = "/", @@ -54,7 +56,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7197", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -67,6 +69,8 @@ [Fact] public void should_call_global_di_handlers() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -80,7 +84,7 @@ new FileHostAndPort { Host = "localhost", - Port = 7187, + Port = port, } }, UpstreamPathTemplate = "/", @@ -89,7 +93,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7187", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -102,6 +106,8 @@ [Fact] public void should_call_global_di_handlers_multiple_times() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -115,7 +121,7 @@ new FileHostAndPort { Host = "localhost", - Port = 9187, + Port = port, } }, UpstreamPathTemplate = "/", @@ -124,7 +130,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:9187", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -153,6 +159,8 @@ [Fact] public void should_call_global_di_handlers_with_dependency() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -166,7 +174,7 @@ new FileHostAndPort { Host = "localhost", - Port = 7188, + Port = port, } }, UpstreamPathTemplate = "/", @@ -177,7 +185,7 @@ var dependency = new FakeDependency(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7188", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi(dependency)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs index b3adf531..3c62afea 100644 --- a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs +++ b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs @@ -26,8 +26,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_load_balance_request_with_least_connection() { - int portOne = 50591; - int portTwo = 51482; + int portOne = RandomPortFinder.GetRandomPort(); + int portTwo = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{portOne}"; var downstreamServiceTwoUrl = $"http://localhost:{portTwo}"; @@ -76,8 +76,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_load_balance_request_with_round_robin() { - var downstreamPortOne = 51701; - var downstreamPortTwo = 53802; + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; diff --git a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs index d7cf7c2d..07cc7299 100644 --- a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs +++ b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs @@ -24,7 +24,9 @@ [Fact] public void should_not_timeout() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -37,7 +39,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51569, + Port = port, } }, DownstreamScheme = "http", @@ -52,7 +54,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51569", 200, string.Empty, 10)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, string.Empty, 10)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithPolly()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -63,7 +65,9 @@ [Fact] public void should_timeout() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -76,7 +80,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51579, + Port = port, } }, DownstreamScheme = "http", @@ -91,7 +95,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51579", 201, string.Empty, 1000)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 201, string.Empty, 1000)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithPolly()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -102,7 +106,9 @@ [Fact] public void should_open_circuit_breaker_then_close() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -116,7 +122,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51892, + Port = port, } }, UpstreamPathTemplate = "/", @@ -131,7 +137,7 @@ } }; - this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51892", "Hello from Laura")) + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port}", "Hello from Laura")) .Given(x => _steps.GivenThereIsAConfiguration(configuration)) .Given(x => _steps.GivenOcelotIsRunningWithPolly()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -152,7 +158,10 @@ [Fact] public void open_circuit_should_not_effect_different_reRoute() - { + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -166,7 +175,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51870, + Port = port1, } }, UpstreamPathTemplate = "/", @@ -187,7 +196,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51880, + Port = port2, } }, UpstreamPathTemplate = "/working", @@ -196,8 +205,8 @@ } }; - this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51870", "Hello from Laura")) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom", 0)) + this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port1}", "Hello from Laura")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port2}/", 200, "Hello from Tom", 0)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithPolly()) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/RandomPortFinder.cs b/test/Ocelot.AcceptanceTests/RandomPortFinder.cs new file mode 100644 index 00000000..ef166fa1 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/RandomPortFinder.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Sockets; +using System.Text; + +namespace Ocelot.AcceptanceTests +{ + public static class RandomPortFinder + { + private static readonly int TrialNumber = 100; + private static readonly int BeginPortRange = 20000; + private static readonly int EndPortRange = 45000; + + private static Random random = new Random(); + private static ConcurrentBag usedPorts = new ConcurrentBag(); + + public static int GetRandomPort() + { + int randomPort = 0; + for (int i = 0; i < TrialNumber; i++) + { + randomPort = random.Next(BeginPortRange, EndPortRange); + if (usedPorts.Any(p => p == randomPort)) + { + continue; + } + else + { + usedPorts.Add(randomPort); + } + + try + { + IPEndPoint ipe = new IPEndPoint(IPAddress.Loopback, randomPort); + using (var socket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) + { + socket.Bind(ipe); + socket.Close(); + return randomPort; + } + } + catch (Exception) + { + continue; + } + } + + throw new Exception("Cannot find available port to bind to."); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs index 44874115..0fcddac2 100644 --- a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs +++ b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs @@ -25,6 +25,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_reason_phrase() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -38,7 +40,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51339, + Port = port, } }, UpstreamPathTemplate = "/", @@ -47,7 +49,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", "some reason")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", "some reason")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index bef0bc51..36c89f9d 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -21,7 +21,9 @@ [Fact] public void should_use_default_request_id_and_forward() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -34,7 +36,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51873, + Port = port, } }, DownstreamScheme = "http", @@ -45,7 +47,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -55,7 +57,9 @@ [Fact] public void should_use_request_id_and_forward() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -68,7 +72,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51873, + Port = port, } }, DownstreamScheme = "http", @@ -80,7 +84,7 @@ var requestId = Guid.NewGuid().ToString(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId)) @@ -90,7 +94,9 @@ [Fact] public void should_use_global_request_id_and_forward() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -103,7 +109,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51873, + Port = port, } }, DownstreamScheme = "http", @@ -119,7 +125,7 @@ var requestId = Guid.NewGuid().ToString(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId)) @@ -129,7 +135,9 @@ [Fact] public void should_use_global_request_id_create_and_forward() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -142,7 +150,7 @@ new FileHostAndPort { Host = "localhost", - Port = 51873, + Port = port, } }, DownstreamScheme = "http", @@ -156,7 +164,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs index cd5be7a7..2eb35c62 100644 --- a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs +++ b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs @@ -20,7 +20,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_304_when_service_returns_304() - { + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -34,7 +36,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51092, + Port = port, } }, UpstreamPathTemplate = "/{everything}", @@ -43,7 +45,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51092", "/inline.132.bundle.js", 304)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/inline.132.bundle.js", 304)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/inline.132.bundle.js")) diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index fd04af61..1ebc1113 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -21,6 +21,7 @@ [Fact] public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error() { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -36,7 +37,7 @@ new FileHostAndPort { Host = "localhost", - Port = 53876, + Port = port, } }, DownstreamScheme = "http", @@ -44,7 +45,7 @@ } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53876")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -55,6 +56,8 @@ [Fact] public void should_log_warning_if_downstream_service_returns_internal_server_error() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -69,7 +72,7 @@ new FileHostAndPort { Host = "localhost", - Port = 53876, + Port = port, }, }, DownstreamScheme = "http", @@ -77,7 +80,7 @@ }, }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53876")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithLogger()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 15eaf8d9..1ce53882 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -24,7 +24,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_match_forward_slash_in_pattern_before_next_forward_slash() { - var port = 31879; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -69,6 +69,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_forward_slash_and_placeholder_only() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -84,14 +86,14 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57873, + Port = port, } } } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57873/", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -103,6 +105,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_favouring_forward_slash_with_path_route() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -116,7 +120,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51880, + Port = port, } }, UpstreamPathTemplate = "/{url}", @@ -140,7 +144,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", "/test", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/test", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test")) @@ -152,6 +156,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_favouring_forward_slash() { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { ReRoutes = new List @@ -180,7 +185,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 50810, + Port = port, } }, UpstreamPathTemplate = "/", @@ -189,7 +194,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:50810/", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -201,6 +206,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_favouring_forward_slash_route_because_it_is_first() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -214,7 +221,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51880, + Port = port, } }, UpstreamPathTemplate = "/", @@ -238,7 +245,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -250,6 +257,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_nothing_and_placeholder_only() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -263,7 +272,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51005, + Port = port, } }, UpstreamPathTemplate = "/{url}", @@ -272,7 +281,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51005", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("")) @@ -284,6 +293,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -297,7 +308,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 58589, + Port = port, } }, UpstreamPathTemplate = "/", @@ -306,7 +317,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58589", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -318,6 +329,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void bug() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -331,7 +344,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51874, + Port = port, } }, UpstreamPathTemplate = "/vacancy/", @@ -347,7 +360,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51874, + Port = port, } }, UpstreamPathTemplate = "/vacancy/{vacancyId}", @@ -357,7 +370,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/api/v1/vacancy/1", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/v1/vacancy/1", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1")) @@ -369,6 +382,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_path_missing_forward_slash_as_first_char() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -382,7 +397,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51206, + Port = port, } }, UpstreamPathTemplate = "/", @@ -391,7 +406,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51206", "/api/products", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -403,6 +418,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_host_has_trailing_slash() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -416,7 +433,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51990, + Port = port, } }, UpstreamPathTemplate = "/", @@ -425,7 +442,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51990", "/api/products", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -437,6 +454,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_template_does_not() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -450,7 +469,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 58804, + Port = port, } }, UpstreamPathTemplate = "/products/", @@ -459,7 +478,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58804", "/products", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products")) @@ -471,6 +490,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_not_found_when_upstream_url_ends_with_forward_slash_but_template_does_not() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -484,7 +505,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54015, + Port = port, } }, UpstreamPathTemplate = "/products", @@ -493,7 +514,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54015", "/products", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) @@ -504,6 +525,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_not_found() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -517,7 +540,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54072, + Port = port, } }, UpstreamPathTemplate = "/products/{productId}", @@ -526,7 +549,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54072", "/products", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) @@ -537,6 +560,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_complex_url() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -550,7 +575,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 55961, + Port = port, } }, UpstreamPathTemplate = "/products/{productId}", @@ -559,7 +584,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:55961", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) @@ -571,6 +596,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_complex_url_that_starts_with_placeholder() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -584,7 +611,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51116, + Port = port, } }, UpstreamPathTemplate = "/{variantId}/products/{productId}", @@ -593,7 +620,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51116", "/api/23/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/23/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("23/products/1")) @@ -605,6 +632,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_add_trailing_slash_to_downstream_url() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -618,7 +647,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51809, + Port = port, } }, UpstreamPathTemplate = "/products/{productId}", @@ -627,7 +656,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51809", "/api/products/1", 200, "Some Product")) + this.Given(x => GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) @@ -638,6 +667,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_201_with_simple_url() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -650,7 +681,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 56615, + Port = port, } }, DownstreamScheme = "http", @@ -660,7 +691,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:56615", "/", 201, string.Empty)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -672,6 +703,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_201_with_complex_query_string() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -686,7 +719,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 57771, + Port = port, } }, UpstreamHttpMethod = new List { "Get" }, @@ -694,7 +727,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57771", "/newThing", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/newThing", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-")) @@ -706,6 +739,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_placeholder_for_final_url_path() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -719,7 +754,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 55609, + Port = port, } }, UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", @@ -728,7 +763,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:55609", "/api/products/1", 200, "Some Product")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1")) @@ -740,6 +775,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_201_with_simple_url_and_multiple_upstream_http_method() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -752,7 +789,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 59911, + Port = port, } }, DownstreamScheme = "http", @@ -762,7 +799,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:59911", "", 201, string.Empty)) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) @@ -774,6 +811,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_and_any_upstream_http_method() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -786,7 +825,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 59187, + Port = port, } }, DownstreamScheme = "http", @@ -796,7 +835,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:59187", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -808,6 +847,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_404_when_calling_upstream_route_with_no_matching_downstream_re_route_github_issue_134() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -821,7 +862,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54079, + Port = port, } }, UpstreamPathTemplate = "/vacancy/", @@ -837,7 +878,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54079, + Port = port, } }, UpstreamPathTemplate = "/vacancy/{vacancyId}", @@ -847,7 +888,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54079", "/api/v1/vacancy/1", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/v1/vacancy/1", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1")) @@ -858,6 +899,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_set_trailing_slash_on_url_template() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -871,7 +914,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 51899, + Port = port, } }, UpstreamPathTemplate = "/platform/{url}", @@ -880,7 +923,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", "/api/swagger/lib/backbone-min.js", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/swagger/lib/backbone-min.js", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/platform/swagger/lib/backbone-min.js")) @@ -893,6 +936,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_use_priority() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -924,14 +969,14 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 52879, + Port = port, } }, } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52879/", "/goods/delete", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/goods/delete", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/goods/delete")) @@ -943,7 +988,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_match_multiple_paths_with_catch_all() { - var port = 61999; + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -978,6 +1024,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_271() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -993,7 +1041,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 54879, + Port = port, } }, }, @@ -1015,7 +1063,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54879/", "/api/v1/modules/Test", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/api/v1/modules/Test", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/v1/modules/Test")) diff --git a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs index 58e54b2f..c8339381 100644 --- a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs @@ -24,6 +24,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -38,7 +39,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 61879, + Port = port, } }, UpstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", @@ -47,7 +48,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates")) @@ -61,7 +62,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); - var port = 57359; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -99,6 +100,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -113,7 +115,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 64879, + Port = port, } }, UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", @@ -122,7 +124,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}")) @@ -136,6 +138,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -150,7 +153,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 64879, + Port = port, } }, UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", @@ -159,7 +162,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates")) @@ -172,6 +175,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -186,7 +190,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 64879, + Port = port, } }, UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", @@ -195,7 +199,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?test=1")) @@ -208,6 +212,7 @@ namespace Ocelot.AcceptanceTests { var subscriptionId = Guid.NewGuid().ToString(); var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -222,7 +227,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 64879, + Port = port, } }, UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", @@ -231,7 +236,7 @@ namespace Ocelot.AcceptanceTests } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId=1")) diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index b464a3d1..c3d5479c 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -33,10 +33,12 @@ [Fact] public void should_use_consul_service_discovery_and_load_balance_request() { - var consulPort = 8502; + var consulPort = RandomPortFinder.GetRandomPort(); + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); var serviceName = "product"; - var downstreamServiceOneUrl = "http://localhost:50881"; - var downstreamServiceTwoUrl = "http://localhost:50882"; + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { @@ -44,7 +46,7 @@ { Service = serviceName, Address = "localhost", - Port = 50881, + Port = servicePort1, ID = Guid.NewGuid().ToString(), Tags = new string[0] }, @@ -55,7 +57,7 @@ { Service = serviceName, Address = "localhost", - Port = 50882, + Port = servicePort2, ID = Guid.NewGuid().ToString(), Tags = new string[0] }, @@ -100,9 +102,10 @@ [Fact] public void should_handle_request_to_consul_for_downstream_service_and_make_request() { - const int consulPort = 8505; + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); const string serviceName = "web"; - const string downstreamServiceOneUrl = "http://localhost:8080"; + string downstreamServiceOneUrl = $"http://localhost:{servicePort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { @@ -110,7 +113,7 @@ { Service = serviceName, Address = "localhost", - Port = 8080, + Port = servicePort, ID = "web_90_0_2_224_8080", Tags = new[] { "version-v1" } }, @@ -154,9 +157,9 @@ [Fact] public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes() { - const int consulPort = 8513; + int consulPort = RandomPortFinder.GetRandomPort(); const string serviceName = "web"; - const int downstreamServicePort = 8087; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() @@ -204,10 +207,10 @@ [Fact] public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes() { - var consulPort = 8510; + var consulPort = RandomPortFinder.GetRandomPort(); var serviceName = "product"; - var serviceOnePort = 50888; - var serviceTwoPort = 50889; + var serviceOnePort = RandomPortFinder.GetRandomPort(); + var serviceTwoPort = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}"; var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; @@ -264,9 +267,10 @@ public void should_use_token_to_make_request_to_consul() { var token = "abctoken"; - var consulPort = 8515; + var consulPort = RandomPortFinder.GetRandomPort(); var serviceName = "web"; - var downstreamServiceOneUrl = "http://localhost:8081"; + var servicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { @@ -274,7 +278,7 @@ { Service = serviceName, Address = "localhost", - Port = 8081, + Port = servicePort, ID = "web_90_0_2_224_8080", Tags = new[] { "version-v1" } }, @@ -320,10 +324,12 @@ [Fact] public void should_send_request_to_service_after_it_becomes_available_in_consul() { - var consulPort = 8501; + var consulPort = RandomPortFinder.GetRandomPort(); var serviceName = "product"; - var downstreamServiceOneUrl = "http://localhost:50879"; - var downstreamServiceTwoUrl = "http://localhost:50880"; + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() { @@ -331,7 +337,7 @@ { Service = serviceName, Address = "localhost", - Port = 50879, + Port = servicePort1, ID = Guid.NewGuid().ToString(), Tags = new string[0] }, @@ -342,7 +348,7 @@ { Service = serviceName, Address = "localhost", - Port = 50880, + Port = servicePort2, ID = Guid.NewGuid().ToString(), Tags = new string[0] }, @@ -396,9 +402,9 @@ [Fact] public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request() { - const int consulPort = 8518; + int consulPort = RandomPortFinder.GetRandomPort(); const string serviceName = "web"; - const int downstreamServicePort = 8082; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var serviceEntryOne = new ServiceEntry() diff --git a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs index 57dd3855..03643a7f 100644 --- a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs @@ -23,6 +23,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_fix_issue_555() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -41,13 +43,13 @@ namespace Ocelot.AcceptanceTests ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 19081, + Port = port, Type = "ServiceFabric" } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/a", 200, "Hello from Laura", "b=c")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/a", 200, "Hello from Laura", "b=c")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/a?b=c")) @@ -59,6 +61,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_support_service_fabric_naming_and_dns_service_stateless_and_guest() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -77,13 +81,13 @@ namespace Ocelot.AcceptanceTests ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 19081, + Port = port, Type = "ServiceFabric" } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "test=best")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "test=best")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?test=best")) @@ -95,6 +99,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_support_service_fabric_naming_and_dns_service_statefull_and_actors() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -113,13 +119,13 @@ namespace Ocelot.AcceptanceTests ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 19081, + Port = port, Type = "ServiceFabric" } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "PartitionKind=test&PartitionKey=1")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "PartitionKind=test&PartitionKey=1")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?PartitionKind=test&PartitionKey=1")) @@ -131,6 +137,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_support_placeholder_in_service_fabric_service_name() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -149,13 +157,13 @@ namespace Ocelot.AcceptanceTests ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { Host = "localhost", - Port = 19081, + Port = port, Type = "ServiceFabric" } } }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/Service_1.0/Api/values", 200, "Hello from Laura", "test=best")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/Service_1.0/Api/values", 200, "Hello from Laura", "test=best")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/1.0/values?test=best")) diff --git a/test/Ocelot.AcceptanceTests/SslTests.cs b/test/Ocelot.AcceptanceTests/SslTests.cs index a0ea17a4..58c9e6a1 100644 --- a/test/Ocelot.AcceptanceTests/SslTests.cs +++ b/test/Ocelot.AcceptanceTests/SslTests.cs @@ -23,7 +23,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_dangerous_accept_any_server_certificate_validator() { - int port = 51129; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -60,7 +60,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_not_dangerous_accept_any_server_certificate_validator() { - int port = 52129; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { diff --git a/test/Ocelot.AcceptanceTests/StartupTests.cs b/test/Ocelot.AcceptanceTests/StartupTests.cs index 3bab3f57..5fc2ea0e 100644 --- a/test/Ocelot.AcceptanceTests/StartupTests.cs +++ b/test/Ocelot.AcceptanceTests/StartupTests.cs @@ -27,6 +27,8 @@ [Fact] public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -40,7 +42,7 @@ new FileHostAndPort { Host = "localhost", - Port = 52179, + Port = port, } }, UpstreamPathTemplate = "/", @@ -51,7 +53,7 @@ var fakeRepo = new FakeFileConfigurationRepository(); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52179", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunningWithBlowingUpDiskRepo(fakeRepo)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) diff --git a/test/Ocelot.AcceptanceTests/StickySessionsTests.cs b/test/Ocelot.AcceptanceTests/StickySessionsTests.cs index 77f8b176..c4073d87 100644 --- a/test/Ocelot.AcceptanceTests/StickySessionsTests.cs +++ b/test/Ocelot.AcceptanceTests/StickySessionsTests.cs @@ -25,8 +25,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_use_same_downstream_host() { - var downstreamPortOne = 51375; - var downstreamPortTwo = 51892; + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; @@ -76,8 +76,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_use_different_downstream_host_for_different_re_route() { - var downstreamPortOne = 52881; - var downstreamPortTwo = 52892; + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; @@ -154,8 +154,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_use_same_downstream_host_for_different_re_route() { - var downstreamPortOne = 53881; - var downstreamPortTwo = 53892; + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; diff --git a/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs b/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs index 284ebd6f..6e3e843b 100644 --- a/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs +++ b/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs @@ -28,9 +28,11 @@ [Fact] public void should_fix_issue_194() { - var consulPort = 8503; - var downstreamServiceOneUrl = "http://localhost:8362"; - var downstreamServiceTwoUrl = "http://localhost:8330"; + var consulPort = RandomPortFinder.GetRandomPort(); + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; var configuration = new FileConfiguration @@ -46,7 +48,7 @@ new FileHostAndPort { Host = "localhost", - Port = 8362, + Port = servicePort1, } }, UpstreamPathTemplate = "/api/user/{user}", @@ -61,7 +63,7 @@ new FileHostAndPort { Host = "localhost", - Port = 8330, + Port = servicePort2, } }, UpstreamPathTemplate = "/api/product/{product}", diff --git a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs index 4af5a974..ebfcf805 100644 --- a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs +++ b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs @@ -23,7 +23,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_and_hosts_match() { - int port = 64905; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -60,7 +60,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes() { - int port = 64904; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -113,7 +113,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed() { - int port = 64903; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -166,7 +166,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed_with_no_host_first() { - int port = 64902; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -218,7 +218,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_404_with_simple_url_and_hosts_dont_match() { - int port = 64901; + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { diff --git a/test/Ocelot.AcceptanceTests/WebSocketTests.cs b/test/Ocelot.AcceptanceTests/WebSocketTests.cs index 425416e2..a8a39166 100644 --- a/test/Ocelot.AcceptanceTests/WebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/WebSocketTests.cs @@ -29,7 +29,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_proxy_websocket_input_to_downstream_service() { - var downstreamPort = 5001; + var downstreamPort = RandomPortFinder.GetRandomPort(); var downstreamHost = "localhost"; var config = new FileConfiguration @@ -64,9 +64,9 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_proxy_websocket_input_to_downstream_service_and_use_load_balancer() { - var downstreamPort = 5005; + var downstreamPort = RandomPortFinder.GetRandomPort(); var downstreamHost = "localhost"; - var secondDownstreamPort = 5006; + var secondDownstreamPort = RandomPortFinder.GetRandomPort(); var secondDownstreamHost = "localhost"; var config = new FileConfiguration From d032774aa0d5b717c40ac1e79564cb567b573a83 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Wed, 11 Mar 2020 19:52:24 +0000 Subject: [PATCH 27/28] small refactor of RandomPortFinder --- .../RandomPortFinder.cs | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/RandomPortFinder.cs b/test/Ocelot.AcceptanceTests/RandomPortFinder.cs index ef166fa1..1bb6b9f4 100644 --- a/test/Ocelot.AcceptanceTests/RandomPortFinder.cs +++ b/test/Ocelot.AcceptanceTests/RandomPortFinder.cs @@ -1,55 +1,58 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Sockets; -using System.Text; - -namespace Ocelot.AcceptanceTests +namespace Ocelot.AcceptanceTests { + using System; + using System.Collections.Concurrent; + using System.Linq; + using System.Net; + using System.Net.Sockets; + public static class RandomPortFinder { - private static readonly int TrialNumber = 100; - private static readonly int BeginPortRange = 20000; - private static readonly int EndPortRange = 45000; - - private static Random random = new Random(); - private static ConcurrentBag usedPorts = new ConcurrentBag(); + private const int TrialNumber = 100; + private const int BeginPortRange = 20000; + private const int EndPortRange = 45000; + private static readonly Random Random = new Random(); + private static readonly ConcurrentBag UsedPorts = new ConcurrentBag(); public static int GetRandomPort() { - int randomPort = 0; - for (int i = 0; i < TrialNumber; i++) + for (var i = 0; i < TrialNumber; i++) { - randomPort = random.Next(BeginPortRange, EndPortRange); - if (usedPorts.Any(p => p == randomPort)) - { - continue; - } - else - { - usedPorts.Add(randomPort); - } + var randomPort = Random.Next(BeginPortRange, EndPortRange); - try + if (!PortInUse(randomPort)) { - IPEndPoint ipe = new IPEndPoint(IPAddress.Loopback, randomPort); - using (var socket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) + try { - socket.Bind(ipe); - socket.Close(); - return randomPort; + return UsePort(randomPort); + } + catch (Exception) + { + // ignored } } - catch (Exception) - { - continue; - } } throw new Exception("Cannot find available port to bind to."); } + + private static int UsePort(int randomPort) + { + UsedPorts.Add(randomPort); + + var ipe = new IPEndPoint(IPAddress.Loopback, randomPort); + + using (var socket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) + { + socket.Bind(ipe); + socket.Close(); + return randomPort; + } + } + + private static bool PortInUse(int randomPort) + { + return UsedPorts.Any(p => p == randomPort); + } } } From 36064b13d038a44ff525c875e90e6e8a2eae12f7 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Wed, 11 Mar 2020 20:09:36 +0000 Subject: [PATCH 28/28] use random port generator on tests from master --- test/Ocelot.AcceptanceTests/HttpTests.cs | 10 +++++----- test/Ocelot.AcceptanceTests/MethodTests.cs | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs index b63ad4bb..57947d07 100644 --- a/test/Ocelot.AcceptanceTests/HttpTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpTests.cs @@ -25,7 +25,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_using_http_one() { - const int port = 53219; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -63,7 +63,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_using_http_one_point_one() { - const int port = 53279; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -101,7 +101,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_using_http_two_point_zero() { - const int port = 53675; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -143,7 +143,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_500_when_using_http_one_to_talk_to_server_running_http_two() { - const int port = 53677; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -184,7 +184,7 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_using_http_two_to_talk_to_server_running_http_one_point_one() { - const int port = 53679; + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { diff --git a/test/Ocelot.AcceptanceTests/MethodTests.cs b/test/Ocelot.AcceptanceTests/MethodTests.cs index abfbfecf..e70dc7e1 100644 --- a/test/Ocelot.AcceptanceTests/MethodTests.cs +++ b/test/Ocelot.AcceptanceTests/MethodTests.cs @@ -24,6 +24,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_get_converted_to_post() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -39,7 +41,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 53171, + Port = port, }, }, DownstreamHttpMethod = "POST", @@ -47,7 +49,7 @@ namespace Ocelot.AcceptanceTests }, }; - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53171/", "/", "POST")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) @@ -58,6 +60,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_get_converted_to_post_with_content() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -73,7 +77,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 53271, + Port = port, }, }, DownstreamHttpMethod = "POST", @@ -84,7 +88,7 @@ namespace Ocelot.AcceptanceTests const string expected = "here is some content"; var httpContent = new StringContent(expected); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53271/", "/", "POST")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) @@ -96,6 +100,8 @@ namespace Ocelot.AcceptanceTests [Fact] public void should_return_response_200_when_get_converted_to_get_with_content() { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { ReRoutes = new List @@ -111,7 +117,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 53272, + Port = port, }, }, DownstreamHttpMethod = "GET", @@ -122,7 +128,7 @@ namespace Ocelot.AcceptanceTests const string expected = "here is some content"; var httpContent = new StringContent(expected); - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53272/", "/", "GET")) + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "GET")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/", httpContent))