Feature/automatic routes with sd (#351)

* #340 started looking at supporting automatic routing when using service discovery

* #340 getting old routing tests to pass

* #340 renamed stuff to provider rather than finder, as its not longer finding anything

* #340 working towards supporting dynamic routing

* #340 loads of refactoring to make configuration work with dynamic routing

* #340 refactor consul config code so the registry class owns it

* #340 default to consul to maintain backwards compat

* #340 added docs, finished this branches todos
This commit is contained in:
Tom Pallister 2018-05-14 21:26:10 +01:00 committed by GitHub
parent dadb43ef6f
commit 1e2e953b2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 2082 additions and 1221 deletions

View File

@ -178,3 +178,10 @@ 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!).
Dynamic Routing
^^^^^^^^^^^^^^^
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/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.

View File

@ -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 <https://github.com/TomPallister/Ocelot/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.

View File

@ -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 @@ namespace Ocelot.Configuration.Builder
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 @@ namespace Ocelot.Configuration.Builder
private List<AddHeader> _addHeadersToDownstream;
private List<AddHeader> _addHeadersToUpstream;
private bool _dangerousAcceptAnyServerCertificateValidator;
private string _qosKey;
public DownstreamReRouteBuilder()
{
@ -153,27 +151,15 @@ namespace Ocelot.Configuration.Builder
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)
public DownstreamReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
{
_reRouteKey = reRouteKey;
return this;
}
public DownstreamReRouteBuilder WithQosKey(string qosKey)
{
_qosKey = qosKey;
_loadBalancerKey = loadBalancerKey;
return this;
}
@ -267,7 +253,6 @@ namespace Ocelot.Configuration.Builder
_httpHandlerOptions,
_useServiceDiscovery,
_enableRateLimiting,
_useQos,
_qosOptions,
_downstreamScheme,
_requestIdHeaderKey,
@ -283,12 +268,11 @@ namespace Ocelot.Configuration.Builder
_isAuthorised,
_authenticationOptions,
new PathTemplate(_downstreamPathTemplate),
_reRouteKey,
_loadBalancerKey,
_delegatingHandlers,
_addHeadersToDownstream,
_addHeadersToUpstream,
_dangerousAcceptAnyServerCertificateValidator,
_qosKey);
_dangerousAcceptAnyServerCertificateValidator);
}
}
}

View File

@ -8,6 +8,8 @@
private int _timeoutValue;
private string _key;
public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
{
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
@ -26,9 +28,15 @@
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);
}
}
}

View File

@ -5,7 +5,6 @@ namespace Ocelot.Configuration.Builder
private bool _isAuthenticated;
private bool _isAuthorised;
private bool _isCached;
private bool _isQoS;
private bool _enableRateLimiting;
public ReRouteOptionsBuilder WithIsCached(bool isCached)
@ -26,12 +25,6 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteOptionsBuilder WithIsQos(bool isQoS)
{
_isQoS = isQoS;
return this;
}
public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting)
{
_enableRateLimiting = enableRateLimiting;
@ -40,7 +33,7 @@ namespace Ocelot.Configuration.Builder
public ReRouteOptions Build()
{
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _isQoS, _enableRateLimiting);
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting);
}
}
}

View File

@ -105,7 +105,21 @@ namespace Ocelot.Configuration.Creator
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<IInternalConfiguration>(config);
}
@ -160,8 +174,6 @@ namespace Ocelot.Configuration.Creator
var reRouteKey = CreateReRouteKey(fileReRoute);
var qosKey = CreateQosKey(fileReRoute);
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
@ -172,19 +184,19 @@ namespace Ocelot.Configuration.Creator
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 @@ namespace Ocelot.Configuration.Creator
.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 @@ namespace Ocelot.Configuration.Creator
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 @@ namespace Ocelot.Configuration.Creator
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)}";
}
}
}

View File

@ -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);
}
}
}

View File

@ -7,6 +7,6 @@ namespace Ocelot.Configuration.Creator
/// </summary>
public interface IHttpHandlerOptionsCreator
{
HttpHandlerOptions Create(FileReRoute fileReRoute);
HttpHandlerOptions Create(FileHttpHandlerOptions fileReRoute);
}
}

View File

@ -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);
}
}

View File

@ -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)}";
}
}
}

View File

@ -10,14 +10,12 @@ namespace Ocelot.Configuration.Creator
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 @@ namespace Ocelot.Configuration.Creator
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);

View File

@ -7,12 +7,12 @@ namespace Ocelot.Configuration.Creator
{
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)

View File

@ -16,8 +16,7 @@ namespace Ocelot.Configuration
HttpHandlerOptions httpHandlerOptions,
bool useServiceDiscovery,
bool enableEndpointEndpointRateLimiting,
bool isQos,
QoSOptions qosOptionsOptions,
QoSOptions qosOptions,
string downstreamScheme,
string requestIdKey,
bool isCached,
@ -36,8 +35,7 @@ namespace Ocelot.Configuration
List<string> delegatingHandlers,
List<AddHeader> addHeadersToDownstream,
List<AddHeader> addHeadersToUpstream,
bool dangerousAcceptAnyServerCertificateValidator,
string qosKey)
bool dangerousAcceptAnyServerCertificateValidator)
{
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
AddHeadersToDownstream = addHeadersToDownstream;
@ -51,8 +49,7 @@ namespace Ocelot.Configuration
HttpHandlerOptions = httpHandlerOptions;
UseServiceDiscovery = useServiceDiscovery;
EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting;
IsQos = isQos;
QosOptionsOptions = qosOptionsOptions;
QosOptions = qosOptions;
DownstreamScheme = downstreamScheme;
RequestIdKey = requestIdKey;
IsCached = isCached;
@ -69,10 +66,8 @@ namespace Ocelot.Configuration
DownstreamPathTemplate = downstreamPathTemplate;
LoadBalancerKey = loadBalancerKey;
AddHeadersToUpstream = addHeadersToUpstream;
QosKey = qosKey;
}
public string QosKey { get; }
public string Key { get; }
public PathTemplate UpstreamPathTemplate { get; }
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace { get; }
@ -82,8 +77,7 @@ namespace Ocelot.Configuration
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; }

View File

@ -6,6 +6,9 @@
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
RateLimitOptions = new FileRateLimitOptions();
LoadBalancerOptions = new FileLoadBalancerOptions();
QoSOptions = new FileQoSOptions();
HttpHandlerOptions = new FileHttpHandlerOptions();
}
public string RequestIdKey { get; set; }
@ -14,6 +17,14 @@
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; }
}
}

View File

@ -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);
}
}
}

View File

@ -5,8 +5,19 @@ namespace Ocelot.Configuration
public interface IInternalConfiguration
{
List<ReRoute> ReRoutes { get; }
string AdministrationPath {get;}
ServiceProviderConfiguration ServiceProviderConfiguration {get;}
string RequestId {get;}
LoadBalancerOptions LoadBalancerOptions { get; }
string DownstreamScheme { get; }
QoSOptions QoSOptions { get; }
HttpHandlerOptions HttpHandlerOptions { get; }
}
}

View File

@ -4,17 +4,33 @@ namespace Ocelot.Configuration
{
public class InternalConfiguration : IInternalConfiguration
{
public InternalConfiguration(List<ReRoute> reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
public InternalConfiguration(
List<ReRoute> 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<ReRoute> 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; }
}
}

View File

@ -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);
}
}
}

View File

@ -8,12 +8,14 @@ namespace Ocelot.Configuration
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 @@ namespace Ocelot.Configuration
public int TimeoutValue { get; }
public TimeoutStrategy TimeoutStrategy { get; }
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 && TimeoutValue > 0;
public string Key { get; }
}
}

View File

@ -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; }
}
}

View File

@ -30,20 +30,18 @@ namespace Ocelot.Configuration.Repository
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);
}

View File

@ -103,7 +103,9 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>();
_services.AddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
_services.AddSingleton<IDownstreamRouteProvider, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>();
_services.TryAddSingleton<IDownstreamRouteProviderFactory, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteProviderFactory>();
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();

View File

@ -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<DownstreamRoute> 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<string>(){ upstreamHttpMethod })
.Build();
return new OkResponse<DownstreamRoute>(new DownstreamRoute(new List<PlaceholderNameAndValue>(), 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;
}
}
}

View File

@ -7,7 +7,7 @@ using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public class DownstreamRouteFinder : IDownstreamRouteFinder
public class DownstreamRouteFinder : IDownstreamRouteProvider
{
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
@ -18,7 +18,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
public Response<DownstreamRoute> Get(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
{
var downstreamRoutes = new List<DownstreamRoute>();

View File

@ -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<string, IDownstreamRouteProvider> _providers;
public DownstreamRouteProviderFactory(IServiceProvider provider)
{
_providers = provider.GetServices<IDownstreamRouteProvider>().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;
}
}
}

View File

@ -1,11 +0,0 @@
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public interface IDownstreamRouteFinder
{
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost);
}
}

View File

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public interface IDownstreamRouteProvider
{
Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost);
}
}

View File

@ -0,0 +1,9 @@
namespace Ocelot.DownstreamRouteFinder.Finder
{
using Configuration;
public interface IDownstreamRouteProviderFactory
{
IDownstreamRouteProvider Get(IInternalConfiguration config);
}
}

View File

@ -12,13 +12,13 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IDownstreamRouteProviderFactory _factory;
private readonly IInternalConfigurationRepository _repo;
private readonly IMultiplexer _multiplexer;
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamRouteFinder downstreamRouteFinder,
IDownstreamRouteProviderFactory downstreamRouteFinder,
IInternalConfigurationRepository repo,
IMultiplexer multiplexer)
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
@ -26,7 +26,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
_repo = repo;
_multiplexer = multiplexer;
_next = next;
_downstreamRouteFinder = downstreamRouteFinder;
_factory = downstreamRouteFinder;
}
public async Task Invoke(DownstreamContext context)
@ -35,20 +35,11 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
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);
var provider = _factory.Get(context.Configuration);
var downstreamRoute = provider.Get(upstreamUrlPath, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
if (downstreamRoute.IsError)
{

View File

@ -73,7 +73,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
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)

View File

@ -9,6 +9,8 @@ using Ocelot.Middleware;
namespace Ocelot.Errors.Middleware
{
using Configuration;
/// <summary>
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500
/// </summary>
@ -33,7 +35,19 @@ namespace Ocelot.Errors.Middleware
{
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 @@ namespace Ocelot.Errors.Middleware
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))
{

View File

@ -18,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public async Task<Response<ServiceHostAndPort>> 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<ServiceHostAndPort>(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));

View File

@ -22,7 +22,7 @@ namespace Ocelot.LoadBalancer.Middleware
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");

View File

@ -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 @@ namespace Ocelot.Middleware
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
public HttpContext HttpContext { get; }
public DownstreamReRoute DownstreamReRoute { get; set; }
@ -32,6 +27,8 @@ namespace Ocelot.Middleware
public List<Error> Errors { get; }
public IInternalConfiguration Configuration { get; set; }
public bool IsError => Errors.Count > 0;
}
}

View File

@ -23,7 +23,7 @@ namespace Ocelot.Middleware.Multiplexer
var downstreamContext = new DownstreamContext(context.HttpContext)
{
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
ServiceProviderConfiguration = context.ServiceProviderConfiguration,
Configuration = context.Configuration,
DownstreamReRoute = reRoute.DownstreamReRoute[i],
};

View File

@ -68,7 +68,7 @@ namespace Ocelot.Requester
handlers.Add(() => (DelegatingHandler)_factory.Get());
}
if (request.IsQos)
if (request.QosOptions.UseQos)
{
var qosProvider = _qosProviderHouse.Get(request);

View File

@ -58,9 +58,9 @@ namespace Ocelot.Requester
.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))
{

View File

@ -19,15 +19,15 @@ namespace Ocelot.Requester.QoS
{
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
_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<HttpRequestException>()
.Or<TimeoutRejectedException>()
.Or<TimeoutException>()
.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(

View File

@ -14,7 +14,7 @@ namespace Ocelot.Requester.QoS
public IQoSProvider Get(DownstreamReRoute reRoute)
{
if (reRoute.IsQos)
if (reRoute.QosOptions.UseQos)
{
return new PollyQoSProvider(reRoute, _loggerFactory);
}

View File

@ -21,26 +21,26 @@ namespace Ocelot.Requester.QoS
{
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<IQoSProvider>(_qoSProviders[reRoute.QosKey]);
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.QosOptions.Key]);
}
qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.QosKey, qosProvider);
Add(reRoute.QosOptions.Key, qosProvider);
return new OkResponse<IQoSProvider>(qosProvider);
}
catch (Exception ex)
{
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
{
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}")
});
}
}

View File

@ -4,8 +4,8 @@ namespace Ocelot.ServiceDiscovery.Configuration
{
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;
}

View File

@ -22,12 +22,7 @@ namespace Ocelot.ServiceDiscovery.Providers
{;
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
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);
}

View File

@ -208,6 +208,121 @@ namespace Ocelot.AcceptanceTests
.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()
{

View File

@ -55,7 +55,7 @@ namespace Ocelot.UnitTests.Configuration
_internalRepo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().Build(), "")));
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().Build(), "", It.IsAny<LoadBalancerOptions>(), It.IsAny<string>(), It.IsAny<QoSOptions>(), It.IsAny<HttpHandlerOptions>())));
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
}
@ -140,7 +140,10 @@ namespace Ocelot.UnitTests.Configuration
{
_internalRepo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), "")));
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "",
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);
}

View File

@ -38,7 +38,7 @@ namespace Ocelot.UnitTests.Configuration
{
var fileConfig = new FileConfiguration();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
var config = new InternalConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig, "asdf");
var config = new InternalConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build());
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
.And(x => GivenTheRepoReturns(new OkResponse()))

View File

@ -86,7 +86,7 @@
.WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithReRouteKey("CookieStickySessions:sessionid")
.WithLoadBalancerKey("CookieStickySessions:sessionid")
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -199,7 +199,7 @@
.WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() {"Get"})
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)})
.WithReRouteKey("/laura|Get")
.WithLoadBalancerKey("/laura|Get")
.Build();
var lauraReRoute = new ReRouteBuilder()
@ -218,7 +218,7 @@
.WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() { "Get" })
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) })
.WithReRouteKey("/tom|Get")
.WithLoadBalancerKey("/tom|Get")
.Build();
var tomReRoute = new ReRouteBuilder()
@ -361,7 +361,6 @@
.Build();
var serviceOptions = new ReRouteOptionsBuilder()
.WithIsQos(true)
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -410,7 +409,7 @@
.WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"})
.WithReRouteKey("/api/products/{productId}|Get")
.WithLoadBalancerKey("/api/products/{productId}|Get")
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -462,7 +461,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"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 @@
.WithUpstreamHttpMethod(new List<string> {"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 @@
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"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 @@
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"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 @@
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"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 @@
{
new ClaimToThing("CustomerId", "CustomerId", "", 0),
})
.WithReRouteKey("/api/products/{productId}|Get")
.WithLoadBalancerKey("/api/products/{productId}|Get")
.Build();
var expected = new List<ReRoute>
@ -784,7 +783,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"})
.WithAuthenticationOptions(authenticationOptions)
.WithReRouteKey("/api/products/{productId}|Get")
.WithLoadBalancerKey("/api/products/{productId}|Get")
.Build();
var expected = new List<ReRoute>
@ -942,16 +941,15 @@
private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions)
{
_qosOptionsCreator
.Setup(x => x.Create(_fileConfiguration.ReRoutes[0]))
.Setup(x => x.Create(_fileConfiguration.ReRoutes[0].QoSOptions, It.IsAny<string>(), It.IsAny<string[]>()))
.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 GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions)
{
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileReRoute>()))
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileHttpHandlerOptions>()))
.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()

View File

@ -103,7 +103,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenICreateHttpHandlerOptions()
{
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute);
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute.HttpHandlerOptions);
}
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected)

View File

@ -105,6 +105,10 @@ namespace Ocelot.UnitTests.Configuration
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; }
}
}
}

View File

@ -50,7 +50,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenICreate()
{
_result = _creator.Create(_fileReRoute);
_result = _creator.Create(_fileReRoute.QoSOptions);
}
private void ThenTheFollowingIsReturned(QoSOptions expected)

View File

@ -29,11 +29,6 @@ namespace Ocelot.UnitTests.Configuration
{
EnableRateLimiting = true
},
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 1
},
AuthenticationOptions = new FileAuthenticationOptions()
{
AuthenticationProviderKey = "Test"
@ -52,7 +47,6 @@ namespace Ocelot.UnitTests.Configuration
.WithIsAuthenticated(true)
.WithIsAuthorised(true)
.WithIsCached(true)
.WithIsQos(true)
.WithRateLimiting(true)
.Build();
@ -76,7 +70,6 @@ namespace Ocelot.UnitTests.Configuration
{
_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);
}

View File

@ -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<DownstreamRoute> _result;
private string _upstreamHost;
private string _upstreamUrlPath;
private string _upstreamHttpMethod;
private IInternalConfiguration _configuration;
private Mock<IQoSOptionsCreator> _qosOptionsCreator;
public DownstreamRouteCreatorTests()
{
_qosOptionsCreator = new Mock<IQoSOptionsCreator>();
_qoSOptions = new QoSOptionsBuilder().Build();
_handlerOptions = new HttpHandlerOptionsBuilder().Build();
_loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build();
_qosOptionsCreator
.Setup(x => x.Create(It.IsAny<QoSOptions>(), It.IsAny<string>(), It.IsAny<string[]>()))
.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<QoSOptions>(), It.IsAny<string>(), It.IsAny<string[]>()))
.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<string[]>()), 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);
}
}
}

View File

@ -21,7 +21,8 @@
public class DownstreamRouteFinderMiddlewareTests
{
private readonly Mock<IDownstreamRouteFinder> _finder;
private readonly Mock<IDownstreamRouteProvider> _finder;
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
private readonly Mock<IInternalConfigurationRepository> _repo;
private Response<DownstreamRoute> _downstreamRoute;
private IInternalConfiguration _config;
@ -35,20 +36,22 @@
public DownstreamRouteFinderMiddlewareTests()
{
_repo = new Mock<IInternalConfigurationRepository>();
_finder = new Mock<IDownstreamRouteFinder>();
_finder = new Mock<IDownstreamRouteProvider>();
_factory = new Mock<IDownstreamRouteProviderFactory>();
_factory.Setup(x => x.Get(It.IsAny<IInternalConfiguration>())).Returns(_finder.Object);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_multiplexer = new Mock<IMultiplexer>();
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _repo.Object, _multiplexer.Object);
_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(), "");
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")
@ -76,23 +79,21 @@
private void GivenTheFollowingConfig(IInternalConfiguration config)
{
_config = config;
_repo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(_config));
_downstreamContext.Configuration = config;
}
private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_finder
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{
_downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues);
_downstreamContext.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration);
_downstreamContext.Configuration.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration);
}
}
}

View File

@ -15,7 +15,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
{
public class DownstreamRouteFinderTests
{
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IDownstreamRouteProvider _downstreamRouteFinder;
private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher;
private readonly Mock<IPlaceholderNameAndValueFinder> _finder;
private string _upstreamUrlPath;
@ -709,7 +709,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig)
{
_reRoutesConfig = reRoutesConfig;
_config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "");
_config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build());
}
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
@ -719,7 +719,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void WhenICallTheFinder()
{
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost);
_result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost);
}
private void ThenTheFollowingIsReturned(DownstreamRoute expected)

View File

@ -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<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
services.AddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
services.AddSingleton<IDownstreamRouteProvider, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
services.AddSingleton<IDownstreamRouteProvider, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>();
var provider = services.BuildServiceProvider();
_factory = new DownstreamRouteProviderFactory(provider);
}
[Fact]
public void should_return_downstream_route_finder()
{
var reRoutes = new List<ReRoute>
{
new ReRouteBuilder().Build()
};
this.Given(_ => GivenTheReRoutes(reRoutes))
.When(_ => WhenIGet())
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>())
.BDDfy();
}
[Fact]
public void should_return_downstream_route_finder_as_no_service_discovery()
{
var spConfig = new ServiceProviderConfigurationBuilder().Build();
var reRoutes = new List<ReRoute>
{
};
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
.When(_ => WhenIGet())
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>())
.BDDfy();
}
[Fact]
public void should_return_downstream_route_creator()
{
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).Build();
var reRoutes = new List<ReRoute>
{
};
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
.When(_ => WhenIGet())
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>())
.BDDfy();
}
private void ThenTheResultShouldBe<T>()
{
_result.ShouldBeOfType<T>();
}
private void WhenIGet()
{
_result = _factory.Get(_config);
}
private void GivenTheReRoutes(List<ReRoute> reRoutes)
{
_config = new InternalConfiguration(reRoutes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build());
}
private void GivenTheReRoutes(List<ReRoute> reRoutes, ServiceProviderConfiguration config)
{
_config = new InternalConfiguration(reRoutes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build());
}
}
}

View File

@ -193,7 +193,8 @@
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()

View File

@ -51,7 +51,7 @@ namespace Ocelot.UnitTests.Errors
[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 @@ namespace Ocelot.UnitTests.Errors
[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 @@ namespace Ocelot.UnitTests.Errors
[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 @@ namespace Ocelot.UnitTests.Errors
[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))

View File

@ -33,7 +33,7 @@ namespace Ocelot.UnitTests.LoadBalancer
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 @@ namespace Ocelot.UnitTests.LoadBalancer
{
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 @@ namespace Ocelot.UnitTests.LoadBalancer
{
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 @@ namespace Ocelot.UnitTests.LoadBalancer
{
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()))

View File

@ -117,7 +117,8 @@ namespace Ocelot.UnitTests.LoadBalancer
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)

View File

@ -38,14 +38,21 @@ namespace Ocelot.UnitTests.Requester
[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<string>
{
"FakeDelegatingHandler",
"FakeDelegatingHandlerTwo"
})
.WithReRouteKey("")
.WithLoadBalancerKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
@ -67,7 +74,14 @@ namespace Ocelot.UnitTests.Requester
[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<string>
{
@ -75,7 +89,7 @@ namespace Ocelot.UnitTests.Requester
"FakeDelegatingHandler",
"FakeDelegatingHandlerFour"
})
.WithReRouteKey("")
.WithLoadBalancerKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
@ -97,14 +111,21 @@ namespace Ocelot.UnitTests.Requester
[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<string>
{
"FakeDelegatingHandlerTwo",
"FakeDelegatingHandler"
})
.WithReRouteKey("")
.WithLoadBalancerKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
@ -126,13 +147,20 @@ namespace Ocelot.UnitTests.Requester
[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<string>
{
"FakeDelegatingHandler",
})
.WithReRouteKey("")
.WithLoadBalancerKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
@ -153,9 +181,16 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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<string>
{
"FakeDelegatingHandler",
"FakeDelegatingHandlerTwo"
})
.WithReRouteKey("")
.WithLoadBalancerKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
@ -197,8 +236,15 @@ namespace Ocelot.UnitTests.Requester
[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<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
@ -213,8 +259,12 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
@ -241,8 +298,15 @@ namespace Ocelot.UnitTests.Requester
[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<IQoSProvider>(It.IsAny<Error>())))

View File

@ -46,10 +46,13 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
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();

View File

@ -47,10 +47,13 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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();

View File

@ -31,9 +31,12 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_return_no_qos_provider()
{
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.WithUpstreamHttpMethod(new List<string> { "get" })
.WithIsQos(false)
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
@ -53,7 +56,6 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder()
.WithUpstreamHttpMethod(new List<string> { "get" })
.WithIsQos(true)
.WithQosOptions(qosOptions)
.Build();

View File

@ -26,7 +26,13 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[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 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed()
{
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build();
var useQoSOptions = new QoSOptionsBuilder()
.WithTimeoutValue(1)
.WithKey("test")
.WithDurationOfBreak(1)
.WithExceptionsAllowedBeforeBreaking(1)
.Build();
var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("test").WithIsQos(true).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))