Merge pull request #46 from ThreeMammals/develop

merge newest code
This commit is contained in:
geffzhang 2018-05-19 09:17:40 +08:00 committed by GitHub
commit 6f2eaa412f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 2335 additions and 1330 deletions

View File

@ -51,7 +51,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
* Headers / Query String / Claims Transformation * Headers / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers * Custom Middleware / Delegating Handlers
* Configuration / Administration REST API * Configuration / Administration REST API
* Platform / Cloud agnostic * Platform / Cloud Agnostic
## How to install ## How to install

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 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!). 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 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 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. 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 public class DownstreamReRouteBuilder
{ {
private AuthenticationOptions _authenticationOptions; private AuthenticationOptions _authenticationOptions;
private string _reRouteKey; private string _loadBalancerKey;
private string _downstreamPathTemplate; private string _downstreamPathTemplate;
private string _upstreamTemplate; private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern; private UpstreamPathTemplate _upstreamTemplatePattern;
@ -25,7 +25,6 @@ namespace Ocelot.Configuration.Builder
private CacheOptions _fileCacheOptions; private CacheOptions _fileCacheOptions;
private string _downstreamScheme; private string _downstreamScheme;
private LoadBalancerOptions _loadBalancerOptions; private LoadBalancerOptions _loadBalancerOptions;
private bool _useQos;
private QoSOptions _qosOptions; private QoSOptions _qosOptions;
private HttpHandlerOptions _httpHandlerOptions; private HttpHandlerOptions _httpHandlerOptions;
private bool _enableRateLimiting; private bool _enableRateLimiting;
@ -41,7 +40,6 @@ namespace Ocelot.Configuration.Builder
private List<AddHeader> _addHeadersToDownstream; private List<AddHeader> _addHeadersToDownstream;
private List<AddHeader> _addHeadersToUpstream; private List<AddHeader> _addHeadersToUpstream;
private bool _dangerousAcceptAnyServerCertificateValidator; private bool _dangerousAcceptAnyServerCertificateValidator;
private string _qosKey;
public DownstreamReRouteBuilder() public DownstreamReRouteBuilder()
{ {
@ -153,27 +151,15 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public DownstreamReRouteBuilder WithIsQos(bool input)
{
_useQos = input;
return this;
}
public DownstreamReRouteBuilder WithQosOptions(QoSOptions input) public DownstreamReRouteBuilder WithQosOptions(QoSOptions input)
{ {
_qosOptions = input; _qosOptions = input;
return this; return this;
} }
public DownstreamReRouteBuilder WithReRouteKey(string reRouteKey) public DownstreamReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
{ {
_reRouteKey = reRouteKey; _loadBalancerKey = loadBalancerKey;
return this;
}
public DownstreamReRouteBuilder WithQosKey(string qosKey)
{
_qosKey = qosKey;
return this; return this;
} }
@ -267,7 +253,6 @@ namespace Ocelot.Configuration.Builder
_httpHandlerOptions, _httpHandlerOptions,
_useServiceDiscovery, _useServiceDiscovery,
_enableRateLimiting, _enableRateLimiting,
_useQos,
_qosOptions, _qosOptions,
_downstreamScheme, _downstreamScheme,
_requestIdHeaderKey, _requestIdHeaderKey,
@ -283,12 +268,11 @@ namespace Ocelot.Configuration.Builder
_isAuthorised, _isAuthorised,
_authenticationOptions, _authenticationOptions,
new PathTemplate(_downstreamPathTemplate), new PathTemplate(_downstreamPathTemplate),
_reRouteKey, _loadBalancerKey,
_delegatingHandlers, _delegatingHandlers,
_addHeadersToDownstream, _addHeadersToDownstream,
_addHeadersToUpstream, _addHeadersToUpstream,
_dangerousAcceptAnyServerCertificateValidator, _dangerousAcceptAnyServerCertificateValidator);
_qosKey);
} }
} }
} }

View File

@ -8,6 +8,8 @@
private int _timeoutValue; private int _timeoutValue;
private string _key;
public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking) public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
{ {
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
@ -26,9 +28,15 @@
return this; return this;
} }
public QoSOptionsBuilder WithKey(string input)
{
_key = input;
return this;
}
public QoSOptions Build() 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 _isAuthenticated;
private bool _isAuthorised; private bool _isAuthorised;
private bool _isCached; private bool _isCached;
private bool _isQoS;
private bool _enableRateLimiting; private bool _enableRateLimiting;
public ReRouteOptionsBuilder WithIsCached(bool isCached) public ReRouteOptionsBuilder WithIsCached(bool isCached)
@ -26,12 +25,6 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteOptionsBuilder WithIsQos(bool isQoS)
{
_isQoS = isQoS;
return this;
}
public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting)
{ {
_enableRateLimiting = enableRateLimiting; _enableRateLimiting = enableRateLimiting;
@ -40,7 +33,7 @@ namespace Ocelot.Configuration.Builder
public ReRouteOptions Build() 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 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); return new OkResponse<IInternalConfiguration>(config);
} }
@ -160,8 +174,6 @@ namespace Ocelot.Configuration.Creator
var reRouteKey = CreateReRouteKey(fileReRoute); var reRouteKey = CreateReRouteKey(fileReRoute);
var qosKey = CreateQosKey(fileReRoute);
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
@ -172,19 +184,19 @@ namespace Ocelot.Configuration.Creator
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); 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 rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
var region = _regionCreator.Create(fileReRoute); var region = _regionCreator.Create(fileReRoute);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute); var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions);
var hAndRs = _headerFAndRCreator.Create(fileReRoute); var hAndRs = _headerFAndRCreator.Create(fileReRoute);
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
var lbOptions = CreateLoadBalancerOptions(fileReRoute); var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions);
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithKey(fileReRoute.Key) .WithKey(fileReRoute.Key)
@ -205,9 +217,7 @@ namespace Ocelot.Configuration.Creator
.WithDownstreamScheme(fileReRoute.DownstreamScheme) .WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancerOptions(lbOptions) .WithLoadBalancerOptions(lbOptions)
.WithDownstreamAddresses(downstreamAddresses) .WithDownstreamAddresses(downstreamAddresses)
.WithReRouteKey(reRouteKey) .WithLoadBalancerKey(reRouteKey)
.WithQosKey(qosKey)
.WithIsQos(fileReRouteOptions.IsQos)
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption) .WithRateLimitOptions(rateLimitOption)
@ -226,9 +236,13 @@ namespace Ocelot.Configuration.Creator
return reRoute; 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) private string CreateReRouteKey(FileReRoute fileReRoute)
@ -238,14 +252,7 @@ namespace Ocelot.Configuration.Creator
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
} }
return CreateQosKey(fileReRoute); return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
}
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;
} }
} }
} }

View File

@ -6,19 +6,19 @@ namespace Ocelot.Configuration.Creator
{ {
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{ {
private IServiceTracer _tracer; private readonly IServiceTracer _tracer;
public HttpHandlerOptionsCreator(IServiceTracer tracer) public HttpHandlerOptionsCreator(IServiceTracer tracer)
{ {
_tracer = 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, return new HttpHandlerOptions(options.AllowAutoRedirect,
fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing); options.UseCookieContainer, useTracing);
} }
} }
} }

View File

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

View File

@ -4,6 +4,8 @@ namespace Ocelot.Configuration.Creator
{ {
public interface IQoSOptionsCreator 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 namespace Ocelot.Configuration.Creator
{ {
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using System.Linq;
public class QoSOptionsCreator : IQoSOptionsCreator public class QoSOptionsCreator : IQoSOptionsCreator
{ {
public QoSOptions Create(FileReRoute fileReRoute) public QoSOptions Create(FileQoSOptions options)
{ {
return new QoSOptionsBuilder() return new QoSOptionsBuilder()
.WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking) .WithExceptionsAllowedBeforeBreaking(options.ExceptionsAllowedBeforeBreaking)
.WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak) .WithDurationOfBreak(options.DurationOfBreak)
.WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue) .WithTimeoutValue(options.TimeoutValue)
.Build(); .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 isAuthenticated = IsAuthenticated(fileReRoute);
var isAuthorised = IsAuthorised(fileReRoute); var isAuthorised = IsAuthorised(fileReRoute);
var isCached = IsCached(fileReRoute); var isCached = IsCached(fileReRoute);
var isQos = IsQoS(fileReRoute);
var enableRateLimiting = IsEnableRateLimiting(fileReRoute); var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
var options = new ReRouteOptionsBuilder() var options = new ReRouteOptionsBuilder()
.WithIsAuthenticated(isAuthenticated) .WithIsAuthenticated(isAuthenticated)
.WithIsAuthorised(isAuthorised) .WithIsAuthorised(isAuthorised)
.WithIsCached(isCached) .WithIsCached(isCached)
.WithIsQos(isQos)
.WithRateLimiting(enableRateLimiting) .WithRateLimiting(enableRateLimiting)
.Build(); .Build();
@ -29,11 +27,6 @@ namespace Ocelot.Configuration.Creator
return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false; 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) private bool IsAuthenticated(FileReRoute fileReRoute)
{ {
return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey); return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey);

View File

@ -7,12 +7,12 @@ namespace Ocelot.Configuration.Creator
{ {
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration) public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
{ {
//todo log or return error here dont just default to something that wont work.. var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0; var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul";
return new ServiceProviderConfigurationBuilder() return new ServiceProviderConfigurationBuilder()
.WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host) .WithHost(host)
.WithPort(serviceProviderPort) .WithPort(port)
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type) .WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token) .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey) .WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)

View File

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

View File

@ -6,6 +6,9 @@
{ {
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider(); ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
RateLimitOptions = new FileRateLimitOptions(); RateLimitOptions = new FileRateLimitOptions();
LoadBalancerOptions = new FileLoadBalancerOptions();
QoSOptions = new FileQoSOptions();
HttpHandlerOptions = new FileHttpHandlerOptions();
} }
public string RequestIdKey { get; set; } public string RequestIdKey { get; set; }
@ -14,6 +17,14 @@
public FileRateLimitOptions RateLimitOptions { get; set; } public FileRateLimitOptions RateLimitOptions { get; set; }
public FileQoSOptions QoSOptions { get; set; }
public string BaseUrl { 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 public interface IInternalConfiguration
{ {
List<ReRoute> ReRoutes { get; } List<ReRoute> ReRoutes { get; }
string AdministrationPath {get;} string AdministrationPath {get;}
ServiceProviderConfiguration ServiceProviderConfiguration {get;} ServiceProviderConfiguration ServiceProviderConfiguration {get;}
string RequestId {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 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; ReRoutes = reRoutes;
AdministrationPath = administrationPath; AdministrationPath = administrationPath;
ServiceProviderConfiguration = serviceProviderConfiguration; ServiceProviderConfiguration = serviceProviderConfiguration;
RequestId = requestId; RequestId = requestId;
LoadBalancerOptions = loadBalancerOptions;
DownstreamScheme = downstreamScheme;
QoSOptions = qoSOptions;
HttpHandlerOptions = httpHandlerOptions;
} }
public List<ReRoute> ReRoutes { get; } public List<ReRoute> ReRoutes { get; }
public string AdministrationPath {get;} public string AdministrationPath {get;}
public ServiceProviderConfiguration ServiceProviderConfiguration {get;} public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
public string RequestId {get;} public string RequestId {get;}
public LoadBalancerOptions LoadBalancerOptions { get; }
public string DownstreamScheme { get; }
public QoSOptions QoSOptions { get; }
public HttpHandlerOptions HttpHandlerOptions { get; }
} }
} }

View File

@ -1,10 +1,12 @@
using Ocelot.LoadBalancer.LoadBalancers;
namespace Ocelot.Configuration namespace Ocelot.Configuration
{ {
public class LoadBalancerOptions public class LoadBalancerOptions
{ {
public LoadBalancerOptions(string type, string key, int expiryInMs) public LoadBalancerOptions(string type, string key, int expiryInMs)
{ {
Type = type; Type = type ?? nameof(NoLoadBalancer);
Key = key; Key = key;
ExpiryInMs = expiryInMs; ExpiryInMs = expiryInMs;
} }

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 exceptionsAllowedBeforeBreaking,
int durationofBreak, int durationofBreak,
int timeoutValue, int timeoutValue,
string key,
TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic) TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic)
{ {
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
DurationOfBreak = durationofBreak; DurationOfBreak = durationofBreak;
TimeoutValue = timeoutValue; TimeoutValue = timeoutValue;
TimeoutStrategy = timeoutStrategy; TimeoutStrategy = timeoutStrategy;
Key = key;
} }
public int ExceptionsAllowedBeforeBreaking { get; } public int ExceptionsAllowedBeforeBreaking { get; }
@ -23,5 +25,8 @@ namespace Ocelot.Configuration
public int TimeoutValue { get; } public int TimeoutValue { get; }
public TimeoutStrategy TimeoutStrategy { 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 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; IsAuthenticated = isAuthenticated;
IsAuthorised = isAuthorised; IsAuthorised = isAuthorised;
IsCached = isCached; IsCached = isCached;
IsQos = isQos;
EnableRateLimiting = isEnableRateLimiting; EnableRateLimiting = isEnableRateLimiting;
} }
public bool IsAuthenticated { get; private set; } public bool IsAuthenticated { get; private set; }
public bool IsAuthorised { get; private set; } public bool IsAuthorised { get; private set; }
public bool IsCached { get; private set; } public bool IsCached { get; private set; }
public bool IsQos { get; private set; }
public bool EnableRateLimiting { get; private set; } public bool EnableRateLimiting { get; private set; }
} }
} }

View File

@ -30,20 +30,18 @@ namespace Ocelot.Configuration.Repository
var internalConfig = repo.Get(); var internalConfig = repo.Get();
_configurationKey = "InternalConfiguration"; _configurationKey = "InternalConfiguration";
var consulHost = "localhost";
var consulPort = 8500;
string token = null; string token = null;
if (!internalConfig.IsError) if (!internalConfig.IsError)
{ {
consulHost = string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.Host) ? consulHost : internalConfig.Data.ServiceProviderConfiguration?.Host; token = internalConfig.Data.ServiceProviderConfiguration.Token;
consulPort = internalConfig.Data.ServiceProviderConfiguration?.Port ?? consulPort; _configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey) ?
token = internalConfig.Data.ServiceProviderConfiguration?.Token; internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey : _configurationKey;
_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); _consul = factory.Get(config);
} }

View File

@ -103,7 +103,9 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>(); _services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>(); _services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>(); _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<IHttpRequester, HttpClientHttpRequester>();
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>(); _services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>(); _services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();

View File

@ -0,0 +1,120 @@
namespace Ocelot.DownstreamRouteFinder.Finder
{
using System.Collections.Concurrent;
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;
private readonly ConcurrentDictionary<string, OkResponse<DownstreamRoute>> _cache;
public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator)
{
_qoSOptionsCreator = qoSOptionsCreator;
_cache = new ConcurrentDictionary<string, OkResponse<DownstreamRoute>>();
}
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);
if(_cache.TryGetValue(loadBalancerKey, out var downstreamRoute))
{
return downstreamRoute;
}
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();
downstreamRoute = new OkResponse<DownstreamRoute>(new DownstreamRoute(new List<PlaceholderNameAndValue>(), reRoute));
_cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute);
return downstreamRoute;
}
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)
{
if(upstreamUrlPath.IndexOf('/', 1) == -1)
{
return "/";
}
return upstreamUrlPath
.Substring(upstreamUrlPath.IndexOf('/', 1));
}
private static string GetServiceName(string upstreamUrlPath)
{
if(upstreamUrlPath.IndexOf('/', 1) == -1)
{
return upstreamUrlPath
.Substring(1);
}
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 namespace Ocelot.DownstreamRouteFinder.Finder
{ {
public class DownstreamRouteFinder : IDownstreamRouteFinder public class DownstreamRouteFinder : IDownstreamRouteProvider
{ {
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder; private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
@ -18,7 +18,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; _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>(); 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 public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly OcelotRequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteProviderFactory _factory;
private readonly IInternalConfigurationRepository _repo; private readonly IInternalConfigurationRepository _repo;
private readonly IMultiplexer _multiplexer; private readonly IMultiplexer _multiplexer;
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next, public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IDownstreamRouteFinder downstreamRouteFinder, IDownstreamRouteProviderFactory downstreamRouteFinder,
IInternalConfigurationRepository repo, IInternalConfigurationRepository repo,
IMultiplexer multiplexer) IMultiplexer multiplexer)
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>()) :base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
@ -26,7 +26,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
_repo = repo; _repo = repo;
_multiplexer = multiplexer; _multiplexer = multiplexer;
_next = next; _next = next;
_downstreamRouteFinder = downstreamRouteFinder; _factory = downstreamRouteFinder;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(DownstreamContext context)
@ -35,20 +35,11 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
var upstreamHost = context.HttpContext.Request.Headers["Host"]; 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}"); 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) if (downstreamRoute.IsError)
{ {

View File

@ -73,7 +73,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
private static bool ServiceFabricRequest(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) private static bool RequestForStatefullService(string query)

View File

@ -9,6 +9,8 @@ using Ocelot.Middleware;
namespace Ocelot.Errors.Middleware namespace Ocelot.Errors.Middleware
{ {
using Configuration;
/// <summary> /// <summary>
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500 /// Catches all unhandled exceptions thrown by middleware, logs and returns a 500
/// </summary> /// </summary>
@ -33,7 +35,19 @@ namespace Ocelot.Errors.Middleware
{ {
try 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"); Logger.LogDebug("ocelot pipeline started");
@ -53,19 +67,9 @@ namespace Ocelot.Errors.Middleware
Logger.LogDebug("ocelot pipeline finished"); 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... var key = configuration.RequestId;
//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;
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds)) if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
{ {

View File

@ -29,7 +29,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
var bus = new InMemoryBus<StickySession>(); var bus = new InMemoryBus<StickySession>();
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus); return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus);
default: default:
return new NoLoadBalancer(await serviceProvider.Get()); return new NoLoadBalancer(async () => await serviceProvider.Get());
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Middleware; using Ocelot.Middleware;
@ -9,22 +10,23 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{ {
public class NoLoadBalancer : ILoadBalancer public class NoLoadBalancer : ILoadBalancer
{ {
private readonly List<Service> _services; private readonly Func<Task<List<Service>>> _services;
public NoLoadBalancer(List<Service> services) public NoLoadBalancer(Func<Task<List<Service>>> services)
{ {
_services = services; _services = services;
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
{ {
//todo no point spinning a task up here, also first or default could be null.. var services = await _services();
if (_services == null || _services.Count == 0) //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")); return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));
} }
var service = await Task.FromResult(_services.FirstOrDefault()); var service = await Task.FromResult(services.FirstOrDefault());
return new OkResponse<ServiceHostAndPort>(service.HostAndPort); return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
} }

View File

@ -20,13 +20,13 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
{ {
var services = await _services.Invoke(); var services = await _services();
if (_last >= services.Count) if (_last >= services.Count)
{ {
_last = 0; _last = 0;
} }
var next = await Task.FromResult(services[_last]); var next = services[_last];
_last++; _last++;
return new OkResponse<ServiceHostAndPort>(next.HostAndPort); return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
} }

View File

@ -22,7 +22,7 @@ namespace Ocelot.LoadBalancer.Middleware
public async Task Invoke(DownstreamContext context) 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) if(loadBalancer.IsError)
{ {
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error"); 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.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Request.Middleware; using Ocelot.Request.Middleware;
namespace Ocelot.Middleware namespace Ocelot.Middleware
@ -20,8 +17,6 @@ namespace Ocelot.Middleware
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; } public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
public HttpContext HttpContext { get; } public HttpContext HttpContext { get; }
public DownstreamReRoute DownstreamReRoute { get; set; } public DownstreamReRoute DownstreamReRoute { get; set; }
@ -32,6 +27,8 @@ namespace Ocelot.Middleware
public List<Error> Errors { get; } public List<Error> Errors { get; }
public IInternalConfiguration Configuration { get; set; }
public bool IsError => Errors.Count > 0; public bool IsError => Errors.Count > 0;
} }
} }

View File

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

View File

@ -68,7 +68,7 @@ namespace Ocelot.Requester
handlers.Add(() => (DelegatingHandler)_factory.Get()); handlers.Add(() => (DelegatingHandler)_factory.Get());
} }
if (request.IsQos) if (request.QosOptions.UseQos)
{ {
var qosProvider = _qosProviderHouse.Get(request); 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}"); .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 ? _defaultTimeout
: TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptionsOptions.TimeoutValue); : TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptions.TimeoutValue);
_httpClient = new HttpClient(CreateHttpMessageHandler(httpclientHandler, context.DownstreamReRoute)) _httpClient = new HttpClient(CreateHttpMessageHandler(httpclientHandler, context.DownstreamReRoute))
{ {

View File

@ -19,15 +19,15 @@ namespace Ocelot.Requester.QoS
{ {
_logger = loggerFactory.CreateLogger<PollyQoSProvider>(); _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 _circuitBreakerPolicy = Policy
.Handle<HttpRequestException>() .Handle<HttpRequestException>()
.Or<TimeoutRejectedException>() .Or<TimeoutRejectedException>()
.Or<TimeoutException>() .Or<TimeoutException>()
.CircuitBreakerAsync( .CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: reRoute.QosOptionsOptions.ExceptionsAllowedBeforeBreaking, exceptionsAllowedBeforeBreaking: reRoute.QosOptions.ExceptionsAllowedBeforeBreaking,
durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptionsOptions.DurationOfBreak), durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptions.DurationOfBreak),
onBreak: (ex, breakDelay) => onBreak: (ex, breakDelay) =>
{ {
_logger.LogError( _logger.LogError(

View File

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

View File

@ -21,26 +21,26 @@ namespace Ocelot.Requester.QoS
{ {
try 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); 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); qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.QosKey, qosProvider); Add(reRoute.QosOptions.Key, qosProvider);
return new OkResponse<IQoSProvider>(qosProvider); return new OkResponse<IQoSProvider>(qosProvider);
} }
catch (Exception ex) catch (Exception ex)
{ {
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>() 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) public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token)
{ {
Host = host; Host = string.IsNullOrEmpty(host) ? "localhost" : host;
Port = port; Port = port > 0 ? port : 8500;
KeyOfServiceInConsul = keyOfServiceInConsul; KeyOfServiceInConsul = keyOfServiceInConsul;
Token = token; Token = token;
} }

View File

@ -22,12 +22,7 @@ namespace Ocelot.ServiceDiscovery.Providers
{; {;
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>(); _logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
var consulHost = string.IsNullOrEmpty(config?.Host) ? "localhost" : config.Host; _config = config;
var consulPort = config?.Port ?? 8500;
_config = new ConsulRegistryConfiguration(consulHost, consulPort, config?.KeyOfServiceInConsul, config?.Token);
_consul = clientFactory.Get(_config); _consul = clientFactory.Get(_config);
} }

View File

@ -208,6 +208,121 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .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] [Fact]
public void should_use_token_to_make_request_to_consul() public void should_use_token_to_make_request_to_consul()
{ {

View File

@ -55,7 +55,7 @@ namespace Ocelot.UnitTests.Configuration
_internalRepo _internalRepo
.Setup(x => x.Get()) .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); _repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
} }
@ -140,7 +140,10 @@ namespace Ocelot.UnitTests.Configuration
{ {
_internalRepo _internalRepo
.Setup(x => x.Get()) .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); _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 fileConfig = new FileConfiguration();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); 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)) this.Given(x => GivenTheFollowingConfiguration(fileConfig))
.And(x => GivenTheRepoReturns(new OkResponse())) .And(x => GivenTheRepoReturns(new OkResponse()))

View File

@ -86,7 +86,7 @@
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithReRouteKey("CookieStickySessions:sessionid") .WithLoadBalancerKey("CookieStickySessions:sessionid")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -199,7 +199,7 @@
.WithDownstreamScheme("http") .WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() {"Get"}) .WithUpstreamHttpMethod(new List<string>() {"Get"})
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)}) .WithDownstreamAddresses(new List<DownstreamHostAndPort>() {new DownstreamHostAndPort("localhost", 51878)})
.WithReRouteKey("/laura|Get") .WithLoadBalancerKey("/laura|Get")
.Build(); .Build();
var lauraReRoute = new ReRouteBuilder() var lauraReRoute = new ReRouteBuilder()
@ -218,7 +218,7 @@
.WithDownstreamScheme("http") .WithDownstreamScheme("http")
.WithUpstreamHttpMethod(new List<string>() { "Get" }) .WithUpstreamHttpMethod(new List<string>() { "Get" })
.WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) }) .WithDownstreamAddresses(new List<DownstreamHostAndPort>() { new DownstreamHostAndPort("localhost", 51878) })
.WithReRouteKey("/tom|Get") .WithLoadBalancerKey("/tom|Get")
.Build(); .Build();
var tomReRoute = new ReRouteBuilder() var tomReRoute = new ReRouteBuilder()
@ -361,7 +361,6 @@
.Build(); .Build();
var serviceOptions = new ReRouteOptionsBuilder() var serviceOptions = new ReRouteOptionsBuilder()
.WithIsQos(true)
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -410,7 +409,7 @@
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -462,7 +461,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithDelegatingHandlers(handlers) .WithDelegatingHandlers(handlers)
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -507,7 +506,7 @@
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUseServiceDiscovery(true) .WithUseServiceDiscovery(true)
.WithServiceName("ProductService") .WithServiceName("ProductService")
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -558,7 +557,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUseServiceDiscovery(false) .WithUseServiceDiscovery(false)
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -601,7 +600,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -646,7 +645,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithRequestIdKey("blahhhh") .WithRequestIdKey("blahhhh")
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -741,7 +740,7 @@
{ {
new ClaimToThing("CustomerId", "CustomerId", "", 0), new ClaimToThing("CustomerId", "CustomerId", "", 0),
}) })
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -784,7 +783,7 @@
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithAuthenticationOptions(authenticationOptions) .WithAuthenticationOptions(authenticationOptions)
.WithReRouteKey("/api/products/{productId}|Get") .WithLoadBalancerKey("/api/products/{productId}|Get")
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -942,16 +941,15 @@
private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions)
{ {
_qosOptionsCreator _qosOptionsCreator
.Setup(x => x.Create(_fileConfiguration.ReRoutes[0])) .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].QoSOptions, It.IsAny<string>(), It.IsAny<string[]>()))
.Returns(qosOptions); .Returns(qosOptions);
} }
private void ThenTheQosOptionsAre(QoSOptions qosOptions) private void ThenTheQosOptionsAre(QoSOptions qosOptions)
{ {
_config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); _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].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue);
_config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue);
} }
private void ThenTheServiceProviderCreatorIsCalledCorrectly() private void ThenTheServiceProviderCreatorIsCalledCorrectly()
@ -992,13 +990,13 @@
private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions) private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions)
{ {
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())) _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileHttpHandlerOptions>()))
.Returns(httpHandlerOptions); .Returns(httpHandlerOptions);
} }
private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly() 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() private void GivenTheDownstreamAddresses()

View File

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

View File

@ -105,6 +105,10 @@ namespace Ocelot.UnitTests.Configuration
public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException(); public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException();
public string RequestId {get;} 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() private void WhenICreate()
{ {
_result = _creator.Create(_fileReRoute); _result = _creator.Create(_fileReRoute.QoSOptions);
} }
private void ThenTheFollowingIsReturned(QoSOptions expected) private void ThenTheFollowingIsReturned(QoSOptions expected)

View File

@ -29,11 +29,6 @@ namespace Ocelot.UnitTests.Configuration
{ {
EnableRateLimiting = true EnableRateLimiting = true
}, },
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 1
},
AuthenticationOptions = new FileAuthenticationOptions() AuthenticationOptions = new FileAuthenticationOptions()
{ {
AuthenticationProviderKey = "Test" AuthenticationProviderKey = "Test"
@ -52,7 +47,6 @@ namespace Ocelot.UnitTests.Configuration
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.WithIsAuthorised(true) .WithIsAuthorised(true)
.WithIsCached(true) .WithIsCached(true)
.WithIsQos(true)
.WithRateLimiting(true) .WithRateLimiting(true)
.Build(); .Build();
@ -76,7 +70,6 @@ namespace Ocelot.UnitTests.Configuration
{ {
_result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated);
_result.IsAuthorised.ShouldBe(expected.IsAuthorised); _result.IsAuthorised.ShouldBe(expected.IsAuthorised);
_result.IsQos.ShouldBe(expected.IsQos);
_result.IsCached.ShouldBe(expected.IsCached); _result.IsCached.ShouldBe(expected.IsCached);
_result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
} }

View File

@ -0,0 +1,268 @@
using Ocelot.DownstreamRouteFinder.Finder;
using Xunit;
using Shouldly;
using Ocelot.Configuration;
using System.Net.Http;
namespace Ocelot.UnitTests.DownstreamRouteFinder
{
using System;
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;
private Response<DownstreamRoute> _resultTwo;
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_cache_downstream_route()
{
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreate())
.And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreateAgain())
.Then(_ => ThenTheDownstreamRoutesAreTheSameReference())
.BDDfy();
}
[Fact]
public void should_not_cache_downstream_route()
{
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/"))
.When(_ => WhenICreate())
.And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreateAgain())
.Then(_ => ThenTheDownstreamRoutesAreTheNotSameReference())
.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_with_only_first_segment_no_traling_slash()
{
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_with_segments_no_traling_slash()
{
var upstreamUrlPath = "/auth/test";
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath))
.When(_ => WhenICreate())
.Then(_ => ThenThePathDoesNotHaveTrailingSlash())
.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 ThenThePathDoesNotHaveTrailingSlash()
{
_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 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);
}
private void WhenICreateAgain()
{
_resultTwo = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost);
}
private void ThenTheDownstreamRoutesAreTheSameReference()
{
_result.ShouldBe(_resultTwo);
}
private void ThenTheDownstreamRoutesAreTheNotSameReference()
{
_result.ShouldNotBe(_resultTwo);
}
}
}

View File

@ -21,7 +21,8 @@
public class DownstreamRouteFinderMiddlewareTests public class DownstreamRouteFinderMiddlewareTests
{ {
private readonly Mock<IDownstreamRouteFinder> _finder; private readonly Mock<IDownstreamRouteProvider> _finder;
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
private readonly Mock<IInternalConfigurationRepository> _repo; private readonly Mock<IInternalConfigurationRepository> _repo;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private IInternalConfiguration _config; private IInternalConfiguration _config;
@ -35,20 +36,22 @@
public DownstreamRouteFinderMiddlewareTests() public DownstreamRouteFinderMiddlewareTests()
{ {
_repo = new Mock<IInternalConfigurationRepository>(); _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()); _downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask; _next = context => Task.CompletedTask;
_multiplexer = new Mock<IMultiplexer>(); _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] [Fact]
public void should_call_scoped_data_repository_correctly() 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() var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("any old string") .WithDownstreamPathTemplate("any old string")
@ -76,23 +79,21 @@
private void GivenTheFollowingConfig(IInternalConfiguration config) private void GivenTheFollowingConfig(IInternalConfiguration config)
{ {
_config = config; _config = config;
_repo _downstreamContext.Configuration = config;
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(_config));
} }
private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_finder _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); .Returns(_downstreamRoute);
} }
private void ThenTheScopedDataRepositoryIsCalledCorrectly() private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{ {
_downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues); _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 public class DownstreamRouteFinderTests
{ {
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteProvider _downstreamRouteFinder;
private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher; private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher;
private readonly Mock<IPlaceholderNameAndValueFinder> _finder; private readonly Mock<IPlaceholderNameAndValueFinder> _finder;
private string _upstreamUrlPath; private string _upstreamUrlPath;
@ -709,7 +709,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig)
{ {
_reRoutesConfig = reRoutesConfig; _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) private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
@ -719,7 +719,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void WhenICallTheFinder() private void WhenICallTheFinder()
{ {
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost); _result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost);
} }
private void ThenTheFollowingIsReturned(DownstreamRoute expected) 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) 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() private void WhenICallTheMiddleware()

View File

@ -51,7 +51,7 @@ namespace Ocelot.UnitTests.Errors
[Fact] [Fact]
public void NoDownstreamException() 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()) this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config)) .And(_ => GivenTheConfigurationIs(config))
@ -64,7 +64,7 @@ namespace Ocelot.UnitTests.Errors
[Fact] [Fact]
public void DownstreamException() 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()) this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config)) .And(_ => GivenTheConfigurationIs(config))
@ -76,7 +76,7 @@ namespace Ocelot.UnitTests.Errors
[Fact] [Fact]
public void ShouldSetRequestId() 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()) this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config)) .And(_ => GivenTheConfigurationIs(config))
@ -89,7 +89,7 @@ namespace Ocelot.UnitTests.Errors
[Fact] [Fact]
public void ShouldSetAspDotNetRequestId() 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()) this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config)) .And(_ => GivenTheConfigurationIs(config))

View File

@ -33,7 +33,7 @@ namespace Ocelot.UnitTests.LoadBalancer
public void should_store_load_balancer_on_first_request() public void should_store_load_balancer_on_first_request()
{ {
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithReRouteKey("test") .WithLoadBalancerKey("test")
.Build(); .Build();
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
@ -46,7 +46,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0))
.WithReRouteKey("test") .WithLoadBalancerKey("test")
.Build(); .Build();
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
@ -60,12 +60,12 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0))
.WithReRouteKey("test") .WithLoadBalancerKey("test")
.Build(); .Build();
var reRouteTwo = new DownstreamReRouteBuilder() var reRouteTwo = new DownstreamReRouteBuilder()
.WithLoadBalancerOptions(new LoadBalancerOptions("FakeRoundRobinLoadBalancer", "", 0)) .WithLoadBalancerOptions(new LoadBalancerOptions("FakeRoundRobinLoadBalancer", "", 0))
.WithReRouteKey("testtwo") .WithLoadBalancerKey("testtwo")
.Build(); .Build();
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))
@ -92,12 +92,12 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0))
.WithReRouteKey("test") .WithLoadBalancerKey("test")
.Build(); .Build();
var reRouteTwo = new DownstreamReRouteBuilder() var reRouteTwo = new DownstreamReRouteBuilder()
.WithLoadBalancerOptions(new LoadBalancerOptions("LeastConnection", "", 0)) .WithLoadBalancerOptions(new LoadBalancerOptions("LeastConnection", "", 0))
.WithReRouteKey("test") .WithLoadBalancerKey("test")
.Build(); .Build();
this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer()))

View File

@ -117,7 +117,8 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheConfigurationIs(ServiceProviderConfiguration config) private void GivenTheConfigurationIs(ServiceProviderConfiguration config)
{ {
_config = 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) private void GivenTheDownStreamUrlIs(string downstreamUrl)

View File

@ -0,0 +1,17 @@
using Ocelot.Configuration;
using Ocelot.LoadBalancer.LoadBalancers;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.LoadBalancer
{
public class LoadBalancerOptionsTests
{
[Fact]
public void should_default_to_no_load_balancer()
{
var options = new LoadBalancerOptionsBuilder().Build();
options.Type.ShouldBe(nameof(NoLoadBalancer));
}
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Middleware; using Ocelot.Middleware;
@ -16,6 +18,12 @@ namespace Ocelot.UnitTests.LoadBalancer
private NoLoadBalancer _loadBalancer; private NoLoadBalancer _loadBalancer;
private Response<ServiceHostAndPort> _result; private Response<ServiceHostAndPort> _result;
public NoLoadBalancerTests()
{
_services = new List<Service>();
_loadBalancer = new NoLoadBalancer(() => Task.FromResult(_services));
}
[Fact] [Fact]
public void should_return_host_and_port() public void should_return_host_and_port()
{ {
@ -25,6 +33,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
new Service("product", hostAndPort, string.Empty, string.Empty, new string[0]) new Service("product", hostAndPort, string.Empty, string.Empty, new string[0])
}; };
this.Given(x => x.GivenServices(services)) this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort()) .When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenTheHostAndPortIs(hostAndPort)) .Then(x => x.ThenTheHostAndPortIs(hostAndPort))
@ -34,25 +43,43 @@ namespace Ocelot.UnitTests.LoadBalancer
[Fact] [Fact]
public void should_return_error_if_no_services() public void should_return_error_if_no_services()
{ {
var services = new List<Service>(); this.When(x => x.WhenIGetTheNextHostAndPort())
this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenThereIsAnError()) .Then(x => x.ThenThereIsAnError())
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_return_error_if_no_services_then_when_services_available_return_host_and_port()
{
var hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
var services = new List<Service>
{
new Service("product", hostAndPort, string.Empty, string.Empty, new string[0])
};
this.Given(_ => WhenIGetTheNextHostAndPort())
.And(_ => ThenThereIsAnError())
.And(_ => GivenServices(services))
.When(_ => WhenIGetTheNextHostAndPort())
.Then(_ => ThenTheHostAndPortIs(hostAndPort))
.BDDfy();
}
[Fact] [Fact]
public void should_return_error_if_null_services() public void should_return_error_if_null_services()
{ {
List<Service> services = null; this.Given(x => x.GivenServicesAreNull())
this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort()) .When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenThereIsAnError()) .Then(x => x.ThenThereIsAnError())
.BDDfy(); .BDDfy();
} }
private void GivenServicesAreNull()
{
_loadBalancer = new NoLoadBalancer(() => Task.FromResult((List<Service>)null));
}
private void ThenThereIsAnError() private void ThenThereIsAnError()
{ {
_result.IsError.ShouldBeTrue(); _result.IsError.ShouldBeTrue();
@ -60,12 +87,11 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenServices(List<Service> services) private void GivenServices(List<Service> services)
{ {
_services = services; _services.AddRange(services);
} }
private void WhenIGetTheNextHostAndPort() private void WhenIGetTheNextHostAndPort()
{ {
_loadBalancer = new NoLoadBalancer(_services);
_result = _loadBalancer.Lease(new DownstreamContext(new DefaultHttpContext())).Result; _result = _loadBalancer.Lease(new DownstreamContext(new DefaultHttpContext())).Result;
} }

View File

@ -38,14 +38,21 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_follow_ordering_add_specifics() 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)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
"FakeDelegatingHandlerTwo" "FakeDelegatingHandlerTwo"
}) })
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -67,7 +74,14 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_follow_ordering_order_specifics_and_globals() 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)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
@ -75,7 +89,7 @@ namespace Ocelot.UnitTests.Requester
"FakeDelegatingHandler", "FakeDelegatingHandler",
"FakeDelegatingHandlerFour" "FakeDelegatingHandlerFour"
}) })
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -97,14 +111,21 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_follow_ordering_order_specifics() 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)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandlerTwo", "FakeDelegatingHandlerTwo",
"FakeDelegatingHandler" "FakeDelegatingHandler"
}) })
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -126,13 +147,20 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_follow_ordering_order_and_only_add_specifics_in_config() 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)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
}) })
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -153,9 +181,16 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_follow_ordering_dont_add_specifics() 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)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -175,14 +210,18 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_apply_re_route_specific() public void should_apply_re_route_specific()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
"FakeDelegatingHandlerTwo" "FakeDelegatingHandlerTwo"
}) })
.WithReRouteKey("") .WithLoadBalancerKey("")
.Build(); .Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
@ -197,8 +236,15 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_all_from_all_routes_provider_and_qos() public void should_all_from_all_routes_provider_and_qos()
{ {
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) var qosOptions = new QoSOptionsBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .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)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>()))) .And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
@ -213,8 +259,12 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_return_provider_with_no_delegates() public void should_return_provider_with_no_delegates()
{ {
var reRoute = new DownstreamReRouteBuilder().WithIsQos(false) var qosOptions = new QoSOptionsBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithLoadBalancerKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheServiceProviderReturnsNothing()) .And(x => GivenTheServiceProviderReturnsNothing())
@ -226,8 +276,15 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_return_provider_with_qos_delegate() public void should_return_provider_with_qos_delegate()
{ {
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) var qosOptions = new QoSOptionsBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .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)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>()))) .And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
@ -241,8 +298,15 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_return_error() public void should_return_error()
{ {
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) var qosOptions = new QoSOptionsBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .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)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>()))) .And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))

View File

@ -46,10 +46,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_build_http_client() public void should_build_http_client()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();
@ -63,10 +66,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_log_if_ignoring_ssl_errors() public void should_log_if_ignoring_ssl_errors()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.WithDangerousAcceptAnyServerCertificateValidator(true) .WithDangerousAcceptAnyServerCertificateValidator(true)
.Build(); .Build();
@ -82,10 +88,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_call_delegating_handlers_in_order() public void should_call_delegating_handlers_in_order()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();
@ -110,10 +119,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_re_use_cookies_from_container() public void should_re_use_cookies_from_container()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();
@ -142,10 +154,13 @@ namespace Ocelot.UnitTests.Requester
HttpMethod method = new HttpMethod(verb); HttpMethod method = new HttpMethod(verb);
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();

View File

@ -47,10 +47,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_call_request_correctly() public void should_call_request_correctly()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();
@ -70,10 +73,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_call_request_unable_to_complete_request() public void should_call_request_unable_to_complete_request()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
.Build(); .Build();
@ -92,10 +98,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void http_client_request_times_out() public void http_client_request_times_out()
{ {
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithIsQos(false) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false))
.WithReRouteKey("") .WithLoadBalancerKey("")
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build()) .WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())
.Build(); .Build();

View File

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

View File

@ -26,7 +26,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_store_qos_provider_on_first_request() 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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.Then(x => x.ThenItIsAdded()) .Then(x => x.ThenItIsAdded())
@ -36,7 +42,13 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_not_store_qos_provider_on_first_request() 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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute)) .When(x => x.WhenWeGetTheQoSProvider(reRoute))
@ -47,8 +59,21 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_store_qos_providers_by_key() public void should_store_qos_providers_by_key()
{ {
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build(); var qosOptions = new QoSOptionsBuilder()
var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("testTwo").Build(); .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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider())) .And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider()))
@ -62,7 +87,12 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_return_error_if_no_qos_provider_with_key() 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)) this.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenAnErrorIsReturned()) .Then(x => x.ThenAnErrorIsReturned())
@ -72,9 +102,24 @@ namespace Ocelot.UnitTests.Requester
[Fact] [Fact]
public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed() 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())) this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute)) .When(x => x.WhenWeGetTheQoSProvider(reRoute))