mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 14:10:50 +08:00 
			
		
		
		
	
							
								
								
									
										182
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								README.md
									
									
									
									
									
								
							@@ -1,91 +1,91 @@
 | 
			
		||||
[<img src="http://threemammals.com/images/ocelot_logo.png">](http://threemammals.com/ocelot)
 | 
			
		||||
 | 
			
		||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
 | 
			
		||||
[](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
 | 
			
		||||
 | 
			
		||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?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
 | 
			
		||||
 | 
			
		||||
[ 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)
 | 
			
		||||
 | 
			
		||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
 | 
			
		||||
[](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
 | 
			
		||||
 | 
			
		||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?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
 | 
			
		||||
 | 
			
		||||
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)}";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,6 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public interface IHttpHandlerOptionsCreator
 | 
			
		||||
    {
 | 
			
		||||
        HttpHandlerOptions Create(FileReRoute fileReRoute);
 | 
			
		||||
        HttpHandlerOptions Create(FileHttpHandlerOptions fileReRoute);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
    {
 | 
			
		||||
        ReRouteOptions Create(FileReRoute fileReRoute);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)}";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs
									
									
									
									
									
										Normal 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Ocelot/Configuration/LoadBalancerOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Ocelot/Configuration/LoadBalancerOptionsBuilder.cs
									
									
									
									
									
										Normal 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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>();
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
namespace Ocelot.DownstreamRouteFinder.Finder
 | 
			
		||||
{
 | 
			
		||||
    using Configuration;
 | 
			
		||||
 | 
			
		||||
    public interface IDownstreamRouteProviderFactory
 | 
			
		||||
    {
 | 
			
		||||
        IDownstreamRouteProvider Get(IInternalConfiguration config);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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");
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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],
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ namespace Ocelot.Requester
 | 
			
		||||
                handlers.Add(() => (DelegatingHandler)_factory.Get());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (request.IsQos)
 | 
			
		||||
            if (request.QosOptions.UseQos)
 | 
			
		||||
            {
 | 
			
		||||
                var qosProvider = _qosProviderHouse.Get(request);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -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}")
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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()))
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
 
 | 
			
		||||
@@ -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()))
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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>())))
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user