diff --git a/docs/features/routing.rst b/docs/features/routing.rst index 664021a65..06928371d 100644 --- a/docs/features/routing.rst +++ b/docs/features/routing.rst @@ -177,4 +177,11 @@ and } In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have -matched /goods/{catchAll} (because this is the first ReRoute in the list!). \ No newline at end of file +matched /goods/{catchAll} (because this is the first ReRoute in the list!). + +Dynamic Routing +^^^^^^^^^^^^^^^ + +This feature was requested in `issue 340 `_. The idea is to enable dynamic routing +when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if +this sounds interesting to you. diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst index 6fe25c120..b212a2e6c 100644 --- a/docs/features/servicediscovery.rst +++ b/docs/features/servicediscovery.rst @@ -1,3 +1,5 @@ +.. service-discovery: + Service Discovery ================= @@ -88,3 +90,65 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work. +Dynamic Routing +^^^^^^^^^^^^^^^ + +This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using +a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segmentof the upstream path to lookup the +downstream service with the service discovery provider. + +An example of this would be calling ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of +the path which is product and use it as a key to look up the service in consul. If consul returns a service Ocelot will request it on whatever host and +port comes back from consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products. +Ocelot will apprend any query string to the downstream url as normal. + +In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you +need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme. + +In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but +talk to private services over http) that will be applied to all of the dynamic ReRoutes. + +The config might look something like + +.. code-block:: json + + { + "ReRoutes": [], + "Aggregates": [], + "GlobalConfiguration": { + "RequestIdKey": null, + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 8510, + "Type": null, + "Token": null, + "ConfigurationKey": null + }, + "RateLimitOptions": { + "ClientIdHeader": "ClientId", + "QuotaExceededMessage": null, + "RateLimitCounterPrefix": "ocelot", + "DisableRateLimitHeaders": false, + "HttpStatusCode": 429 + }, + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 0, + "DurationOfBreak": 0, + "TimeoutValue": 0 + }, + "BaseUrl": null, + "LoadBalancerOptions": { + "Type": "LeastConnection", + "Key": null, + "Expiry": 0 + }, + "DownstreamScheme": "http", + "HttpHandlerOptions": { + "AllowAutoRedirect": false, + "UseCookieContainer": false, + "UseTracing": false + } + } + } + +Please take a look through all of the docs to understand these options. \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 225feb6fa..b782ab0f2 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -9,7 +9,7 @@ namespace Ocelot.Configuration.Builder public class DownstreamReRouteBuilder { private AuthenticationOptions _authenticationOptions; - private string _reRouteKey; + private string _loadBalancerKey; private string _downstreamPathTemplate; private string _upstreamTemplate; private UpstreamPathTemplate _upstreamTemplatePattern; @@ -25,7 +25,6 @@ public class DownstreamReRouteBuilder private CacheOptions _fileCacheOptions; private string _downstreamScheme; private LoadBalancerOptions _loadBalancerOptions; - private bool _useQos; private QoSOptions _qosOptions; private HttpHandlerOptions _httpHandlerOptions; private bool _enableRateLimiting; @@ -41,7 +40,6 @@ public class DownstreamReRouteBuilder private List _addHeadersToDownstream; private List _addHeadersToUpstream; private bool _dangerousAcceptAnyServerCertificateValidator; - private string _qosKey; public DownstreamReRouteBuilder() { @@ -153,27 +151,15 @@ public DownstreamReRouteBuilder WithCacheOptions(CacheOptions input) return this; } - public DownstreamReRouteBuilder WithIsQos(bool input) - { - _useQos = input; - return this; - } - public DownstreamReRouteBuilder WithQosOptions(QoSOptions input) { _qosOptions = input; return this; } - public DownstreamReRouteBuilder WithReRouteKey(string reRouteKey) - { - _reRouteKey = reRouteKey; - return this; - } - - public DownstreamReRouteBuilder WithQosKey(string qosKey) + public DownstreamReRouteBuilder WithLoadBalancerKey(string loadBalancerKey) { - _qosKey = qosKey; + _loadBalancerKey = loadBalancerKey; return this; } @@ -267,7 +253,6 @@ public DownstreamReRoute Build() _httpHandlerOptions, _useServiceDiscovery, _enableRateLimiting, - _useQos, _qosOptions, _downstreamScheme, _requestIdHeaderKey, @@ -283,12 +268,11 @@ public DownstreamReRoute Build() _isAuthorised, _authenticationOptions, new PathTemplate(_downstreamPathTemplate), - _reRouteKey, + _loadBalancerKey, _delegatingHandlers, _addHeadersToDownstream, _addHeadersToUpstream, - _dangerousAcceptAnyServerCertificateValidator, - _qosKey); + _dangerousAcceptAnyServerCertificateValidator); } } } diff --git a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs index 93d18257e..69660d83c 100644 --- a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs @@ -6,7 +6,9 @@ public class QoSOptionsBuilder private int _durationOfBreak; - private int _timeoutValue; + private int _timeoutValue; + + private string _key; public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking) { @@ -26,9 +28,15 @@ public QoSOptionsBuilder WithTimeoutValue(int timeoutValue) return this; } + public QoSOptionsBuilder WithKey(string input) + { + _key = input; + return this; + } + public QoSOptions Build() { - return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue); + return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue, _key); } } } diff --git a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs index e5729823d..525f653e0 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs @@ -5,7 +5,6 @@ public class ReRouteOptionsBuilder private bool _isAuthenticated; private bool _isAuthorised; private bool _isCached; - private bool _isQoS; private bool _enableRateLimiting; public ReRouteOptionsBuilder WithIsCached(bool isCached) @@ -26,12 +25,6 @@ public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised) return this; } - public ReRouteOptionsBuilder WithIsQos(bool isQoS) - { - _isQoS = isQoS; - return this; - } - public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) { _enableRateLimiting = enableRateLimiting; @@ -40,7 +33,7 @@ public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) public ReRouteOptions Build() { - return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _isQoS, _enableRateLimiting); + return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs index 06789116f..97045bc8b 100644 --- a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs @@ -104,8 +104,22 @@ private async Task> SetUpConfiguration(FileConf } var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration); - - var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey); + + var lbOptions = CreateLoadBalancerOptions(fileConfiguration.GlobalConfiguration.LoadBalancerOptions); + + var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions); + + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions); + + var config = new InternalConfiguration(reRoutes, + _adminPath.Path, + serviceProviderConfiguration, + fileConfiguration.GlobalConfiguration.RequestIdKey, + lbOptions, + fileConfiguration.GlobalConfiguration.DownstreamScheme, + qosOptions, + httpHandlerOptions + ); return new OkResponse(config); } @@ -160,8 +174,6 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl var reRouteKey = CreateReRouteKey(fileReRoute); - var qosKey = CreateQosKey(fileReRoute); - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); @@ -172,19 +184,19 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); - var qosOptions = _qosOptionsCreator.Create(fileReRoute); + var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod.ToArray()); var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting); var region = _regionCreator.Create(fileReRoute); - var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute); + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions); var hAndRs = _headerFAndRCreator.Create(fileReRoute); var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); - var lbOptions = CreateLoadBalancerOptions(fileReRoute); + var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions); var reRoute = new DownstreamReRouteBuilder() .WithKey(fileReRoute.Key) @@ -205,9 +217,7 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl .WithDownstreamScheme(fileReRoute.DownstreamScheme) .WithLoadBalancerOptions(lbOptions) .WithDownstreamAddresses(downstreamAddresses) - .WithReRouteKey(reRouteKey) - .WithQosKey(qosKey) - .WithIsQos(fileReRouteOptions.IsQos) + .WithLoadBalancerKey(reRouteKey) .WithQosOptions(qosOptions) .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) .WithRateLimitOptions(rateLimitOption) @@ -226,9 +236,13 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl return reRoute; } - private LoadBalancerOptions CreateLoadBalancerOptions(FileReRoute fileReRoute) + private LoadBalancerOptions CreateLoadBalancerOptions(FileLoadBalancerOptions options) { - return new LoadBalancerOptions(fileReRoute.LoadBalancerOptions.Type, fileReRoute.LoadBalancerOptions.Key, fileReRoute.LoadBalancerOptions.Expiry); + return new LoadBalancerOptionsBuilder() + .WithType(options.Type) + .WithKey(options.Key) + .WithExpiryInMs(options.Expiry) + .Build(); } private string CreateReRouteKey(FileReRoute fileReRoute) @@ -238,14 +252,7 @@ private string CreateReRouteKey(FileReRoute fileReRoute) return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; } - return CreateQosKey(fileReRoute); - } - - private string CreateQosKey(FileReRoute fileReRoute) - { - //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain - var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}"; - return loadBalancerKey; + return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}"; } } } diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs index 332c25b79..7ce77b345 100644 --- a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs @@ -6,19 +6,19 @@ namespace Ocelot.Configuration.Creator { public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator { - private IServiceTracer _tracer; + private readonly IServiceTracer _tracer; public HttpHandlerOptionsCreator(IServiceTracer tracer) { _tracer = tracer; } - public HttpHandlerOptions Create(FileReRoute fileReRoute) + public HttpHandlerOptions Create(FileHttpHandlerOptions options) { - var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false; + var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) && options.UseTracing; - return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect, - fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing); + return new HttpHandlerOptions(options.AllowAutoRedirect, + options.UseCookieContainer, useTracing); } } } diff --git a/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs index 34e5ffd1a..801376511 100644 --- a/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs @@ -7,6 +7,6 @@ namespace Ocelot.Configuration.Creator /// public interface IHttpHandlerOptionsCreator { - HttpHandlerOptions Create(FileReRoute fileReRoute); + HttpHandlerOptions Create(FileHttpHandlerOptions fileReRoute); } } diff --git a/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs index a8c84dc37..3ad42d9f3 100644 --- a/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs @@ -4,6 +4,8 @@ namespace Ocelot.Configuration.Creator { public interface IQoSOptionsCreator { - QoSOptions Create(FileReRoute fileReRoute); + QoSOptions Create(FileQoSOptions options); + QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods); + QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods); } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs index f19845a8c..1f7983d6c 100644 --- a/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs @@ -6,4 +6,4 @@ public interface IReRouteOptionsCreator { ReRouteOptions Create(FileReRoute fileReRoute); } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs b/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs index 3cf6b1cd1..30adaed20 100644 --- a/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs @@ -1,17 +1,47 @@ -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.File; - namespace Ocelot.Configuration.Creator { + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.File; + using System.Linq; + public class QoSOptionsCreator : IQoSOptionsCreator { - public QoSOptions Create(FileReRoute fileReRoute) + public QoSOptions Create(FileQoSOptions options) { return new QoSOptionsBuilder() - .WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking) - .WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak) - .WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue) + .WithExceptionsAllowedBeforeBreaking(options.ExceptionsAllowedBeforeBreaking) + .WithDurationOfBreak(options.DurationOfBreak) + .WithTimeoutValue(options.TimeoutValue) .Build(); } + + public QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods) + { + var key = CreateKey(pathTemplate, httpMethods); + + return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); + } + + public QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods) + { + var key = CreateKey(pathTemplate, httpMethods); + + return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); + } + + private QoSOptions Map(string key, int timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking) + { + return new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(exceptionsAllowedBeforeBreaking) + .WithDurationOfBreak(durationOfBreak) + .WithTimeoutValue(timeoutValue) + .WithKey(key) + .Build(); + } + + private string CreateKey(string pathTemplate, string[] httpMethods) + { + return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", httpMethods)}"; + } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs index 8defc420e..c777a50a8 100644 --- a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs @@ -10,14 +10,12 @@ public ReRouteOptions Create(FileReRoute fileReRoute) var isAuthenticated = IsAuthenticated(fileReRoute); var isAuthorised = IsAuthorised(fileReRoute); var isCached = IsCached(fileReRoute); - var isQos = IsQoS(fileReRoute); var enableRateLimiting = IsEnableRateLimiting(fileReRoute); var options = new ReRouteOptionsBuilder() .WithIsAuthenticated(isAuthenticated) .WithIsAuthorised(isAuthorised) .WithIsCached(isCached) - .WithIsQos(isQos) .WithRateLimiting(enableRateLimiting) .Build(); @@ -29,11 +27,6 @@ private static bool IsEnableRateLimiting(FileReRoute fileReRoute) return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false; } - private bool IsQoS(FileReRoute fileReRoute) - { - return fileReRoute.QoSOptions?.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions?.TimeoutValue > 0; - } - private bool IsAuthenticated(FileReRoute fileReRoute) { return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey); @@ -48,5 +41,5 @@ private bool IsCached(FileReRoute fileReRoute) { return fileReRoute.FileCacheOptions.TtlSeconds > 0; } - } -} \ No newline at end of file + } +} diff --git a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs index 7fbb49e4e..4943aac69 100644 --- a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs @@ -7,12 +7,12 @@ public class ServiceProviderConfigurationCreator : IServiceProviderConfiguration { public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration) { - //todo log or return error here dont just default to something that wont work.. - var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; + var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; + var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul"; return new ServiceProviderConfigurationBuilder() - .WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) - .WithPort(serviceProviderPort) + .WithHost(host) + .WithPort(port) .WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type) .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token) .WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey) diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index b4118db2e..f6cc876d7 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -16,8 +16,7 @@ public DownstreamReRoute( HttpHandlerOptions httpHandlerOptions, bool useServiceDiscovery, bool enableEndpointEndpointRateLimiting, - bool isQos, - QoSOptions qosOptionsOptions, + QoSOptions qosOptions, string downstreamScheme, string requestIdKey, bool isCached, @@ -36,8 +35,7 @@ public DownstreamReRoute( List delegatingHandlers, List addHeadersToDownstream, List addHeadersToUpstream, - bool dangerousAcceptAnyServerCertificateValidator, - string qosKey) + bool dangerousAcceptAnyServerCertificateValidator) { DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; AddHeadersToDownstream = addHeadersToDownstream; @@ -51,8 +49,7 @@ public DownstreamReRoute( HttpHandlerOptions = httpHandlerOptions; UseServiceDiscovery = useServiceDiscovery; EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting; - IsQos = isQos; - QosOptionsOptions = qosOptionsOptions; + QosOptions = qosOptions; DownstreamScheme = downstreamScheme; RequestIdKey = requestIdKey; IsCached = isCached; @@ -69,10 +66,8 @@ public DownstreamReRoute( DownstreamPathTemplate = downstreamPathTemplate; LoadBalancerKey = loadBalancerKey; AddHeadersToUpstream = addHeadersToUpstream; - QosKey = qosKey; } - public string QosKey { get; } public string Key { get; } public PathTemplate UpstreamPathTemplate { get; } public List UpstreamHeadersFindAndReplace { get; } @@ -82,8 +77,7 @@ public DownstreamReRoute( public HttpHandlerOptions HttpHandlerOptions { get; } public bool UseServiceDiscovery { get; } public bool EnableEndpointEndpointRateLimiting { get; } - public bool IsQos { get; } - public QoSOptions QosOptionsOptions { get; } + public QoSOptions QosOptions { get; } public string DownstreamScheme { get; } public string RequestIdKey { get; } public bool IsCached { get; } diff --git a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs index 8dea9ed64..27f1f2aed 100644 --- a/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs +++ b/src/Ocelot/Configuration/File/FileGlobalConfiguration.cs @@ -6,6 +6,9 @@ public FileGlobalConfiguration() { ServiceDiscoveryProvider = new FileServiceDiscoveryProvider(); RateLimitOptions = new FileRateLimitOptions(); + LoadBalancerOptions = new FileLoadBalancerOptions(); + QoSOptions = new FileQoSOptions(); + HttpHandlerOptions = new FileHttpHandlerOptions(); } public string RequestIdKey { get; set; } @@ -13,7 +16,15 @@ public FileGlobalConfiguration() public FileServiceDiscoveryProvider ServiceDiscoveryProvider { get;set; } public FileRateLimitOptions RateLimitOptions { get; set; } - + + public FileQoSOptions QoSOptions { get; set; } + public string BaseUrl { get ;set; } + + public FileLoadBalancerOptions LoadBalancerOptions { get; set; } + + public string DownstreamScheme { get; set; } + + public FileHttpHandlerOptions HttpHandlerOptions { get; set; } } } diff --git a/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs new file mode 100644 index 000000000..1cfd69c62 --- /dev/null +++ b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs @@ -0,0 +1,32 @@ +namespace Ocelot.Configuration +{ + public class HttpHandlerOptionsBuilder + { + private bool _allowAutoRedirect; + private bool _useCookieContainer; + private bool _useTracing; + + public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input) + { + _allowAutoRedirect = input; + return this; + } + + public HttpHandlerOptionsBuilder WithUseCookieContainer(bool input) + { + _useCookieContainer = input; + return this; + } + + public HttpHandlerOptionsBuilder WithUseTracing(bool input) + { + _useTracing = input; + return this; + } + + public HttpHandlerOptions Build() + { + return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing); + } + } +} diff --git a/src/Ocelot/Configuration/IInternalConfiguration.cs b/src/Ocelot/Configuration/IInternalConfiguration.cs index c1781c48a..253f7ffc0 100644 --- a/src/Ocelot/Configuration/IInternalConfiguration.cs +++ b/src/Ocelot/Configuration/IInternalConfiguration.cs @@ -5,8 +5,19 @@ namespace Ocelot.Configuration public interface IInternalConfiguration { List ReRoutes { get; } + string AdministrationPath {get;} + ServiceProviderConfiguration ServiceProviderConfiguration {get;} + string RequestId {get;} + + LoadBalancerOptions LoadBalancerOptions { get; } + + string DownstreamScheme { get; } + + QoSOptions QoSOptions { get; } + + HttpHandlerOptions HttpHandlerOptions { get; } } } diff --git a/src/Ocelot/Configuration/InternalConfiguration.cs b/src/Ocelot/Configuration/InternalConfiguration.cs index 429bb9c0e..cd939a867 100644 --- a/src/Ocelot/Configuration/InternalConfiguration.cs +++ b/src/Ocelot/Configuration/InternalConfiguration.cs @@ -4,17 +4,33 @@ namespace Ocelot.Configuration { public class InternalConfiguration : IInternalConfiguration { - public InternalConfiguration(List reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId) + public InternalConfiguration( + List reRoutes, + string administrationPath, + ServiceProviderConfiguration serviceProviderConfiguration, + string requestId, + LoadBalancerOptions loadBalancerOptions, + string downstreamScheme, + QoSOptions qoSOptions, + HttpHandlerOptions httpHandlerOptions) { ReRoutes = reRoutes; AdministrationPath = administrationPath; ServiceProviderConfiguration = serviceProviderConfiguration; RequestId = requestId; + LoadBalancerOptions = loadBalancerOptions; + DownstreamScheme = downstreamScheme; + QoSOptions = qoSOptions; + HttpHandlerOptions = httpHandlerOptions; } public List ReRoutes { get; } public string AdministrationPath {get;} public ServiceProviderConfiguration ServiceProviderConfiguration {get;} public string RequestId {get;} + public LoadBalancerOptions LoadBalancerOptions { get; } + public string DownstreamScheme { get; } + public QoSOptions QoSOptions { get; } + public HttpHandlerOptions HttpHandlerOptions { get; } } } diff --git a/src/Ocelot/Configuration/LoadBalancerOptionsBuilder.cs b/src/Ocelot/Configuration/LoadBalancerOptionsBuilder.cs new file mode 100644 index 000000000..c1790aa40 --- /dev/null +++ b/src/Ocelot/Configuration/LoadBalancerOptionsBuilder.cs @@ -0,0 +1,32 @@ +namespace Ocelot.Configuration +{ + public class LoadBalancerOptionsBuilder + { + private string _type; + private string _key; + private int _expiryInMs; + + public LoadBalancerOptionsBuilder WithType(string type) + { + _type = type; + return this; + } + + public LoadBalancerOptionsBuilder WithKey(string key) + { + _key = key; + return this; + } + + public LoadBalancerOptionsBuilder WithExpiryInMs(int expiryInMs) + { + _expiryInMs = expiryInMs; + return this; + } + + public LoadBalancerOptions Build() + { + return new LoadBalancerOptions(_type, _key, _expiryInMs); + } + } +} diff --git a/src/Ocelot/Configuration/QoSOptions.cs b/src/Ocelot/Configuration/QoSOptions.cs index 8c5d6d27f..3b7733a3a 100644 --- a/src/Ocelot/Configuration/QoSOptions.cs +++ b/src/Ocelot/Configuration/QoSOptions.cs @@ -8,12 +8,14 @@ public QoSOptions( int exceptionsAllowedBeforeBreaking, int durationofBreak, int timeoutValue, + string key, TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic) { ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; DurationOfBreak = durationofBreak; TimeoutValue = timeoutValue; TimeoutStrategy = timeoutStrategy; + Key = key; } public int ExceptionsAllowedBeforeBreaking { get; } @@ -23,5 +25,8 @@ public QoSOptions( public int TimeoutValue { get; } public TimeoutStrategy TimeoutStrategy { get; } + + public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 && TimeoutValue > 0; + public string Key { get; } } } diff --git a/src/Ocelot/Configuration/ReRouteOptions.cs b/src/Ocelot/Configuration/ReRouteOptions.cs index e3a7a7b14..13875ac8f 100644 --- a/src/Ocelot/Configuration/ReRouteOptions.cs +++ b/src/Ocelot/Configuration/ReRouteOptions.cs @@ -2,19 +2,17 @@ namespace Ocelot.Configuration { public class ReRouteOptions { - public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isQos, bool isEnableRateLimiting) + public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting) { IsAuthenticated = isAuthenticated; IsAuthorised = isAuthorised; IsCached = isCached; - IsQos = isQos; EnableRateLimiting = isEnableRateLimiting; } public bool IsAuthenticated { get; private set; } public bool IsAuthorised { get; private set; } public bool IsCached { get; private set; } - public bool IsQos { get; private set; } public bool EnableRateLimiting { get; private set; } } } diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs index e1b6c0865..d8a352a7f 100644 --- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs @@ -30,20 +30,18 @@ public ConsulFileConfigurationRepository( var internalConfig = repo.Get(); _configurationKey = "InternalConfiguration"; - var consulHost = "localhost"; - var consulPort = 8500; + string token = null; if (!internalConfig.IsError) { - consulHost = string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.Host) ? consulHost : internalConfig.Data.ServiceProviderConfiguration?.Host; - consulPort = internalConfig.Data.ServiceProviderConfiguration?.Port ?? consulPort; - token = internalConfig.Data.ServiceProviderConfiguration?.Token; - _configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.ConfigurationKey) ? - internalConfig.Data.ServiceProviderConfiguration?.ConfigurationKey : _configurationKey; + token = internalConfig.Data.ServiceProviderConfiguration.Token; + _configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey) ? + internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey : _configurationKey; } - var config = new ConsulRegistryConfiguration(consulHost, consulPort, _configurationKey, token); + var config = new ConsulRegistryConfiguration(internalConfig.Data.ServiceProviderConfiguration.Host, + internalConfig.Data.ServiceProviderConfiguration.Port, _configurationKey, token); _consul = factory.Get(config); } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 672e85400..3ff2defcf 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -103,7 +103,9 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); - _services.TryAddSingleton(); + _services.AddSingleton(); + _services.AddSingleton(); + _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs new file mode 100644 index 000000000..f3a63f543 --- /dev/null +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs @@ -0,0 +1,97 @@ +namespace Ocelot.DownstreamRouteFinder.Finder +{ + using System.Collections.Generic; + using Configuration; + using Configuration.Builder; + using Configuration.Creator; + using Configuration.File; + using LoadBalancer.LoadBalancers; + using Responses; + using UrlMatcher; + + public class DownstreamRouteCreator : IDownstreamRouteProvider + { + private readonly IQoSOptionsCreator _qoSOptionsCreator; + + public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator) + { + _qoSOptionsCreator = qoSOptionsCreator; + } + + public Response Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost) + { + var serviceName = GetServiceName(upstreamUrlPath); + + var downstreamPath = GetDownstreamPath(upstreamUrlPath); + + if(HasQueryString(downstreamPath)) + { + downstreamPath = RemoveQueryString(downstreamPath); + } + + var downstreamPathForKeys = $"/{serviceName}{downstreamPath}"; + + var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions); + + var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod }); + + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithServiceName(serviceName) + .WithLoadBalancerKey(loadBalancerKey) + .WithDownstreamPathTemplate(downstreamPath) + .WithUseServiceDiscovery(true) + .WithHttpHandlerOptions(configuration.HttpHandlerOptions) + .WithQosOptions(qosOptions) + .WithDownstreamScheme(configuration.DownstreamScheme) + .WithLoadBalancerOptions(configuration.LoadBalancerOptions) + .Build(); + + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoute(downstreamReRoute) + .WithUpstreamHttpMethod(new List(){ upstreamHttpMethod }) + .Build(); + + return new OkResponse(new DownstreamRoute(new List(), reRoute)); + } + + private static string RemoveQueryString(string downstreamPath) + { + return downstreamPath + .Substring(0, downstreamPath.IndexOf('?')); + } + + private static bool HasQueryString(string downstreamPath) + { + return downstreamPath.Contains("?"); + } + + private static string GetDownstreamPath(string upstreamUrlPath) + { + return upstreamUrlPath + .Substring(upstreamUrlPath.IndexOf('/', 1)); + } + + private static string GetServiceName(string upstreamUrlPath) + { + return upstreamUrlPath + .Substring(1, upstreamUrlPath.IndexOf('/', 1)) + .TrimEnd('/'); + } + + private string CreateLoadBalancerKey(string downstreamTemplatePath, string httpMethod, LoadBalancerOptions loadBalancerOptions) + { + if (!string.IsNullOrEmpty(loadBalancerOptions.Type) && !string.IsNullOrEmpty(loadBalancerOptions.Key) && loadBalancerOptions.Type == nameof(CookieStickySessions)) + { + return $"{nameof(CookieStickySessions)}:{loadBalancerOptions.Key}"; + } + + return CreateQoSKey(downstreamTemplatePath, httpMethod); + } + + private string CreateQoSKey(string downstreamTemplatePath, string httpMethod) + { + var loadBalancerKey = $"{downstreamTemplatePath}|{httpMethod}"; + return loadBalancerKey; + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index df70b0b39..d7713aeee 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -1,62 +1,62 @@ -using System.Collections.Generic; -using System.Linq; -using Ocelot.Configuration; -using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.Errors; -using Ocelot.Responses; - -namespace Ocelot.DownstreamRouteFinder.Finder -{ - public class DownstreamRouteFinder : IDownstreamRouteFinder - { - private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; - private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder; - - public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder) - { - _urlMatcher = urlMatcher; - _placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; - } - - public Response FindDownstreamRoute(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost) - { - var downstreamRoutes = new List(); - - var applicableReRoutes = configuration.ReRoutes - .Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost)) - .OrderByDescending(x => x.UpstreamTemplatePattern.Priority); - - foreach (var reRoute in applicableReRoutes) - { - var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template); - - if (urlMatch.Data.Match) - { - downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute)); - } - } - - if (downstreamRoutes.Any()) - { - var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); - var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); - - return notNullOption != null ? new OkResponse(notNullOption) : new OkResponse(nullOption); - } - - return new ErrorResponse(new UnableToFindDownstreamRouteError(path, httpMethod)); - } - - private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost) - { - return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost); - } - - private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute) - { - var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value); - - return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute); - } - } -} +using System.Collections.Generic; +using System.Linq; +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Errors; +using Ocelot.Responses; + +namespace Ocelot.DownstreamRouteFinder.Finder +{ + public class DownstreamRouteFinder : IDownstreamRouteProvider + { + private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; + private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder; + + public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder) + { + _urlMatcher = urlMatcher; + _placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; + } + + public Response Get(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost) + { + var downstreamRoutes = new List(); + + var applicableReRoutes = configuration.ReRoutes + .Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost)) + .OrderByDescending(x => x.UpstreamTemplatePattern.Priority); + + foreach (var reRoute in applicableReRoutes) + { + var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template); + + if (urlMatch.Data.Match) + { + downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute)); + } + } + + if (downstreamRoutes.Any()) + { + var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); + var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); + + return notNullOption != null ? new OkResponse(notNullOption) : new OkResponse(nullOption); + } + + return new ErrorResponse(new UnableToFindDownstreamRouteError(path, httpMethod)); + } + + private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost) + { + return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost); + } + + private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute) + { + var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value); + + return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute); + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs new file mode 100644 index 000000000..b615b6136 --- /dev/null +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs @@ -0,0 +1,38 @@ +namespace Ocelot.DownstreamRouteFinder.Finder +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Configuration; + using Microsoft.Extensions.DependencyInjection; + + public class DownstreamRouteProviderFactory : IDownstreamRouteProviderFactory + { + private readonly Dictionary _providers; + + public DownstreamRouteProviderFactory(IServiceProvider provider) + { + _providers = provider.GetServices().ToDictionary(x => x.GetType().Name); + } + + public IDownstreamRouteProvider Get(IInternalConfiguration config) + { + if(!config.ReRoutes.Any() && IsServiceDiscovery(config.ServiceProviderConfiguration)) + { + return _providers[nameof(DownstreamRouteCreator)]; + } + + return _providers[nameof(DownstreamRouteFinder)]; + } + + private bool IsServiceDiscovery(ServiceProviderConfiguration config) + { + if(!string.IsNullOrEmpty(config?.Host) || config?.Port > 0) + { + return true; + } + + return false; + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteFinder.cs deleted file mode 100644 index 1d8fae158..000000000 --- a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteFinder.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using Ocelot.Configuration; -using Ocelot.Responses; - -namespace Ocelot.DownstreamRouteFinder.Finder -{ - public interface IDownstreamRouteFinder - { - Response FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost); - } -} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs new file mode 100644 index 000000000..df14065d7 --- /dev/null +++ b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Ocelot.Configuration; +using Ocelot.Responses; + +namespace Ocelot.DownstreamRouteFinder.Finder +{ + public interface IDownstreamRouteProvider + { + Response Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost); + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProviderFactory.cs b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProviderFactory.cs new file mode 100644 index 000000000..35fdd297d --- /dev/null +++ b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProviderFactory.cs @@ -0,0 +1,9 @@ +namespace Ocelot.DownstreamRouteFinder.Finder +{ + using Configuration; + + public interface IDownstreamRouteProviderFactory + { + IDownstreamRouteProvider Get(IInternalConfiguration config); + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs index 970636d37..5c40228dc 100644 --- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs +++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs @@ -1,69 +1,60 @@ -using System.Threading.Tasks; -using System.Linq; -using Ocelot.Configuration.Repository; -using Ocelot.DownstreamRouteFinder.Finder; -using Ocelot.Infrastructure.Extensions; -using Ocelot.Logging; -using Ocelot.Middleware; -using Ocelot.Middleware.Multiplexer; - -namespace Ocelot.DownstreamRouteFinder.Middleware -{ - public class DownstreamRouteFinderMiddleware : OcelotMiddleware - { - private readonly OcelotRequestDelegate _next; - private readonly IDownstreamRouteFinder _downstreamRouteFinder; - private readonly IInternalConfigurationRepository _repo; - private readonly IMultiplexer _multiplexer; - - public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next, - IOcelotLoggerFactory loggerFactory, - IDownstreamRouteFinder downstreamRouteFinder, - IInternalConfigurationRepository repo, - IMultiplexer multiplexer) - :base(loggerFactory.CreateLogger()) - { - _repo = repo; - _multiplexer = multiplexer; - _next = next; - _downstreamRouteFinder = downstreamRouteFinder; - } - - public async Task Invoke(DownstreamContext context) - { - var upstreamUrlPath = context.HttpContext.Request.Path.ToString(); - - var upstreamHost = context.HttpContext.Request.Headers["Host"]; - - var configuration = _repo.Get(); - - if (configuration.IsError) - { - Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}"); - SetPipelineError(context, configuration.Errors); - return; - } - - context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration; - - Logger.LogDebug($"Upstream url path is {upstreamUrlPath}"); - - var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.HttpContext.Request.Method, configuration.Data, upstreamHost); - - if (downstreamRoute.IsError) - { - Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}"); - - SetPipelineError(context, downstreamRoute.Errors); - return; - } - - var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value)); - Logger.LogDebug($"downstream templates are {downstreamPathTemplates}"); - - context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues; - - await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next); - } - } -} +using System.Threading.Tasks; +using System.Linq; +using Ocelot.Configuration.Repository; +using Ocelot.DownstreamRouteFinder.Finder; +using Ocelot.Infrastructure.Extensions; +using Ocelot.Logging; +using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; + +namespace Ocelot.DownstreamRouteFinder.Middleware +{ + public class DownstreamRouteFinderMiddleware : OcelotMiddleware + { + private readonly OcelotRequestDelegate _next; + private readonly IDownstreamRouteProviderFactory _factory; + private readonly IInternalConfigurationRepository _repo; + private readonly IMultiplexer _multiplexer; + + public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next, + IOcelotLoggerFactory loggerFactory, + IDownstreamRouteProviderFactory downstreamRouteFinder, + IInternalConfigurationRepository repo, + IMultiplexer multiplexer) + :base(loggerFactory.CreateLogger()) + { + _repo = repo; + _multiplexer = multiplexer; + _next = next; + _factory = downstreamRouteFinder; + } + + public async Task Invoke(DownstreamContext context) + { + var upstreamUrlPath = context.HttpContext.Request.Path.ToString(); + + var upstreamHost = context.HttpContext.Request.Headers["Host"]; + + Logger.LogDebug($"Upstream url path is {upstreamUrlPath}"); + + var provider = _factory.Get(context.Configuration); + + var downstreamRoute = provider.Get(upstreamUrlPath, context.HttpContext.Request.Method, context.Configuration, upstreamHost); + + if (downstreamRoute.IsError) + { + Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}"); + + SetPipelineError(context, downstreamRoute.Errors); + return; + } + + var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value)); + Logger.LogDebug($"downstream templates are {downstreamPathTemplates}"); + + context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues; + + await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next); + } + } +} diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index bc0547836..defe20a9b 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -73,7 +73,7 @@ public async Task Invoke(DownstreamContext context) private static bool ServiceFabricRequest(DownstreamContext context) { - return context.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery; + return context.Configuration.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery; } private static bool RequestForStatefullService(string query) diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs index 41b0b83d6..b7e51ef93 100644 --- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs +++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs @@ -9,6 +9,8 @@ namespace Ocelot.Errors.Middleware { + using Configuration; + /// /// Catches all unhandled exceptions thrown by middleware, logs and returns a 500 /// @@ -32,8 +34,20 @@ public ExceptionHandlerMiddleware(OcelotRequestDelegate next, public async Task Invoke(DownstreamContext context) { try - { - TrySetGlobalRequestId(context); + { + //try and get the global request id and set it for logs... + //should this basically be immutable per request...i guess it should! + //first thing is get config + var configuration = _configRepo.Get(); + + if (configuration.IsError) + { + throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}"); + } + + TrySetGlobalRequestId(context, configuration.Data); + + context.Configuration = configuration.Data; Logger.LogDebug("ocelot pipeline started"); @@ -53,19 +67,9 @@ public async Task Invoke(DownstreamContext context) Logger.LogDebug("ocelot pipeline finished"); } - private void TrySetGlobalRequestId(DownstreamContext context) + private void TrySetGlobalRequestId(DownstreamContext context, IInternalConfiguration configuration) { - //try and get the global request id and set it for logs... - //should this basically be immutable per request...i guess it should! - //first thing is get config - var configuration = _configRepo.Get(); - - if(configuration.IsError) - { - throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}"); - } - - var key = configuration.Data.RequestId; + var key = configuration.RequestId; if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds)) { diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs index 89f455454..5cb28023f 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs @@ -18,7 +18,7 @@ public NoLoadBalancer(List services) public async Task> Lease(DownstreamContext downstreamContext) { - //todo no point spinning a task up here, also first or default could be null.. + //todo first or default could be null.. if (_services == null || _services.Count == 0) { return new ErrorResponse(new ServicesAreEmptyError("There were no services in NoLoadBalancer")); diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index fc971a163..9568b9cfb 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -22,7 +22,7 @@ public LoadBalancingMiddleware(OcelotRequestDelegate next, public async Task Invoke(DownstreamContext context) { - var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration); + var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration); if(loadBalancer.IsError) { Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error"); diff --git a/src/Ocelot/Middleware/DownstreamContext.cs b/src/Ocelot/Middleware/DownstreamContext.cs index 1913eea9a..eb21b4bb6 100644 --- a/src/Ocelot/Middleware/DownstreamContext.cs +++ b/src/Ocelot/Middleware/DownstreamContext.cs @@ -1,11 +1,8 @@ -using System; using System.Collections.Generic; -using System.Net.Http; using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Errors; -using Ocelot.Middleware.Multiplexer; using Ocelot.Request.Middleware; namespace Ocelot.Middleware @@ -20,8 +17,6 @@ public DownstreamContext(HttpContext httpContext) public List TemplatePlaceholderNameAndValues { get; set; } - public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;} - public HttpContext HttpContext { get; } public DownstreamReRoute DownstreamReRoute { get; set; } @@ -32,6 +27,8 @@ public DownstreamContext(HttpContext httpContext) public List Errors { get; } + public IInternalConfiguration Configuration { get; set; } + public bool IsError => Errors.Count > 0; } } diff --git a/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs index ffe530e87..351f84716 100644 --- a/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs +++ b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs @@ -23,7 +23,7 @@ public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRe var downstreamContext = new DownstreamContext(context.HttpContext) { TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues, - ServiceProviderConfiguration = context.ServiceProviderConfiguration, + Configuration = context.Configuration, DownstreamReRoute = reRoute.DownstreamReRoute[i], }; diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs index 029698558..340afb47c 100644 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs +++ b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs @@ -68,7 +68,7 @@ public Response>> Get(DownstreamReRoute request) handlers.Add(() => (DelegatingHandler)_factory.Get()); } - if (request.IsQos) + if (request.QosOptions.UseQos) { var qosProvider = _qosProviderHouse.Get(request); diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 18160e02b..cb1fd6dbf 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -58,9 +58,9 @@ public IHttpClient Create(DownstreamContext context) .LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}"); } - var timeout = context.DownstreamReRoute.QosOptionsOptions.TimeoutValue == 0 + var timeout = context.DownstreamReRoute.QosOptions.TimeoutValue == 0 ? _defaultTimeout - : TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptionsOptions.TimeoutValue); + : TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptions.TimeoutValue); _httpClient = new HttpClient(CreateHttpMessageHandler(httpclientHandler, context.DownstreamReRoute)) { diff --git a/src/Ocelot/Requester/QoS/PollyQoSProvider.cs b/src/Ocelot/Requester/QoS/PollyQoSProvider.cs index 029650e5f..afbf9c3a3 100644 --- a/src/Ocelot/Requester/QoS/PollyQoSProvider.cs +++ b/src/Ocelot/Requester/QoS/PollyQoSProvider.cs @@ -19,15 +19,15 @@ public PollyQoSProvider(DownstreamReRoute reRoute, IOcelotLoggerFactory loggerFa { _logger = loggerFactory.CreateLogger(); - _timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.TimeoutValue), reRoute.QosOptionsOptions.TimeoutStrategy); + _timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(reRoute.QosOptions.TimeoutValue), reRoute.QosOptions.TimeoutStrategy); _circuitBreakerPolicy = Policy .Handle() .Or() .Or() .CircuitBreakerAsync( - exceptionsAllowedBeforeBreaking: reRoute.QosOptionsOptions.ExceptionsAllowedBeforeBreaking, - durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.DurationOfBreak), + exceptionsAllowedBeforeBreaking: reRoute.QosOptions.ExceptionsAllowedBeforeBreaking, + durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptions.DurationOfBreak), onBreak: (ex, breakDelay) => { _logger.LogError( diff --git a/src/Ocelot/Requester/QoS/QoSProviderFactory.cs b/src/Ocelot/Requester/QoS/QoSProviderFactory.cs index f985c9b5a..45fa78b32 100644 --- a/src/Ocelot/Requester/QoS/QoSProviderFactory.cs +++ b/src/Ocelot/Requester/QoS/QoSProviderFactory.cs @@ -14,7 +14,7 @@ public QoSProviderFactory(IOcelotLoggerFactory loggerFactory) public IQoSProvider Get(DownstreamReRoute reRoute) { - if (reRoute.IsQos) + if (reRoute.QosOptions.UseQos) { return new PollyQoSProvider(reRoute, _loggerFactory); } diff --git a/src/Ocelot/Requester/QoS/QosProviderHouse.cs b/src/Ocelot/Requester/QoS/QosProviderHouse.cs index 73629e7f7..f0e44b39d 100644 --- a/src/Ocelot/Requester/QoS/QosProviderHouse.cs +++ b/src/Ocelot/Requester/QoS/QosProviderHouse.cs @@ -21,26 +21,26 @@ public Response Get(DownstreamReRoute reRoute) { try { - if (_qoSProviders.TryGetValue(reRoute.QosKey, out var qosProvider)) + if (_qoSProviders.TryGetValue(reRoute.QosOptions.Key, out var qosProvider)) { - if (reRoute.IsQos && qosProvider.CircuitBreaker == null) + if (reRoute.QosOptions.UseQos && qosProvider.CircuitBreaker == null) { qosProvider = _qoSProviderFactory.Get(reRoute); - Add(reRoute.QosKey, qosProvider); + Add(reRoute.QosOptions.Key, qosProvider); } - return new OkResponse(_qoSProviders[reRoute.QosKey]); + return new OkResponse(_qoSProviders[reRoute.QosOptions.Key]); } qosProvider = _qoSProviderFactory.Get(reRoute); - Add(reRoute.QosKey, qosProvider); + Add(reRoute.QosOptions.Key, qosProvider); return new OkResponse(qosProvider); } catch (Exception ex) { return new ErrorResponse(new List() { - new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.QosKey}, exception was {ex}") + new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.QosOptions.Key}, exception was {ex}") }); } } diff --git a/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs b/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs index 13ae68d24..b99f43ccf 100644 --- a/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs +++ b/src/Ocelot/ServiceDiscovery/Configuration/ConsulRegistryConfiguration.cs @@ -4,8 +4,8 @@ public class ConsulRegistryConfiguration { public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token) { - Host = host; - Port = port; + Host = string.IsNullOrEmpty(host) ? "localhost" : host; + Port = port > 0 ? port : 8500; KeyOfServiceInConsul = keyOfServiceInConsul; Token = token; } diff --git a/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs b/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs index 4c1afcc75..88bcc5d5c 100644 --- a/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs +++ b/src/Ocelot/ServiceDiscovery/Providers/ConsulServiceDiscoveryProvider.cs @@ -22,12 +22,7 @@ public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration config, IOcelo {; _logger = factory.CreateLogger(); - var consulHost = string.IsNullOrEmpty(config?.Host) ? "localhost" : config.Host; - - var consulPort = config?.Port ?? 8500; - - _config = new ConsulRegistryConfiguration(consulHost, consulPort, config?.KeyOfServiceInConsul, config?.Token); - + _config = config; _consul = clientFactory.Get(_config); } diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index 870ab7e81..0d5f7d0c1 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -208,6 +208,121 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ .BDDfy(); } + [Fact] + public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes() + { + const int consulPort = 8513; + const string serviceName = "web"; + const int downstreamServicePort = 8087; + var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = downstreamServicePort, + ID = "web_90_0_2_224_8080", + Tags = new[] {"version-v1"} + }, + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Host = "localhost", + Port = consulPort + }, + DownstreamScheme = "http", + HttpHandlerOptions = new FileHttpHandlerOptions + { + AllowAutoRedirect = true, + UseCookieContainer = true, + UseTracing = false + }, + QoSOptions = new FileQoSOptions + { + TimeoutValue = 100, + DurationOfBreak = 1000, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/web/something")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes() + { + var consulPort = 8510; + var serviceName = "product"; + var serviceOnePort = 50888; + var serviceTwoPort = 50889; + var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}"; + var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = serviceOnePort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = serviceTwoPort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = consulPort + }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + DownstreamScheme = "http" + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) + .BDDfy(); + } + [Fact] public void should_use_token_to_make_request_to_consul() { diff --git a/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationRepositoryTests.cs index 6642bb8dd..d26a69fce 100644 --- a/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConsulFileConfigurationRepositoryTests.cs @@ -55,7 +55,7 @@ public ConsulFileConfigurationRepositoryTests() _internalRepo .Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(new List(), "", new ServiceProviderConfigurationBuilder().Build(), ""))); + .Returns(new OkResponse(new InternalConfiguration(new List(), "", new ServiceProviderConfigurationBuilder().Build(), "", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))); _repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object); } @@ -140,7 +140,10 @@ private void GivenTheConfigKeyComesFromFileConfig(string key) { _internalRepo .Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(new List(), "", new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), ""))); + .Returns(new OkResponse(new InternalConfiguration(new List(), "", + new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), "", + new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), + new HttpHandlerOptionsBuilder().Build()))); _repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object); } diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs index ff33872c5..57f8c4d7b 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs @@ -38,7 +38,7 @@ public void should_set_configuration() { var fileConfig = new FileConfiguration(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf"); + var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); 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 a097f9211..c5bba51cf 100644 --- a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs @@ -86,7 +86,7 @@ public void should_set_up_sticky_sessions_config() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) - .WithReRouteKey("CookieStickySessions:sessionid") + .WithLoadBalancerKey("CookieStickySessions:sessionid") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -199,7 +199,7 @@ public void should_set_up_aggregate_re_route() .WithDownstreamScheme("http") .WithUpstreamHttpMethod(new List() {"Get"}) .WithDownstreamAddresses(new List() {new DownstreamHostAndPort("localhost", 51878)}) - .WithReRouteKey("/laura|Get") + .WithLoadBalancerKey("/laura|Get") .Build(); var lauraReRoute = new ReRouteBuilder() @@ -218,7 +218,7 @@ public void should_set_up_aggregate_re_route() .WithDownstreamScheme("http") .WithUpstreamHttpMethod(new List() { "Get" }) .WithDownstreamAddresses(new List() { new DownstreamHostAndPort("localhost", 51878) }) - .WithReRouteKey("/tom|Get") + .WithLoadBalancerKey("/tom|Get") .Build(); var tomReRoute = new ReRouteBuilder() @@ -361,7 +361,6 @@ public void should_call_qos_options_creator() .Build(); var serviceOptions = new ReRouteOptionsBuilder() - .WithIsQos(true) .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -410,7 +409,7 @@ public void should_use_downstream_host() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -462,7 +461,7 @@ public void should_use_downstream_scheme() .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) .WithDelegatingHandlers(handlers) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -507,7 +506,7 @@ public void should_use_service_discovery_for_downstream_service_host() .WithUpstreamHttpMethod(new List {"Get"}) .WithUseServiceDiscovery(true) .WithServiceName("ProductService") - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -558,7 +557,7 @@ public void should_not_use_service_discovery_for_downstream_host_url_when_no_ser .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) .WithUseServiceDiscovery(false) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -601,7 +600,7 @@ public void should_call_template_pattern_creator_correctly() .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -646,7 +645,7 @@ public void should_call_request_id_creator() .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) .WithRequestIdKey("blahhhh") - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration @@ -741,7 +740,7 @@ public void should_create_with_headers_to_extract(FileConfiguration fileConfig) { new ClaimToThing("CustomerId", "CustomerId", "", 0), }) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); var expected = new List @@ -784,7 +783,7 @@ public void should_create_with_authentication_properties(FileConfiguration fileC .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List {"Get"}) .WithAuthenticationOptions(authenticationOptions) - .WithReRouteKey("/api/products/{productId}|Get") + .WithLoadBalancerKey("/api/products/{productId}|Get") .Build(); var expected = new List @@ -942,16 +941,15 @@ private void GivenTheRequestIdCreatorReturns(string requestId) private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) { _qosOptionsCreator - .Setup(x => x.Create(_fileConfiguration.ReRoutes[0])) + .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].QoSOptions, It.IsAny(), It.IsAny())) .Returns(qosOptions); } private void ThenTheQosOptionsAre(QoSOptions qosOptions) { - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); - - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); + _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); } private void ThenTheServiceProviderCreatorIsCalledCorrectly() @@ -992,13 +990,13 @@ private void ThenTheRegionCreatorIsCalledCorrectly() private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions) { - _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny())) + _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny())) .Returns(httpHandlerOptions); } private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly() { - _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once()); + _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0].HttpHandlerOptions), Times.Once()); } private void GivenTheDownstreamAddresses() diff --git a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs index 7316dcb36..c86c55d00 100644 --- a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs @@ -103,7 +103,7 @@ private void GivenTheFollowing(FileReRoute fileReRoute) private void WhenICreateHttpHandlerOptions() { - _httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute); + _httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute.HttpHandlerOptions); } private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected) diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 50bf1f856..dfa6362f6 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -105,6 +105,10 @@ public List ReRoutes public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException(); public string RequestId {get;} + public LoadBalancerOptions LoadBalancerOptions { get; } + public string DownstreamScheme { get; } + public QoSOptions QoSOptions { get; } + public HttpHandlerOptions HttpHandlerOptions { get; } } } } diff --git a/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs index 0ce64a383..0a68c238b 100644 --- a/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs @@ -50,7 +50,7 @@ private void GivenTheFollowingReRoute(FileReRoute fileReRoute) private void WhenICreate() { - _result = _creator.Create(_fileReRoute); + _result = _creator.Create(_fileReRoute.QoSOptions); } private void ThenTheFollowingIsReturned(QoSOptions expected) @@ -60,4 +60,4 @@ private void ThenTheFollowingIsReturned(QoSOptions expected) _result.TimeoutValue.ShouldBe(expected.TimeoutValue); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs index 6961c968a..d23aabc0c 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs @@ -29,11 +29,6 @@ public void should_create_re_route_options() { EnableRateLimiting = true }, - QoSOptions = new FileQoSOptions - { - ExceptionsAllowedBeforeBreaking = 1, - TimeoutValue = 1 - }, AuthenticationOptions = new FileAuthenticationOptions() { AuthenticationProviderKey = "Test" @@ -52,7 +47,6 @@ public void should_create_re_route_options() .WithIsAuthenticated(true) .WithIsAuthorised(true) .WithIsCached(true) - .WithIsQos(true) .WithRateLimiting(true) .Build(); @@ -76,9 +70,8 @@ private void ThenTheFollowingIsReturned(ReRouteOptions expected) { _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); _result.IsAuthorised.ShouldBe(expected.IsAuthorised); - _result.IsQos.ShouldBe(expected.IsQos); _result.IsCached.ShouldBe(expected.IsCached); _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); } } -} \ No newline at end of file +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs new file mode 100644 index 000000000..e0ffa10f6 --- /dev/null +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs @@ -0,0 +1,194 @@ +using Ocelot.DownstreamRouteFinder.Finder; +using Xunit; +using Shouldly; +using Ocelot.Configuration; +using System.Net.Http; + +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using Moq; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.DownstreamRouteFinder; + using Ocelot.LoadBalancer.LoadBalancers; + using Responses; + using TestStack.BDDfy; + + public class DownstreamRouteCreatorTests + { + private readonly DownstreamRouteCreator _creator; + private readonly QoSOptions _qoSOptions; + private readonly HttpHandlerOptions _handlerOptions; + private readonly LoadBalancerOptions _loadBalancerOptions; + private Response _result; + private string _upstreamHost; + private string _upstreamUrlPath; + private string _upstreamHttpMethod; + private IInternalConfiguration _configuration; + private Mock _qosOptionsCreator; + + public DownstreamRouteCreatorTests() + { + _qosOptionsCreator = new Mock(); + _qoSOptions = new QoSOptionsBuilder().Build(); + _handlerOptions = new HttpHandlerOptionsBuilder().Build(); + _loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build(); + _qosOptionsCreator + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_qoSOptions); + _creator = new DownstreamRouteCreator(_qosOptionsCreator.Object); + } + + [Fact] + public void should_create_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamRouteIsCreated()) + .BDDfy(); + } + + [Fact] + 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); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamPathIsForwardSlash()) + .BDDfy(); + } + + [Fact] + 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); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheQueryStringIsRemoved()) + .BDDfy(); + } + + [Fact] + 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); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheStickySessionLoadBalancerIsUsed(loadBalancerOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_qos() + { + var qoSOptions = new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(1) + .WithTimeoutValue(1) + .Build(); + + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration)) + .And(_ => GivenTheQosCreatorReturns(qoSOptions)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheQosOptionsAreSet(qoSOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_handler_options() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheHandlerOptionsAreSet()) + .BDDfy(); + } + + private void GivenTheQosCreatorReturns(QoSOptions options) + { + _qosOptionsCreator + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(options); + } + + private void ThenTheDownstreamRouteIsCreated() + { + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.ReRoute.UpstreamHttpMethod[0].ShouldBe(HttpMethod.Get); + _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + _result.Data.ReRoute.DownstreamReRoute[0].UseServiceDiscovery.ShouldBeTrue(); + _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldNotBeNull(); + _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldNotBeNull(); + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamScheme.ShouldBe("http"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(NoLoadBalancer)); + _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); + _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(_qoSOptions); + } + + private void ThenTheDownstreamPathIsForwardSlash() + { + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/"); + _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET"); + } + + private void ThenTheQueryStringIsRemoved() + { + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + } + + private void ThenTheStickySessionLoadBalancerIsUsed(LoadBalancerOptions expected) + { + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe($"{nameof(CookieStickySessions)}:boom"); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(CookieStickySessions)); + _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.ShouldBe(expected); + } + + private void ThenTheQosOptionsAreSet(QoSOptions expected) + { + _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(expected); + _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue(); + _qosOptionsCreator + .Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny()), Times.Once); + } + + private void GivenTheConfiguration(IInternalConfiguration config) + { + _upstreamHost = "doesnt matter"; + _upstreamUrlPath = "/auth/test"; + _upstreamHttpMethod = "GET"; + _configuration = config; + } + + private void GivenTheConfiguration(IInternalConfiguration config, string upstreamUrlPath) + { + _upstreamHost = "doesnt matter"; + _upstreamUrlPath = upstreamUrlPath; + _upstreamHttpMethod = "GET"; + _configuration = config; + } + + private void ThenTheHandlerOptionsAreSet() + { + _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); + } + + private void WhenICreate() + { + _result = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index 95bcaf475..19b50e240 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -1,98 +1,99 @@ -namespace Ocelot.UnitTests.DownstreamRouteFinder -{ - using System.Collections.Generic; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.Finder; - using Ocelot.DownstreamRouteFinder.Middleware; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Ocelot.Responses; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - using Ocelot.Configuration.Repository; - using Ocelot.Middleware; - using Ocelot.Middleware.Multiplexer; - - public class DownstreamRouteFinderMiddlewareTests - { - private readonly Mock _finder; - private readonly Mock _repo; - private Response _downstreamRoute; - private IInternalConfiguration _config; - private Mock _loggerFactory; - private Mock _logger; - private readonly DownstreamRouteFinderMiddleware _middleware; - private readonly DownstreamContext _downstreamContext; - private OcelotRequestDelegate _next; - private readonly Mock _multiplexer; - - public DownstreamRouteFinderMiddlewareTests() - { - _repo = new Mock(); - _finder = new Mock(); - _downstreamContext = new DownstreamContext(new DefaultHttpContext()); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = context => Task.CompletedTask; - _multiplexer = new Mock(); - _middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _repo.Object, _multiplexer.Object); - } - - [Fact] - public void should_call_scoped_data_repository_correctly() - { - var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), ""); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithUpstreamHttpMethod(new List {"Get"}) - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteFinderReturns( - new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => GivenTheFollowingConfig(config)) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) - .BDDfy(); - } - - private void WhenICallTheMiddleware() - { - _middleware.Invoke(_downstreamContext).GetAwaiter().GetType(); - } - - private void GivenTheFollowingConfig(IInternalConfiguration config) - { - _config = config; - _repo - .Setup(x => x.Get()) - .Returns(new OkResponse(_config)); - } - - private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) - { - _downstreamRoute = new OkResponse(downstreamRoute); - _finder - .Setup(x => x.FindDownstreamRoute(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_downstreamRoute); - } - - private void ThenTheScopedDataRepositoryIsCalledCorrectly() - { - _downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues); - _downstreamContext.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration); - } - } -} +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Finder; + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Ocelot.Responses; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + using Ocelot.Configuration.Repository; + using Ocelot.Middleware; + using Ocelot.Middleware.Multiplexer; + + public class DownstreamRouteFinderMiddlewareTests + { + private readonly Mock _finder; + private readonly Mock _factory; + private readonly Mock _repo; + private Response _downstreamRoute; + private IInternalConfiguration _config; + private Mock _loggerFactory; + private Mock _logger; + private readonly DownstreamRouteFinderMiddleware _middleware; + private readonly DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + private readonly Mock _multiplexer; + + public DownstreamRouteFinderMiddlewareTests() + { + _repo = new Mock(); + _finder = new Mock(); + _factory = new Mock(); + _factory.Setup(x => x.Get(It.IsAny())).Returns(_finder.Object); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = context => Task.CompletedTask; + _multiplexer = new Mock(); + _middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _factory.Object, _repo.Object, _multiplexer.Object); + } + + [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 downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod(new List {"Get"}) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteFinderReturns( + new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(downstreamReRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => GivenTheFollowingConfig(config)) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) + .BDDfy(); + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetType(); + } + + private void GivenTheFollowingConfig(IInternalConfiguration config) + { + _config = config; + _downstreamContext.Configuration = config; + } + + private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) + { + _downstreamRoute = new OkResponse(downstreamRoute); + _finder + .Setup(x => x.Get(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_downstreamRoute); + } + + private void ThenTheScopedDataRepositoryIsCalledCorrectly() + { + _downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues); + _downstreamContext.Configuration.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 7406b1c58..6b66abe3d 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -1,739 +1,739 @@ -using System.Collections.Generic; -using Moq; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.DownstreamRouteFinder; -using Ocelot.DownstreamRouteFinder.Finder; -using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.Responses; -using Ocelot.Values; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.DownstreamRouteFinder -{ - public class DownstreamRouteFinderTests - { - private readonly IDownstreamRouteFinder _downstreamRouteFinder; - private readonly Mock _mockMatcher; - private readonly Mock _finder; - private string _upstreamUrlPath; - private Response _result; - private List _reRoutesConfig; - private InternalConfiguration _config; - private Response _match; - private string _upstreamHttpMethod; - private string _upstreamHost; - - public DownstreamRouteFinderTests() - { - _mockMatcher = new Mock(); - _finder = new Mock(); - _downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object); - } - - [Fact] - public void should_return_highest_priority_when_first() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) - .Build() - }, string.Empty, serviceProviderConfig)) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build()) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_highest_priority_when_lowest() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .Build() - }, string.Empty, serviceProviderConfig)) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build()) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_route() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_not_append_slash_to_upstream_url_path() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) - .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher")) - .BDDfy(); - } - - [Fact] - public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_not_return_route() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("somPath") - .WithUpstreamPathTemplate("somePath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) - .Build()) - .WithUpstreamPathTemplate("somePath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) - .Build(), - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(false)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List()) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List()) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_host_matches() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_upstreamhost_is_null() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_not_return_route_when_host_doesnt_match() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("DONTMATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_host_matches_but_null_host_on_same_path_first() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("THENULLPATH") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build()) - .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2)) - .BDDfy(); - } - - private void GivenTheUpstreamHostIs(string upstreamHost) - { - _upstreamHost = upstreamHost; - } - - private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) - { - _finder - .Setup(x => x.Find(It.IsAny(), It.IsAny())) - .Returns(response); - } - - private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) - { - _upstreamHttpMethod = upstreamHttpMethod; - } - - private void ThenAnErrorResponseIsReturned() - { - _result.IsError.ShouldBeTrue(); - } - - private void ThenTheUrlMatcherIsCalledCorrectly() - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); - } - - private void ThenTheUrlMatcherIsCalledCorrectly(int times) - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Exactly(times)); - } - - private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) - { - _mockMatcher - .Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); - } - - private void ThenTheUrlMatcherIsNotCalled() - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never); - } - - private void GivenTheUrlMatcherReturns(Response match) - { - _match = match; - _mockMatcher - .Setup(x => x.Match(It.IsAny(), It.IsAny())) - .Returns(_match); - } - - private void GivenTheConfigurationIs(List reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) - { - _reRoutesConfig = reRoutesConfig; - _config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, ""); - } - - private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) - { - _upstreamUrlPath = upstreamUrlPath; - } - - private void WhenICallTheFinder() - { - _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost); - } - - private void ThenTheFollowingIsReturned(DownstreamRoute expected) - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value); - _result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority); - - for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) - { - _result.Data.TemplatePlaceholderNameAndValues[i].Name.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Name); - _result.Data.TemplatePlaceholderNameAndValues[i].Value.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Value); - } - - _result.IsError.ShouldBeFalse(); - } - } -} +using System.Collections.Generic; +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.DownstreamRouteFinder; +using Ocelot.DownstreamRouteFinder.Finder; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Responses; +using Ocelot.Values; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + public class DownstreamRouteFinderTests + { + private readonly IDownstreamRouteProvider _downstreamRouteFinder; + private readonly Mock _mockMatcher; + private readonly Mock _finder; + private string _upstreamUrlPath; + private Response _result; + private List _reRoutesConfig; + private InternalConfiguration _config; + private Response _match; + private string _upstreamHttpMethod; + private string _upstreamHost; + + public DownstreamRouteFinderTests() + { + _mockMatcher = new Mock(); + _finder = new Mock(); + _downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object); + } + + [Fact] + public void should_return_highest_priority_when_first() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build() + }, string.Empty, serviceProviderConfig)) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_highest_priority_when_lowest() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) + .Build(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .Build() + }, string.Empty, serviceProviderConfig)) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build()) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_route() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_not_append_slash_to_upstream_url_path() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) + .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher")) + .BDDfy(); + } + + [Fact] + public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_not_return_route() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("somPath") + .WithUpstreamPathTemplate("somePath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) + .Build()) + .WithUpstreamPathTemplate("somePath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) + .Build(), + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(false)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_host_matches() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_upstreamhost_is_null() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_when_host_doesnt_match() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("DONTMATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_host_matches_but_null_host_on_same_path_first() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("THENULLPATH") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build()) + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2)) + .BDDfy(); + } + + private void GivenTheUpstreamHostIs(string upstreamHost) + { + _upstreamHost = upstreamHost; + } + + private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) + { + _finder + .Setup(x => x.Find(It.IsAny(), It.IsAny())) + .Returns(response); + } + + private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) + { + _upstreamHttpMethod = upstreamHttpMethod; + } + + private void ThenAnErrorResponseIsReturned() + { + _result.IsError.ShouldBeTrue(); + } + + private void ThenTheUrlMatcherIsCalledCorrectly() + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); + } + + private void ThenTheUrlMatcherIsCalledCorrectly(int times) + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Exactly(times)); + } + + private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) + { + _mockMatcher + .Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); + } + + private void ThenTheUrlMatcherIsNotCalled() + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never); + } + + private void GivenTheUrlMatcherReturns(Response match) + { + _match = match; + _mockMatcher + .Setup(x => x.Match(It.IsAny(), It.IsAny())) + .Returns(_match); + } + + 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()); + } + + private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) + { + _upstreamUrlPath = upstreamUrlPath; + } + + private void WhenICallTheFinder() + { + _result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost); + } + + private void ThenTheFollowingIsReturned(DownstreamRoute expected) + { + _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value); + _result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority); + + for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) + { + _result.Data.TemplatePlaceholderNameAndValues[i].Name.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Name); + _result.Data.TemplatePlaceholderNameAndValues[i].Value.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Value); + } + + _result.IsError.ShouldBeFalse(); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs new file mode 100644 index 000000000..65ed47371 --- /dev/null +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.DownstreamRouteFinder; +using Ocelot.DownstreamRouteFinder.Finder; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Responses; +using Ocelot.Values; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using Ocelot.Configuration.Creator; + + public class DownstreamRouteProviderFactoryTests + { + private readonly DownstreamRouteProviderFactory _factory; + private IInternalConfiguration _config; + private IDownstreamRouteProvider _result; + + public DownstreamRouteProviderFactoryTests() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + var provider = services.BuildServiceProvider(); + _factory = new DownstreamRouteProviderFactory(provider); + } + + [Fact] + public void should_return_downstream_route_finder() + { + var reRoutes = new List + { + new ReRouteBuilder().Build() + }; + + this.Given(_ => GivenTheReRoutes(reRoutes)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_as_no_service_discovery() + { + var spConfig = new ServiceProviderConfigurationBuilder().Build(); + var reRoutes = new List + { + }; + + this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_creator() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).Build(); + var reRoutes = new List + { + }; + this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + private void ThenTheResultShouldBe() + { + _result.ShouldBeOfType(); + } + + private void WhenIGet() + { + _result = _factory.Get(_config); + } + + private void GivenTheReRoutes(List reRoutes) + { + _config = new InternalConfiguration(reRoutes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + } + + private void GivenTheReRoutes(List reRoutes, ServiceProviderConfiguration config) + { + _config = new InternalConfiguration(reRoutes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build()); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 637973461..4e81e59fe 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -192,8 +192,9 @@ public void should_create_service_fabric_url_with_query_string_for_stateful_serv } private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config) - { - _downstreamContext.ServiceProviderConfiguration = config; + { + var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); + _downstreamContext.Configuration = configuration; } private void WhenICallTheMiddleware() diff --git a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs index 8802a5bc5..ce88b4688 100644 --- a/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Errors/ExceptionHandlerMiddlewareTests.cs @@ -51,7 +51,7 @@ public ExceptionHandlerMiddlewareTests() [Fact] public void NoDownstreamException() { - var config = new InternalConfiguration(null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -64,7 +64,7 @@ public void NoDownstreamException() [Fact] public void DownstreamException() { - var config = new InternalConfiguration(null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -76,7 +76,7 @@ public void DownstreamException() [Fact] public void ShouldSetRequestId() { - var config = new InternalConfiguration(null, null, null, "requestidkey"); + var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) @@ -89,7 +89,7 @@ public void ShouldSetRequestId() [Fact] public void ShouldSetAspDotNetRequestId() { - var config = new InternalConfiguration(null, null, null, null); + var config = new InternalConfiguration(null, null, null, null, null, null, null, null); this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream()) .And(_ => GivenTheConfigurationIs(config)) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs index 1ea409dfb..6e0ae9bd6 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs @@ -33,7 +33,7 @@ public LoadBalancerHouseTests() public void should_store_load_balancer_on_first_request() { var reRoute = new DownstreamReRouteBuilder() - .WithReRouteKey("test") + .WithLoadBalancerKey("test") .Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) @@ -46,7 +46,7 @@ public void should_not_store_load_balancer_on_second_request() { var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) - .WithReRouteKey("test") + .WithLoadBalancerKey("test") .Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) @@ -60,12 +60,12 @@ public void should_store_load_balancers_by_key() { var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) - .WithReRouteKey("test") + .WithLoadBalancerKey("test") .Build(); var reRouteTwo = new DownstreamReRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeRoundRobinLoadBalancer", "", 0)) - .WithReRouteKey("testtwo") + .WithLoadBalancerKey("testtwo") .Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) @@ -92,12 +92,12 @@ public void should_get_new_load_balancer_if_reroute_load_balancer_has_changed() { var reRoute = new DownstreamReRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) - .WithReRouteKey("test") + .WithLoadBalancerKey("test") .Build(); var reRouteTwo = new DownstreamReRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("LeastConnection", "", 0)) - .WithReRouteKey("test") + .WithLoadBalancerKey("test") .Build(); this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 9e919d9f4..3bf97341c 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -117,7 +117,8 @@ private void WhenICallTheMiddleware() private void GivenTheConfigurationIs(ServiceProviderConfiguration config) { _config = config; - _downstreamContext.ServiceProviderConfiguration = config; + var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); + _downstreamContext.Configuration = configuration; } private void GivenTheDownStreamUrlIs(string downstreamUrl) diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs index 28a4bcf23..2ca7a8d5c 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs @@ -38,14 +38,21 @@ public DelegatingHandlerHandlerProviderFactoryTests() [Fact] public void should_follow_ordering_add_specifics() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", "FakeDelegatingHandlerTwo" }) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -67,7 +74,14 @@ public void should_follow_ordering_add_specifics() [Fact] public void should_follow_ordering_order_specifics_and_globals() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) .WithDelegatingHandlers(new List { @@ -75,7 +89,7 @@ public void should_follow_ordering_order_specifics_and_globals() "FakeDelegatingHandler", "FakeDelegatingHandlerFour" }) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -97,14 +111,21 @@ public void should_follow_ordering_order_specifics_and_globals() [Fact] public void should_follow_ordering_order_specifics() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) .WithDelegatingHandlers(new List { "FakeDelegatingHandlerTwo", "FakeDelegatingHandler" }) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -126,13 +147,20 @@ public void should_follow_ordering_order_specifics() [Fact] public void should_follow_ordering_order_and_only_add_specifics_in_config() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", }) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -153,9 +181,16 @@ public void should_follow_ordering_order_and_only_add_specifics_in_config() [Fact] public void should_follow_ordering_dont_add_specifics() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -175,14 +210,18 @@ public void should_follow_ordering_dont_add_specifics() [Fact] public void should_apply_re_route_specific() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", "FakeDelegatingHandlerTwo" }) - .WithReRouteKey("") + .WithLoadBalancerKey("") .Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) @@ -197,8 +236,15 @@ public void should_apply_re_route_specific() [Fact] public void should_all_from_all_routes_provider_and_qos() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) @@ -213,8 +259,12 @@ public void should_all_from_all_routes_provider_and_qos() [Fact] public void should_return_provider_with_no_delegates() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheServiceProviderReturnsNothing()) @@ -226,8 +276,15 @@ public void should_return_provider_with_no_delegates() [Fact] public void should_return_provider_with_qos_delegate() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) @@ -241,8 +298,15 @@ public void should_return_provider_with_qos_delegate() [Fact] public void should_return_error() { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new ErrorResponse(It.IsAny()))) diff --git a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs index e42275807..8b2ae449d 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs @@ -46,10 +46,13 @@ public HttpClientBuilderTests() [Fact] public void should_build_http_client() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); @@ -63,10 +66,13 @@ public void should_build_http_client() [Fact] public void should_log_if_ignoring_ssl_errors() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .WithDangerousAcceptAnyServerCertificateValidator(true) .Build(); @@ -82,10 +88,13 @@ public void should_log_if_ignoring_ssl_errors() [Fact] public void should_call_delegating_handlers_in_order() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); @@ -110,10 +119,13 @@ public void should_call_delegating_handlers_in_order() [Fact] public void should_re_use_cookies_from_container() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); @@ -142,10 +154,13 @@ public void should_add_verb_to_cache_key(string verb) HttpMethod method = new HttpMethod(verb); + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); diff --git a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs index d2210cf0b..3530c2b51 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs @@ -47,10 +47,13 @@ public HttpClientHttpRequesterTest() [Fact] public void should_call_request_correctly() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); @@ -70,10 +73,13 @@ public void should_call_request_correctly() [Fact] public void should_call_request_unable_to_complete_request() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); @@ -92,10 +98,13 @@ public void should_call_request_unable_to_complete_request() [Fact] public void http_client_request_times_out() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() - .WithIsQos(false) + .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) - .WithReRouteKey("") + .WithLoadBalancerKey("") .WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build()) .Build(); diff --git a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs index bc10e89e0..f42926566 100644 --- a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs @@ -31,9 +31,12 @@ public QoSProviderFactoryTests() [Fact] public void should_return_no_qos_provider() { + var qosOptions = new QoSOptionsBuilder() + .Build(); + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) .WithUpstreamHttpMethod(new List { "get" }) - .WithIsQos(false) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -53,7 +56,6 @@ public void should_return_polly_qos_provider() var reRoute = new DownstreamReRouteBuilder() .WithUpstreamHttpMethod(new List { "get" }) - .WithIsQos(true) .WithQosOptions(qosOptions) .Build(); diff --git a/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs index 9c56a3230..a2db01982 100644 --- a/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs +++ b/test/Ocelot.UnitTests/Requester/QosProviderHouseTests.cs @@ -25,8 +25,14 @@ public QosProviderHouseTests() [Fact] public void should_store_qos_provider_on_first_request() - { - var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build(); + { + var qosOptions = new QoSOptionsBuilder() + .WithKey("test") + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .Then(x => x.ThenItIsAdded()) @@ -36,7 +42,13 @@ public void should_store_qos_provider_on_first_request() [Fact] public void should_not_store_qos_provider_on_first_request() { - var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build(); + var qosOptions = new QoSOptionsBuilder() + .WithKey("test") + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .When(x => x.WhenWeGetTheQoSProvider(reRoute)) @@ -47,8 +59,21 @@ public void should_not_store_qos_provider_on_first_request() [Fact] public void should_store_qos_providers_by_key() { - var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build(); - var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("testTwo").Build(); + var qosOptions = new QoSOptionsBuilder() + .WithKey("test") + .Build(); + + var qosOptionsTwo = new QoSOptionsBuilder() + .WithKey("testTwo") + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .Build(); + + var reRouteTwo = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptionsTwo) + .Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider())) @@ -62,7 +87,12 @@ public void should_store_qos_providers_by_key() [Fact] public void should_return_error_if_no_qos_provider_with_key() { - var reRoute = new DownstreamReRouteBuilder().Build(); + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(qosOptions) + .Build(); this.When(x => x.WhenWeGetTheQoSProvider(reRoute)) .Then(x => x.ThenAnErrorIsReturned()) @@ -72,9 +102,24 @@ public void should_return_error_if_no_qos_provider_with_key() [Fact] public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed() { - var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build(); - - var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("test").WithIsQos(true).Build(); + var useQoSOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithKey("test") + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var dontUseQoSOptions = new QoSOptionsBuilder() + .WithKey("test") + .Build(); + + var reRoute = new DownstreamReRouteBuilder() + .WithQosOptions(dontUseQoSOptions) + .Build(); + + var reRouteTwo = new DownstreamReRouteBuilder() + .WithQosOptions(useQoSOptions) + .Build(); this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider())) .When(x => x.WhenWeGetTheQoSProvider(reRoute))