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

182
README.md
View File

@ -1,91 +1,91 @@
[<img src="http://threemammals.com/images/ocelot_logo.png">](http://threemammals.com/ocelot)
[![Build status](https://ci.appveyor.com/api/projects/status/r6sv51qx36sis1je?branch=develop&svg=true)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
[![Build Status](https://travis-ci.org/ThreeMammals/Ocelot.svg?branch=develop)](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
[![Windows Build history](https://buildstats.info/appveyor/chart/TomPallister/ocelot-fcfpb?branch=develop&includeBuildsFromPullRequest=false)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?branch=develop)](https://coveralls.io/github/ThreeMammals/Ocelot?branch=develop)
# Ocelot
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
a micro services / service orientated architecture
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that asp.net core supports.
In particular I want easy integration with
IdentityServer reference and bearer tokens.
We have been unable to find this in my current workplace
without having to write our own Javascript middlewares
to handle the IdentityServer reference tokens. We would
rather use the IdentityServer code that already exists
to do this.
Ocelot is a bunch of middlewares in a specific order.
Ocelot manipulates the HttpRequest object into a state specified by its configuration until
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
used to make a request to a downstream service. The middleware that makes the request is
the last thing in the Ocelot pipeline. It does not call the next middleware.
The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
is returned to the client. That is basically it with a bunch of other features!
## Features
A quick list of Ocelot's capabilities for more information see the [documentation](http://ocelot.readthedocs.io/en/latest/).
* Routing
* Request Aggregation
* Service Discovery with Consul & Eureka
* Service Fabric
* WebSockets
* Authentication
* Authorisation
* Rate Limiting
* Caching
* Retry policies / QoS
* Load Balancing
* Logging / Tracing / Correlation
* Headers / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers
* Configuration / Administration REST API
* Platform / Cloud agnostic
## How to install
Ocelot is designed to work with ASP.NET Core only and it targets `netstandard2.0`. This means it can be used anywhere `.NET Standard 2.0` is supported, including `.NET Core 2.0` and `.NET Framework 4.6.1` and up. [This](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) documentation may prove helpful when working out if Ocelot would be suitable for you.
Install Ocelot and it's dependencies using NuGet.
`Install-Package Ocelot`
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
## Documentation
Please click [here](http://ocelot.readthedocs.io/en/latest/) for the Ocleot documentation. This includes lots of information and will be helpful if you want to understand the features Ocelot currently offers.
## Coming up
You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
## Contributing
We love to receive contributions from the community so please keep them coming :)
Pull requests, issues and commentary welcome!
Please complete the relavent template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes
before doing any work incase this is something we are already doing or it might not make sense. We can also give
advice on the easiest way to do things :)
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
## Things that are currently annoying me
[![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
[<img src="http://threemammals.com/images/ocelot_logo.png">](http://threemammals.com/ocelot)
[![Build status](https://ci.appveyor.com/api/projects/status/r6sv51qx36sis1je?branch=develop&svg=true)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
[![Build Status](https://travis-ci.org/ThreeMammals/Ocelot.svg?branch=develop)](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
[![Windows Build history](https://buildstats.info/appveyor/chart/TomPallister/ocelot-fcfpb?branch=develop&includeBuildsFromPullRequest=false)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?branch=develop)](https://coveralls.io/github/ThreeMammals/Ocelot?branch=develop)
# Ocelot
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
a micro services / service orientated architecture
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that asp.net core supports.
In particular I want easy integration with
IdentityServer reference and bearer tokens.
We have been unable to find this in my current workplace
without having to write our own Javascript middlewares
to handle the IdentityServer reference tokens. We would
rather use the IdentityServer code that already exists
to do this.
Ocelot is a bunch of middlewares in a specific order.
Ocelot manipulates the HttpRequest object into a state specified by its configuration until
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
used to make a request to a downstream service. The middleware that makes the request is
the last thing in the Ocelot pipeline. It does not call the next middleware.
The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
is returned to the client. That is basically it with a bunch of other features!
## Features
A quick list of Ocelot's capabilities for more information see the [documentation](http://ocelot.readthedocs.io/en/latest/).
* Routing
* Request Aggregation
* Service Discovery with Consul & Eureka
* Service Fabric
* WebSockets
* Authentication
* Authorisation
* Rate Limiting
* Caching
* Retry policies / QoS
* Load Balancing
* Logging / Tracing / Correlation
* Headers / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers
* Configuration / Administration REST API
* Platform / Cloud Agnostic
## How to install
Ocelot is designed to work with ASP.NET Core only and it targets `netstandard2.0`. This means it can be used anywhere `.NET Standard 2.0` is supported, including `.NET Core 2.0` and `.NET Framework 4.6.1` and up. [This](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) documentation may prove helpful when working out if Ocelot would be suitable for you.
Install Ocelot and it's dependencies using NuGet.
`Install-Package Ocelot`
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
## Documentation
Please click [here](http://ocelot.readthedocs.io/en/latest/) for the Ocleot documentation. This includes lots of information and will be helpful if you want to understand the features Ocelot currently offers.
## Coming up
You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
## Contributing
We love to receive contributions from the community so please keep them coming :)
Pull requests, issues and commentary welcome!
Please complete the relavent template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes
before doing any work incase this is something we are already doing or it might not make sense. We can also give
advice on the easiest way to do things :)
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
## Things that are currently annoying me
[![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)

View File

@ -177,4 +177,11 @@ and
}
In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
Dynamic Routing
^^^^^^^^^^^^^^^
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
this sounds interesting to you.

View File

@ -1,3 +1,5 @@
.. service-discovery:
Service Discovery
=================
@ -88,3 +90,65 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
Dynamic Routing
^^^^^^^^^^^^^^^
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing when using
a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segmentof the upstream path to lookup the
downstream service with the service discovery provider.
An example of this would be calling ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
the path which is product and use it as a key to look up the service in consul. If consul returns a service Ocelot will request it on whatever host and
port comes back from consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products.
Ocelot will apprend any query string to the downstream url as normal.
In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you
need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.
In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but
talk to private services over http) that will be applied to all of the dynamic ReRoutes.
The config might look something like
.. code-block:: json
{
"ReRoutes": [],
"Aggregates": [],
"GlobalConfiguration": {
"RequestIdKey": null,
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 8510,
"Type": null,
"Token": null,
"ConfigurationKey": null
},
"RateLimitOptions": {
"ClientIdHeader": "ClientId",
"QuotaExceededMessage": null,
"RateLimitCounterPrefix": "ocelot",
"DisableRateLimitHeaders": false,
"HttpStatusCode": 429
},
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0,
"TimeoutValue": 0
},
"BaseUrl": null,
"LoadBalancerOptions": {
"Type": "LeastConnection",
"Key": null,
"Expiry": 0
},
"DownstreamScheme": "http",
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
"UseCookieContainer": false,
"UseTracing": false
}
}
}
Please take a look through all of the docs to understand these options.

View File

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

View File

@ -6,7 +6,9 @@
private int _durationOfBreak;
private int _timeoutValue;
private int _timeoutValue;
private string _key;
public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
{
@ -26,9 +28,15 @@
return this;
}
public QoSOptionsBuilder WithKey(string input)
{
_key = input;
return this;
}
public QoSOptions Build()
{
return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue);
return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue, _key);
}
}
}

View File

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

View File

@ -104,8 +104,22 @@ namespace Ocelot.Configuration.Creator
}
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);
var lbOptions = CreateLoadBalancerOptions(fileConfiguration.GlobalConfiguration.LoadBalancerOptions);
var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions);
var config = new InternalConfiguration(reRoutes,
_adminPath.Path,
serviceProviderConfiguration,
fileConfiguration.GlobalConfiguration.RequestIdKey,
lbOptions,
fileConfiguration.GlobalConfiguration.DownstreamScheme,
qosOptions,
httpHandlerOptions
);
return new OkResponse<IInternalConfiguration>(config);
}
@ -160,8 +174,6 @@ namespace Ocelot.Configuration.Creator
var reRouteKey = CreateReRouteKey(fileReRoute);
var qosKey = CreateQosKey(fileReRoute);
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
@ -172,19 +184,19 @@ namespace Ocelot.Configuration.Creator
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
var qosOptions = _qosOptionsCreator.Create(fileReRoute);
var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod.ToArray());
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
var region = _regionCreator.Create(fileReRoute);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions);
var hAndRs = _headerFAndRCreator.Create(fileReRoute);
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
var lbOptions = CreateLoadBalancerOptions(fileReRoute);
var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions);
var reRoute = new DownstreamReRouteBuilder()
.WithKey(fileReRoute.Key)
@ -205,9 +217,7 @@ namespace Ocelot.Configuration.Creator
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancerOptions(lbOptions)
.WithDownstreamAddresses(downstreamAddresses)
.WithReRouteKey(reRouteKey)
.WithQosKey(qosKey)
.WithIsQos(fileReRouteOptions.IsQos)
.WithLoadBalancerKey(reRouteKey)
.WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption)
@ -226,9 +236,13 @@ namespace Ocelot.Configuration.Creator
return reRoute;
}
private LoadBalancerOptions CreateLoadBalancerOptions(FileReRoute fileReRoute)
private LoadBalancerOptions CreateLoadBalancerOptions(FileLoadBalancerOptions options)
{
return new LoadBalancerOptions(fileReRoute.LoadBalancerOptions.Type, fileReRoute.LoadBalancerOptions.Key, fileReRoute.LoadBalancerOptions.Expiry);
return new LoadBalancerOptionsBuilder()
.WithType(options.Type)
.WithKey(options.Key)
.WithExpiryInMs(options.Expiry)
.Build();
}
private string CreateReRouteKey(FileReRoute fileReRoute)
@ -238,14 +252,7 @@ namespace Ocelot.Configuration.Creator
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
}
return CreateQosKey(fileReRoute);
}
private string CreateQosKey(FileReRoute fileReRoute)
{
//note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain
var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
return loadBalancerKey;
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
}
}
}

View File

@ -6,19 +6,19 @@ namespace Ocelot.Configuration.Creator
{
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{
private IServiceTracer _tracer;
private readonly IServiceTracer _tracer;
public HttpHandlerOptionsCreator(IServiceTracer tracer)
{
_tracer = tracer;
}
public HttpHandlerOptions Create(FileReRoute fileReRoute)
public HttpHandlerOptions Create(FileHttpHandlerOptions options)
{
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) && options.UseTracing;
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
return new HttpHandlerOptions(options.AllowAutoRedirect,
options.UseCookieContainer, useTracing);
}
}
}

View File

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

View File

@ -4,6 +4,8 @@ namespace Ocelot.Configuration.Creator
{
public interface IQoSOptionsCreator
{
QoSOptions Create(FileReRoute fileReRoute);
QoSOptions Create(FileQoSOptions options);
QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods);
QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods);
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ocelot.Configuration.Creator
{
ReRouteOptions Create(FileReRoute fileReRoute);
}
}
}

View File

@ -1,17 +1,47 @@
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using System.Linq;
public class QoSOptionsCreator : IQoSOptionsCreator
{
public QoSOptions Create(FileReRoute fileReRoute)
public QoSOptions Create(FileQoSOptions options)
{
return new QoSOptionsBuilder()
.WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking)
.WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak)
.WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue)
.WithExceptionsAllowedBeforeBreaking(options.ExceptionsAllowedBeforeBreaking)
.WithDurationOfBreak(options.DurationOfBreak)
.WithTimeoutValue(options.TimeoutValue)
.Build();
}
public QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods)
{
var key = CreateKey(pathTemplate, httpMethods);
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
}
public QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods)
{
var key = CreateKey(pathTemplate, httpMethods);
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
}
private QoSOptions Map(string key, int timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking)
{
return new QoSOptionsBuilder()
.WithExceptionsAllowedBeforeBreaking(exceptionsAllowedBeforeBreaking)
.WithDurationOfBreak(durationOfBreak)
.WithTimeoutValue(timeoutValue)
.WithKey(key)
.Build();
}
private string CreateKey(string pathTemplate, string[] httpMethods)
{
return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", httpMethods)}";
}
}
}
}

View File

@ -10,14 +10,12 @@ namespace Ocelot.Configuration.Creator
var isAuthenticated = IsAuthenticated(fileReRoute);
var isAuthorised = IsAuthorised(fileReRoute);
var isCached = IsCached(fileReRoute);
var isQos = IsQoS(fileReRoute);
var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
var options = new ReRouteOptionsBuilder()
.WithIsAuthenticated(isAuthenticated)
.WithIsAuthorised(isAuthorised)
.WithIsCached(isCached)
.WithIsQos(isQos)
.WithRateLimiting(enableRateLimiting)
.Build();
@ -29,11 +27,6 @@ namespace Ocelot.Configuration.Creator
return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false;
}
private bool IsQoS(FileReRoute fileReRoute)
{
return fileReRoute.QoSOptions?.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions?.TimeoutValue > 0;
}
private bool IsAuthenticated(FileReRoute fileReRoute)
{
return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey);
@ -48,5 +41,5 @@ namespace Ocelot.Configuration.Creator
{
return fileReRoute.FileCacheOptions.TtlSeconds > 0;
}
}
}
}
}

View File

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

View File

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

View File

@ -6,6 +6,9 @@
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
RateLimitOptions = new FileRateLimitOptions();
LoadBalancerOptions = new FileLoadBalancerOptions();
QoSOptions = new FileQoSOptions();
HttpHandlerOptions = new FileHttpHandlerOptions();
}
public string RequestIdKey { get; set; }
@ -13,7 +16,15 @@
public FileServiceDiscoveryProvider ServiceDiscoveryProvider { get;set; }
public FileRateLimitOptions RateLimitOptions { get; set; }
public FileQoSOptions QoSOptions { get; set; }
public string BaseUrl { get ;set; }
public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
public string DownstreamScheme { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
}
}

View File

@ -0,0 +1,32 @@
namespace Ocelot.Configuration
{
public class HttpHandlerOptionsBuilder
{
private bool _allowAutoRedirect;
private bool _useCookieContainer;
private bool _useTracing;
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
{
_allowAutoRedirect = input;
return this;
}
public HttpHandlerOptionsBuilder WithUseCookieContainer(bool input)
{
_useCookieContainer = input;
return this;
}
public HttpHandlerOptionsBuilder WithUseTracing(bool input)
{
_useTracing = input;
return this;
}
public HttpHandlerOptions Build()
{
return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing);
}
}
}

View File

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

View File

@ -4,17 +4,33 @@ namespace Ocelot.Configuration
{
public class InternalConfiguration : IInternalConfiguration
{
public InternalConfiguration(List<ReRoute> reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
public InternalConfiguration(
List<ReRoute> reRoutes,
string administrationPath,
ServiceProviderConfiguration serviceProviderConfiguration,
string requestId,
LoadBalancerOptions loadBalancerOptions,
string downstreamScheme,
QoSOptions qoSOptions,
HttpHandlerOptions httpHandlerOptions)
{
ReRoutes = reRoutes;
AdministrationPath = administrationPath;
ServiceProviderConfiguration = serviceProviderConfiguration;
RequestId = requestId;
LoadBalancerOptions = loadBalancerOptions;
DownstreamScheme = downstreamScheme;
QoSOptions = qoSOptions;
HttpHandlerOptions = httpHandlerOptions;
}
public List<ReRoute> ReRoutes { get; }
public string AdministrationPath {get;}
public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
public string RequestId {get;}
public LoadBalancerOptions LoadBalancerOptions { get; }
public string DownstreamScheme { get; }
public QoSOptions QoSOptions { get; }
public HttpHandlerOptions HttpHandlerOptions { get; }
}
}

View File

@ -1,10 +1,12 @@
using Ocelot.LoadBalancer.LoadBalancers;
namespace Ocelot.Configuration
{
public class LoadBalancerOptions
{
public LoadBalancerOptions(string type, string key, int expiryInMs)
{
Type = type;
Type = type ?? nameof(NoLoadBalancer);
Key = key;
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 durationofBreak,
int timeoutValue,
string key,
TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic)
{
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
DurationOfBreak = durationofBreak;
TimeoutValue = timeoutValue;
TimeoutStrategy = timeoutStrategy;
Key = key;
}
public int ExceptionsAllowedBeforeBreaking { get; }
@ -23,5 +25,8 @@ namespace Ocelot.Configuration
public int TimeoutValue { get; }
public TimeoutStrategy TimeoutStrategy { get; }
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 && TimeoutValue > 0;
public string Key { get; }
}
}

View File

@ -2,19 +2,17 @@ namespace Ocelot.Configuration
{
public class ReRouteOptions
{
public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isQos, bool isEnableRateLimiting)
public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting)
{
IsAuthenticated = isAuthenticated;
IsAuthorised = isAuthorised;
IsCached = isCached;
IsQos = isQos;
EnableRateLimiting = isEnableRateLimiting;
}
public bool IsAuthenticated { get; private set; }
public bool IsAuthorised { get; private set; }
public bool IsCached { get; private set; }
public bool IsQos { get; private set; }
public bool EnableRateLimiting { get; private set; }
}
}

View File

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

View File

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

View File

@ -0,0 +1,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

@ -1,62 +1,62 @@
using System.Collections.Generic;
using System.Linq;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public class DownstreamRouteFinder : IDownstreamRouteFinder
{
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_urlMatcher = urlMatcher;
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
{
var downstreamRoutes = new List<DownstreamRoute>();
var applicableReRoutes = configuration.ReRoutes
.Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost))
.OrderByDescending(x => x.UpstreamTemplatePattern.Priority);
foreach (var reRoute in applicableReRoutes)
{
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
if (urlMatch.Data.Match)
{
downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute));
}
}
if (downstreamRoutes.Any())
{
var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
}
return new ErrorResponse<DownstreamRoute>(new UnableToFindDownstreamRouteError(path, httpMethod));
}
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
{
return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost);
}
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
{
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute);
}
}
}
using System.Collections.Generic;
using System.Linq;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.Finder
{
public class DownstreamRouteFinder : IDownstreamRouteProvider
{
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_urlMatcher = urlMatcher;
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> Get(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
{
var downstreamRoutes = new List<DownstreamRoute>();
var applicableReRoutes = configuration.ReRoutes
.Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost))
.OrderByDescending(x => x.UpstreamTemplatePattern.Priority);
foreach (var reRoute in applicableReRoutes)
{
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
if (urlMatch.Data.Match)
{
downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute));
}
}
if (downstreamRoutes.Any())
{
var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
}
return new ErrorResponse<DownstreamRoute>(new UnableToFindDownstreamRouteError(path, httpMethod));
}
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
{
return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost);
}
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
{
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute);
}
}
}

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

@ -1,69 +1,60 @@
using System.Threading.Tasks;
using System.Linq;
using Ocelot.Configuration.Repository;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
namespace Ocelot.DownstreamRouteFinder.Middleware
{
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IInternalConfigurationRepository _repo;
private readonly IMultiplexer _multiplexer;
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamRouteFinder downstreamRouteFinder,
IInternalConfigurationRepository repo,
IMultiplexer multiplexer)
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
{
_repo = repo;
_multiplexer = multiplexer;
_next = next;
_downstreamRouteFinder = downstreamRouteFinder;
}
public async Task Invoke(DownstreamContext context)
{
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
var upstreamHost = context.HttpContext.Request.Headers["Host"];
var configuration = _repo.Get();
if (configuration.IsError)
{
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
SetPipelineError(context, configuration.Errors);
return;
}
context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration;
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.HttpContext.Request.Method, configuration.Data, upstreamHost);
if (downstreamRoute.IsError)
{
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
SetPipelineError(context, downstreamRoute.Errors);
return;
}
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
}
}
}
using System.Threading.Tasks;
using System.Linq;
using Ocelot.Configuration.Repository;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
namespace Ocelot.DownstreamRouteFinder.Middleware
{
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
private readonly IDownstreamRouteProviderFactory _factory;
private readonly IInternalConfigurationRepository _repo;
private readonly IMultiplexer _multiplexer;
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamRouteProviderFactory downstreamRouteFinder,
IInternalConfigurationRepository repo,
IMultiplexer multiplexer)
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
{
_repo = repo;
_multiplexer = multiplexer;
_next = next;
_factory = downstreamRouteFinder;
}
public async Task Invoke(DownstreamContext context)
{
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
var upstreamHost = context.HttpContext.Request.Headers["Host"];
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
var provider = _factory.Get(context.Configuration);
var downstreamRoute = provider.Get(upstreamUrlPath, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
if (downstreamRoute.IsError)
{
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
SetPipelineError(context, downstreamRoute.Errors);
return;
}
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
}
}
}

View File

@ -73,7 +73,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
private static bool ServiceFabricRequest(DownstreamContext context)
{
return context.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery;
return context.Configuration.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery;
}
private static bool RequestForStatefullService(string query)

View File

@ -9,6 +9,8 @@ using Ocelot.Middleware;
namespace Ocelot.Errors.Middleware
{
using Configuration;
/// <summary>
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500
/// </summary>
@ -32,8 +34,20 @@ namespace Ocelot.Errors.Middleware
public async Task Invoke(DownstreamContext context)
{
try
{
TrySetGlobalRequestId(context);
{
//try and get the global request id and set it for logs...
//should this basically be immutable per request...i guess it should!
//first thing is get config
var configuration = _configRepo.Get();
if (configuration.IsError)
{
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
}
TrySetGlobalRequestId(context, configuration.Data);
context.Configuration = configuration.Data;
Logger.LogDebug("ocelot pipeline started");
@ -53,19 +67,9 @@ namespace Ocelot.Errors.Middleware
Logger.LogDebug("ocelot pipeline finished");
}
private void TrySetGlobalRequestId(DownstreamContext context)
private void TrySetGlobalRequestId(DownstreamContext context, IInternalConfiguration configuration)
{
//try and get the global request id and set it for logs...
//should this basically be immutable per request...i guess it should!
//first thing is get config
var configuration = _configRepo.Get();
if(configuration.IsError)
{
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
}
var key = configuration.Data.RequestId;
var key = configuration.RequestId;
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
{

View File

@ -29,7 +29,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
var bus = new InMemoryBus<StickySession>();
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus);
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.Threading.Tasks;
using Ocelot.Middleware;
@ -9,22 +10,23 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{
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;
}
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
{
//todo no point spinning a task up here, also first or default could be null..
if (_services == null || _services.Count == 0)
var services = await _services();
//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"));
}
var service = await Task.FromResult(_services.FirstOrDefault());
var service = await Task.FromResult(services.FirstOrDefault());
return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
}

View File

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

View File

@ -22,7 +22,7 @@ namespace Ocelot.LoadBalancer.Middleware
public async Task Invoke(DownstreamContext context)
{
var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration);
var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration);
if(loadBalancer.IsError)
{
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");

View File

@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Request.Middleware;
namespace Ocelot.Middleware
@ -20,8 +17,6 @@ namespace Ocelot.Middleware
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
public HttpContext HttpContext { get; }
public DownstreamReRoute DownstreamReRoute { get; set; }
@ -32,6 +27,8 @@ namespace Ocelot.Middleware
public List<Error> Errors { get; }
public IInternalConfiguration Configuration { get; set; }
public bool IsError => Errors.Count > 0;
}
}

View File

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

View File

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

View File

@ -58,9 +58,9 @@ namespace Ocelot.Requester
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}");
}
var timeout = context.DownstreamReRoute.QosOptionsOptions.TimeoutValue == 0
var timeout = context.DownstreamReRoute.QosOptions.TimeoutValue == 0
? _defaultTimeout
: TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptionsOptions.TimeoutValue);
: TimeSpan.FromMilliseconds(context.DownstreamReRoute.QosOptions.TimeoutValue);
_httpClient = new HttpClient(CreateHttpMessageHandler(httpclientHandler, context.DownstreamReRoute))
{

View File

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

View File

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

View File

@ -21,26 +21,26 @@ namespace Ocelot.Requester.QoS
{
try
{
if (_qoSProviders.TryGetValue(reRoute.QosKey, out var qosProvider))
if (_qoSProviders.TryGetValue(reRoute.QosOptions.Key, out var qosProvider))
{
if (reRoute.IsQos && qosProvider.CircuitBreaker == null)
if (reRoute.QosOptions.UseQos && qosProvider.CircuitBreaker == null)
{
qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.QosKey, qosProvider);
Add(reRoute.QosOptions.Key, qosProvider);
}
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.QosKey]);
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.QosOptions.Key]);
}
qosProvider = _qoSProviderFactory.Get(reRoute);
Add(reRoute.QosKey, qosProvider);
Add(reRoute.QosOptions.Key, qosProvider);
return new OkResponse<IQoSProvider>(qosProvider);
}
catch (Exception ex)
{
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
{
new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.QosKey}, exception was {ex}")
new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.QosOptions.Key}, exception was {ex}")
});
}
}

View File

@ -4,8 +4,8 @@ namespace Ocelot.ServiceDiscovery.Configuration
{
public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token)
{
Host = host;
Port = port;
Host = string.IsNullOrEmpty(host) ? "localhost" : host;
Port = port > 0 ? port : 8500;
KeyOfServiceInConsul = keyOfServiceInConsul;
Token = token;
}

View File

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

View File

@ -208,6 +208,121 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes()
{
const int consulPort = 8513;
const string serviceName = "web";
const int downstreamServicePort = 8087;
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = downstreamServicePort,
ID = "web_90_0_2_224_8080",
Tags = new[] {"version-v1"}
},
};
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "localhost",
Port = consulPort
},
DownstreamScheme = "http",
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = true,
UseCookieContainer = true,
UseTracing = false
},
QoSOptions = new FileQoSOptions
{
TimeoutValue = 100,
DurationOfBreak = 1000,
ExceptionsAllowedBeforeBreaking = 1
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/web/something"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes()
{
var consulPort = 8510;
var serviceName = "product";
var serviceOnePort = 50888;
var serviceTwoPort = 50889;
var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}";
var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = serviceOnePort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = serviceTwoPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
},
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
DownstreamScheme = "http"
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50))
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
.BDDfy();
}
[Fact]
public void should_use_token_to_make_request_to_consul()
{

View File

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

View File

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

View File

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

View File

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

View File

@ -105,6 +105,10 @@ namespace Ocelot.UnitTests.Configuration
public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException();
public string RequestId {get;}
public LoadBalancerOptions LoadBalancerOptions { get; }
public string DownstreamScheme { get; }
public QoSOptions QoSOptions { get; }
public HttpHandlerOptions HttpHandlerOptions { get; }
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,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

@ -1,98 +1,99 @@
namespace Ocelot.UnitTests.DownstreamRouteFinder
{
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Configuration.Repository;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
public class DownstreamRouteFinderMiddlewareTests
{
private readonly Mock<IDownstreamRouteFinder> _finder;
private readonly Mock<IInternalConfigurationRepository> _repo;
private Response<DownstreamRoute> _downstreamRoute;
private IInternalConfiguration _config;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private readonly DownstreamRouteFinderMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
private readonly Mock<IMultiplexer> _multiplexer;
public DownstreamRouteFinderMiddlewareTests()
{
_repo = new Mock<IInternalConfigurationRepository>();
_finder = new Mock<IDownstreamRouteFinder>();
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_multiplexer = new Mock<IMultiplexer>();
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _repo.Object, _multiplexer.Object);
}
[Fact]
public void should_call_scoped_data_repository_correctly()
{
var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "");
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> {"Get"})
.Build();
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => GivenTheFollowingConfig(config))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_middleware.Invoke(_downstreamContext).GetAwaiter().GetType();
}
private void GivenTheFollowingConfig(IInternalConfiguration config)
{
_config = config;
_repo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(_config));
}
private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_finder
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{
_downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues);
_downstreamContext.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration);
}
}
}
namespace Ocelot.UnitTests.DownstreamRouteFinder
{
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Configuration.Repository;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
public class DownstreamRouteFinderMiddlewareTests
{
private readonly Mock<IDownstreamRouteProvider> _finder;
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
private readonly Mock<IInternalConfigurationRepository> _repo;
private Response<DownstreamRoute> _downstreamRoute;
private IInternalConfiguration _config;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private readonly DownstreamRouteFinderMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
private readonly Mock<IMultiplexer> _multiplexer;
public DownstreamRouteFinderMiddlewareTests()
{
_repo = new Mock<IInternalConfigurationRepository>();
_finder = new Mock<IDownstreamRouteProvider>();
_factory = new Mock<IDownstreamRouteProviderFactory>();
_factory.Setup(x => x.Get(It.IsAny<IInternalConfiguration>())).Returns(_finder.Object);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask;
_multiplexer = new Mock<IMultiplexer>();
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _factory.Object, _repo.Object, _multiplexer.Object);
}
[Fact]
public void should_call_scoped_data_repository_correctly()
{
var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build());
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> {"Get"})
.Build();
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => GivenTheFollowingConfig(config))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_middleware.Invoke(_downstreamContext).GetAwaiter().GetType();
}
private void GivenTheFollowingConfig(IInternalConfiguration config)
{
_config = config;
_downstreamContext.Configuration = config;
}
private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_finder
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{
_downstreamContext.TemplatePlaceholderNameAndValues.ShouldBe(_downstreamRoute.Data.TemplatePlaceholderNameAndValues);
_downstreamContext.Configuration.ServiceProviderConfiguration.ShouldBe(_config.ServiceProviderConfiguration);
}
}
}

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

@ -192,8 +192,9 @@
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{
_downstreamContext.ServiceProviderConfiguration = config;
{
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);
_downstreamContext.Configuration = configuration;
}
private void WhenICallTheMiddleware()

View File

@ -51,7 +51,7 @@ namespace Ocelot.UnitTests.Errors
[Fact]
public void NoDownstreamException()
{
var config = new InternalConfiguration(null, null, null, null);
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
@ -64,7 +64,7 @@ namespace Ocelot.UnitTests.Errors
[Fact]
public void DownstreamException()
{
var config = new InternalConfiguration(null, null, null, null);
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
@ -76,7 +76,7 @@ namespace Ocelot.UnitTests.Errors
[Fact]
public void ShouldSetRequestId()
{
var config = new InternalConfiguration(null, null, null, "requestidkey");
var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
@ -89,7 +89,7 @@ namespace Ocelot.UnitTests.Errors
[Fact]
public void ShouldSetAspDotNetRequestId()
{
var config = new InternalConfiguration(null, null, null, null);
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))

View File

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

View File

@ -117,7 +117,8 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheConfigurationIs(ServiceProviderConfiguration config)
{
_config = config;
_downstreamContext.ServiceProviderConfiguration = config;
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);
_downstreamContext.Configuration = configuration;
}
private void GivenTheDownStreamUrlIs(string downstreamUrl)

View File

@ -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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Middleware;
@ -16,6 +18,12 @@ namespace Ocelot.UnitTests.LoadBalancer
private NoLoadBalancer _loadBalancer;
private Response<ServiceHostAndPort> _result;
public NoLoadBalancerTests()
{
_services = new List<Service>();
_loadBalancer = new NoLoadBalancer(() => Task.FromResult(_services));
}
[Fact]
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])
};
this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenTheHostAndPortIs(hostAndPort))
@ -34,25 +43,43 @@ namespace Ocelot.UnitTests.LoadBalancer
[Fact]
public void should_return_error_if_no_services()
{
var services = new List<Service>();
this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort())
this.When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenThereIsAnError())
.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]
public void should_return_error_if_null_services()
{
List<Service> services = null;
this.Given(x => x.GivenServices(services))
this.Given(x => x.GivenServicesAreNull())
.When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenThereIsAnError())
.BDDfy();
}
private void GivenServicesAreNull()
{
_loadBalancer = new NoLoadBalancer(() => Task.FromResult((List<Service>)null));
}
private void ThenThereIsAnError()
{
_result.IsError.ShouldBeTrue();
@ -60,12 +87,11 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenServices(List<Service> services)
{
_services = services;
_services.AddRange(services);
}
private void WhenIGetTheNextHostAndPort()
{
_loadBalancer = new NoLoadBalancer(_services);
_result = _loadBalancer.Lease(new DownstreamContext(new DefaultHttpContext())).Result;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -25,8 +25,14 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_store_qos_provider_on_first_request()
{
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build();
{
var qosOptions = new QoSOptionsBuilder()
.WithKey("test")
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.Build();
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.Then(x => x.ThenItIsAdded())
@ -36,7 +42,13 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_not_store_qos_provider_on_first_request()
{
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build();
var qosOptions = new QoSOptionsBuilder()
.WithKey("test")
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.Build();
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute))
@ -47,8 +59,21 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_store_qos_providers_by_key()
{
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build();
var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("testTwo").Build();
var qosOptions = new QoSOptionsBuilder()
.WithKey("test")
.Build();
var qosOptionsTwo = new QoSOptionsBuilder()
.WithKey("testTwo")
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.Build();
var reRouteTwo = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptionsTwo)
.Build();
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.And(x => x.GivenThereIsAQoSProvider(reRouteTwo, new FakePollyQoSProvider()))
@ -62,7 +87,12 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_return_error_if_no_qos_provider_with_key()
{
var reRoute = new DownstreamReRouteBuilder().Build();
var qosOptions = new QoSOptionsBuilder()
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions)
.Build();
this.When(x => x.WhenWeGetTheQoSProvider(reRoute))
.Then(x => x.ThenAnErrorIsReturned())
@ -72,9 +102,24 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_get_new_qos_provider_if_reroute_qos_provider_has_changed()
{
var reRoute = new DownstreamReRouteBuilder().WithQosKey("test").Build();
var useQoSOptions = new QoSOptionsBuilder()
.WithTimeoutValue(1)
.WithKey("test")
.WithDurationOfBreak(1)
.WithExceptionsAllowedBeforeBreaking(1)
.Build();
var reRouteTwo = new DownstreamReRouteBuilder().WithQosKey("test").WithIsQos(true).Build();
var dontUseQoSOptions = new QoSOptionsBuilder()
.WithKey("test")
.Build();
var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(dontUseQoSOptions)
.Build();
var reRouteTwo = new DownstreamReRouteBuilder()
.WithQosOptions(useQoSOptions)
.Build();
this.Given(x => x.GivenThereIsAQoSProvider(reRoute, new FakeQoSProvider()))
.When(x => x.WhenWeGetTheQoSProvider(reRoute))